docs: 新增《DLD960_IoT_MQTT协议》§6.9 4G 配置同步(V1.14),0x7D 帧方案作废

- 协议 §6.9:BLE→DBN→Air780 配置同步(权威源 / 载体 0x8F / 命令复用 / 载荷拆两条 /
  拉取为主+推送为辅 / 明确不同步项 / 异常处理 / 实施状态)
- 版本 V1.13 → V1.14,修订记录加行(插 V1.13 前);§6.6 增补充说明
- 行尾:全文纯 LF,改动零行尾污染
- devlog:新增 2026-09-10 条目(结论 + 6 条代码事实级硬约束 + 文件改动 + 待办)
This commit is contained in:
wangfq
2026-09-10 21:46:00 +08:00
parent c879ed04e2
commit 2044efe14f
2 changed files with 231 additions and 2 deletions
+174 -2
View File
@@ -2,7 +2,7 @@
> 基于《DLD960 串口通信协议》V1.01,将设备管理、参数配置、数据上报映射到 MQTT 协议。
> 交互格式:JSON。
> 版本:V1.132026-08-31initialize extra_info 增加 imei/iccid 可选字段
> 版本:V1.142026-09-10,新增 §6.9 4G 配置同步(BLE → DBN → Air780);0x8F 定义为本机侧私有帧,帧长上限 260B
---
@@ -1484,9 +1484,12 @@ dld960/{dev_serial}/{direction}
|-----|------|
| `ssc_net_set` / `ssc_net_query` | SSC 有线网络配置(4G 不适用) |
| `iot_net_set` / `iot_net_query` | IoT 有线网络配置(4G 不适用) |
| `iot_topic_set` / `iot_topic_query` | Topic 配置(4G 主题由 Air780 配置) |
| `iot_topic_set` / `iot_topic_query` | Topic 配置(4G 主题由 Air780 配置,同步链路见 §6.9 |
> 经 4G 通道下发以上命令:设备回 `code=4 unsupported`。
>
> 补充(§6.9):这些配置**仅能经 BLE 写入 DBN**。DBN 作为权威源,经 UART1 把配置同步给 4G 通道;
> Air780 **不接受**平台经 4G 下发配置(否则会改掉自己正在使用的连接参数,形成第二权威源)。
## 6.7 link 对象(4G 特有字段)
@@ -1507,6 +1510,174 @@ dld960/{dev_serial}/{direction}
4. **事件面约束**:4G 通道仅报线圈事件,不报 DBN 内部网络事件(§6.4)
5. **可选兜底**:若启用 `frame_report` / `frame_cmd`(Air780 未识别帧 / 未实现转换命令),平台需按《DLD960Loop_串口通信协议》解析/组帧
## 6.9 4G 配置同步(BLE → DBN → Air780V1.14 新增)
### 6.9.1 背景与权威源
4G 通道的 MQTT 连接参数(服务器地址、端口、ClientID、账号、密码、发布/订阅主题)由**蓝牙小程序经 BLE 写入 DBN**。§6.6 已规定这些配置**不允许经 4G 通道下发**(回 `code=4`),因此 **BLE 是网络配置的唯一入口**。若 DBN 不同步给 Air780,Air780 只能使用自身固件内的默认值,**平台侧改配置无效**。
配置权威源 = **DBN 侧 flash**`IOT_NET_INFO` / `IOT_Topic` 结构,`cfig_flash.c` 持久化)。
| 角色 | 职责 |
|------|------|
| 蓝牙小程序 | **唯一配置入口**,经 BLE 写 DBN |
| DBN | **唯一权威源**:持久化 + 应答拉取 + 主动推送 |
| Air780 | 配置**消费者**:fskv 缓存仅用于加速启动;**唯一合法写入者 = 收到的 DBN 配置帧**(平台下发、产线预置均不得写入,否则产生第二权威源) |
### 6.9.2 帧格式
配置同步复用 **0x8F 本机侧私有帧**(与 BLE 侧 `MAGIC_BYTE_DBN_DEFAULT` 同值),沿用《DLD960Loop 串口通信协议》帧布局:
| 偏移 | 字段 | 说明 |
|------|------|------|
| 0 | `0x8F` | 本机侧私有帧魔数 |
| 1 | `Addr` | 设备地址 |
| 2 | `LEN` | `1 + DATA 长度`(最小 1 |
| 3 | `CMD` | 见 §6.9.3 |
| 4.. | `DATA` | 载荷,见 §6.9.4 |
| -2 | `XOR` | 自 `Addr` 起算(**不含**魔数) |
| -1 | `SUM` | 自 `Addr` 起算(**不含**魔数) |
帧总长 = `LEN + 5``XOR` / `SUM` 覆盖 = `Addr``2 + LEN` 字节。
**UART1 魔数语义(链路两端一致)**
| 魔数 | 处理 |
|------|------|
| `0x7F` | 业务帧:转发 UART2Loop |
| `0x8F` | **本机侧私有帧**:链路两端各自本地消费,**均不转发** |
| 其他 | 丢弃,并回找最近魔数重新同步(resync) |
**⚠ 帧长上限(两侧必须同步扩容)**
| 帧类型 | 帧长上限 | 说明 |
|--------|----------|------|
| `0x7F` Loop 业务帧 | **70 B**`LUP_MAX_PKG_LEN`,不变) | 与 Loop 协议隔离,维持原纪律 |
| `0x8F` 本机侧同步帧 | **260 B**`LEN` ≤ 255) | 覆盖最坏载荷(见 §6.9.4.3),**DBN 帧缓冲需由 70 B 扩至 260 BAir780 parser 缓冲同步 ≥ 260 B** |
> 注:`LEN` 为 1 字节,理论上限 255,故同步帧最大 260 B255 + 5)。B 端(Air780)解析器与 A 端(DBN)装配器**必须使用同一上限**,否则超长帧会被判 `LEN` 非法丢弃。
### 6.9.3 命令
复用 BLE 侧既有命令码(`dbn_ble_srv.h`),保证**同一命令在 BLE 通道与 UART1 同步通道语义一致**:
| CMD | 名称 | 方向 | 说明 |
|-----|------|------|------|
| `0x14` | `GET_IOT_NET` | 双向 | →:请求 net 配置(DATA 空);←:应答 net 配置(DATA 见 §6.9.4.1 |
| `0x16` | `GET_IOT_TOPIC` | 双向 | →:请求 topic 配置(DATA 空);←:应答 topic 配置(DATA 见 §6.9.4.2 |
**应答方向统一使用 `GET_*` 命令码**:载荷由 DBN 侧既有构造器 `set_response_iot_net()` / `set_response_iot_topic()` 生成,与 **BLE 读响应逐字节同源**(零新增序列化代码,避免出现第二套口径)。DBN 主动推送时同样使用 `GET_*` + 对应载荷(语义 = “当前权威值”)。
### 6.9.4 载荷
`0x00` 分隔字符串序列;字符串按 `strlen` **紧凑发送**(不补足结构体定长)。
#### 6.9.4.1 netCMD `0x14`
| 序号 | 字段 | 类型 | 最大长度 | 说明 |
|------|------|------|----------|------|
| 1 | `remote_addr` | 字符串 | 63 | 服务器域名或 IP |
| 2 | `mqtt_port` | ASCII 十进制 | 5 | 如 `1883` |
| 3 | `client_id` | 字符串 | 63 | 空 = 使用本机序列号 |
| 4 | `username` | 字符串 | 63 | |
| 5 | `password` | 字符串 | 31 | 末字段**无**结尾 `0x00` |
#### 6.9.4.2 topicCMD `0x16`
| 序号 | 字段 | 类型 | 最大长度 | 说明 |
|------|------|------|----------|------|
| 1 | `clientid_enable` | ASCII `'0'` / `'1'` | 1 | `1` = 启用自定义 ClientID |
| 2 | `topic_pub` | 字符串 | 63 | 上报主题 |
| 3 | `topic_sub` | 字符串 | 63 | 下发主题 |
#### 6.9.4.3 长度核算
| 载荷 | 典型长度 | 最坏长度 | 结论 |
|------|----------|----------|------|
| net | ≈ 59 B | 63+1+5+1+63+1+63+1+31 = **229 B** | 单帧可传(≤ 260 B 上限) |
| topic | ≈ 51 B | 1+1+63+1+63 = **129 B** | 单帧可传 |
**必须拆成两条独立记录**`net` / `topic`):既与 BLE 两个命令的粒度对齐,也使 Air780 侧 fskv 单值(**≤ 255 B**`luat_fskv_set` 限制)安全:net 229 B < 255 B ✓,topic 129 B ✓。
Air780 侧建议**直接存原始载荷字节串(不转 JSON)**,加载时按 `0x00` 拆分 —— 与 DBN 侧构造器逐字节同源。
### 6.9.5 交互流程
**① 拉取(主:兜底一切不一致)**
```
Air780 上电 / 链路建立:
① fskv.init() → 失败则降级为纯拉取模式(功能不受影响)
② 读 fskv 缓存 → 有则立即用缓存发起 MQTT 连接(不必等 DBN 就绪)
③ 链路建立后 → 发 0x8F CMD=0x14 / 0x16DATA 空)
④ DBN 应答 → 与缓存比对:相同则不动;不同则写 fskv + 断开重连重订阅
```
**② 推送(辅:免重启立即生效)**
```
BLE 写 SET_IOT_NET(0x13) / SET_IOT_TOPIC(0x15) / UPDATE_DEV_SERIAL(0x09) 成功
→ DBN 持久化后,若 UART1 链路可用,立即发 0x8F CMD=0x14 / 0x16 + 权威载荷
→ Air780 比对,变化则写 fskv + 重连重订阅
```
DBN **不维护“待同步”状态位**:推送时链路不可用则不重试、不记账,等 Air780 下次拉取即可(无状态设计,避免状态机与真实链路状态不一致)。
**③ 约束**
- Air780 收到配置后 **先落 fskv 再重连**(避免掉电后反复以旧参数重连)
- fskv **仅在值实际变化时写入**littlefs 擦写磨损,4 KB 擦写块)
- 重连流程:断开 → 以新参数连接 → 重订阅 `topic_sub` → 恢复上报
### 6.9.6 其他触发同步的 BLE 命令
| BLE 命令 | 值 | 是否必须同步 | 说明 |
|----------|-----|--------------|------|
| `CMD_DBN_UPDATE_DEV_SERIAL``0x09` | 设备序列号 | **必须** | Air780 的 Topic 族与上报 JSON 的 `dev_serial` 均由序列号派生。序列号变更 → Topic 族变更,Air780 需重算并重连 |
| `CMD_DBN_SET_SUB_CODE``0x22` | `iot_enable` 位 | **必须** | IoT 通道总使能;DBN 有线 WCHNET MQTT 与 Air780 4G 通道均受此位控制。不同步 → DBN 已停发而 Air780 仍在连接(白耗流量),平台侧表现为“在线无数据” |
| `CMD_DBN_RW_UART_BAUD``0x31` | UART1 波特率 | **禁止在线修改** | 该命令可改任意串口波特率,含 UART1(4G 链路)。在线改动而两侧未同步 = **链路永久失联,只能拆机重刷**。建议固件侧对 `uart_num == 1` 直接拒绝 |
### 6.9.7 明确不同步的配置
| 配置 | 原因 |
|------|------|
| `local_net_cfg`(有线 IP/网关/掩码 + 各端口)、`net_center_info``lssc_ip` / `tcp_port`) | 有线以太网通道专用,与 4G 模组无关 |
| `CMD_DBN_SET_CJQ_*`(车检器参数)、`CMD_DBN_LOOP_*` | 经 UART2 下发给 Loop,方向与 4G 无关 |
| `CMD_DBN_SET_SUB_CODE``iot_enable` 外的位 | 局部外设使能(网口 / 雷达 / 激光 / LoRa 等) |
| `CMD_DBN_OFFLOG_*` / `CMD_DBN_SNAP_*` | 本地 flash 读写;4G 侧仅作数据通道(§4 `log_*``stream=snapshot` 已覆盖) |
| `CMD_DBN_CHECK_PASS` / `CMD_DBN_MODIFY_PASS` | 蓝牙访问密码,纯本地 |
| `CMD_DBN_SET_FACTORY` / `CMD_DBN_RESET_DEV` | 本地动作;若涉及重启,链路断开重连由 Air780 自行处理 |
| 所有 `GET_*`(读操作) | 无同步语义 |
**不随载荷同步的字段**`IOT_NET_INFO.mode`(IP / DNS 标志)。实测该字段**无 BLE 写入路径**、恒为 `0``IOT_Addr_IP_Mode`),当前仅 DBN 有线 MQTT 路径读取。Air780 侧按主机串自行判别地址类型(LuatOS `socket.connect(host, port)` 对 IP 与域名均兼容),无需同步。
### 6.9.8 异常处理
| 场景 | 行为 |
|------|------|
| 0x8F 帧校验失败 | 丢弃 + 回找最近魔数 resync;**不回错**(配置链路静默重试成本低) |
| `LEN` 非法 / 帧超上限 | 丢弃并重新同步(`LEN < 1``LEN + 5 > 260` |
| Air780 请求时 DBN 尚无有效配置 | DBN 回**出厂默认值**(`cfig_flash.c` 初始化值),保证 Air780 有明确行为而非空值 |
| fskv 挂载失败(首刷 / 分区异常) | `fskv.init()` 返回 false → **降级为纯拉取模式**,不阻塞业务;缓存丢失不影响正确性 |
| 推送时 UART1 链路不可用 | 不重试、不记状态;等 Air780 下次拉取 |
| Air780 重连失败(服务器不可达 / 鉴权失败) | 按既有重连策略退避重试;MQTT 失败**不影响** UART1 链路与配置同步 |
| 载荷含 `0x00` 的字段(如密码本身含 `0x00`) | 不支持;字段均为可见 ASCII 串 |
> **已知缺口(待办)**DBN 自身经**有线 WCHNET 直连 MQTT** 的路径(`peripheral_main.c`**仅实现 IP 模式,域名模式分支为空**。故配置为域名时,有线通道 MQTT 不工作(4G 通道不受影响)。是否补齐由产品决定。
### 6.9.9 实施状态
| 侧 | 项目 | 状态 |
|----|------|------|
| DBN | UART1 通道 + 0x8F 本机侧私有帧接入本机指令处理器(不转发) | **已实现**2026-09-10 |
| DBN | UART1 帧缓冲 70 B → 260 B 扩容 | 待实现 |
| DBN | 0x8F 配置请求应答 + BLE 写成功后主动推送 | 待实现 |
| DBN | `CMD_DBN_RW_UART_BAUD` 拒绝 `uart_num == 1` | 待实现 |
| Air780 | `frame_parser.lua` 支持 0x8F(本地消费,不转发)| 待实现 |
| Air780 | parser 缓冲 ≥ 260 B | 待实现 |
| Air780 | fskv 缓存 + 启动即连 + 拉取校准 + 变化重连重订阅 | 待实现 |
| Air780 | 配置读取由 require 期 `local` 改为重连时重读 | 待实现 |
---
# 修订记录
@@ -1521,6 +1692,7 @@ dld960/{dev_serial}/{direction}
| V1.05 | 2026-07-15 | 增加**设备时钟同步**(§2.3,方案B):设备无 RTC,`initialize` 上线后平台经 `report_config` 命令下发 Unix `ts`,设备据此校准,之后上行 `ts` 为真实 Unix 时间;校准前为上电秒数 | wangfq |
| V1.06 | 2026-08-04 | 增加**脱机事件日志**命令:`log_stat`(统计/分页定位)、`log_query`(按全局序号分页,count≤4)、`log_clear`(清除+审计留痕);§2.3 补充日志双时间戳语义(`ts_ms` 相对 + `unix_ts` 已同步,0=未同步,锚点回算规则) | wangfq |
| V1.07 | 2026-08-18 | `log_stat` / `log_query` / `log_clear` 增加**快照流**支持(`stream=snapshot`,与 BLE 0x28/0x29/0x2A 同语义):快照统计 capacity 随芯片动态(48064~449472)、快照分页 count≤14 通道记录 JSON ~810B 超发送缓冲,实测修正;BLE 原始通道仍 ≤2)、快照清除审计留痕;`capacity`/`count` 类型修正为 uint32W25Q256 事件流 130944 超 16bit | wangfq |
| V1.14 | 2026-09-10 | 新增 **§6.9 4G 配置同步(BLE → DBN → Air780**:明确配置权威源为 DBN flashAir780 侧 fskv 仅作启动缓存,**唯一写入者 = DBN 配置帧**,避免第二权威源);复用 **0x8F 本机侧私有帧**(与 BLE 同魔数 `MAGIC_BYTE_DBN_DEFAULT`,链路两端各自本地消费、均不转发),命令复用 `GET_IOT_NET(0x14)` / `GET_IOT_TOPIC(0x16)`,载荷与 BLE 读响应**逐字节同源**(复用 `set_response_iot_net()` / `set_response_iot_topic()`,零新增序列化)、拆 net/topic 两条(最坏 229B / 129B,均 ≤ fskv 单值 255B);**0x8F 帧长上限独立定为 260B**(Loop 业务帧 70B 上限不变,两侧帧缓冲需同步扩容);同步触发点补 `UPDATE_DEV_SERIAL(0x09)` / `SET_SUB_CODE``iot_enable` 位 / **禁改 UART1 波特率**V1.11 的 **0x7D 帧配置同步方案作废**`iot_net_info.mode` 实测无 BLE 写入路径、恒为 IP 模式,故不随载荷同步 | wangfq |
| V1.13 | 2026-08-31 | `initialize``extra_info` 增加可选字段 **`imei`** / **`iccid`**4G 模块 IMEI / 流量卡 ICCID;无 4G 模块时省略或空串;4G 通道由 Air780 填真实值,§6.2 说明) | wangfq |
| V1.12 | 2026-08-31 | **4G 通道适配修订:方案 B(hex 透传)改为方案 C(Air780 协议转换)**——Air780 解析 0x7F 帧并转换为**标准 JSON 命令**loop_data / event_report / initialize / heartbeat,与有线通道一致),平台零改动;V1.11 的 frame_report / frame_cmd 降级为**可选兜底**4G 事件面 = **仅线圈事件**car_enter/car_leave/loop_cut/loop_restore,不含 DBN 内部网络事件);命令响应链路 Air780 单命令状态机(超时回 code=5);link 对象保留;Air780 以 Lua 复刻 iot_event_report 逻辑(沿检测 + ACK + 5s×3 重发 + 16 深队列 + 跨重连同 msg_id),可行性已评估(2026-08-31 | wangfq |
| V1.11 | 2026-08-31 | **4G 通道适配(方案 B:原始帧透传 + hex 封装)**:新增 `frame_report`dev→srv,§6.3/ `frame_cmd`srv→dev,§6.4)两命令(4G 通道专用);4G 通道**不使用**有线标准 JSON 业务命令(loop_data/event_report),不适用网络配置类命令(ssc_net_*/iot_net_*/iot_topic_*4G 通道下发回 code=4);上行附加 `link` 对象(IMEI/ICCID/IMSI/MSISDN/CSQ,§6.6);链路层数据面 0x7F 帧字节流透传(魔数分流在 vd960DBN 侧),0x7D 帧仅 DBN↔Air780 配置同步/握手;平台双通道区分解析 + 新增《DLD960Loop_串口通信协议》解析依赖(§6.7);实施主体:Air8781PAir780EPMvd960Air 工程,vd960DBN UART1 通道列入开发计划(固件未实现) | wangfq |