Commit Graph
56 Commits
Author SHA1 Message Date
wangfq 181467b9e6 feat(vd960DBN): MQTT 协议 V1.10 固件实现 — 网络上报携带地感版本 (2026-08-21)
协议 V1.10 (3be9e50) 三处新增落地:
1. dev_info_query 响应 data 补 loop_ver/loop_hw_ver (0x4A 缓存值, 未完成/失败为空串)
2. initialize 上报同样携带 (尽力, 可为空)
3. 新增 §4.25 loop_version_query: 0x4A 异步实时查询, 响应在 iot_verq_poll() 回包

固件:
- loop_uart_proto.h/c: 新增 LoopVerCache + g_lup_ver_cache (0x4A 缓存);
  lup_cache_from_info() 写缓存, lup_refresh_version_cache() 后台刷新 (通道空闲才发)
- iot_mqtt_srv.c: dev_info_query/initialize 加字段; loop_version_query 分支
  (暂存 msg_id/deadline 300ms, busy 拒绝重复查询); iot_verq_poll() 异步回包
  (RESPONSE_READY -> code=0 + 三字段 + 同步刷缓存; TIMEOUT -> code=5 保缓存值);
  后台刷新: 上电 iot_mqtt_init + OTA done (iot_ota_report_result ok=1) 置 dirty

关键设计: 版本来源=0x4A 实时查询 (非 OTA 元数据 version=目标版本);
缓存值语义 dev_info_query/initialize 同步回包不等异步; 与 TCP 共用 g_lup_cmd
单命令通道 (后发覆盖, 先发者超时 — 低频命令, 待板上验证时序)

单测: tests/test_iot_loop_ver.c — gcc 隔离 (extract+embed 6 函数 + mock)
44 断言全过: 版本帧解析(正常+4 错误路径) / 缓存写入+格式化(空串/NULL 防护) /
verq_poll 成功回包(code=0+三字段+topic+缓存刷新+通道释放) / 超时回包(code=5+缓存值) /
后台刷新(发起/忙不消费/响应消费/残留超时清理)

验证: check_c_balance.py 3 文件 OK; 待板上验证 UART2 0x4A 与 loop_data 共用总线时序
2026-08-21 08:26:11 +08:00
wangfq 51298693da fix(vd960DBN): OTA 刷写成功误判未启动 — 状态回 idle + 补 ota_report 主动上报
现场: 远程平台发起 OTA, 设备物理刷写成功 (70块全ACK, flash DONE),
但平台 ota_status 轮询只见 state=ready → 误判'刷写未启动'。

根因:
1. ota_flash_done 刷写成功后台侧状态置 READY (与协议状态机
   'FLASHING──成功──▶IDLE(清槽/保留)' 不符), 平台无法区分
   '待刷 ready' 与 '已刷完' → 持续 ready 判未启动
2. 固件漏实现协议 §5.5 ota_report 主动上报 (done/failed 重发 3次×5s),
   平台收不到成功信号, 只能靠 ota_status 轮询兜底

修复 (协议 V1.09 + 固件):
- 协议: 刷写成功状态明确回 idle (镜像保留, last_result=0, 可重刷);
  ota_report 升级为刷写结果主依据; 平台判定指引 (idle+size>0+last_result=0
  =成功; 不得以轮询未见 flashing 或持续 ready 判未启动)
- ota_srv.c: ota_flash_done → OTA_STATE_IDLE (镜像保留) + 上报 done;
  ota_flash_fail → 补 ota_report failed (保留 event_report 告警);
  ota_cmd_begin 兼容 idle+size/crc32 一致 → 直接回 ready (免下载重刷)
- iot_mqtt_srv.c: 新增 ota_report 上报状态机 (立即首发 + 5s×3 重发,
  同 msg_id/ts), 共享 _iot_pub_payload (event_report 复用, RAM 零新增)
- 单测: test_flash_flow/test_flash_ready_timeout 断言 ota_report 上报,
  state 断言 READY→IDLE; 新增 test_begin_reflash; 10/10 全过 + 22 py 断言
2026-08-20 18:56:21 +08:00
wangfq c305ca651f fix(vd960DBN): OTA 下载失败 — hex 提取兼容 json.dumps 空格
现象: DBNMQTTool 第一片 ota_data code=1 crc/gap, received=0, 重试全败

根因: 工具 json.dumps 默认带空格 ("data": "hex"), 设备端 strstr 找
"data":" 无空格格式 → 提取失败 → hexbuf 空 → 解码失败 code=1
(simple_parse_json 容错空格所以其他命令正常, 只有手工 strstr 踩坑)

修复:
- ota_srv.c/h: 新增 ota_json_extract_hex() 兼容有无空格 (扫描 data 键跳过空白)
- iot_mqtt_srv.c: ota_data 改用该函数, 删 strstr 手工提取 + hp 残留
- 单测 +test_json_extract_hex: 无空格/带空格(事故场景)/512hex/找不到 4 场景

验证: 单测 11/11 PASS; 语法 0 错误; 待板级重测下载
2026-08-20 14:13:06 +08:00
wangfq 49736c341c feat(vd960DBN): Loop MCU 远程 OTA 固件实现 (MQTT V1.08, ROADMAP P1.4 ①)
先存后刷: MQTT 分片下载 → W25Qxx OTA 暂存区 (0x010000 512KB) → 全镜像 CRC32
复核 → 本地 0x9F ISP 透传刷写 Loop MCU (AT32F421)

- 新增 ota_srv.c/h: CRC32(ISO-HDLC 逐位零表)/OtaMeta 双备份(节流 4KB flush)/
  会话状态机(begin/data/end/abort/flash)/本地刷写状态机(非阻塞 tick: A5→A6→A7
  停等 ACK 1s×3)/安全窗口(有车拒绝 force 跳过)/失败 event_report ota_error
- iot_mqtt_srv.c: 6 个 ota_* 命令分发 + 会话静默(event_report 积压/offlog·快照
  落盘暂停, 结束补发) + IOT_EVT_OTA_ERROR + iot_any_car
- usart_biz.c: uart_srv OTA 分支 ACK 分发 (本地刷写→ota_flash_feed_ack, BLE 透传不变)
- peripheral_main.c: ota_init + ota_poll 挂主循环
- offlog: OTA_START 0x60/OTA_RESULT 0x61 审计事件
- tests/test_ota_srv.c: gcc 隔离单测 9 组全过 (mock NOR/UART2/时钟, 嵌入真实实现)
  抓到 2 个真 bug: ①A7 序号未递减(bootloader 等不到末包) ②重发时 retry 清零(超时永不失败)
- 语法检查: ota_srv/iot_mqtt_srv/usart_biz/offlog 0 新增错误 (基线 interrupt 假阳性除外)
- devlog 置顶条目 + 待板上验证清单
2026-08-20 12:09:13 +08:00
wangfq d4ce0ba23a chore(vd960DBN): 固件版本 1.02.03 → 1.02.04 (SPI 存储适配)
内容: SPI Flash 识别去厂商代码 + 恢复 factory 配置写入 (板级验证通过)。
README/CHANGELOG/devlog 同步; 手册/规格书配套版本更新 (文档保持 V1.02, 同日补充不升版)
2026-08-19 15:23:35 +08:00
wangfq 832d2953b0 chore(vd960DBN): 固件版本 1.02.01 → 1.02.03 (2026-08-19)
OTA 0x9F 透传修复板级验证通过, 用户更新版本号三段式一致。
V1.02.03 内容: BLE→Loop OTA 修复 (0x9F 帧透传回 BLE) +
8-18 脱机日志快照流/hex 上报 + MQTT 查询命令补齐。
README/CHANGELOG/devlog 同步; 已知约束: OTA 模式无自动退出 (维持现状)
2026-08-19 11:20:03 +08:00
wangfq d67f955fd4 fix(vd960DBN): BLE→Loop OTA 失效修复 — 0x9F 帧透传回 BLE
DMA 改造(fed4335)后 UART2 RX 走 lup_feed_byte 只认 0x7F, Loop bootloader
回的 0x9F 响应帧(pre_ok/addr_ok/data ACK)全被吞; OTA 是停等协议
(0xA7 WITH_BACK 每块必回 ACK), 工具等不到 ACK 升级必然卡死。

- loop_uart_proto: 新增 lup_feed_byte_ota() 0x9F 帧状态机 (SUM 校验无 XOR,
  复用 g_lup_parser), 枚举追加 OTA 专用状态
- usart_biz: uart2_dma_poll 按 g_flag_counter_ota.flag 切换 0x9F/0x7F 解析器,
  模式切换 reset, OTA 溢出阈值放宽整缓冲, 补收帧 tick 归零;
  uart_srv OTA 分支 0x9F 帧透传回 BLE + 清 flag
- 单测 tests/test_lup_ota_parser.c 8 断言全过 (bootloader 真实帧向量)
- 遗留: g_flag_counter_ota.flag 无退出机制, 升级后需断电重启
2026-08-19 11:01:45 +08:00
wangfq 5a1893cd1c feat(vd960DBN)+fix(DBNMQTTool): log_query 改 hex 原始字节上报 (2026-08-18)
背景: MQTT 快照流实测 MQTTSerialize_publish failed — JSON 化快照记录 ~810B/条 超 800B 发送缓冲
方案(用户拍板): 对齐 BLE 通道, 原始字节 hex 上报

固件 (V4.3):
- offlog.c/h: 新增 offlog_evt_to_hex() (32B→64 hex)
- snapshot.c/h: 新增 snap_rec_to_hex() (64B→128 hex); 删 SNAP_MAX_QUERY_JSON, 恢复 count=2
- tcp_json_srv.c / iot_mqtt_srv.c: log_query 改 {"seq":N,"hex":"..."}; SEND_BUF 保持 800
- 2 条快照 hex 响应 406B < 800B

文档: TCP JSON V1.03 / MQTT V1.07 §4.17 records 改 hex + 解析表引用 BLE §6.4/§7

工具: parse_offlog_hex/parse_snap_hex/offlog_payload_desc + hex 展示; 验证: gcc 9 断言 + 工具解析全过 + offscreen UI
2026-08-18 14:04:26 +08:00
wangfq f1c9358aad feat(vd960DBN): TCP/MQTT log_* 命令支持快照流 stream=snapshot (2026-08-18)
- snapshot.c/h: 新增 snap_rec_to_json() — SnapRec 64B → JSON (channels 对齐 0xC0, variation 3B 符号扩展, misc_type 全枚举)
- tcp_json_srv.c: handle_log_stat/query/clear 加 stream 解析 + snapshot 分支
- iot_mqtt_srv.c: log_stat/query/clear 加 stream 解析 + snapshot 分支
- 事件流 count=0 按上限处理 (与 BLE 对齐); 快照 QUERY count≤2, CLEAR ~45ms
- 新增 gcc 隔离单测 tests/test_snap_to_json.c 34 断言全过
- devlog V4.1
2026-08-18 09:01:22 +08:00
wangfq ae9f5eaf4c chore(vd960DBN): 固件版本 1.02.01 (三段式) + cmcng.h 注释修复 (2026-08-17)
用户更新:
- FIRMWARE_VER "1.02.01" (MAIN=1 SUB=2, 新增 FIRMWARE_VER_SUBSUB=1)
- 注释修复: GBK 乱码注释恢复中文 (文件 GBK→UTF-8 编码转换, 中文注释正常)
- 注意: BLE 上报仍为 MAIN/SUB 两字节 (1.02), SUBSUB 仅字符串上报使用
2026-08-17 17:19:40 +08:00
wangfq 263dc4e05d chore(vd960DBN): 固件版本 1.0 → 1.1 (2026-08-17)
cmcng.h FIRMWARE_VER "1.0"→"1.1" (MAIN=1 SUB=1):
- 修正字符串与数字不一致 (旧 "1.0" vs MAIN=1 SUB=1)
- 版本内容: 栈溢出修复 + RAM 瘦身 + UART2 RX DMA + 清理
- 注释用 ASCII (cmcng.h 为 GBK 编码)
2026-08-17 16:55:51 +08:00
wangfq cb63c581be perf(vd960DBN): 二轮RAM瘦身 — RECE_BUF_LEN 1024 + ARP 16 + 中断路径static (2026-08-17)
.map 分析: BLE 栈固定占 RAM 低 16KB 不可裁; 用户 48KB 内 .bss 41.7KB。
- net_config.h: RECE_BUF_LEN MSS×2(1400)→1024 (MSS=700帧+头≈740B, 5数组省~2.6KB)
- net_config.h: WCHNET_NUM_ARP_TABLE 50→16 (停车场景IP少, 省~0.8KB)
- tcp_json_srv.c: tmp_buf[RECE_BUF_LEN]/frame[800] 局部→static (中断上下文栈数组防溢出)

栈 4.7KB→~6.3KB, 中断栈峰值 -1.8KB。devlog 记录 .map 分析结论。
2026-08-17 14:26:22 +08:00
wangfq 421334ec48 perf(vd960DBN): RAM 瘦身 — .bss 省 4.8KB, 栈 1.9KB→~6.7KB (2026-08-17)
栈溢出根因确认后 .bss 瘦身四刀:
- net_config.h: ETH_MAX_PACKET_SIZE 1520→768 (MSS=700最大帧758B, 省3.76KB)
- iot_mqtt_srv.c: payload[1400]→800 (loop_data最大~604B, 省600B)
- iot_mqtt_srv.h: IOT_MQTT_SEND_BUF_LEN 1024→800 (_iot_send_buf/resp/buf 同步, 省224B)
- net_srv.c: MAX_MQTTBUF_LEN 1024→800 (512仍不够勿回改, 省224B)

⚠ ETH 768 为 MSS=700 最小安全值; 若现场有大UDP包截断改回1024。
待办: .map 确认 BLE 栈占用; RECE_BUF_LEN/ARP 表可再省; 局部大数组转static。
2026-08-17 14:10:52 +08:00
wangfq 8cf01544f6 feat(vd960DBN): 快照区延后初始化(开机3s后) — 避开启动早期SPI重负载窗口 (2026-08-17)
诊断: 08-14恢复快照区后频繁重启。RSTSCKR无复位标志(非硬件复位) +
BOOT_CNT恒=1(.noinit被清, RAM全丢级) + 实验A(跳过offlog_boot)仍崩
→ 启动早期SPI重负载(offlog/snap init 擦+整扇区写回~80ms×2)让系统
进入临界状态, 后续SPI操作(load_cfg读参数区)成为压垮点。

方案:
- snapshot.c/h: 新增 snap_delayed_init(), 主循环每轮调用, 开机3s后
  首次进入执行 snap_init (非阻塞, mstick()>=3000 门控)
- peripheral_main.c: main() 移除 snap_init 调用; 主循环喂狗后加
  snap_delayed_init(); 撤销实验A(恢复offlog_boot)/实验B(恢复load_cfg)
- 3s内传感帧由 snap_enqueue/snap_flush 的 !_ready 门控自动丢弃不落盘
- 保留 BOOT_CNT 诊断打印(.noinit 计数器+地址)
- devlog 置顶 2026-08-17 条目
2026-08-17 10:31:39 +08:00
wangfq 8873b334d2 feat(vd960DBN): 恢复快照区功能, 移除 sim_snap_spi 实验桩, UART2 波特率注释修正
- peripheral_main.c: snap_init() 恢复 (去 #if 0), offlog_boot() 恢复
  (上电复位原因记入事件日志); 删除 sim_snap_spi 模拟写密度实验函数
  及主循环调用 (printf 重入修复后 SPI 写无罪已确诊, 实验收尾)
- loop_uart_proto.h/c: UART2 波特率注释 115200/19200 -> 192000
  (usart_biz.c 硬编码 192000 为实际值; g_storage_uart_baud=19200
  为独立配置项不驱动 UART2, BLE 显示 19200 不代表实际速率)
- devlog: 新增 2026-08-14 条目, UART2 RX DMA 方案列入待办
2026-08-14 09:46:23 +08:00
wangfq 4bdf8a7993 fix(vd960DBN): IWDG 无条件启用+喂狗 — 卡死兜底 (卡死定位闭环)
现场: printf 重入修复后乱码消失, 但 LUP 帧处理卡死(连 JSON:
sensor_cb enter 都没打印), 且 iot_enable=0 模式 IWDG 未初始化
(iot_mqtt_poll 才喂狗, tcp_json 分支不喂) → 卡死永久冻结无兜底。

修复:
- main 无条件 iot_watchdog_init() (IWDG 4s 超时)
- 主循环 while 开头无条件 iot_watchdog_kick() (原只在 iot_mqtt_poll)
- json_sensor_callback / iot_evt_sensor_cb 加 FAULT_MARKER(MK_EVT_CB_IN/OUT)

效果: 卡死 → 4s IWDG 复位 → 上电 FAULT_DIAG 打印 marker 定位卡死点
2026-08-13 16:15:26 +08:00
wangfq 9f5102e9e8 feat(vd960DBN): fault_diag — .noinit RAM 现场 + 执行轨迹 marker 定位神秘复位
现象: LUP 帧后复位, RST=0x00000000 (无标志) 且 HardFault 打印缺失
(上次 0x10000000=SFT 确认 HardFault 路径, 这次连打印都没有)
方案: .noinit 段复位不清零, 跨复位留痕
- Link.ld: 新增 .noinit 段 (startup 只清 .bss 不碰)
- fault_diag.h: FaultDiag{magic,mcause,mepc,mtval,marker,boot_cnt} + FAULT_MARKER 轨迹宏
- HardFault_Handler: 纯 RAM 写 mcause/mepc/mtval (官方 __get_MCAUSE/MEPC/MTVAL,
  不依赖 UART/printf, 栈坏也能留痕) + 尽力打印
- main 开头 fault_diag_init(): 打印上次现场 + 递增 boot_cnt
- 轨迹 marker: 主循环 LOOP_TOP/UART_SRV/SIM_SPI/POLL_BLE + LUP 链
  LUP_FRAME/INGEST/EVT_FEED 入口出口, 复位后看 marker 停在哪一步
2026-08-13 15:44:07 +08:00
wangfq eadc67584f fix(vd960DBN): 复位原因位定义错位修正 — RST_REASON=0x08000000=PORRSTF
现场: RST_REASON 打印 0x08000000, 对照 WCH ch32v20x.h 发现
offlog.h 位定义整体错位 (IWDG=bit31实为bit29, SFT=bit24实为bit28,
POR=bit25实为bit27, LPWR=bit29实为bit31)
结论: 0x08000000=PORRSTF=上电/掉电复位 → 电源跌落方向 (SPI擦除电流脉冲?)
修正:
- offlog.h 位定义对齐 WCH 库, 新增 OFFLOG_RST_RMVF=bit24
- peripheral_main.c RMVF 清除改 bit24 (原写 bit27=PORRSTF 只读位,
  复位标志从未清除, 历史累积污染诊断)
- 协议文档 boot 事件位解析同步
测试回归全过
2026-08-12 18:58:16 +08:00
wangfq 3d4814bffe fix(vd960DBN): init/clear 懒擦修复不断重启 — 34s 全量擦除超 IWDG 4s
现场: 烧录快照固件后 CH32V208 不断重启 + 串口乱码
根因: snap_init 首擦 751 扇区 ~34s (头在擦完才写→复位后仍全新→死循环);
      snap_clear 34s / offlog_clear 2.8s 同类; 均超 IWDG 4s
修复(懒擦): init/clear 只擦写指针起点扇区 ~45ms, 其余由环形写切扇区
      逻辑自动擦; clear 为逻辑清除 (count=0 旧数据不可读)
附带: usart_biz.c 非 0x7F 帧 %s 打印改 hex (Loop 数据当字符串=乱码源)
      snapshot.h 线程模型注释修正 (lup_process_frame 实际在主循环 uart_srv)
测试: test_snapshot 9例(新增 lazy_erase) + offlog/ble_offlog 回归全过
2026-08-12 18:31:56 +08:00
wangfq 92887a3d92 feat(vd960DBN): 传感快照区落地 snapshot.c + BLE SNAP_STAT/QUERY/CLEAR
- snapshot.h/c: 64B 定长 SnapRec (头部16B + 4x12B 0xC0线圈数据原样)
  环形分区独立于事件日志, 快照区 = 总容量 - 固定区576KB - 事件区
- 线程模型: USART2 ISR 只打包+RAM暂存(8深满丢新), 主循环 snap_flush 落盘
  (iot_sensor_ingest 在中断上下文, SPI 45ms 擦除严禁进中断)
- dbn_ble_srv: SNAP_STAT(0x28)/SNAP_QUERY(0x29)/SNAP_CLEAR(0x2A)
  QUERY 上限 2 条 (64x2+2=130B, ODR 教训防御)
- iot_mqtt_srv: ingest 挂 snap_enqueue, publish_sensor 挂 snap_flush
  (放在 READY 检查前, 断网照常落盘)
- peripheral_main: offlog_init 后加 snap_init
- 清空审计: 写事件流 log_clear payload[0]=2
- 测试: test_snapshot.c 8 例全过, offlog/ble_offlog 回归全过
- 文档: DLD960_BLE协议 V1.02 + ROADMAP P1.2 状态 + devlog
2026-08-12 17:40:14 +08:00
wangfq a8120cc852 feat(vd960DBN): 事件日志区动态分区 — JEDEC ID 选档 (ROADMAP 落地)
- offlog.h: OfflogPart 运行时分区表 + g_offlog_part; AREA_BASE 改 0x090000
  (参数64KB+OTA512KB); 容量宏展开为运行时值(主体零改动)
- offlog.c: offlog_part_detect() 读 JEDEC ID → 事件区容量
  W25Q32=512KB/16256条, Q64=1MB/32640, Q128=2MB/65408, Q256=4MB/130944
- storage.c/h: SPI_Flash_ReadJEDEC_ID() (0x9F, 校验 EF 40)
- 旧固件事件区 0x010000 自动废弃(新地址无 magic → 重建), 预期行为
- 测试: mock JEDEC=0x16 + g_offlog_part, 15 例全过
- 文档: BLE/MQTT/TCP 协议 capacity 8064→动态, 交互示例, devlog
2026-08-12 16:46:19 +08:00
wangfq d6174b9d3f feat(vd960DBN): BLE 读取脱机日志 — OFFLOG_STAT/QUERY/CLEAR (0x25/0x26/0x27)
蓝牙通道补齐脱机日志读取 (此前仅 MQTT V1.06 / TCP JSON V1.02 有 log_* 命令):
- 3 条 BLE 命令, 语义对齐 MQTT log_stat/log_query/log_clear
- QUERY 直接传 32B OfflogEvt 原始结构 (二进制协议, 无需 JSON), 复用
  idx = start_seq - seq_first 定位, 不新增 offlog API
- 缓冲扩容: MAX_BLE_TMP_BUF_LEN 100→132, 新增 MAX_BLE_DAT_BUF_LEN=132
  (QUERY 响应 1+4x32B=129B), clear_buf_dbn_ble_all memset 同步
- 新协议文档 docs/DLD960_BLE协议.md V1.00 (帧格式+分包+命令+记录结构)
- 隔离测试 test_ble_offlog.c 嵌入源文件 3 case 真实文本, 7 例全过;
  offlog 回归 8 例 ALL PASS
- devlog V4.0; README 协议矩阵补 BLE 行
2026-08-10 14:24:20 +08:00
wangfq 1a01316c7f feat(vd960DBN): offlog 协议导出命令分发 — log_stat/log_query/log_clear 落地
协议先行 (08893d5, MQTT V1.06 + TCP JSON V1.02) 的固件侧实现:
- offlog 新增导出 API: enabled/seq_last/type_str/evt_to_json
  (seq_first=seq_last-count+1, idx=start_seq-seq_first, count≤4)
- MQTT iot_handle_publish 三分支: log_stat/log_query/log_clear
  (log_query 响应 static buf 防大栈; log_clear 阻塞~2.8s 主循环可接受)
- TCP JSON 三 handler + cmd 表 (需鉴权)
- 单测新增 test_export_json (8 例全过): boot rst 大端/coil sub/evt_retry/data:null/seq 公式
- devlog V3.9
2026-08-05 08:53:18 +08:00
wangfq 2b1e888a73 refactor(vd960DBN): tcp_json_srv data_json[2048] 宏化 → TCP_JSON_DATA_BUF_LEN(1024)
三处 char data_json[2048] (sensor_report×2 + loop_param_query×1)
改用头文件宏 TCP_JSON_DATA_BUF_LEN, 默认 1024。

容量核验: format_sensor_json 4通道~660B / format_loop_param_json~920B
均 < 1024, format 函数有 snprintf 溢出保护; 最终帧受 MAX_FRAME=800 截断。
附带: 栈上缓冲 2048→1024 每处省 1KB
2026-08-04 17:06:29 +08:00
wangfq 5155219c92 fix(vd960DBN): 修复 offlog 编译错误 — 声明补齐 + RCC 宏移入公共头
MRS 编译报错:
1. offlog.c: SPI_Flash_Erase_Sector 隐式声明 → storage.h 补声明
2. peripheral_main.c: OFFLOG_RCC_RSTSCKR 未声明 → RCC 宏从 offlog.c
   移到 offlog.h (peripheral_main.c 只 include offlog.h)
3. offlog.c 删除本地重复 RCC 宏定义

单测重跑 ALL PASS
2026-08-04 16:51:53 +08:00
wangfq ac3fb512d5 feat(vd960DBN): 脱机事件日志系统 (W25Q32 环形) — 解决重复上线取证
背景: 设备挂平台测试出现重复上线(现场未断电), 无本地日志无法区分
设备真复位 vs MQTT 断连重连。iot_handle_suback 每次重连都发
initialize 是重复上线的直接证据, 日志需能区分二者。

实现 (V3.6, ROADMAP P1.2 事件流先行):
- offlog.c/h: W25Q32 256KB 环形事件日志 (头扇区+63数据扇区, 8064条)
  - 32B 定长记录: magic/type/len/flags/seq/ts_ms/unix_ts/boot_seq/payload[12]
  - 8 类事件: BOOT(复位原因)/IOT_CONNECT/READY/DISCONN/RECONN/
    EVT_RETRY/GIVEUP/COIL/TIME_ANCHOR/LOG_CLEAR
  - 掉电恢复: 头扇区写指针锚点 + 上电 seq 连续性扫描
  - 编译期断言防 32B padding 回归
- 插桩 (全部主循环上下文, socket 中断内不写 SPI):
  - main(): offlog_init + RCC_RSTSCKR 复位原因采集/清除
  - iot_mqtt_poll(): MQTT 状态沿检测 (CONNECT/READY/DISCONN)
  - 重连退避/TCP超时/CONNACK拒绝: RECONN/DISCONN(4)/(3)
  - iot_evt_process/enqueue: EVT_RETRY/GIVEUP/COIL
  - net_srv.c dev_time_sync: TIME_ANCHOR 时钟同步锚点
- tests/test_offlog.c: gcc 隔离单测 6 例 (mock W25Q32 NOR 语义)

关键坑: ①中断内写SPI阻塞 ②结构体36B padding致环形错乱
③扇区级覆盖粒度count扣减语义 (均单测抓出)

待办: P1.3 导出命令(log_query/stat/clear) + 快照流 + 复位原因板上验证
2026-08-04 15:04:12 +08:00
wangfq 8f1bbf75f8 refactor(vd960DBN): 看门狗精简 — 去掉平台响应超时, 只保留硬件 IWDG
heartbeat 是单向设备上报, 平台不回复; 用 '3×心跳无平台回复→重启'
逻辑不成立。保活依赖三层已有机制:
  MQTT层: PINGREQ/PINGRESP (60s) → broker不回 → TCP超时
  TCP层:  KeepAlive → WCHNET SINT_STAT_DISCONNECT/TIMEOUT
  硬件层: IWDG (4s) → 主循环卡死 → 硬件复位
2026-07-23 15:21:59 +08:00
wangfq 127fed4b07 feat(vd960DBN): 硬件 IWDG 看门狗 + 平台响应超时重启
设计:
  1. 硬件 IWDG: LSI≈40kHz, Prescaler=256, Reload=625 → ~4s 超时
     主循环 iot_mqtt_poll() 每轮喂狗, 防死循环/硬故障

  2. 平台响应看门狗: 3×心跳周期(180s)内无平台回复 → NVIC_SystemReset
     _iot_last_recv_ms 刷新点:
     - iot_handle_publish() — 收到平台任意 PUBLISH 命令
     - iot_evt_handle_ack() — event_report 的 ACK
     - iot_handle_suback() — 刚连上 READY 时初始化计时

  3. 仅在 IOT_STATE_READY 时检查 (未连上不触发)
2026-07-23 14:49:07 +08:00
wangfq 199cb5d23f config: WCHNET_TCP_MSS 576→700, TCP_JSON_MAX_FRAME 4096→800
- WCHNET_TCP_MSS=700: 减少 TCP 分段, 配合 iot_mqtt_send 循环重试
  → RECE_BUF_LEN 自动扩至 1400 (原 1152)
- TCP_JSON_MAX_FRAME=800: 匹配实际 JSON 帧大小
2026-07-23 14:30:47 +08:00
wangfq d61ff830aa fix(vd960DBN): BUFF_STACK_SIZE 512→64 对齐 Loop 侧串口帧缓冲
- 串口帧缓冲装的是 Loop↔DBN 的 0x7F 私有协议帧, 与 Loop 侧发送缓冲(64)同源
- 当前最大帧 0x0C 多线圈上报实测 56B (Len=51), 64 留 8B 余量, 512 严重浪费
- g_pkg_uart_1/2 各省 448B, 合计回收 ~896B SRAM
- 顺带删除零引用死结构体 USART_DMA_UNIT + RX_BUFFER_LEN 宏
2026-07-16 14:52:16 +08:00
wangfq 3e28174b40 feat(mqtt): 设备时钟同步方案B — 平台经 report_config 下发 Unix ts (协议V1.05)
设备无RTC/SNTP, 原上行 ts=mstick()/1000(上电秒数)。方案B:
- net_srv.c 新增 dev_time_sync()/dev_time_now(): 门槛≥1600000000挡上电秒数,
  已校准返回真Unix, 未校准退回上电秒数; 无符号相减处理mstick 49.7天回绕
- manage_mqtt_recv_message 从下行命令信封 ts 校准 (report_config主同步点)
- 全部上行 ts (14处命令响应+initialize+loop_data+event_report) 改 dev_time_now()
  heartbeat uptime 字段保留 mstick()/1000 (运行时长非时间戳)
- 协议 V1.05 §2.3 时间同步 + report_config 兼任同步点职责
- tests/test_dev_time_sync.c 6组全过

平台侧配合: 收到 initialize 后下发 report_config 时 ts 填当前 Unix 时间
2026-07-15 15:25:44 +08:00
wangfq e3eaa111fe fix(mqtt): 修复 loop_data>512B 溢出发垃圾包致 broker RST 重连风暴 (现场P0)
根因(非平台推测的环形缓冲/event_report队列):
  mqtt_publish 的 mqttBuf=512B < loop_data(4通道)604B
  → MQTTSerialize_publish 返回 -2 不写 buf
  → len 为 uint32_t, -2 变 42.9亿
  → WCHNET 把清零缓冲+越界相邻全局(temp_guide=report_config→report_c)
    当 520B 垃圾包发出 → broker 见非法类型0x00 RST → 重连风暴

修复(net_srv.c):
  - MAX_MQTTBUF_LEN 512→1024 (容纳 604B loop_data)
  - mqtt_publish: len uint32→int + 守卫 if(len<=0)return (序列化失败绝不发残缓冲)
  - keepalive 9→60 (报告建议, 9s过激)

event_report 协议违规修复(iot_mqtt_srv.c):
  - 重连重发保持原 msg_id/ts (V1.04 §5.3-2), 不再作废换号致平台去重失效

验证: tests/test_mqtt_publish_overflow.c 复现旧垃圾包+验证守卫/大缓冲;
      tests/test_event_report.c T8 断言重连同 msg_id. 均全过.
遗留: ts=上电秒数非Unix时间戳, 待定方案(设备无RTC/SNTP)

Refs: docs/incidents/2026-07-15-DC045A49718F-protocol-error.md
2026-07-15 14:58:03 +08:00
wangfq 4112b7cbbf feat(iot_mqtt): 实现 event_report 平台必答+设备重发 (协议 V1.04); 时间单位统一 50ms
时间单位:
- 串口协议 V1.06 / TCP JSON 协议: 杂项时间量、事件 value 由 5ms 修正为
  50ms, 对齐 Loop 固件 50ms tick 实现 (TMR15 5ms×10)

event_report (iot_mqtt_srv.c + net_srv.c):
- 沿检测双路汇聚: Step1 消费路径 + lup 回调覆盖 uart_srv 路径, 不漏帧
- 16 深环形队列, 事件仅 ACK(code=0) 后出队; 溢出丢最旧
- 5s 超时重发同 msg_id/原始 ts ×3 次, 耗尽挂起, 新事件/重连沿合并补报
- 帧消费提前至 READY/enable 之前: 断网期间事件入队, 重连补报
- 线圈断开期间屏蔽 car 沿; loop_restore value=本地计时/50
- msg_id 独立 uint32 计数 (g_iot_msg_id 为 uint8 混用会破坏去重窗口)
- net_srv.c ACK 路由: cmd=event_report 回显帧确认出队, 不回 unsupported
- gcc 隔离单测 8 组全过 (tests/test_event_report.c), 单包 6 条实测 317B
2026-07-15 12:27:22 +08:00
wangfq bfc9646e48 proto(V1.05): variation 由 2B无符号 扩展为 3B有符号
背景: variation 是遥测量, 用于离线跟踪分析车检器算法。原 2B 无符号
存在两个缺陷: (1)CAPVD域量级~131072, 大车全覆盖时偏差可超65535而回绕,
污染数据; (2)绝对值丢失方向, 分不清'车进入'与'反向漂移'。

改动 (三端联动, 同版本发布):
- 协议 DLD960Loop_串口通信协议.md: 变化量 2B->3B有符号补码, 定义
  variation=Origin-CAPVD (正=车/裕量, 负=反向漂移); 每单元11->12B,
  Len 47->51; 补符号扩展说明; 示例报文重算(XOR=FE SUM=38); 修订记录V1.05
- vd960Loop main.c: 组包 uint32->int32, Origin-CAPVD, 3B LE, +-2^23饱和防回绕
- vd960DBN loop_uart_proto.h: variation uint16->int32
- vd960DBN loop_uart_proto.c: 步长11->12, data_len/11->/12, misc偏移右移1B,
  variation 3B解码+bit23符号扩展
- vd960DBN iot_mqtt_srv.c: fast_mode 阈值判断改 abs(variation)>=10, 正负变化都加速

验证: 本地 gcc 隔离单测 编解码往返/符号扩展/饱和/方向语义/fast_mode 全通过;
大车 diff=100000 旧版回绕34464 新版正确保留。字节账: 帧56B < Loop缓冲64B
< DBN缓冲512B < MSS576, 均不溢出。整体固件编译需在 Keil/MounRiver 侧完成。
2026-07-14 14:47:39 +08:00
wangfq 652fc5f938 feat: V1.03 initialize 上线消息 — 固件+工具同步
vd960DBN:
- dev_initialize_pub() 改为 V1.03 格式 (msg_id/cmd/ts/data/extra_info)
- iot_mqtt_srv: 订阅改双主题 dld960/{sn}/srv, 响应 topic 改 dev
- SUBACK 后自动发 initialize 告知服务器上线
- net_srv.h: 加 dev_initialize_pub 声明

DBNMQTTool:
- protocol.py: 加 CMD_INITIALIZE
- device_manager: DeviceInfo 加 extra_info, 加 mark_online()
- main.py: 接收 initialize 消息, 自动标记设备上线
- docs: 协议 V1.03 + devlog V3.1
2026-07-09 21:32:39 +08:00
wangfq c515638032 fix: MQTT协议 V1.02 同步 — 字段名缩短 + 时间单位修正
- docs/DLD960_IoT_MQTT协议.md: 字段缩写 + 时间单位 5ms→50ms
- vd960DBN/iot_mqtt_srv.c: 6字段缩短对齐协议 (level/iscar/freq/diff/sens/cndtn)
- vd960DBN/tcp_json_srv.c: gap_ms5/passtime_ms → gap/passtime
- vd960DBN/loop_uart_proto.h: 注释 5ms→50ms
- DBNMQTTool/main.py: 模拟样本数据字段同步
2026-07-09 10:18:03 +08:00
wangfq 8425b473d3 fix: net_srv.h 添加 mqtt_publish() 声明供 iot_mqtt_srv.c 调用 2026-07-08 16:12:20 +08:00
wangfq a7de4dfcde feat(vd960DBN): MQTT/TCP report_config 支持完整7参数配置
- 新增 ReportConfig 结构体 (sensor_type/enable/once/env_eval/interval/ack_required/timeout)
- net_srv.c: MQTT report_config handler — 查询/设置均返回完整配置JSON
- tcp_json_srv.c: TCP JSON handle_report_config 同步升级,兼容旧 active_report
- iot_mqtt_srv.c: g_report_active → g_report_cfg.enable
- g_report_active 全局变量替换为 g_report_cfg 结构体
2026-07-07 18:39:45 +08:00
wangfq ab3a4cea0a fix: 出厂默认 topic 使用真实设备序列号 + PRINT 修正
- cfig_flash.c: factory_dev_info() 用 gMacAddr 生成真实主题 dld960/{SN}/srv + dld960/{SN}/dev
- net_srv.h: TOPIC_DEFAULT 加注释标明为格式模板
- net_srv.c: 修复上次提交的 PRINT 宏转义异常
2026-07-07 10:57:20 +08:00
wangfq b707241429 feat: MQTT 双主题同步到 vd960DBN 固件 + DBNMQTTool devlog
vd960DBN 固件改动:
- net_srv.h: 默认 topic 改为 dld960/{sn}/srv + dld960/{sn}/dev
- net_srv.c: dg_subscribe_display_topic() 直接用 topic_sub 订阅(不再追加 client_id/sn 后缀)
- net_srv.c: mqtt_deserialize_publish() topic 过滤改用 g_iot_topic.topic_sub(不再硬编码 TOPIC_DEFAULT_SUBSCRIBE)

DBNMQTTool 文档:
- devlog.md: 清理过期 topic 引用语义
2026-07-07 09:46:54 +08:00
wangfq 220f778117 fix(vd960DBN): 删除重复 poll_mqtt 定义 + 加头文件声明 2026-07-06 17:30:28 +08:00
wangfq 884a622cab refactor(vd960DBN): 缓冲区 2048→1024,单次交互不超 1KB
- IOT_MQTT_RECV_BUF_LEN: 2048 → 1024
- IOT_MQTT_SEND_BUF_LEN: 2048 → 1024
- data_json 改用宏
- iot_handle_publish 的 json[] 改 static 防栈溢出
2026-07-06 13:47:10 +08:00
wangfq e6dbdb5296 fix(vd960DBN): 修复无法设置 IoT 模式的 bug 2026-07-06 10:24:15 +08:00
wangfq 6f5effae8c fix(vd960DBN): iot_mqtt_srv compilation errors
- Add extern g_report_active (from tcp_json_srv.h)
- Fix iot_make_topic call: 4-arg -> 5-arg (add NULL for 'status' sub)
- Fix SOCK_INF field names: DestPort->DesPort, DestAddr->IPAddr
- Fix struct _WCHNET_IPAddr -> uint8_t broker_ip[4] (type not available)
2026-07-03 18:12:55 +08:00
wangfq 5333fe40de refactor(vd960DBN): replace compile-time NET_SSC/NET_JSON/NET_IOT macros with runtime g_sub_code_enable.iot_enable
- Remove NET_SSC_ENABLE, NET_JSON_ENABLE, NET_IOT_ENABLE from net_config.h
- Socket config always allocates max: 1 UDP + 2 TCP + 1 TCP_LISTEN
- All #if guards in net_srv.c/h and peripheral_main.c replaced with
  runtime g_sub_code_enable.iot_enable checks
- iot_enable=0 -> SSC mode (UDP + TCP client + JSON server)
- iot_enable=1 -> IoT MQTT mode (MQTT client via iot_mqtt_srv)
2026-07-03 18:07:32 +08:00
wangfq 9629729dc9 feat: IoT MQTT 客户端实现 — NET_SSC_ENABLE=0 时启用
新增文件:
- iot_mqtt_srv.h/c: MQTT 客户端 (TCP→CONNECT→SUBSCRIBE→READY)
  支持: dev_info_query, pwd_verify (其他命令框架预留)
  上报: loop_data (传感器), heartbeat (心跳 60s)
  断线重连: 指数退避 5s~60s
  协议: DLD960 IoT MQTT V1.00 (dld960/{sn}/...)

修改:
- net_config.h: 新增 NET_IOT_ENABLE = !NET_SSC_ENABLE,
  IoT 模式分配 1 TCP socket (MQTT client)
- net_srv.c: IoT 初始化 + socket 中断路由
- peripheral_main.c: IoT poll + sensor publish 调用

模式:
  NET_SSC_ENABLE=1 → SCC + TCP JSON Server (原有行为)
  NET_SSC_ENABLE=0 → IoT MQTT Client (新增)
2026-07-03 17:36:22 +08:00
wangfq 5966eb8657 feat: TCP Server 自动重启机制 — 3种超时条件
触发条件(任一满足即重启):
1. 5分钟无任何连接 (从未连或上次连接后超时)
2. 5分钟无数据交互 (已连接但静默)
3. Auth 密码错误3次

重启流程: 关闭socket→清状态→重新SocketCreat+SocketListen
保护机制: 最多连续重启3次, 超限后进入10分钟冷却期

其他改动:
- tcp_json_srv_init 去掉 _init_done 限制, 支持重新初始化
- 新增 tcp_json_restart() 函数
- 新增 g_json_last_comm_ts / g_json_last_connect_ts 追踪
- CONNECT 时清零 restart_count (正常连接重置计数器)
2026-07-03 11:46:17 +08:00
wangfq d9059fac92 rename: passtime_ms5 → passtime_ms 2026-07-03 11:08:53 +08:00
wangfq 2163b89d28 feat: 协议 V1.04 — 主动上报增加继电器输出次数 (misc_type=3)
- loop_uart_proto.h: LUP_CoilSensor.misc 联合体新增 relay_count
- tcp_json_srv.c: format_sensor_json 处理 misc_type=3 → relay_count
- docs: 更新协议文档至 V1.04
2026-07-02 16:50:06 +08:00
wangfq 6acd788d13 fix: 0xC0 帧通过回调直接驱动网络上报
loop_uart_proto:
- 新增 lup_sensor_cb_t 回调类型 + lup_set_sensor_callback
- lup_process_frame 收到 0xC0 → 调用注册的回调推送数据

tcp_json_srv:
- json_sensor_callback: 检查 g_report_active → 解析 → TCP 发送
- tcp_json_srv_init: 注册回调

usart_biz:
- uart_srv: 0xC0 由回调直接 TCP 推送,BLE 连接时也转发 BLE
- 移除旧的 _report_flag 轮询路径

数据流: ISR → lup_process_frame(校验) → json_sensor_callback → WCHNET_SocketSend
2026-07-02 14:15:28 +08:00