Skip to content

Releases: xiangwhy/sms-forwarder-esp32

v4.0.27 — review 14 finding 全修 + 2 patch

Choose a tag to compare

@xiangwhy xiangwhy released this 26 Jun 03:07

v4.0.27 (2026-06-26)

HEAD-wide 10-angle code review 翻出 14 P0 全修 + 2 个真机 patch。

改动

v4.0.26 review 14 finding 全修 (f887b34):

  • #1 loadConfig 6x strcpysnprintf(NVS 超长串缓冲溢出)
  • #2 cmgs drain 加 10ms delay(防双 core 抢 usbh_cdc buffer race)
  • #3 reserved-DCS sniff udHexdataHex(defense-in-depth)
  • #4 pdu_ud_offset_ex 内 sniff 跳 UDH concat IE 头
  • #5 cmgs_job gen counter 防 worker hang UAF
  • #6 web/config.h SSID full XSS escape(补 < > & ' 
 

  • #7 parse_udh get2 bounds + hex 校验
  • #9 /api/raw sanitizeForJson
  • #10 handle_restart 改 detached task
  • #11 7-bit numChars-=7 strstrparse_udh(防假命中)
  • #12 pdu_udh_offsetudhStart 参数(限定 IE pattern 扫描起点)
  • #13 cfgIsHardcoded 用 NVS cfg.provisioned 标记
  • #15 TX ref 8→16-bit(防 +CMS ERROR 331 重复 ref)
  • #14 REFUTED(/api/factory 算 OTA 范围不需 auth)

v4.0.26.1 (2c57777): 回退 #8 TON-aware valueOctets

  • 真机 Verify 号码显示 @@òD£ 乱码
  • ML307 oaLen 字段单位 = packed octets×2(非标准),BCD 公式碰巧正确

v4.0.26.2 (7846202): stash_udh_part UDHL+UDH 没跳过

  • True App 2 段 concat 解码出 P77ΔΘΣΘÄΠΣÄΠΘΞΔX ÆΣ Ψ 乱码
  • pdu_ud_offset_ex 返回 UD 段起始(UDHL 字节位置),不是 UD 数据起始
  • 修法:读 UDHL byte 算 (UDHL+1)*2 跳过 UDH

测试

  • host test 326/3 PASS(3 baseline reference FAIL 不动)
  • 真机验证:dtac 英文 OTP ✅ / Verify 泰文 OTP ✅ / True App 英文 concat OTP ✅

烧录

OTA: curl -X POST http://<device-ip>/update -F "file=@firmware.bin"

v4.0.24.1 — UCS-2 UDH skip + review fix (305/3 host test)

Choose a tag to compare

@xiangwhy xiangwhy released this 23 Jun 03:57

v4.0.24.1 — UCS-2 UDH skip + review fix

主改动 (3 commits, 1 spec)

  • 86df7b4 fix(sms): UCS-2/8-bit decode 路径 skip UDH (dtacIR concat 9 段 乱码修复) — main.cpp:1894 caller 加 pdu_udh_offset_ex
  • 2f8f32e fix(sms): UDH skip 加 udhi gate — 防单条 SMS 第一字节被误判 UDHL
  • ae61e72 chore: bump FW_VERSION v4.0.23 → v4.0.24

v4.0.24.1 review fix batch (commit 2014d05)

8 finding 全修 (3 high + 1 medium + 4 low cleanup):

  • Finding 1 (high)pdu_udh_offset_ex 加 ML307 stripped-UDHL guard (8-bit IEI=0x00 + 16-bit IEI=0x08 strict total/seq 验证)
  • Finding 2 (high)main.cpp:1971 7-bit fallback 也用 dataHex (跳过 UDH concat IE 头)
  • Finding 3 (medium)pdu_udh_offset_ex 加 strict total/seq 验证 (顺手修 finding 1 防 port IE 误判)
  • Finding 4 — 删 dead dataByteLen 变量 (-Wunused-variable)
  • Finding 5 — 加 ESP_LOGW 提示 truncated PDU / UDHL>140 / non-hex
  • Finding 6 — 删 pdu_udh_offset_ex UDHL re-parse 重复 (pdu_ud_offset_ex 已 parse)
  • Finding 8 — 加 fixture J (8-bit concat stripped) + K (16-bit concat stripped) TDD red

finding 7 (strstr('0804'/'0003') pre-existing latent bug) 留 v4.0.24+ 考虑。

验证

  • host test 305 PASS / 3 FAIL (D/E/F 仍 FAIL = v4.0.23 strict-DCS 撤回 历史 reference, 不动)
  • fixture J pre-fix got 38 (Ԁλँ prefix + 38 bytes), post-fix PASS (n=11 "Hello World")
  • fixture K pre-fix got 29 (丢前 3 bytes), post-fix PASS (n=11 "Hello World")
  • 已烧, 当前生产

约束守住

  • 0 改 STK / NVS / OTA / WiFi / push 路径
  • 0 改 7-bit 主路径 (numChars-=7 仍 strstr 检测, 跟 spec 决策一致)
  • 0 改 sniff helper (looks_like_ucs2_be / is_strict_utf8)
  • 0 改 stash_udh_part (用旧 pdu_udh_offset)
  • bump 严格 2 处 (FW_VERSION 宏 + 主页 app.h fw-tag)

v4.0.11.9 — NVS 0 noise + TWDT/stack 7 fix + TRUE 拼接 + 8MB 救砖

Choose a tag to compare

@xiangwhy xiangwhy released this 19 Jun 05:55

修复 (v4.0.6 → v4.0.11.9 五批 5 commit)

v4.0.6 — NVS / Web / Dashboard P0 批次 (commit 865402c)

  • NVS 找不到值 4 个 bug 批次:loadConfig 静默失败、AP 模式 dashboard 裸奔、按钮事件丢失、1970 时间戳
  • NTP 真实时间:接 esp_netif_sntp_init + ntp.aliyun.com,时区 CST-8,/api/status 新增 deviceTimeMs / timeSynced
  • 恢复出厂:/api/factorynvs_flash_erase() + restart 进 AP 模式
  • /send 历史清空 + WiFi 扫描下拉

v4.0.8 — PDU 解码 5 修 + 8MB 救砖 + 凭据硬编 (commit a14bf3b)

  • 8MB 救砖:board_upload.flash_size=8MB + 双 OTA partitions,efuse 实测 8MB 但老 config 当 4MB → bootloader 校验越界 boot loop
  • 凭据硬编占位符:YOUR_WIFI_SSID / YOUR_WIFI_PASS / YOUR_PUSHPLUS_TOKEN,首次烧必须 web 配网
  • PDU 解码 5 修:UCS-2 高半区 surrogate-pair 边界、7-bit UDL padding、phone 短号短长度 bug、SCA 长度越界、TP-DCS 0xC0/0xF8 表格
  • C++ host test 大扩:607 行新测试用例,绑定生产 pdu_codec.cpp 跑 (v3.6 Python 模拟弃用)

v4.0.9 — concat pdu_udh_offset 真定位 UDH 头 + nvsSentList 性能 (commit 3bc2798)

  • pdu_udh_offset 搜索范围 从 0 → UDH 起点,SCA 含 '0804'/'0003' 不再误识别为 concat IE
  • nvsSentList 性能:isKey("s0") 早返空 history(本批 v4.0.11.9 完整闭环)

v4.0.11.2 — TRUE 拼接 5 P0 批次 (commit 42c0a8a)

  • 字母数字 sender("True App") 长短信拼接:UDH 头 6 字节 IE 正确解析 (ref/total/seq),UDH 范围限定不再覆盖 SCA
  • TRUE 拼接边界 bug:中间 seq 缺失时不再误拼接
  • host test 增 148 行 覆盖 UDH 边界用例

v4.0.11.9 — NVS noise 0 + TWDT/stack 7 fix 批次 (commit d71debc)

  • TWDT 踢 cmgs_worker + net_task (v4.0.11.3):10-30s 长阻塞,5s TWDT 会触发 esp_restart;在 setup() 创完 task 后统一踢出 (task 体内 delete 报 "task not found")
  • nvsQNoteErr pcTaskGetName trace (v4.0.11.5):5s 节流日志加 task 名,定位刷屏源
  • nvsQSanityCheck lazy create pqueue (v4.0.11.6):工厂清 NVS 后 namespace 不存在 → readwrite 写 head=0 count=0,5s readonly begin 不再刷 NOT_FOUND
  • csq_poll stack 2048→4096 (v4.0.11.7):boot 35s 实测 overflow,4096 同 usb_rx
  • nvsEnsureNamespace 复用 (v4.0.11.8):lazy create cfg + sent namespace,防 loadConfig + nvsSentList 刷 NOT_FOUND
  • nvsSentList isKey("s0") 早返 (v4.0.11.9):isKey 走 nvs_get_str 不 log_e vs getString ESP_LOGE,空 history 不再 32 sX NOT_FOUND 刷屏

修复效果 (v4.0.11.9 实测)

  • ✅ 60s-once 32 sX NOT_FOUND noise: 从每次 32 行 → 0 行
  • ✅ 5s steady-state pqueue NOT_FOUND: 0 行
  • ✅ csq_poll boot 35s stack overflow: 修
  • ✅ TWDT 5s esp_restart: 修

未修 (后续批次)

  • 4 P0 (code-review-p0-pre-flash):
    • UAF on cmgs_send_pdu_async timeout
    • check_dashboard_auth 永久 true (6 个 POST 端点裸奔)
    • smsRateLimitOk 死代码 (return true + if (false && !...) 短路)
    • handleApiCfgPost 无输入验证 (空 SSID / token<16 / otaPass<4 都收)
  • 1 P1 PLAUSIBLE: pdu_udh_offset 搜索区域未限定 (本 v4.0.9 部分修, 待二次复查)
  • 1 死代码: g_cmgsJobSem 未初始化声明

验证

  • C++ host test: 86/86 pass(tests/host/,绑定 pdu_codec.cpp 生产代码)
  • 实机 v4.0.11.9 (boot #73): 60s NVS noise = 0,OTA 3.6s 烧完,板子稳定运行 4.6 min+

重要:首次使用请 web 配网

固件默认 WiFi/token 改为占位符 YOUR_WIFI_SSID / YOUR_WIFI_PASS / YOUR_PUSHPLUS_TOKEN,首次烧录后必须走 AP 配网页面(SMS-Forwarder-XXXXXX,密码 12345678,192.168.4.1)填写真实凭据。

如 NVS 已有配置(OTA 升级场景),配置直接保留,无需重新配网。

资产

文件 SHA-256 大小
v4.0.11.9.factory.bin 0d6a1a5323c310c10664388eba2918d1e192ab85bf082c488f9707f5146fadf5 1,280,576 bytes

构建来源

  • Commit: d71debc6ea9ed3de1f62e28fa584babc510e0ddd
  • 平台: pioarduino 55.03.39 (IDF 5.5 + Arduino-ESP32 3.x)
  • Flash: 1,280,576 bytes (38% of 3.3MB app partition, 8MB dual-OTA)

v4.0.11.21

Choose a tag to compare

@xiangwhy xiangwhy released this 19 Jun 13:14

v4.0.11.21 — 外层到内层间距 40→16px

改动

  • `.app-nav` margin-bottom: 16px → 8px
  • `.container` padding-top: 24px → 8px (3 处: dashboard/send/config 子页)
  • 总间距从 40px 减到 16px, 视觉更紧凑

累计 (含前 2 个 commit)

v4.0.11.19 (e86e2c2) — dashboard 6 P0 批次:

  • UI 顺序对调 (接收在前, 发送在后)
  • "最近发送" NVS 对齐 → 改纯 RAM ring (跟接收一样重启丢)
  • "最近接收 SMS" 加清空按钮
  • `/api/sent` 返新格式 `{uptimeMs, items:[{ageMs,phone,body,ok,err}]}`
  • `cmgs_send_pdu_async` 1s 分段 wait + 每段 reset WDT (修 async_tcp 5s TWDT)
  • 净删: nvsSentEnqueue/Clear/List (8.3KB flash)

v4.0.11.20 (6b290bf) — `.app-nav` 圆角卡片化:

  • `.app-header` 去背景 + 去 border-bottom
  • `.app-nav` 加 border + border-radius:12px + margin-bottom, 跟下面 .card 视觉一致

v4.0.11.21 (fccf3b2) — 外层到内层间距 40→16px

验证

  • Build: SUCCESS, 0 error
  • 实机: fw 字段 `v4.0.11.21`, 持续运行无 WDT panic
  • API: `/api/sent` 新格式 / `/api/sent/clear` / `/api/recent/clear` 全通过
  • 重启后 sent 为空 (NVS 不再写)
  • 浏览器硬刷 (Cmd+Shift+R) 看新间距效果

v4.0.5 — 泰文 DCS=0 误标修复 + BOOT 15s 回归

Choose a tag to compare

@xiangwhy xiangwhy released this 18 Jun 02:25

修复

  • task #37: 泰文 DCS=0 误标 — 部分运营商/网关对非 ASCII 短信使用 DCS=0(GSM 7-bit default)发送,但实际内容是 UCS-2 BE(如泰文 0E23 0E2B 0E31...)。现在 raw body sniff(高字节落在 BMP 高频区 + 至少 4 对字符 / 80% 命中)会强制走 UCS-2 解码,不再乱码 ÅF8Xb!øì*…
  • 7-bit UDH 长短信 — 已用 4 段 UDH 实测:Keep this code secure ...(English, ref 0, total 4, seq 1-4) → 推送成功。
  • DCS 表对齐 gammu GSM_GetMessageCoding — 0xC0/0xD0 (MWI GSM7)、0xF8-0xFB (8-bit) 修正,严格走 3GPP TS 23.040 §9.2.3.10。
  • BOOT_HOLD_MS 60000 → 15000 — 长按 15s 即可清 NVS(回归原设定)。
  • snprintf BUG — AP SSID 拼接格式串残留 %02X%02X%02X 但只剩 AP_SSID_PREFIX 一个参数,导致编译警告 + 不可预期 SSID。已修。

已知问题

  • 字母数字 sender(+CMT: "True App")的 4 段以上 UDH 拼接,可能丢段。手机号 sender 稳定。
  • RNDIS 4G 上网: v4.0.3 已禁用死路径,本版本不变。

重要:首次使用请 web 配网

固件默认 WiFi/token 改为占位符 YOUR_WIFI_SSID / YOUR_WIFI_PASS / YOUR_PUSHPLUS_TOKEN,首次烧录后必须走 AP 配网页面(SMS-Forwarder-XXXXXX,密码 12345678,192.168.4.1)填写真实凭据。

如 NVS 已有配置(OTA 升级场景),配置直接保留,无需重新配网。

验证

  • C++ host test: 45/45 pass(tests/host/,绑定 pdu_codec.cpp 生产代码)
  • 实机: 4G CMT (泰文 DCS=0 + 英文 7-bit UDH 4 段) + pushplus + AP 配网

资产

文件 SHA-256
v4.0.5.factory.bin fc70ba53d995a4661ff7663a168eb8b7a26047ffbe5121bd658d865083b06c65

构建来源

  • Commit: 38f79fada13b1a4c5f36e1504991927b40a3050e
  • 平台: pioarduino 55.03.39 (IDF 5.5 + Arduino-ESP32 3.x)
  • Flash size: 1,131,799 bytes (28.3% of 4MB app partition)

SMS Forwarder v4.0.2

Choose a tag to compare

@xiangwhy xiangwhy released this 13 Jun 11:44

v4.0.2 — 长短信拼接修复 + 短号显示修复

Bug 修复

  • 长短信被截断为多条推送: UDH 拼接 4 处 bug (commit abab923)
    • parse_udh 8-bit 分支误读 spec, ref/total/seq 偏移错位
    • stash_udh_part 8-bit udhSkip 14→12 (6 字节 UDH)
    • stash_udh_part strstr 锚点 body+1→body+2 (UDHL 占 2 hex)
    • clear_udh_ref 修 slot 复用死锁 (60s 超时后只清 received 不清 refId)
  • phone 短号显示乱码 (commit e482811)
    • decodePhoneField 纯数字短号 (如 "10086910") 被误判 UCS2 hex 解出 ဈ椐
    • 改 ASCII-safe 优先 (含数字/+/-/空格/字母 → 直接返回 raw)

新增

  • AT+CSDH=1 显式声明 (实测 no-op, ML307 默认已是 1)

实测

  • 2 段长短信 (ref 0x11/0x12/0x13, 泰文) → 1 条推送, body 干净
  • 短号 "10086910" (2 段, 中文 body) → phone=10086910 正确显示

已知风险

  • IRAM 100% 满 (15,356/16,384), 加 IRAM_ATTR 函数会链接失败
  • AT+CSCS="UCS2" 启动时仍超时 (实测 UCS2 数据仍正常输出, ML307 行为降级)
  • RNDIS 仍禁用 (v4.0 沿用)

升级说明

直接 pio run -t upload 烧录, NVS 配置不需重置, 启动次数/历史保留。

详细改动见 CHANGELOG

v4.0.1 — 开机上线通知

Choose a tag to compare

@xiangwhy xiangwhy released this 12 Jun 19:35

v4.0.1 — 开机上线通知

启动后自动推送一条"设备已上线"到 pushplus,内容包括:

  • FW 版本
  • 启动次数 (NVS 持久化, 长按 BOOT 5s 清 NVS 会重置)
  • 4G 模组状态
  • WiFi IP

变更

  • 新增 push_boot_notification()
  • g_bootCount 持久化到 NVS (key: stat/bootCount)
  • setup() 末尾自动调用,WiFi 未起则跳过

烧录

# 整包烧 (bootloader + partitions + app)
esptool.py --chip esp32s3 -p /dev/cu.usbserial-XXXX \
  write_flash 0x0 v4.0.1.factory.bin

完整变更见 v4.0。

v4.0 — 稳定版 (短短信 + pushplus + OTA)

Choose a tag to compare

@xiangwhy xiangwhy released this 12 Jun 19:17

v4.0 — ESP32-S3 SMS Forwarder

ESP32-S3 + USB 4G 模组 + WiFi pushplus 推送 SMS 转发器。

已验证 ✅

  • USB CDC 4G 模组 (iot_usbh_cdc 3.x, ML307 itf=2)
  • 收 +CMT 短信 (UCS2 BE 解码,中英泰日文都支持)
  • pushplus HTTPS 推送
  • NVS push 队列 (推失败落盘,30s 重发)
  • Web Dashboard (/dashboard,30s 自动刷新)
  • Web OTA (BasicAuth,密码存 NVS,首次配网时填)
  • AP 配网 (192.168.4.1,无密码或默认 12345678)
  • 3 LED 状态 (GPIO7=4G, GPIO15=WIFI, GPIO6=NET)
  • BOOT 长按 5s 清 NVS

技术栈

  • ESP-IDF 5.5 + Arduino-ESP32 3.x
  • pioarduino platform-espressif32@55.03.39
  • iot_usbh_cdc ^3.0
  • ESP32Async/AsyncTCP + ESPAsyncWebServer

烧录

# 整包烧 (bootloader + partitions + app)
esptool.py --chip esp32s3 -p /dev/cu.usbserial-XXXX \
  write_flash 0x0 v4.0.factory.bin

# 或 PIO
pio run -t upload --upload-port /dev/cu.usbserial-XXXX

已知问题 ⚠️

  • RNDIS 4G 上网:v4.0 禁用(无 stack_input 修复会 NULL deref panic),v4.1 修
  • 长短信 UDH 拼接:当 sender 是字母数字 ID + total > 2 段时可能丢段,临时方案用手机号 sender 测稳定
  • 数据用量统计 / ArduinoOTA:已砍,用 web OTA 替代

文档

完整使用见 README_v4.0.md