docs(vd960DBN): 开发日志 — V3.4 今日MQTT栈重构完整记录

This commit is contained in:
wangfq
2026-07-23 15:22:29 +08:00
parent 8f1bbf75f8
commit bdc2720d96
+71 -1
View File
@@ -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 断连修复 |