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
This commit is contained in:
@@ -22,7 +22,7 @@
|
||||
#define IOT_MQTT_RECONNECT_MAX_MS 60000 // 最大重连间隔 (指数退避上限)
|
||||
#define IOT_MQTT_HEARTBEAT_MS 60000 // 心跳上报周期 (60s)
|
||||
#define IOT_MQTT_RECV_BUF_LEN 1024 // 接收缓冲区 (单次交互 ≤1024)
|
||||
#define IOT_MQTT_SEND_BUF_LEN 800 // 发送缓冲区 (2026-08-17: 1024→800, loop_data~604B+头~30B<800, 省 224B .bss)
|
||||
#define IOT_MQTT_SEND_BUF_LEN 800 // 发送缓冲区 (2026-08-18: hex 上报方案, 快照 2 条 128hex×2+头 ~420B; loop_data ~604B; 不扩容)
|
||||
#define IOT_MQTT_TOPIC_MAX_LEN 128 // Topic 最大长度
|
||||
|
||||
/*===========================================================================
|
||||
|
||||
@@ -162,7 +162,8 @@ void offlog_clear(void); /* 清空事件区 (清空
|
||||
uint8_t offlog_enabled(void); /* 日志功能是否启用 (Flash 初始化成功) */
|
||||
uint32_t offlog_seq_last(void); /* 最新一条记录全局序号 (0=空) */
|
||||
const char *offlog_type_str(uint8_t type); /* 事件类型数字 → 协议字符串名 */
|
||||
int offlog_evt_to_json(const OfflogEvt *e, char *buf, int buf_len); /* 记录 → JSON 对象 */
|
||||
int offlog_evt_to_json(const OfflogEvt *e, char *buf, int buf_len); /* 记录 → JSON 对象 (旧方案, 保留) */
|
||||
int offlog_evt_to_hex(const OfflogEvt *e, char *buf, int buf_len); /* 32B → 64 hex (TCP/MQTT log_query, V1.03/V1.07) */
|
||||
#define OFFLOG_MAX_QUERY_RECORDS 4 /* log_query 分页上限 (MQTT ≤500B 发布限制) */
|
||||
|
||||
#endif /* _OFFLOG_H__ */
|
||||
|
||||
@@ -115,7 +115,7 @@ typedef struct {
|
||||
Loop 帧最快 150ms, publish_sensor 每轮 flush, 8 深 ≈ 1.2s 余量;
|
||||
CH32V208 SRAM 紧张: 64B × 8 = 512B, 远小于 SPI_FLASH_BUF 的 4KB */
|
||||
#define SNAP_RAM_DEPTH 8
|
||||
#define SNAP_MAX_QUERY_RECORDS 2 /* 快照 QUERY 分页上限: (132-2)/64 */
|
||||
#define SNAP_MAX_QUERY_RECORDS 2 /* QUERY 分页上限: BLE 原始 64B×2 (132B 缓冲); TCP/MQTT hex 上报 64B->128 字符×2 亦安全 (2026-08-18) */
|
||||
|
||||
|
||||
/*===========================================================================
|
||||
@@ -133,6 +133,7 @@ uint32_t snap_seq_last(void); /* 最新一条记录全局序
|
||||
uint32_t snap_boot_seq(void); /* 当前 boot 序号 */
|
||||
int snap_read_idx(uint32_t idx, SnapRec *out); /* 按逻辑序号读, 0=成功 -1=越界 */
|
||||
void snap_clear(void); /* 清空快照区 (审计写入事件流) */
|
||||
int snap_rec_to_json(const SnapRec *r, char *buf, int buf_len); /* 记录 -> JSON (TCP/MQTT log_query) */
|
||||
int snap_rec_to_json(const SnapRec *r, char *buf, int buf_len); /* 记录 -> JSON (旧方案, 保留) */
|
||||
int snap_rec_to_hex(const SnapRec *r, char *buf, int buf_len); /* 64B -> 128 hex (TCP/MQTT log_query, V1.03/V1.07) */
|
||||
|
||||
#endif /* _SNAPSHOT_H__ */
|
||||
|
||||
@@ -590,7 +590,7 @@ static void iot_handle_publish(const char *topic, uint8_t *payload, int payload_
|
||||
iot_mqtt_publish(resp_topic, resp, strlen(resp), 1);
|
||||
|
||||
} else if (strcmp(cmd_str, "log_query") == 0) {
|
||||
/* 分页拉取脱机日志: 按全局序号, event/snapshot 流 (协议 V1.07) */
|
||||
/* 分页拉取脱机日志: 按全局序号, event/snapshot 流, hex 原始字节上报 (协议 V1.07) */
|
||||
static char resp[IOT_MQTT_SEND_BUF_LEN]; /* static: 避免大栈开销 */
|
||||
char stream_buf[16];
|
||||
uint32_t start_seq = 0, req_count = 0;
|
||||
@@ -622,9 +622,12 @@ static void iot_handle_publish(const char *topic, uint8_t *payload, int payload_
|
||||
uint32_t idx = start_seq - seq_first; /* 逻辑索引 = 全局序号 - seq_first */
|
||||
while (fetched < req_count && (idx + fetched) < total) {
|
||||
SnapRec rec;
|
||||
char hex[132]; /* 64B -> 128 hex + NUL */
|
||||
if (snap_read_idx(idx + fetched, &rec) != 0) break;
|
||||
if (fetched > 0) pos += snprintf(resp + pos, sizeof(resp) - pos, ",");
|
||||
pos += snap_rec_to_json(&rec, resp + pos, sizeof(resp) - pos);
|
||||
snap_rec_to_hex(&rec, hex, sizeof(hex));
|
||||
pos += snprintf(resp + pos, sizeof(resp) - pos,
|
||||
"{\"seq\":%lu,\"hex\":\"%s\"}", (unsigned long)rec.seq, hex);
|
||||
fetched++;
|
||||
}
|
||||
}
|
||||
@@ -640,9 +643,12 @@ static void iot_handle_publish(const char *topic, uint8_t *payload, int payload_
|
||||
uint32_t idx = start_seq - seq_first; /* 逻辑索引 = 全局序号 - seq_first */
|
||||
while (fetched < req_count && (idx + fetched) < total) {
|
||||
OfflogEvt evt;
|
||||
char hex[68]; /* 32B -> 64 hex + NUL */
|
||||
if (offlog_read_idx((uint16_t)(idx + fetched), &evt) != 0) break;
|
||||
if (fetched > 0) pos += snprintf(resp + pos, sizeof(resp) - pos, ",");
|
||||
pos += offlog_evt_to_json(&evt, resp + pos, sizeof(resp) - pos);
|
||||
offlog_evt_to_hex(&evt, hex, sizeof(hex));
|
||||
pos += snprintf(resp + pos, sizeof(resp) - pos,
|
||||
"{\"seq\":%lu,\"hex\":\"%s\"}", (unsigned long)evt.seq, hex);
|
||||
fetched++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -489,3 +489,17 @@ void offlog_clear(void)
|
||||
offlog_evt(OFFLOG_EVT_LOG_CLEAR, NULL, 0);
|
||||
offlog_flush_head();
|
||||
}
|
||||
|
||||
/* OfflogEvt -> hex string (flash 原始字节, 小端原样; 上位机按 BLE 协议 §7 字段表解析)
|
||||
Used by TCP JSON / MQTT log_query stream=event (protocol V1.03/V1.07).
|
||||
Returns snprintf written length; 32B -> 64 hex chars. */
|
||||
int offlog_evt_to_hex(const OfflogEvt *e, char *buf, int buf_len)
|
||||
{
|
||||
int i, pos = 0;
|
||||
const uint8_t *p = (const uint8_t *)e;
|
||||
for (i = 0; i < (int)sizeof(OfflogEvt); i++) {
|
||||
if (pos >= buf_len - 3) break;
|
||||
pos += snprintf(buf + pos, buf_len - pos, "%02x", p[i]);
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
@@ -480,3 +480,17 @@ int snap_rec_to_json(const SnapRec *r, char *buf, int buf_len)
|
||||
pos += snprintf(buf + pos, buf_len - pos, "]}");
|
||||
return pos;
|
||||
}
|
||||
|
||||
/* SnapRec -> hex string (flash 原始字节, 小端原样; 上位机按 BLE 协议 §6.4 字段表解析)
|
||||
Used by TCP JSON / MQTT log_query stream=snapshot (protocol V1.03/V1.07).
|
||||
Returns snprintf written length; 64B -> 128 hex chars. */
|
||||
int snap_rec_to_hex(const SnapRec *r, char *buf, int buf_len)
|
||||
{
|
||||
int i, pos = 0;
|
||||
const uint8_t *p = (const uint8_t *)r;
|
||||
for (i = 0; i < (int)sizeof(SnapRec); i++) {
|
||||
if (pos >= buf_len - 3) break;
|
||||
pos += snprintf(buf + pos, buf_len - pos, "%02x", p[i]);
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
@@ -682,7 +682,7 @@ static void handle_log_stat(uint8_t socket, uint32_t msg_id, const char *json) {
|
||||
json_send_ok(socket, msg_id, "log_stat", data_json);
|
||||
}
|
||||
|
||||
/* 4.17 log_query — 分页拉取脱机日志 (event/snapshot 流; 事件≤4, 快照≤2, 协议 V1.03) */
|
||||
/* 4.17 log_query — 分页拉取脱机日志 (event/snapshot 流, hex 原始字节上报, 协议 V1.03) */
|
||||
static void handle_log_query(uint8_t socket, uint32_t msg_id, const char *json) {
|
||||
char data_json[TCP_JSON_DATA_BUF_LEN];
|
||||
char stream_buf[16];
|
||||
@@ -709,9 +709,12 @@ static void handle_log_query(uint8_t socket, uint32_t msg_id, const char *json)
|
||||
uint32_t idx = start_seq - seq_first; /* 逻辑索引 = 全局序号 - seq_first */
|
||||
while (fetched < req_count && (idx + fetched) < total) {
|
||||
SnapRec rec;
|
||||
char hex[132]; /* 64B -> 128 hex + NUL */
|
||||
if (snap_read_idx(idx + fetched, &rec) != 0) break;
|
||||
if (fetched > 0) pos += snprintf(data_json + pos, sizeof(data_json) - pos, ",");
|
||||
pos += snap_rec_to_json(&rec, data_json + pos, sizeof(data_json) - pos);
|
||||
snap_rec_to_hex(&rec, hex, sizeof(hex));
|
||||
pos += snprintf(data_json + pos, sizeof(data_json) - pos,
|
||||
"{\"seq\":%lu,\"hex\":\"%s\"}", (unsigned long)rec.seq, hex);
|
||||
fetched++;
|
||||
}
|
||||
}
|
||||
@@ -727,9 +730,12 @@ static void handle_log_query(uint8_t socket, uint32_t msg_id, const char *json)
|
||||
uint32_t idx = start_seq - seq_first; /* 逻辑索引 = 全局序号 - seq_first */
|
||||
while (fetched < req_count && (idx + fetched) < total) {
|
||||
OfflogEvt evt;
|
||||
char hex[68]; /* 32B -> 64 hex + NUL */
|
||||
if (offlog_read_idx((uint16_t)(idx + fetched), &evt) != 0) break;
|
||||
if (fetched > 0) pos += snprintf(data_json + pos, sizeof(data_json) - pos, ",");
|
||||
pos += offlog_evt_to_json(&evt, data_json + pos, sizeof(data_json) - pos);
|
||||
offlog_evt_to_hex(&evt, hex, sizeof(hex));
|
||||
pos += snprintf(data_json + pos, sizeof(data_json) - pos,
|
||||
"{\"seq\":%lu,\"hex\":\"%s\"}", (unsigned long)evt.seq, hex);
|
||||
fetched++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,41 @@
|
||||
>
|
||||
> 项目定位: DLD960 通信板 — BLE 配网、TCP JSON 协议服务、Loop MCU 串口桥接
|
||||
|
||||
## 2026-08-18 — log_query 改为 hex 原始字节上报(协议 V1.03/V1.07 修订)
|
||||
|
||||
### 背景
|
||||
|
||||
板级实测:MQTT 快照流 `log_query(stream=snapshot)` `fetched=2` 但 **`MQTTSerialize_publish failed`**——JSON 化快照记录(4ch×12 字段 ≈ 810B/条)超 `IOT_MQTT_SEND_BUF_LEN=800B`,事件流 4 条 ~600B 可发,快照 1 条都发不出。
|
||||
|
||||
### 方案(用户拍板):对齐 BLE 通道——原始字节 hex 上报
|
||||
|
||||
- 事件记录 **OfflogEvt 32B → 64 hex 字符**;快照记录 **SnapRec 64B → 128 hex 字符**(flash 存储字节原样小端)
|
||||
- 上位机按《DLD960 BLE 协议》§6.4/§7 字段表解析,与 BLE 通道同语义、解析逻辑复用
|
||||
- 体积实测:**2 条快照 hex 响应 406B < 800B**——无需扩容缓冲、无需砍 count
|
||||
|
||||
### 变更
|
||||
|
||||
| 文件 | 变更 |
|
||||
|------|------|
|
||||
| `offlog.c/h` | 新增 `offlog_evt_to_hex()`(32B→64 hex) |
|
||||
| `snapshot.c/h` | 新增 `snap_rec_to_hex()`(64B→128 hex);删 `SNAP_MAX_QUERY_JSON` 宏,恢复 `SNAP_MAX_QUERY_RECORDS=2` 全通道统一 |
|
||||
| `tcp_json_srv.c` | `log_query` event/snapshot 分支改 `{"seq":N,"hex":"..."}` 上报 |
|
||||
| `iot_mqtt_srv.c` | 同上;`IOT_MQTT_SEND_BUF_LEN` 保持 800(hex 方案体积可控,此前 1024 变更撤销) |
|
||||
| 协议文档 | TCP JSON V1.03 / MQTT V1.07 §4.17:records 改 hex + 解析表引用 BLE 字段表;快照 count 恢复 2 |
|
||||
|
||||
### 验证
|
||||
|
||||
- gcc 单测 `tests/test_snap_to_hex.c`:**9 断言全过**(128 hex、全小写、字段字节序、缓冲截断安全)
|
||||
- 工具 hex 解析器:boot(POR/SFT)/coil/evt_retry payload + 快照 4ch 解析全过;2 条快照 hex 响应 406B
|
||||
- offscreen UI:事件流/快照流 hex 展示 + 翻页正常
|
||||
- 保留 `offlog_evt_to_json`/`snap_rec_to_json`(旧方案,未来可能用于调试导出)
|
||||
|
||||
### 遗留
|
||||
|
||||
- 待板级:MRS 编译 + 真机 MQTT/TCP `log_query(stream=snapshot)` 2 条拉取
|
||||
|
||||
---
|
||||
|
||||
## 2026-08-18 — MQTT 命令集补齐:ssc_net_query / iot_net_query / iot_topic_query
|
||||
|
||||
### 背景
|
||||
@@ -1335,6 +1370,7 @@ TCP 超时要等 ~2 分钟才触发 `SINT_STAT_TIM_OUT`,
|
||||
|
||||
| 版本 | 时间 | 说明 |
|
||||
|------|------|------|
|
||||
| V4.3 | 2026-08-18 | log_query 改 hex 原始字节上报 (协议 V1.03/V1.07 修订): offlog_evt_to_hex/snap_rec_to_hex, 2 条快照 406B<800B, 快照 count 恢复 2 |
|
||||
| V4.2 | 2026-08-18 | MQTT 命令集补齐: ssc_net_query/iot_net_query/iot_topic_query 只读组包 (协议 V1.02 对齐), 工具禁用未实现按钮 |
|
||||
| V4.1 | 2026-08-18 | TCP/MQTT 脱机日志快照流: log_stat/log_query/log_clear 支持 stream=snapshot (协议 V1.03/V1.07), snap_rec_to_json 序列化, 事件流 count=0 按上限 |
|
||||
| V4.0 | 2026-08-10 | BLE 脱机日志接口: OFFLOG_STAT/QUERY/CLEAR (0x25/0x26/0x27), 32B OfflogEvt 二进制直传, 缓冲扩容 132B, 隔离测试 7 例 + 协议文档 V1.00 |
|
||||
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,76 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* mock SnapRec (与 snapshot.h 一致, 64B) */
|
||||
typedef struct {
|
||||
uint8_t magic;
|
||||
uint8_t len;
|
||||
uint8_t flags;
|
||||
uint8_t rsvd;
|
||||
uint32_t seq;
|
||||
uint32_t ts_ms;
|
||||
uint16_t boot_seq;
|
||||
uint16_t rsvd2;
|
||||
uint8_t coils[48];
|
||||
} SnapRec;
|
||||
|
||||
#define SNAP_COILS_MAX 4
|
||||
#define SNAP_COIL_BYTES 12
|
||||
|
||||
int snap_rec_to_hex(const SnapRec *r, char *buf, int buf_len)
|
||||
{
|
||||
int i, pos = 0;
|
||||
const uint8_t *p = (const uint8_t *)r;
|
||||
for (i = 0; i < (int)sizeof(SnapRec); i++) {
|
||||
if (pos >= buf_len - 3) break;
|
||||
pos += snprintf(buf + pos, buf_len - pos, "%02x", p[i]);
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
#define CHECK(cond) do { if (!(cond)) { printf("FAIL %d: %s\n", __LINE__, #cond); fails++; } else { passes++; } } while (0)
|
||||
static int passes = 0, fails = 0;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char buf[256];
|
||||
SnapRec r;
|
||||
|
||||
/* 用例1: 与 test_snap_to_json case2 相同数据, 验证 hex 与 JSON 语义一致
|
||||
ch1: cfg=0xE8(11 1 0 0000: 低频/方向/初始/灵敏度0) cond=0xFF(cond15/断/有车/relay)
|
||||
freq=0x00C383=50051, variation=-10, misc=128 */
|
||||
memset(&r, 0, sizeof(r));
|
||||
r.magic = 0xA6; r.len = 48; r.seq = 999; r.ts_ms = 1234; r.boot_seq = 3;
|
||||
r.coils[0] = 0xE8; r.coils[1] = 0xFF;
|
||||
r.coils[2] = 0x83; r.coils[3] = 0xC3; r.coils[4] = 0x00; /* 50051 */
|
||||
r.coils[5] = 0xF6; r.coils[6] = 0xFF; r.coils[7] = 0xFF; /* -10 */
|
||||
r.coils[8] = 0x80; r.coils[9] = 0x00; r.coils[10] = 0x00; r.coils[11] = 0x00; /* 128 */
|
||||
|
||||
int n = snap_rec_to_hex(&r, buf, sizeof(buf));
|
||||
printf("hex(%d): %s\n", n, buf);
|
||||
CHECK(n == 128);
|
||||
CHECK(strlen(buf) == 128);
|
||||
/* 头部: magic=0xA6 len=0x30(48) flags=0 rsvd=0 seq=0xE7030000(999 LE) */
|
||||
CHECK(strncmp(buf, "a6300000e7030000d204", 20) == 0);
|
||||
/* ch1 12B: e8 ff 83 c3 00 f6 ff ff 80 00 00 00 */
|
||||
CHECK(strncmp(buf + 32, "e8ff83c300f6ffff80000000", 24) == 0);
|
||||
/* 全部小写 */
|
||||
CHECK(strspn(buf, "0123456789abcdef") == 128);
|
||||
|
||||
/* 用例2: 空记录 len=0 */
|
||||
memset(&r, 0, sizeof(r));
|
||||
r.magic = 0xA6;
|
||||
n = snap_rec_to_hex(&r, buf, sizeof(buf));
|
||||
CHECK(n == 128);
|
||||
CHECK(strncmp(buf, "a60000000000000000000000", 24) == 0);
|
||||
|
||||
/* 用例3: 缓冲不足截断安全 (buf_len=10 不越界) */
|
||||
memset(&r, 0, sizeof(r));
|
||||
n = snap_rec_to_hex(&r, buf, 10);
|
||||
CHECK(n == 8);
|
||||
CHECK(strlen(buf) == 8);
|
||||
|
||||
printf("PASS=%d FAIL=%d\n", passes, fails);
|
||||
return fails ? 1 : 0;
|
||||
}
|
||||
Reference in New Issue
Block a user