Files
mobile_comm/luatos/air780epm/module/Air780EPM/demo/vd960Air/uart_app.lua
T
wangfq 8911b04df2 feat(vd960Air): Air8781P 4G UART<->MQTT 桥接工程骨架
基于官方 Air780EPM demo/mqtt 骨架创建 vd960Air 工程:
- 目标: vd960DBN 有线网络失效时,经 Air780EPM 4G 兜底上报
- UART1(115200 8N1)对接 vd960DBN CH32V208 串口1 PB6/PB7
- MQTT 主题按《DLD960_IoT_MQTT协议》V1.10: 订阅 dld960/{sn}/srv, 发布 dld960/{sn}/dev
- 帧协议(设计稿 v0.1): [0x7D][LEN_H][LEN_L][JSON][SUM], 双向对称, ≤2048B
- 上行: DBN JSON 帧 → 4G 校验拆帧 → MQTT publish(JSON 命令面与有线完全一致)
- 下行: MQTT 订阅 → 组帧 → UART1 下发 DBN
- 改造: config.lua 集中配置 / 去掉官方 demo 的 "send from" 演示前缀 / 单路 MQTT
- readme: 架构、协议设计决策(4G 适配协议=命令面复用+网络类裁剪+4G特有扩展)、待确认清单
2026-08-31 11:35:22 +08:00

142 lines
4.8 KiB
Lua

--[[
@module uart_app
@summary vd960Air 串口应用功能模块: UART1 <-> vd960DBN 帧协议对接
@version 0.1
@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;
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 链路独立魔数)
- LEN: uint16 大端,PAYLOAD 字节数
- SUM: uint8 累加和,覆盖 HEAD+LEN_H+LEN_L+PAYLOAD 全部字节取低 8 位
- 方向: 双向对称(vd960DBN 发同样帧格式)
状态机: IDLE → HEAD → LEN_H → LEN_L → DATA → SUM → COMPLETE
注意: 状态机按 LEN 精确吃字节,payload 内出现的 0x7D 不影响帧同步;
仅 IDLE 状态寻找 HEAD;SUM 校验失败 / 超长帧 → 回 IDLE 丢弃。
]]
-- 加载配置
local CFG = require "config"
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_state = RX_IDLE
local rx_len = 0 -- 当前帧 PAYLOAD 长度
local rx_buf = "" -- PAYLOAD 缓冲
local rx_sum = 0 -- 累加和(覆盖 HEAD 起所有字节)
local function rx_reset()
rx_state = RX_IDLE
rx_len = 0
rx_buf = ""
rx_sum = 0
end
-- 一帧完整且校验通过 → 发布到 MQTT 发送队列
local function on_frame_ok(payload)
log.info("uart_app.frame", "len", payload:len())
-- SEND_DATA_REQ(tag, topic, payload, qos)
-- 上行统一发布到 dev 主题(平台侧与有线通道无差别)
sys.publish("SEND_DATA_REQ", "vd960DBN", CFG.CFG_TOPIC_UP, payload, 1)
end
-- 逐字节喂入接收状态机
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_state = RX_LENH
elseif rx_state == RX_LENH then
rx_len = rx_len + b
rx_sum = (rx_sum + b) % 256
if rx_len > MAX_LEN then
log.warn("uart_app.feed", "frame too long", rx_len)
rx_reset()
else
rx_buf = ""
rx_state = RX_LENL
end
elseif rx_state == RX_LENL then
-- 0 长度帧非法
if rx_len == 0 then
rx_reset()
else
rx_state = RX_DATA
end
elseif rx_state == RX_DATA then
rx_buf = rx_buf .. string.char(b)
rx_sum = (rx_sum + b) % 256
if rx_buf:len() >= rx_len then
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")
end
rx_reset()
end
end
-- UART1 接收中断处理
local function read()
local s
while true do
s = uart.read(UART_ID, 1024)
if not s or s:len() == 0 then
break
end
for i = 1, s:len() do
feed_byte(string.byte(s, i))
end
end
end
-- ==================== 下行: MQTT → UART1 ====================
-- mqtt_receiver 发布 RECV_DATA_FROM_SERVER(topic, payload)
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
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
for i = 1, len do
sum = (sum + string.byte(payload, i)) % 256
end
frame = frame .. string.char(sum)
uart.write(UART_ID, frame)
log.info("uart_app.down", "len", len)
end
-- ==================== 初始化 ====================
uart.setup(UART_ID, CFG.CFG_UART_BAUD, 8, 1)
uart.on(UART_ID, "receive", read)
sys.subscribe("RECV_DATA_FROM_SERVER", recv_data_from_server_proc)
log.info("uart_app", "init ok", "baud", CFG.CFG_UART_BAUD, "topic_up", CFG.CFG_TOPIC_UP)