Files
vd_960/vd960DBN/docs/devlog.md
T
wangfq dc7dd1cb26 feat(iot_mqtt): 快速上报增加 car_state 翻转沿触发
- Step2: 任一通道 |variation|>=阈值 或 car_state 翻转沿, 均进入 300ms 快速档
  (高灵敏度档小车 variation 可能不过阈值, 但 Loop 已判翻转, 事件不应慢发)
- 修复首版两坑: if(fast_mode=0) 单等号致沿检测死代码;
  快照刷新移到间隔门控之后, 防止沿被门控吞掉后退化为空闲慢发
- gcc 隔离单测: buggy 沿后延迟 950ms vs fixed 250ms, 出车沿/负向variation回归通过
- devlog 补 2026-07-15 条目
2026-07-15 09:34:04 +08:00

344 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# vd960DBN 开发日志
> MCU: CH32V208 (RISC-V, WCH) | 通信: BLE + ETH (WCHNET) + UART2→Loop MCU | TCP JSON 端口: 5960
>
> 项目定位: DLD960 通信板 — BLE 配网、TCP JSON 协议服务、Loop MCU 串口桥接
---
## 2026-07-15 — MQTT 快速上报增加 car_state 翻转沿触发
### 背景
原 fast_mode 仅由 `|variation| >= 10` 触发。灵敏度设高档时小车信号弱,variation 可能不过阈值,但 Loop MCU 已判定 car_state 翻转——进/出车事件仍按空闲间隔(≥1s)慢发,后台看到的过车时刻误差大。故增加:**任一通道 car_state 翻转沿也触发 300ms 快速上报**。
### 实现要点(两个坑,首版都踩了)
1. **`if (fast_mode = 0)` 单等号** → 赋值恒假,沿检测死代码。修正为直接判断(循环内走到该行 fast_mode 必为 0,无需再判)。
2. **快照刷新必须在间隔门控之后**。若在门控前无条件刷新 `_last_car_state`,沿被门控吞掉(如距上次发布 <300ms)时快照已同步,下一轮检测不到翻转 → 事件退化为空闲间隔上报。修正:`return`(未到间隔)时不刷快照,沿保持"待发"持续顶住 fast_mode,直到真正发布才同步。
```c
/* Step 2 循环内: variation 阈值 与 car_state 沿, 任一命中即 fast */
if (av >= IOT_MQTT_VARIATION_THRESHOLD) { fast_mode = 1; break; }
if (_last_car_state[i] != coils[i].car_state) { fast_mode = 1; break; }
/* Step 3 门控通过后才刷新快照 */
for (i = 0; i < coil_count; i++) _last_car_state[i] = coils[i].car_state;
```
### 验证
本地 gcc 隔离单测(buggy vs fixed 对照):进车沿后 buggy 延迟 950ms(退化空闲间隔),fixed 250ms(≤300ms 快速档);出车沿、负向 variation 回归均通过。
⚠️ 上电首帧若已有车(`_last_car_state` 初值 0 vs car_state=1),会触发一次 fast_mode——属良性,首帧本就该尽快发。
---
## 2026-07-14 — Loop 上报 variation 解析升级 2B→3B 有符号 (协议 V1.05)
### 背景
Loop MCU 上报的 `SENS_MULTI_LOOP_DYNAMIC (0xC0/0x0C)` 中变化量 `variation` 由 2B 无符号扩展为 **3B 有符号补码**(详见 Loop 侧 devlog 与协议 V1.05)。DBN 作为接收/转发端,解析逻辑须同步升级,否则每通道单元 12B 会被按旧的 11B 步长错位解析,**4 通道数据全错**。
### 改动 (4 处)
**1. `loop_uart_proto.h` — 结构体字段类型**
```c
// uint16_t variation; -> int32_t variation; // 3B有符号, = Origin-CAPVD
```
**2. `loop_uart_proto.c` — 解析函数 `lup_parse_sensor_report`**
- 每通道单元步长 `i*11``i*12`
- 通道数计算 `data_len/11``data_len/12`
- 频率仍 3B 无符号;**变化量 3B 解码 + bit23 符号扩展**
```c
int32_t v = c[5] | ((int32_t)c[6] << 8) | ((int32_t)c[7] << 16);
if (v & 0x800000) v |= (int32_t)0xFF000000; // 符号扩展, 漏了负值会变~1600万大正数
cs->variation = v;
```
- 杂项偏移 `c[7..10]``c[8..11]`(整体右移 1B
**3. `iot_mqtt_srv.c` — fast_mode 阈值判断(隐藏坑)**
```c
// variation 变有符号后, 车进入(正)/反向漂移(负) 都应触发加速上报
int32_t av = (v >= 0) ? v : -v;
if (av >= IOT_MQTT_VARIATION_THRESHOLD) fast_mode = 1; // 原 variation>=10 会漏掉负向
```
**4. JSON 输出(`iot_mqtt_srv.c` / `tcp_json_srv.c`**
- `"diff":%d` / `"variation":%d` 格式符不变——CH32V208 上 `int32_t == int``%d` 天然正确输出负号,无需改动。
### 缓冲核查
整帧 52B→56BDBN 侧 `g_pkg_uart_2.pkg[BUFF_STACK_SIZE=512]``LUP_MAX_PKG_LEN=70` 均远大于 56B,接收无截断;转发 MSS=576 不分片。
⚠️ **必须与 Loop 固件同版本发布**(步长死绑定)。
---
## 2026-06-26 — TCP JSON 协议框架搭建
### 1. TCP JSON Server 基础实现
- WCHNET TCP listen socket 创建(端口 5960PROTO_TYPE_TCP
- 参考 WCH 官方 EVT/EXAM/ETH/TCPServer 例程
- `g_net_state.flag` 状态机: `ETH_LibInit→1, WCHNET_CreateUdpSocket→2, WCHNET_CreateTcpSocket→3`
- SSC 禁用时 (`NET_SSC_ENABLE=0`) 手动设 `flag=2` 跳过 UDP 初始化
### 2. 鉴权 + 15 条命令
- `pwd_verify` 鉴权,3次错误 → 60s 锁定
- 命令表驱动分发: `dev_info_query`, `ssc_net_set/query`, `iot_net_set/query`, `iot_topic_set/query`, `pwd_set`, `factory_reset`, `device_reset`, `loop_param_set/query`, `loop_version_query`, `loop_factory_init`, `loop_sens_read/write`
- Deferred 响应模式: Loop MCU 命令异步 → `TcpJsonPending` 挂起 → `json_check_pending()` 轮询
### 3. simple_json 解析器修复
- **6 个 bug 修复**: NULL 解引用崩溃(plain values)、缺少 null terminator、buffer unsafe clear、数组支持缺失
- 字符串值提取时**不带引号**(调用方无需手动 strip quotes
### 4. WCHNET TCP 踩坑
| 问题 | 修复 |
|------|------|
| listen socket 与数据 socket 混用 → 收不到数据 | CONNECT 发到 N, RECV/DISCONNECT 发到 N+1 |
| `WCHNET_SocketSend` 在 listen socket 静默失败 | 改用 `g_json_socket_listen + 1` |
| 接收缓冲区与帧缓冲区重叠 → 数据损坏 | 分离 WCHNET 内部 buf + 帧累加 buf |
| `#if NET_SSC_ENABLE` 误包共享函数 | 移出 `mStopIfError`/`GetMacAddr`/`get_ipstr_to_array` |
---
## 2026-06-30 — Loop MCU 串口协议 (0x7F) + 传感器上报
### 1. USART2 Loop MCU 通信
- 波特率 **192000**(文档写 115200,实际硬件 192000
- 0x7F 协议帧解析器: `lup_feed_byte()` 逐字节状态机 + LEN-based 帧边界
- 校验字节站位: `total_len = 5 + LEN`(CMD 已在 LEN 中,勿重复计)
- 命令: `lup_cmd_send()` 发送 → `lup_cmd_check_timeout()` 轮询超时
### 2. 0xC0 传感器数据上报
- Loop MCU 主动推送 0xC0 帧 → `lup_process_frame()` 校验 → 回调 `json_sensor_callback`
- TCP JSON 输出格式: `{"sens_type":"multi_coil","coils":[...]}`
- `g_report_active` 开关控制上报启停
- 0xC0 帧通过回调直接驱动网络上报,不经 `uart_srv` 阻塞
### 3. 0xC0 帧时间量字段
- `misc_type=0``passtime_ms`(通过时间 / 车间距,根据 `car_state` 区分)
- `misc_type=1``cut_amount`(线圈断开次数)
- `misc_type=2``flow_amount`(车流量)
- `misc_type=3``relay_count`(继电器动作次数)
### 4. 协议文档
- `docs/vd960_loop_protocol_v1.0x.md` — 0x7F 帧格式、校验算法、命令参考
- 波特率修正: 115200 → 192000
---
## 2026-07-01 — passtime_ms5 字段改名
`vd960Loop` 侧时间戳从 5ms 改 50ms 后,DBN 侧同步:
- `passtime_ms5``passtime_ms`(字段名去 5 后缀)
- `loop_uart_proto.h/c` 结构体 + 解析 + `tcp_json_srv.c` JSON 字段同步
---
## 2026-07-02~03 — TCP Server 超时自动重启机制
### 1. 三项超时触发条件
| 条件 | 时间 | 处理 |
|------|------|------|
| 无任何连接 | 5min | `do_restart` → 计数重启 |
| 无数据交互 | 5min | `do_restart` → 计数重启 |
| Auth 密码错 3 次 | 立即 | `tcp_json_restart()` 直接重启 |
### 2. 重启计数保护
- 最多连续重启 **3 次**,超过进入 **10 分钟冷却期**
- `CONNECT` 成功时 `restart_count` 清零(正常连接重置计数,不算异常)
- Auth timeout 不消耗计数器(正常运维行为)
### 3. WCHNET 限制 - listen socket 不可关闭重建
**根因**: `WCHNET_SocketClose(listen)` 后端口 5960 仍标记"已占用"`SocketCreat` 返回 `ERR_ISCONN(0x1D)`
**最终方案**: `tcp_json_restart()` 只关数据 socket(N+1) 断开客户端,listen socket 保持不动,仅重置应用层状态。WCHNET 自动处理下一个 CONNECT。
### 4. Auth timeout 死循环修复
原 Auth timeout 手工 `WCHNET_SocketClose` 后未设 `g_json_socket_listen = 0xFF`,下次 poll 条件仍满足 → 无限打印。改用统一的 `do_restart` 路径后修复。
### 5. Auth 3次失败 → 直接重启
去掉 60s Auth timeout 倒计时。改为连接后 3 次鉴权失败(密码错/格式错)即 `tcp_json_restart()`。连接后无交互由 5min 空闲检查覆盖。
---
## 2026-07-06 — MQTT IoT 基础打通
### 背景
在 CH32V208RISC-V, 栈仅 2KB)上实现 MQTT IoT 协议栈,对标 DBN101GA 参考项目。WCHNET TCP MSS=576, Publish 须 ≤500B。
### 1. MQTT 重构对标 DBN101GA
- `iot_mqtt_srv.c` 对标 DBN101GA 参考实现重写
- MQTT socket 复用 `SocketId_TCP`(而非独立 socket
- `iot_connect_broker``SourPort` 参数
- 出厂默认 topic 使用真实设备序列号
### 2. 栈溢出系列修复
| 问题 | 修复 |
|------|------|
| MQTT buffer 在栈上 → 溢出 | 改为 `static` 全局分配 |
| `iot_mqtt_publish_sensor`: `data_json[2KB] + payload[2KB] = 4KB` 远超 2KB 栈 | 减小缓冲区 + static 分配 |
| 缓冲区 2048 → 1024 | 单次交互不超 1KB |
### 3. MQTT CONNECT 延迟发送
**根因**: `MQTT_connect()` 在中断上下文调用 `WCHNET_SocketSend` → 崩溃。
**修复**: CONNECT 包延到 poll 轮询内发送,避免中断内调用网络 API。
### 4. MQTTPacket 初始化修复
`MQTTPacket_connectData_initializer` 是复合字面量,不能用于赋值。改用临时变量中转。
### 5. IoT socket 重复创建 + broker IP 解析修复
- IoT socket 重复创建 → 连接失败,改为复用已有 socket
- broker IP 字符串解析修正
---
## 2026-07-07 — MQTT 协议 V1.01 + 命令分发
### 1. MQTT 主题压缩:多主题 → 双主题
协议从 V1.00 的多 topic`dld960/{sn}/loop_data`, `/event_report`, `/heartbeat`, `/cmd`, `/cfg`…)压缩为 V1.01 双主题:
| 方向 | V1.00 | V1.01 |
|------|-------|-------|
| 设备→平台 | 5+ topics | `dld960/{sn}/dev` |
| 平台→设备 | 5+ topics | `dld960/{sn}/srv` |
### 2. MQTT 命令分发
`manage_mqtt_recv_message()` 实现 V1.01 协议命令分发,支持 `cmd` / `Method` 双格式:`pwd_verify`, `dev_info_query`, `loop_param_set/query`, `loop_version_query`, `loop_factory_init`, `device_reset`, `factory_reset` 等。
### 3. report_config 完整 7 参数
MQTT/TCP `report_config` 支持完整 7 参数配置:`period`, `loop_data`, `event_report`, `heartbeat`, `passtime`, `cut_amount`, `flow_amount`。与 TCP JSON 协议统一。
### 4. MQTT 网络配置
`ssc_net_set` / `iot_net_set` / `iot_topic_set` 三条命令实现,支持通过 MQTT 远程配置设备网络参数和主题。
---
## 2026-07-08 — MQTT 稳定性修复系列
### 1. g_iot_socket 未初始化 → 硬故障重启
`g_iot_socket` 声明为全局变量但未初始化,值为 `0xFF``WCHNET_SocketSend(0xFF)` 直接触发硬件故障重启。
**修复**: 初始化为 `INVALID_SOCKET`
### 2. loop_data JSON 超 MSS 导致 SocketSend 溢出
WCHNET TCP MSS=576,原 `loop_data` JSON 四通道全字段 ~800B → `WCHNET_SocketSend` 溢出崩溃重启。
**修复**: 精简 JSON 字段至 ~400B(单包达标),再**分批发送**(2通道/包 × 2包)。
### 3. iot_mqtt_send() hex dump 循环 → 栈溢出
调试用的 hex dump 循环(65次 `PRINT`)在仅 2KB 栈的 CH32V208 上直接栈溢出重启。
**修复**: 移除 hex dump 循环。
### 4. MQTT 主动上报无数据 + PINGREQ 缺失
`poll_mqtt()` 未正确触发 `MQTT_Yield()``MQTT_Live()`,导致主动上报无数据、PINGREQ 缺失断连。
**修复**: 改用 `net_srv.c` 统一的 `mqtt_publish()` + 修正 `poll_mqtt()` 调度逻辑。
### 5. 传感数据 topic 修正
topic 修正为 V1.01 双主题协议 `dld960/{sn}/dev`(此前遗留旧 topic 格式)。
### 6. 恢复 loop_data 完整字段
精简 JSON 后缺失部分字段(`freq`, `passtime_ms` 等),恢复完整字段并在分批框架内发送。
---
## 2026-07-10 — 双主题发布统一 + initialize 对齐 + packet_id 断连修复
### 1. 双主题发布统一 (dld960/{sn}/dev)
此前 heartbeat 和传感器上报仍使用旧 topic 路径(`dev/status``g_iot_topic.topic_pub`),与 V1.01 双主题模型不一致。
**修复**: 所有 MQTT 发布统一为 `dld960/{sn}/dev`
- `iot_mqtt_srv.c` heartbeat: `iot_make_topic(..., "dev", "status", ...)``snprintf(..., "dld960/%s/dev", ...)`
- `iot_mqtt_srv.c` iot_mqtt_publish_sensor: `g_iot_topic.topic_pub``dld960/{sn}/dev`
- `net_srv.c` dev_initialize_pub: `g_iot_topic.topic_pub``dld960/{sn}/dev`
### 2. initialize 消息格式对齐 V1.03
`dev_initialize_pub()` 原先缺少顶层 `model`/`hard_ver`/`soft_ver` 字段,且在 `extra_info` 中有已废弃的 `version` 字段。
**修复**: 对齐协议文档 §5.1
- 补充 `model`(PRODUCT_MODEL)、`hard_ver`(HARDWARE_VER)、`soft_ver`(FIRMWARE_VER) 到 `data` 顶层
- 移除 `extra_info.version`
- 缓冲区扩容 256→512 字节
### 3. mqtt_publish() packet_id=0 导致 broker 断连 🔴
**现象**: 设备收到查询指令后正确发布响应 JSON,10ms 后 broker 主动断开 TCP 连接(SockInt stat=0x10)。
**根因**: `net_srv.c``mqtt_publish()` 调用 `MQTTSerialize_publish()``packet_id` 硬编码为 `0`。当 `req_qos=1`(命令响应使用 QoS 1)时,MQTT 规范要求 `packet_id ≠ 0``0` 属于协议违规,broker 检测后直接踢掉连接。
**修复**: 新增 `static uint16_t s_mqtt_pkt_id` 计数器,QoS>0 时自增作为 packet_idQoS=0 保持 0。
```c
static uint16_t s_mqtt_pkt_id = 0;
uint16_t pkt_id = (req_qos > 0) ? ++s_mqtt_pkt_id : 0;
```
### 4. DBNMQTTool 同步更新
- 协议版本注释 V1.01→V1.03
- 协议 Topic 树 + 模拟页增加 `initialize` 支持
- `device_manager.mark_online()` 扩展 `model`/`hard_ver`/`soft_ver` 参数,设备列表正确显示型号
- 增加 `dld960/+/dev/#` 通配订阅兼容旧固件多级 topic
---
## 修订记录
| 版本 | 时间 | 说明 |
|------|------|------|
| V3.2 | 2026-07-10 | 双主题发布统一 + initialize 数据格式对齐 V1.03 + mqtt_publish packet_id=0 断连修复 |
| V3.1 | 2026-07-09 | V1.03: 订阅后发 initialize 上线消息; iot_mqtt_srv 订阅改双主题 |
| V3.0 | 2026-07-08 | MQTT 稳定性修复: socket初始化/buffer溢出/分批发送/hex dump/PINGREQ |
| V2.9 | 2026-07-07 | MQTT V1.01 双主题协议 + 命令分发 + report_config 7参数 |
| V2.8 | 2026-07-07 | MQTT 网络配置: ssc_net_set / iot_net_set / iot_topic_set |
| V2.7 | 2026-07-06 | MQTT IoT 基础打通: 对标DBN101GA + 栈溢出修复 + CONNECT延迟发送 |
| V2.6 | 2026-07-06 | IoT 模式配置 bug 修复 |
| V2.5 | 2026-07-03 | tcp_json_restart 只关数据socket, listen保持不动 (WCHNET限制) |
| V2.4 | 2026-07-03 | 去掉 Auth 60s timeout, 改为3次失败即重启 |
| V2.3 | 2026-07-03 | Auth timeout 不计入重启限额, 修复冷却期阻断连接 |
| V2.2 | 2026-07-03 | Auth timeout 死循环打印修复 → goto do_restart |
| V2.1 | 2026-07-03 | TCP Server 自动重启机制 (3条件 + 3次限额 + 10min冷却) |
| V2.0 | 2026-07-01 | passtime_ms5 → passtime_ms 字段改名 |
| V1.5 | 2026-06-30 | 0xC0 传感器上报 + 时间量字段完善 + 协议文档 |
| V1.4 | 2026-06-30 | USART2 Loop MCU 0x7F 协议实现 |
| V1.3 | 2026-06-30 | simple_json 6 bug 修复 |
| V1.2 | 2026-06-30 | WCHNET buffer 分离 + SocketSend listen→data 修正 |
| V1.1 | 2026-06-30 | NET_SSC_ENABLE 隔离 + #if guard 共享函数修复 |
| V1.0 | 2026-06-26 | TCP JSON 协议框架 (鉴权 + 15条命令) |