From 50b8ce8fd78420ba73e10abd46627318fdbdd857 Mon Sep 17 00:00:00 2001 From: wangfq Date: Mon, 31 Aug 2026 14:14:31 +0800 Subject: [PATCH] =?UTF-8?q?feat(vd960Air):=20=E5=B8=A7=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=20XOR+SUM(=E7=94=A8=E6=88=B7=E5=86=B3?= =?UTF-8?q?=E7=AD=96=202026-08-31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - uart_app.lua: 接收状态机 RX_XOR/RX_SUM 双校验(从 LEN_H 起,不含 HEAD) 下行组帧加 XOR+SUM 两字节,与 DBN↔Loop 0x7F 完全一致 - config.lua/readme: 帧协议 v0.2 定稿标注,移除"待确认" --- .../module/Air780EPM/demo/vd960Air/config.lua | 5 +- .../module/Air780EPM/demo/vd960Air/readme.md | 15 +++-- .../Air780EPM/demo/vd960Air/uart_app.lua | 66 ++++++++++++------- 3 files changed, 52 insertions(+), 34 deletions(-) diff --git a/luatos/air780epm/module/Air780EPM/demo/vd960Air/config.lua b/luatos/air780epm/module/Air780EPM/demo/vd960Air/config.lua index f560fab..7a5fc5c 100644 --- a/luatos/air780epm/module/Air780EPM/demo/vd960Air/config.lua +++ b/luatos/air780epm/module/Air780EPM/demo/vd960Air/config.lua @@ -33,8 +33,9 @@ CFG_MQTT_CLIENT_ID = "vd960Air-" .. mobile.imei() CFG_UART_ID = 1 CFG_UART_BAUD = 115200 --- 帧协议参数(4G <-> vd960DBN UART 链路帧,设计稿 v0.1) --- 帧 = [0x7D] [LEN_H] [LEN_L] [PAYLOAD...] [SUM], 详见 uart_app.lua 头注释 +-- 帧协议参数(4G <-> vd960DBN UART 链路帧,设计稿 v0.2 — 用户决策 2026-08-31 校验统一 XOR+SUM) +-- 帧 = [0x7D] [LEN_H] [LEN_L] [PAYLOAD...] [XOR] [SUM] +-- 校验范围: 从 LEN_H 起到 PAYLOAD 末字节(不含 HEAD,与 DBN↔Loop 0x7F 同风格) CFG_FRAME_HEAD = 0x7D CFG_FRAME_MAXLEN = 2048 -- PAYLOAD 最大字节数(防御: 超长判非法帧) diff --git a/luatos/air780epm/module/Air780EPM/demo/vd960Air/readme.md b/luatos/air780epm/module/Air780EPM/demo/vd960Air/readme.md index 0c81864..88da03b 100644 --- a/luatos/air780epm/module/Air780EPM/demo/vd960Air/readme.md +++ b/luatos/air780epm/module/Air780EPM/demo/vd960Air/readme.md @@ -83,13 +83,14 @@ - JSON 解析失败时原样转发,不阻塞上行 - 开关: `config.lua` `CFG_LINK_ENABLE` -## 3 UART 链路帧协议(设计稿 v0.2) +## 3 UART 链路帧协议(设计稿 v0.2,校验已定稿) ``` -帧 = [HEAD 0x7D] [LEN_H] [LEN_L] [PAYLOAD...] [SUM] +帧 = [HEAD 0x7D] [LEN_H] [LEN_L] [PAYLOAD...] [XOR] [SUM] - HEAD: 0x7D(避开 0x7F DBN↔Loop / 0x8F DBN 专有 / 0x9F OTA 已占用魔数) - LEN: uint16 大端, PAYLOAD(JSON) 字节数, ≤2048 -- SUM: uint8 累加和(HEAD+LEN_H+LEN_L+PAYLOAD 全字节取低 8 位) +- XOR: XOR 校验,从 LEN_H 起到 PAYLOAD 末字节(不含 HEAD) +- SUM: 累加和校验,范围同上 - 双向对称 ``` @@ -108,13 +109,13 @@ vd960DBN 与地感 MCU 的串口逻辑(loop_uart_proto.c,UART2,0x7F 帧): |---|---|---| | LEN 字段 | **1B**,Value 0~64B(协议明确上限) | **2B**,≤2048B | | 魔数 | 0x7F | 0x7D | -| 校验 | XOR + SUM(从 Addr 起) | SUM(设计稿;待定稿确认是否统一 XOR+SUM) | -| 状态机 | IDLE→HEADER→VALUE→CHECK | 同构(IDLE→HEAD→LEN→DATA→CHECK) | +| 校验 | XOR + SUM(从 Addr 起) | **XOR + SUM(从 LEN_H 起,用户决策 2026-08-31)** | +| 状态机 | IDLE→HEADER→VALUE→CHECK | 同构(IDLE→HEAD→LEN→DATA→XOR→SUM) | | 用途 | 命令-响应短帧 | JSON 报文流(loop_data 数百字节) | - 为什么不用 0x7F 帧: LEN 1B 上限 64B,装不下 MQTT JSON -- 状态机同构 → vd960DBN 侧 UART1 通道可复用 `lup_feed_byte` 解析骨架 -- 待确认: 校验是否统一为 XOR+SUM(与 0x7F 风格一致) +- 状态机同构 + 校验统一 → vd960DBN 侧 UART1 通道可直接复用 `lup_feed_byte` 解析骨架 +- ✅ **校验已定稿为 XOR+SUM**(2026-08-31 用户确认),与 0x7F 完全一致 ## 4 文件结构 diff --git a/luatos/air780epm/module/Air780EPM/demo/vd960Air/uart_app.lua b/luatos/air780epm/module/Air780EPM/demo/vd960Air/uart_app.lua index 5aacae0..5ba44d3 100644 --- a/luatos/air780epm/module/Air780EPM/demo/vd960Air/uart_app.lua +++ b/luatos/air780epm/module/Air780EPM/demo/vd960Air/uart_app.lua @@ -1,28 +1,29 @@ --[[ @module uart_app @summary vd960Air 串口应用功能模块: UART1 <-> vd960DBN 帧协议对接 -@version 0.1 +@version 0.2 @date 2026.08.31 @usage 核心职责: 1、打开 UART1(对接 vd960DBN CH32V208 串口1 PB6/PB7); 2、上行: 接收 vd960DBN 的 JSON 帧(复用《DLD960_IoT_MQTT协议》命令面), - 校验后发布 SEND_DATA_REQ 消息 → mqtt_sender publish 到 dld960/{sn}/dev; + 校验后注入 link 对象 → 发布 SEND_DATA_REQ → mqtt_sender publish 到 dld960/{sn}/dev; 3、下行: 订阅 RECV_DATA_FROM_SERVER 消息,将 MQTT 下行 JSON 组帧后经 UART1 下发 vd960DBN。 -UART 链路帧格式(设计稿 v0.1,待与 vd960DBN 侧联调确认): -┌──────┬───────┬───────┬────────────────────┬──────┐ -│ HEAD │ LEN_H │ LEN_L │ PAYLOAD │ SUM │ -│ 0x7D │ 1B │ 1B │ JSON(≤2048B) │ 1B │ -└──────┴───────┴───────┴────────────────────┴──────┘ -- HEAD: 0x7D(与 DBN↔Loop 的 0x7F 帧区分,4G 链路独立魔数) +UART 链路帧格式(v0.2,校验统一 XOR+SUM — 用户决策 2026-08-31,与 DBN↔Loop 0x7F 一致): +┌──────┬───────┬───────┬────────────────────┬──────┬──────┐ +│ HEAD │ LEN_H │ LEN_L │ PAYLOAD │ XOR │ SUM │ +│ 0x7D │ 1B │ 1B │ JSON(≤2048B) │ 1B │ 1B │ +└──────┴───────┴───────┴────────────────────┴──────┴──────┘ +- HEAD: 0x7D(避开 0x7F DBN↔Loop / 0x8F DBN 专有 / 0x9F OTA 已占用魔数) - LEN: uint16 大端,PAYLOAD 字节数 -- SUM: uint8 累加和,覆盖 HEAD+LEN_H+LEN_L+PAYLOAD 全部字节取低 8 位 +- XOR: XOR 校验,从 LEN_H 开始到 PAYLOAD 末字节(不含 HEAD,与 0x7F 同风格) +- SUM: 累加和校验,范围同上 - 方向: 双向对称(vd960DBN 发同样帧格式) -状态机: IDLE → HEAD → LEN_H → LEN_L → DATA → SUM → COMPLETE +状态机: IDLE → HEAD → LEN_H → LEN_L → DATA → XOR → SUM → COMPLETE 注意: 状态机按 LEN 精确吃字节,payload 内出现的 0x7D 不影响帧同步; - 仅 IDLE 状态寻找 HEAD;SUM 校验失败 / 超长帧 → 回 IDLE 丢弃。 + 仅 IDLE 状态寻找 HEAD;校验失败 / 超长帧 → 回 IDLE 丢弃。 ]] -- 加载配置 @@ -34,16 +35,18 @@ local UART_ID = CFG.CFG_UART_ID local MAX_LEN = CFG.CFG_FRAME_MAXLEN -- ==================== 接收状态机 ==================== -local RX_IDLE, RX_HEAD, RX_LENH, RX_LENL, RX_DATA, RX_SUM = 0, 1, 2, 3, 4, 5 +local RX_IDLE, RX_HEAD, RX_LENH, RX_LENL, RX_DATA, RX_XOR, RX_SUM = 0, 1, 2, 3, 4, 5, 6 local rx_state = RX_IDLE local rx_len = 0 -- 当前帧 PAYLOAD 长度 local rx_buf = "" -- PAYLOAD 缓冲 -local rx_sum = 0 -- 累加和(覆盖 HEAD 起所有字节) +local rx_xor = 0 -- XOR 校验(从 LEN_H 起累加) +local rx_sum = 0 -- SUM 校验(从 LEN_H 起累加) local function rx_reset() rx_state = RX_IDLE rx_len = 0 rx_buf = "" + rx_xor = 0 rx_sum = 0 end @@ -75,14 +78,15 @@ local function feed_byte(b) if rx_state == RX_IDLE then if b == CFG.CFG_FRAME_HEAD then rx_state = RX_HEAD - rx_sum = b end elseif rx_state == RX_HEAD then rx_len = b * 256 - rx_sum = (rx_sum + b) % 256 + rx_xor = b + rx_sum = b rx_state = RX_LENH elseif rx_state == RX_LENH then rx_len = rx_len + b + rx_xor = rx_xor ~ b -- XOR 累加(bitwise) rx_sum = (rx_sum + b) % 256 if rx_len > MAX_LEN then log.warn("uart_app.feed", "frame too long", rx_len) @@ -100,15 +104,23 @@ local function feed_byte(b) end elseif rx_state == RX_DATA then rx_buf = rx_buf .. string.char(b) + rx_xor = rx_xor ~ b rx_sum = (rx_sum + b) % 256 if rx_buf:len() >= rx_len then + rx_state = RX_XOR + end + elseif rx_state == RX_XOR then + if b ~= rx_xor then + log.warn("uart_app.feed", "xor fail") + rx_reset() + else rx_state = RX_SUM end elseif rx_state == RX_SUM then if b == rx_sum then on_frame_ok(rx_buf) else - log.warn("uart_app.feed", "checksum fail") + log.warn("uart_app.feed", "sum fail") end rx_reset() end @@ -134,18 +146,22 @@ local function recv_data_from_server_proc(topic, payload) if not payload or payload:len() == 0 then return end - -- 组帧: HEAD + LEN(2B BE) + PAYLOAD + SUM + -- 组帧: HEAD + LEN(2B BE) + PAYLOAD + XOR + SUM + -- 校验范围: 从 LEN_H 起(不含 HEAD,与 0x7F 同风格) local len = payload:len() - local frame = string.char(CFG.CFG_FRAME_HEAD) - .. string.char(math.floor(len / 256)) .. string.char(len % 256) - .. payload - local sum = CFG.CFG_FRAME_HEAD - sum = (sum + math.floor(len / 256)) % 256 - sum = (sum + len % 256) % 256 + local hi = math.floor(len / 256) + local lo = len % 256 + local x = hi ~ lo + local s = (hi + lo) % 256 for i = 1, len do - sum = (sum + string.byte(payload, i)) % 256 + local b = string.byte(payload, i) + x = x ~ b + s = (s + b) % 256 end - frame = frame .. string.char(sum) + local frame = string.char(CFG.CFG_FRAME_HEAD) + .. string.char(hi) .. string.char(lo) + .. payload + .. string.char(x) .. string.char(s) uart.write(UART_ID, frame) log.info("uart_app.down", "len", len) end