diff --git a/vd960DBN/docs/devlog.md b/vd960DBN/docs/devlog.md index b62cfdc..5bb796e 100644 --- a/vd960DBN/docs/devlog.md +++ b/vd960DBN/docs/devlog.md @@ -509,7 +509,76 @@ loop_data JSON 中间嵌入了完整的 MQTT PUBLISH 二进制帧 (event_report ### 修复 | 改动 | 说明 | -|------|------| +|------|------ + +## 2026-07-23 — MQTT 栈重构 + 硬件看门狗 + +### 问题背景 + +vd960DBN 现场出现三类 MQTT 上报异常: +1. **_raw 异常**: loop_data JSON 中间嵌入 MQTT PUBLISH 二进制帧 +2. **频繁 initialize**: 每 4 秒重连, 旧 MQTT 栈与 IoT 栈抢 socket +3. **SocketSend hard fault**: MQTT CONNECT 发送后设备重启 + +### 根因链 + +| 问题 | 根因 | 修复 commit | +|------|------|-------------| +| `_raw` | ① `data_json[1024]` 与 `mqttBuf[1024]` BSS 重叠 → 消灭 data_json | e11a80c | +| `_raw` 仍出现 | ② `mqtt_publish()` 用全局 mqttBuf, event_report 二进制污染 loop_data | 086dbc6 | +| | → 改用 `iot_mqtt_publish()`, buf[1024] 与 mqttBuf 物理隔离 | | +| 频繁 initialize | ③ `WCHNET_HandleSockInt` 调旧 `mqtt_connect()` + IoT 栈也发 CONNECT → 双 CONNECT | 3d017cc | +| | → socket 事件全委托给 `iot_mqtt_handle_sock_int`, 旧栈切断 | | +| SocketSend 重启 | ④ `iot_mqtt_send()` 里 `uint16_t slen` 传 `uint32_t*` → 栈破坏 hard fault | a577537 | +| SocketSend 还重启 | ⑤ `WCHNET_ModifyRecvBuf(_iot_wchnet_buf)` 覆盖原生 SocketRecvBuf → DMA 非法 | 9521a5e | +| | ⑥ `tmp[1152]` 栈变量在中断上下文撑爆 2KB 栈 → 改 static | | +| SocketSend 再重启 | ⑦ WCHNET SocketSend 在中断外调用 hard fault → 改回中断内发 CONNECT | 32f0edb | +| `_raw` 再次出现 | ⑧ `WCHNET_SocketSend` 部分发送 520/607B → 残留拼到下一帧 | 9e83063 | +| | → `iot_mqtt_send` 改为 while 循环重试 | | +| 命令处理缺失 | ⑨ `report_config`/`event_report` ACK 无人处理 → code=4 | 67976ec | +| msg_id 跳号 | ⑩ 心跳 `++g_iot_msg_id` + `iot_mqtt_publish` 双递增 | a9c36e4 | + +### 三层保活 + +``` +┌─ MQTT 层: PINGREQ/PINGRESP (60s) → broker 不回 → TCP 超时 +├─ TCP 层: KeepAlive → WCHNET SINT_STAT_DISCONNECT/TIMEOUT +└─ 硬件层: IWDG (LSI≈40kHz, Prescaler=256, Reload=625 → ~4s) + 主循环 iot_mqtt_poll() 每轮喂狗 +``` +> heartbeat 是单向设备上报, 平台不回复; MQTT PINGREQ/PINGRESP 才是双向保活机制。 + +### 最终架构 + +``` +WCHNET_HandleSockInt (net_srv.c) + └─ iot_enable? → SocketRecvBuf(原生) + KeepLive + iot_mqtt_handle_sock_int() + ├─ CONNECT → iot_mqtt_send_connect() [中断内] → MQTT_CONNECTING + ├─ RECV → _iot_recv_buf → iot_process_recv() + │ ├─ CONNACK → iot_mqtt_send_subscribe() → SUBACK → READY + │ ├─ PUBLISH → iot_handle_publish(report_config/event_report ACK/...) + │ └─ PINGRESP → (MQTT 库自动处理) + └─ DISCONNECT → g_iot_state=DISCONNECTED → 重连退避 + +Main_Circulation: + iot_mqtt_publish_sensor() ← 0xC0→loop_data + event_report + iot_mqtt_poll() ← IWDG喂狗 + 状态机 + PINGREQ(60s) +``` + +### 发送缓冲隔离 (v3) + +``` +event_report → mqtt_publish() → mqttBuf[1024] (net_srv.o BSS) +loop_data → iot_mqtt_publish() → buf[1024] (iot_mqtt_srv.o BSS) +heartbeat → iot_mqtt_publish() → buf[1024] +initialize → iot_mqtt_publish() → buf[1024] +``` + +### msg_id 统一 + +所有发布点用 `g_iot_msg_id + 1` 预读, `iot_mqtt_publish` 内部 `++g_iot_msg_id` (MQTT pktid for QoS>0, 对QoS=0 也递增以保持一致)。 + +---| | 消灭 `data_json[1024]` | 直接在 `payload[1400]` 构建完整 JSON, 省 1024B BSS | | 消除 `%s` 拷贝 | 不再从中间缓冲格式化到 payload, 杜绝尾部二进制残留路径 | | `coil_count` 硬限 | `if (coil_n > 4) coil_n = 4` — 防 0xC0 坏帧致 snprintf 循环溢出 | @@ -521,6 +590,7 @@ loop_data JSON 中间嵌入了完整的 MQTT PUBLISH 二进制帧 (event_report ## 修订记录 | 版本 | 时间 | 说明 | +| V3.4 | 2026-07-23 | MQTT 栈重构: 发送缓冲隔离/命令处理/msg_id统一/看门狗, 共10项修复 | | V3.3 | 2026-07-23 | loop_data: 消灭 data_json[1024] BSS 缓冲 + coil_count 硬限, 防 mqttBuf 重叠致 _raw 异常 | |------|------|------| | V3.2 | 2026-07-10 | 双主题发布统一 + initialize 数据格式对齐 V1.03 + mqtt_publish packet_id=0 断连修复 |