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: 模拟样本数据字段同步
This commit is contained in:
@@ -100,7 +100,7 @@ typedef struct {
|
||||
uint32_t freq; // 频率 (3 bytes, little-endian)
|
||||
uint16_t variation; // 变化量 (2 bytes, little-endian)
|
||||
union {
|
||||
uint32_t passtime_ms; // 通过时间/车间距, 5ms单位 (misc_type=0)
|
||||
uint32_t passtime_ms; // 通过时间/车间距, 50ms单位 (misc_type=0)
|
||||
uint32_t cut_amount; // 线圈断开次数 (misc_type=1)
|
||||
uint32_t flow_amount; // 车流量数 (misc_type=2)
|
||||
uint32_t relay_count; // 继电器输出次数 (misc_type=3)
|
||||
|
||||
@@ -386,72 +386,59 @@ void iot_mqtt_publish_sensor(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Batch-publish: 2 channels per message to keep each under 512 bytes.
|
||||
Full-field JSON (freq_current, freq_diff, condition, misc) per the protocol doc. */
|
||||
static char data_json[1024]; // single buffer for 2-channel JSON
|
||||
static char payload[1024];
|
||||
char *pub_topic = (char *)g_iot_topic.topic_pub;
|
||||
uint8_t batch_start = 0;
|
||||
/* 全通道一次性上报,字段按 DLD960_TCP_JSON协议.md §5.1:
|
||||
ch, freq_level, has_car, loop_ok, freq_current, freq_diff,
|
||||
sensitivity, condition, misc.type, misc.value
|
||||
注意: mqttBuf 需 ≥ 1024 才能容纳 4 通道全字段封包 (~846 字节) */
|
||||
static char data_json[1024];
|
||||
static char payload[1400];
|
||||
char *p = data_json;
|
||||
int remaining = sizeof(data_json);
|
||||
int written;
|
||||
const char *freq_level_names[] = {"high", "mid_high", "mid_low", "low"};
|
||||
uint8_t i;
|
||||
|
||||
while (batch_start < sr.coil_count) {
|
||||
char *p = data_json;
|
||||
int remaining = sizeof(data_json);
|
||||
int written;
|
||||
uint8_t batch_end = batch_start + 2;
|
||||
if (batch_end > sr.coil_count) batch_end = sr.coil_count;
|
||||
written = snprintf(p, remaining, "{\"channels\":[");
|
||||
if (written < 0 || written >= remaining) goto done;
|
||||
p += written; remaining -= written;
|
||||
|
||||
for (i = 0; i < sr.coil_count; i++) {
|
||||
const LUP_CoilSensor *cs = &sr.coils[i];
|
||||
const char *misc_type_str = "time";
|
||||
uint32_t misc_val = 0;
|
||||
|
||||
if (cs->misc_type == 0) { misc_type_str = "time"; misc_val = cs->misc.passtime_ms; }
|
||||
else if (cs->misc_type == 1) { misc_type_str = "cut_count"; misc_val = cs->misc.cut_amount; }
|
||||
else if (cs->misc_type == 2) { misc_type_str = "flow_count"; misc_val = cs->misc.flow_amount; }
|
||||
else if (cs->misc_type == 3) { misc_type_str = "relay_count"; misc_val = cs->misc.relay_count; }
|
||||
|
||||
written = snprintf(p, remaining,
|
||||
"{\"channels\":[");
|
||||
"%s{\"ch\":%d,\"level\":\"%s\",\"iscar\":%s,"
|
||||
"\"loop_ok\":%s,\"freq\":%lu,\"diff\":%d,"
|
||||
"\"sens\":%d,\"cndtn\":%d,"
|
||||
"\"misc\":{\"type\":\"%s\",\"value\":%lu}}",
|
||||
(i > 0) ? "," : "",
|
||||
i + 1,
|
||||
freq_level_names[cs->freq_level],
|
||||
cs->car_state ? "true" : "false",
|
||||
cs->loop_state ? "false" : "true",
|
||||
cs->freq, cs->variation,
|
||||
cs->sensitivity, cs->condition,
|
||||
misc_type_str, misc_val);
|
||||
if (written < 0 || written >= remaining) goto done;
|
||||
p += written; remaining -= written;
|
||||
|
||||
for (i = batch_start; i < batch_end; i++) {
|
||||
const LUP_CoilSensor *cs = &sr.coils[i];
|
||||
const char *misc_type_str = "time";
|
||||
uint32_t misc_val = 0;
|
||||
|
||||
if (cs->misc_type == 0) { misc_type_str = "time"; misc_val = cs->misc.passtime_ms; }
|
||||
else if (cs->misc_type == 1) { misc_type_str = "cut_count"; misc_val = cs->misc.cut_amount; }
|
||||
else if (cs->misc_type == 2) { misc_type_str = "flow_count"; misc_val = cs->misc.flow_amount; }
|
||||
else if (cs->misc_type == 3) { misc_type_str = "relay_count"; misc_val = cs->misc.relay_count; }
|
||||
|
||||
const char *freq_level_names[] = {"high", "mid_high", "mid_low", "low"};
|
||||
|
||||
written = snprintf(p, remaining,
|
||||
"%s{\"ch\":%d,\"freq_level\":\"%s\",\"has_car\":%s,"
|
||||
"\"loop_ok\":%s,\"freq_current\":%lu,\"freq_diff\":%d,"
|
||||
"\"sensitivity\":%d,\"condition\":%d,"
|
||||
"\"misc\":{\"type\":\"%s\",\"value\":%lu}}",
|
||||
(i > batch_start) ? "," : "",
|
||||
i + 1,
|
||||
freq_level_names[cs->freq_level],
|
||||
cs->car_state ? "true" : "false",
|
||||
cs->loop_state ? "false" : "true",
|
||||
cs->freq, cs->variation,
|
||||
cs->sensitivity, cs->condition,
|
||||
misc_type_str, misc_val);
|
||||
if (written < 0 || written >= remaining) goto done;
|
||||
p += written; remaining -= written;
|
||||
}
|
||||
|
||||
snprintf(p, remaining, "]}");
|
||||
|
||||
/* Wrap in outer message and publish */
|
||||
snprintf(payload, sizeof(payload),
|
||||
"{\"msg_id\":%d,\"cmd\":\"loop_data\",\"ts\":%lu,\"data\":%s}",
|
||||
++g_iot_msg_id, mstick() / 1000, data_json);
|
||||
|
||||
uint16_t plen = (uint16_t)strlen(payload);
|
||||
mqtt_publish(pub_topic, payload, 0);
|
||||
|
||||
batch_start = batch_end;
|
||||
}
|
||||
|
||||
done:
|
||||
InitPkgUart(&g_pkg_uart_2);
|
||||
}
|
||||
snprintf(p, remaining, "]}");
|
||||
|
||||
/* Wrap and publish */
|
||||
snprintf(payload, sizeof(payload),
|
||||
"{\"msg_id\":%d,\"cmd\":\"loop_data\",\"ts\":%lu,\"data\":%s}",
|
||||
++g_iot_msg_id, mstick() / 1000, data_json);
|
||||
|
||||
mqtt_publish((char *)g_iot_topic.topic_pub, payload, 0);
|
||||
|
||||
done:
|
||||
InitPkgUart(&g_pkg_uart_2);
|
||||
}
|
||||
|
||||
|
||||
@@ -1044,7 +1044,7 @@ static int format_sensor_json(char *buf, uint16_t buf_size, const LUP_SensorRepo
|
||||
switch (cs->misc_type) {
|
||||
case 0:
|
||||
// car_state=0(无车)→通过时间, car_state=1(有车)→车间距
|
||||
misc_field = cs->car_state ? "\"gap_ms5\"" : "\"passtime_ms\"";
|
||||
misc_field = cs->car_state ? "\"gap\"" : "\"passtime\"";
|
||||
misc_val = cs->misc.passtime_ms;
|
||||
break;
|
||||
case 1: misc_field = "\"cut_amount\""; misc_val = cs->misc.cut_amount; break;
|
||||
|
||||
@@ -107,10 +107,114 @@
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-06 — MQTT IoT 基础打通
|
||||
|
||||
### 背景
|
||||
|
||||
在 CH32V208(RISC-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` 等),恢复完整字段并在分批框架内发送。
|
||||
|
||||
---
|
||||
|
||||
## 修订记录
|
||||
|
||||
| 版本 | 时间 | 说明 |
|
||||
|------|------|------|
|
||||
| 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 不计入重启限额, 修复冷却期阻断连接 |
|
||||
|
||||
Reference in New Issue
Block a user