Files
vd_960/docs/DLD960_IoT_MQTT协议.md
T
wangfq d6873f4b45 docs(MQTT): DLD960_IoT_MQTT协议 V1.10→V1.11 — 4G 通道适配(方案B hex透传)
方案 B(原始帧透传 + hex 封装)正式并入主协议:
- 新增 §6 4G 通道适配: 架构(0x7F帧透传)/命令面差异/上行 frame_report/下行 frame_cmd/不支持命令/link 对象/平台要求
- §3 命令详表加 frame_cmd + frame_report(4G 通道专用标注)
- 头部版本行 + 修订记录 V1.11
- 4G 通道: 不使用有线标准 JSON 业务命令;网络配置类命令回 code=4
- 实施主体: Air8781P(Air780EPM) vd960Air 工程;vd960DBN UART1 通道列入计划(固件未实现,如实标注)
- 同步: README 索引 V1.11 + devlog 置顶条目(协议先行)
2026-08-31 14:45:46 +08:00

53 KiB
Raw Blame History

DLD960 IoT 接口协议(MQTT + JSON

基于《DLD960 串口通信协议》V1.01,将设备管理、参数配置、数据上报映射到 MQTT 协议。 交互格式:JSON。 版本:V1.112026-08-314G 通道适配)


1 通信说明

1.1 连接参数

项目 说明
协议 MQTT 3.1.1 / 5.0
传输层 TCP (TLS 可选)
QoS 配置类 QoS 1,数据上报类 QoS 0/1
编码 UTF-8
序列化 JSON

1.2 Topic 结构

协议仅使用两个主题,所有消息类型通过 JSON 内 cmd 字段区分。

dld960/{dev_serial}/{direction}
字段 说明
dev_serial 设备序列码(6字节十六进制字符串,如 A1B2C3D4E5F6
direction srv = 服务器下发(设备订阅),dev = 设备上报(服务器订阅)

1.2.1 服务器下发 Topic — dld960/{sn}/srv

设备订阅此主题,接收服务器下发的所有命令(配置设置、查询、控制等)。 服务器发布到此主题。

对应串口 CMD0x09 ~ 0x1F, 0x63, 0x64, 0xC5 等。

1.2.2 设备上报 Topic — dld960/{sn}/dev

设备发布到此主题,上报线圈数据、事件、心跳及命令响应。 服务器订阅此主题接收所有设备上行消息。

服务器端可订阅通配符 dld960/+/dev 监听所有设备。


2 JSON 消息格式

2.1 通用结构

{
  "msg_id": 12345,
  "cmd": "dev_info_query",
  "ts": 1719000000,
  "data": { ... }
}
字段 类型 说明
msg_id uint32 消息序列号,递增,用于请求-响应匹配(由发起方生成)
cmd string 命令标识符
ts uint32 Unix 时间戳(秒),发起方填充
data object 命令参数,结构依 cmd 而定

2.2 响应通用结构

{
  "msg_id": 12345,
  "cmd": "dev_info_query",
  "ts": 1719000001,
  "code": 0,
  "msg": "success",
  "data": { ... }
}
字段 类型 说明
code int 0 = 成功,非 0 = 失败码
msg string 错误描述(成功时为 "success"

错误码定义

code 说明
0 成功
1 参数错误(格式/范围不正确)
2 密码验证失败
3 设备忙(操作执行中)
4 不支持的命令
5 内部错误
6 数据超长

OTA 细分错误码(V1.08

顶层 code 保持通用语义,ota_* 命令的细分错误经响应 data.err_code 表达:

err_code 场景 顶层 code
1 单片 CRC 失败 / 乱序缺片(data.offset 指示续传点) 1
2 会话状态不允许(未 begin / 非 downloading 收 ota_data 3
3 安全窗口拒绝(有车压线圈) 3
4 版本冲突(同版本且非 force 1
5 全镜像 CRC 不匹配(data.crc_ok=false 5
6 暂存区写失败(SPI 异常/满) 5

2.3 设备时钟同步(ts 语义)

设备无 RTC/SNTP 时间源,上电后本地时钟为上电秒数(从 0 递增)。为使上行数据带真实 Unix 时间:

  1. 设备上电连接成功后发布 initialize(此时 ts = 上电秒数,值很小,平台据此识别"未校准");
  2. 平台收到 initialize 后,下发 report_config 命令,其信封 ts 字段填当前 Unix 时间
  3. 设备收到后以该 ts 校准本地时钟基准,之后所有上行消息(loop_data / event_report / 心跳 / 命令响应)的 ts 均为真实 Unix 时间

补充约定:

  • report_config 为约定的主同步点;设备亦接受任意下行命令中合法(≥ 1600000000,即 2020-09 之后)的 ts 进行校准,非法/过小值被忽略。
  • 校准前(含首个 initialize),ts 为上电秒数;平台应能容忍并可按数量级区分。
  • 设备重启后需重新校准(无掉电保持)。平台在每次设备 initialize 后都应下发一次带 tsreport_config

脱机日志时间戳语义(V1.06 起)

  • 设备侧事件日志(log_query 拉取)每条记录携带双时间戳:ts_ms(boot 内相对时间,单位 ms,断电归零)+ unix_ts(已同步 Unix 秒,0 = 未同步)。
  • 设备经本协议同步成功后,其后记录的 unix_ts 均为真实 Unix 时间;同步前(含每次上电的 boot 事件)unix_ts = 0,仅 ts_ms 相对时间。
  • 回算规则:绝对时间 = 锚点.unix_ts + (记录.ts_ms - 锚点.ts_ms)/1000,锚点取该 boot 段内第一条 time_anchor 事件(unix_ts 即平台下发值,严格一致)。从未同步过的 boot 段只有相对时间。

3 命令详表

cmd 说明 方向 对应串口
dev_serial_set 更改设备序列码 srv→dev 0x09
dev_info_query 查询设备信息 srv→dev 0x10
ssc_net_set 设置 SSC 网络配置 srv→dev 0x11
ssc_net_query 查询 SSC 网络配置 srv→dev 0x12
iot_net_set 设置 IoT 网络配置 srv→dev 0x13
iot_net_query 查询 IoT 网络配置 srv→dev 0x14
iot_topic_set 设置设备 Topic srv→dev 0x15
iot_topic_query 查询设备 Topic srv→dev 0x16
pwd_verify 验证设备密码 srv→dev 0x1C
pwd_set 设置设备密码 srv→dev 0x1D
factory_reset 设备出厂初始化 srv→dev 0x1E
device_reset 设备复位 srv→dev 0x1F
loop_param_set 设置车检器多路参数 srv→dev 0x63
loop_param_query 读取车检器多路参数 srv→dev 0x64
report_config 设置主动上报 srv→dev 0xC5
log_stat 查询脱机日志统计(事件/快照流) srv→dev
log_query 分页拉取脱机日志(事件/快照流) srv→dev
log_clear 清除脱机日志(事件/快照流,审计留痕) srv→dev
ota_begin 开启 OTA 会话 / 断点续传定位 srv→dev
ota_data OTA 分片下发(256B/片,单片 CRC32) srv→dev
ota_end 结束 OTA 下载,全镜像 CRC32 复核 srv→dev
ota_abort 中止 OTA 会话,释放暂存 srv→dev
ota_flash 触发本地 ISP 刷写(仅 ready 态) srv→dev
ota_status 查询 OTA 状态(含进度) srv→dev
loop_version_query 实时查询地感 Loop MCU 版本(V1.10 srv→dev 0x4A
frame_cmd 4G 通道原始帧透传下发(hex 封装,V1.11) srv→dev
initialize 设备上电初始化登陆 dev→srv
loop_data 线圈传感数据上报 dev→srv 0xC0
event_report 事件上报(平台须应答,见 §5.3 dev→srv
ota_report OTA 进度/结果主动上报 dev→srv
heartbeat 设备心跳 dev→srv
frame_report 4G 通道原始帧透传上报(hex 封装,V1.11) dev→srv

frame_cmd / frame_report4G 通道专用(方案 B:原始帧透传,见 §6),有线通道不使用。4G 通道不使用有线标准 JSON 业务命令(loop_data / event_report 等)。


4 命令详情

4.1 更改设备序列码 dev_serial_set

Topic: dld960/{sn}/srv

请求:

{
  "msg_id": 1,
  "cmd": "dev_serial_set",
  "ts": 1719000000,
  "data": {
    "dev_serial": "A1B2C3D4E5F6"
  }
}

响应: Topic: dld960/{sn}/dev

{
  "msg_id": 1,
  "cmd": "dev_serial_set",
  "ts": 1719000001,
  "code": 0,
  "msg": "success"
}

4.2 查询设备信息 dev_info_query

Topic: dld960/{sn}/srv

请求:

{
  "msg_id": 2,
  "cmd": "dev_info_query",
  "ts": 1719000000
}

响应:

{
  "msg_id": 2,
  "cmd": "dev_info_query",
  "ts": 1719000001,
  "code": 0,
  "msg": "success",
  "data": {
    "dev_serial": "A1B2C3D4E5F6",
    "hard_ver": "1.1",
    "soft_ver": "1.1",
    "loop_ver": "1.2.3",
    "loop_hw_ver": "1.0.0",
    "model": "DLD960",
    "product_code": "960001",
    "sub_code": {
      "net": true,
      "iot": true
    },
    "bus": {
      "bus1": 0,
      "bus2": 0,
      "bus3": 0,
      "bus4": 0
    }
  }
}
data 字段 类型 说明
dev_serial string 12 位十六进制序列码
hard_ver string 硬件版本(整机),格式 "主.次"
soft_ver string 软件版本(整机 DBN MCU 固件),格式 "主.次"
loop_ver string 地感 Loop MCU 固件版本V1.10),格式 "主.次.次"(如 1.2.3);来自 Loop 0x4A 查询缓存,查询未完成/失败则为空字符串
loop_hw_ver string 地感 Loop MCU 硬件版本V1.10),格式 "主.次.次";同上,可为空
model string 产品型号,1~10 字符
product_code string 产品编码,6 位数字字符串
sub_code.net bool 网络功能是否启用
sub_code.iot bool IoT/MQTT 功能是否启用
bus.bus1~4 uint8 各总线探头数

loop_ver 语义(V1.10loop_ver/loop_hw_ver缓存值(设备上电后自动经 Loop 0x4A 查询一次,OTA 刷写成功后刷新);MQTT 命令处理为同步回包,不等 0x4A 异步响应,故查询未完成/失败时回空。平台需最新版本时用 loop_version_query(§4.25)实时查询。

4.3 设置 SSC 网络配置 ssc_net_set

Topic: dld960/{sn}/srv

请求:

{
  "msg_id": 3,
  "cmd": "ssc_net_set",
  "ts": 1719000000,
  "data": {
    "dev_ip": "192.168.1.100",
    "subnet_mask": "255.255.255.0",
    "route_ip": "192.168.1.1",
    "lssc_ip": "192.168.1.200",
    "dns": "8.8.8.8",
    "port": 502
  }
}
data 字段 类型 说明
dev_ip string 设备 IP 地址
subnet_mask string 子网掩码
route_ip string 网关地址
lssc_ip string LSSC 服务器 IP
dns string DNS 服务器 IP
port uint16 端口号

响应:

{
  "msg_id": 3,
  "cmd": "ssc_net_set",
  "ts": 1719000001,
  "code": 0,
  "msg": "success"
}

4.4 查询 SSC 网络配置 ssc_net_query

Topic: dld960/{sn}/srv

请求:

{
  "msg_id": 4,
  "cmd": "ssc_net_query",
  "ts": 1719000000
}

响应: 返回字段同 4.3 的 data

4.5 设置 IoT 网络配置 iot_net_set

Topic: dld960/{sn}/srv

请求:

{
  "msg_id": 5,
  "cmd": "iot_net_set",
  "ts": 1719000000,
  "data": {
    "host": "mqtt.example.com",
    "port": 1883,
    "client_id": "dld960_A1B2C3D4E5F6",
    "username": "admin",
    "password": "secret"
  }
}
data 字段 类型 说明
host string MQTT Broker 域名或 IP
port uint16 MQTT 端口,默认 1883
client_id string MQTT Client ID,空时用空格
username string MQTT 用户名
password string MQTT 密码

响应: 标准成功/失败。

4.6 查询 IoT 网络配置 iot_net_query

Topic: dld960/{sn}/srv

请求:

{
  "msg_id": 6,
  "cmd": "iot_net_query",
  "ts": 1719000000
}

响应: 返回字段同 4.5 的 data

4.7 设置设备 Topic iot_topic_set

Topic: dld960/{sn}/srv

请求:

{
  "msg_id": 7,
  "cmd": "iot_topic_set",
  "ts": 1719000000,
  "data": {
    "client_id_enable": true,
    "topic_pub": "dld960/data/A1B2C3D4E5F6",
    "topic_sub": "dld960/cmd/A1B2C3D4E5F6"
  }
}

响应: 标准成功/失败。

4.8 查询设备 Topic iot_topic_query

Topic: dld960/{sn}/srv

请求:

{
  "msg_id": 8,
  "cmd": "iot_topic_query",
  "ts": 1719000000
}

响应: 返回字段同 4.7 的 data

4.9 验证设备密码 pwd_verify

Topic: dld960/{sn}/srv

请求:

{
  "msg_id": 9,
  "cmd": "pwd_verify",
  "ts": 1719000000,
  "data": {
    "password": "123456"
  }
}
data 字段 类型 说明
password string 6 位数字密码

响应:

{
  "msg_id": 9,
  "cmd": "pwd_verify",
  "ts": 1719000001,
  "code": 0,
  "msg": "success"
}

4.10 设置设备密码 pwd_set

Topic: dld960/{sn}/srv

请求:

{
  "msg_id": 10,
  "cmd": "pwd_set",
  "ts": 1719000000,
  "data": {
    "old_password": "123456",
    "new_password": "654321"
  }
}

响应: 标准成功/失败。旧密码错误时 code=2

4.11 设备出厂初始化 factory_reset

Topic: dld960/{sn}/srv

请求:

{
  "msg_id": 11,
  "cmd": "factory_reset",
  "ts": 1719000000
}

响应:

{
  "msg_id": 11,
  "cmd": "factory_reset",
  "ts": 1719000001,
  "code": 0,
  "msg": "success"
}

4.12 设备复位 device_reset

Topic: dld960/{sn}/srv

请求:

{
  "msg_id": 12,
  "cmd": "device_reset",
  "ts": 1719000000
}

无响应(设备复位后断开连接)。

4.13 设置车检器多路参数 loop_param_set

Topic: dld960/{sn}/srv

请求:

{
  "msg_id": 13,
  "cmd": "loop_param_set",
  "ts": 1719000000,
  "data": {
    "auto_mode": false,
    "channels": [
      {
        "ch": 1,
        "sens": 7,
        "level": "high",
        "delay": 0,
        "output": "exist",
        "exist": 0,
        "dir": 0,
        "safe": 0,
        "fun_mode": 0
      },
      {
        "ch": 2,
        "sens": 7,
        "level": "mid_high",
        "delay": 5,
        "output": "enter_pulse",
        "exist": 10,
        "dir": 0,
        "safe": 0,
        "fun_mode": 0
      },
      {
        "ch": 3,
        "sens": 5,
        "level": "mid_low",
        "delay": 0,
        "output": "exist",
        "exist": 0,
        "dir": 1,
        "safe": 5,
        "fun_mode": 0
      },
      {
        "ch": 4,
        "sens": 8,
        "level": "low",
        "delay": 10,
        "output": "leave_pulse",
        "exist": 15,
        "dir": 0,
        "safe": 0,
        "fun_mode": 0
      }
    ]
  }
}
data 字段 类型 说明
auto_mode bool 自动调频模式,默认 false
channels array 各路通道参数,1~4 路
channels[].ch uint8 通道号 1~4
channels[].sens uint8 sensitivity, 灵敏度 0~9,默认 7
channels[].level string freq_level, "high"(33nF) / "mid_high"(43nF) / "mid_low"(66nF) / "low"(76nF)
channels[].delay uint8 loop_delay, 延时时间,0~200(×0.1s),最大 20s
channels[].output string exist_mode, "exist" / "enter_pulse" / "leave_pulse" / "direction"
channels[].exist uint8 output_mode, 存在方式,0=永久,非0=分钟数
channels[].dir uint8 direction_mode, 方向判别模式,0=触发,1~6=方向输出
channels[].safe uint8 safe_mode, 安全模式,0=关闭,非0=分钟数
channels[].fun_mode uint8 function_mode, 功能模式

响应:

{
  "msg_id": 13,
  "cmd": "loop_param_set",
  "ts": 1719000001,
  "code": 0,
  "msg": "success"
}

4.14 读取车检器多路参数 loop_param_query

Topic: dld960/{sn}/srv

请求:

{
  "msg_id": 14,
  "cmd": "loop_param_query",
  "ts": 1719000000
}

响应:

{
  "msg_id": 14,
  "cmd": "loop_param_query",
  "ts": 1719000001,
  "code": 0,
  "msg": "success",
  "data": {
    "auto_mode": false,
    "channels": [
      {
        "ch": 1,
        "sens": 7,
        "level": "high",
        "delay": 0,
        "output": "exist",
        "exist": 0,
        "dir": 0,
        "safe": 0,
        "fun_mode": 0,
        "f_initial": 105300,
        "f_current": 105280,
        "diff": 20
      }
    ]
  }
}
额外字段 类型 单位 说明
f_initial uint32 Hz freq_initial, 初始频率
f_current uint32 Hz freq_current, 当前实时频率
diff uint32 Hz 变化量(绝对值)

4.15 设置主动上报 report_config

Topic: dld960/{sn}/srv ⚠️ 兼任设备时钟同步点:本命令信封 ts 须填当前 Unix 时间,设备据此校准本地时钟(见 §2.3)。平台应在每次收到设备 initialize 后下发一次。

请求:

{
  "msg_id": 15,
  "cmd": "report_config",
  "ts": 1719000000,
  "data": {
    "sensor_type": 12,
    "enable": true,
    "once": false,
    "env_eval": false,
    "interval": 5,
    "ack_required": false,
    "timeout": 0
  }
}
data 字段 类型 说明
sensor_type uint8 传感器类型,线圈=0x0C(12)
enable bool 是否使能主动上报
once bool 仅上报一次(查询模式)
env_eval bool 环境评估使能
interval uint8 上报间隔(秒),0=实时
ack_required bool 上报是否需要确认
timeout uint8 超时时间(分钟),0=无限制

响应: 标准成功/失败。


4.16 查询脱机日志统计 log_stat

Topic: dld960/{sn}/srv 设备本地 W25Qxx 环形日志(事件区/快照区容量随存储芯片动态,掉电不丢)。用于日志拉取前的分页定位。 通过 data.stream 区分日志流:event(事件日志,缺省)/ snapshot(传感快照)。

请求:

{
  "msg_id": 16,
  "cmd": "log_stat",
  "ts": 1719000000,
  "data": {
    "stream": "event"
  }
}
data 字段 类型 说明
stream string 日志流:event(缺省,可省略)或 snapshot

响应 datastream=event):

{
  "stream": "event",
  "enabled": true,
  "boot_seq": 2,
  "count": 1234,
  "capacity": 16256,
  "seq_first": 100,
  "seq_last": 1333
}

响应 datastream=snapshot):

{
  "stream": "snapshot",
  "enabled": true,
  "boot_seq": 2,
  "count": 1234,
  "capacity": 48064,
  "seq_first": 100,
  "seq_last": 1333
}
data 字段 类型 说明
stream string 日志流:event / snapshot
enabled bool 日志功能是否启用(Flash 初始化成功)
boot_seq uint16 当前启动序号(每次上电 +1,区分复位段)
count uint32 有效记录条数(0~capacity,环形覆盖后 < capacity
capacity uint32 容量上限(随存储芯片与流动态):事件流 W25Q32=16256 / Q64=32640 / Q128=65408 / Q256=130944;快照流 W25Q32=48064 / Q64=105408 / Q128=220096 / Q256=449472
seq_first uint32 逻辑首条记录全局序号(seq_last - count + 1count=0 时为 0
seq_last uint32 最新一条记录全局序号(跨 boot 单调递增)

4.17 分页拉取脱机日志 log_query

Topic: dld960/{sn}/srv 分页按全局序号,不按时间(未同步段时间不可靠)。count 上限按流区分:事件流 4 / 快照流 2(hex 原始字节上报,体积可控)。 通过 data.stream 区分日志流:event(缺省)/ snapshot记录格式为存储原始字节的小写 hex 字符串(与 BLE 通道直传的二进制同源同语义),平台按《DLD960 BLE 协议》字段表解析。

请求:

{
  "msg_id": 17,
  "cmd": "log_query",
  "ts": 1719000000,
  "data": {
    "stream": "event",
    "start_seq": 1330,
    "count": 4
  }
}
data 字段 类型 说明
stream string 日志流:event(缺省,可省略)或 snapshot
start_seq uint32 起始全局序号(含);越界(< seq_first 或 > seq_last)返回空 records
count uint8 拉取条数;事件流上限 4、快照流上限 2,超限按各自上限处理;0 按上限处理

响应 data

{
  "start_seq": 1330,
  "records": [
    {
      "seq": 1330,
      "hex": "a53200000200000001000000..."
    }
  ]
}
字段 类型 说明
seq uint32 全局序号(与 hex 内 offset 4 字段一致,便于快速定位/排序)
hex string 记录原始字节的小写 hex:事件流 OfflogEvt 32B → 64 字符;快照流 SnapRec 64B → 128 字符flash 存储字节原样,小端)

解析字段表(与 BLE 通道完全一致):

结构 字段表
event OfflogEvt 32B 《DLD960 BLE 协议》§7magic(0xA5)/type/len/flags/seq/ts_ms/unix_ts/boot_seq/payload(12B),事件类型与 payload 定义同表
snapshot SnapRec 64B 《DLD960 BLE 协议》§6.4magic(0xA6)/len/flags/seq/ts_ms/boot_seq/coils(4×12B,与 0xC0 线上格式一致)

时间戳语义同事件流:unix_ts 为已同步 Unix 秒(0=未同步),绝对时间用事件流 time_anchor 锚点回算(见 §2.3)。


4.18 清除脱机日志 log_clear

Topic: dld960/{sn}/srv高风险操作:清除动作本身写入事件流(log_clear 审计——谁在何时清了日志,留痕不可清除)。平台侧应做权限控制。

请求:

{
  "msg_id": 18,
  "cmd": "log_clear",
  "ts": 1719000000,
  "data": {
    "stream": "event"
  }
}
data 字段 类型 说明
stream string 日志流:event(缺省,可省略)或 snapshot

响应: 标准成功/失败。

  • stream=event:成功后 log_statcount 归 1(仅剩审计记录),seq_last 继续递增(序号不复位)。阻塞 ~2.8s(63 个数据扇区 SPI 擦除),请勿高频调用。
  • stream=snapshot:成功后 log_statcount 归 0seq_last 继续递增;清除动作写入事件流审计(log_clearpayload 标记快照流)。阻塞 ~45ms(逻辑清除 + 当前写扇区擦除,其余扇区由环形写覆盖时自动擦)。

4.19 开启 OTA 会话 ota_begin

Topic: dld960/{sn}/srv 依据《DLD960_MQTT_OTA协议.md》设计稿 V1.01ROADMAP P1.4 ①:Loop MCU 远程 OTA,先存后刷)。 能力探测:老固件(V1.07 及以下)无 ota_* 命令,收到回 code=4;平台下发前用 ota_statusdev_info_query.soft_ver 判断。

请求:

{
  "msg_id": 401,
  "cmd": "ota_begin",
  "ts": 1719000000,
  "data": {
    "target": "loop",
    "size": 46864,
    "crc32": 305419896,
    "version": "1.1.0",
    "force": false
  }
}
data 字段 类型 说明
target string loop(当前支持);dbn 预留
size uint32 镜像 bin 字节数(≤ 98304 = 96KBSlot 数据区 100KB 预留 4KB 边界余量)
crc32 uint32 全镜像 CRC32(十进制,算法见 §4.19.1)
version string 目标固件版本(写入元数据,审计用;bootloader 不校验版本)
force bool true = 覆盖现有暂存镜像 / 忽略版本冲突(默认 false)

设备行为:

  1. 读暂存元数据:若已有镜像且 size+crc32 与本次一致 →
    • state=ready → 返回 offset=size(平台可直接 ota_flash
    • state=downloading → 返回 offset=received(断点续传)
  2. 不一致 → 分配 Slot(A 当前 / B 回滚),写元数据 state=downloading, received=0,返回 offset=0
  3. force=false 且目标版本 == 当前运行版本 → 回 code=1, err_code=4(防重复刷写,可 force 绕过)

响应 data

{
  "msg_id": 401,
  "cmd": "ota_begin",
  "ts": 1719000001,
  "code": 0,
  "msg": "success",
  "data": {
    "target": "loop",
    "slot": "a",
    "offset": 0,
    "received": 0,
    "size": 46864,
    "crc32": 305419896,
    "state": "downloading"
  }
}

4.19.1 CRC32 算法(必须双方一致)

标准 CRC-32/ISO-HDLCpoly 0x04C11DB7reflected 0xEDB88320),init 0xFFFFFFFFrefin/refout truexorout 0xFFFFFFFF。平台侧 Python zlib.crc32() / binascii.crc32() 即此算法;设备侧查表法。全镜像 CRC = offset 0 ~ size-1 连续;单片 CRC = 仅该 256B。


4.20 OTA 分片下发 ota_data

Topic: dld960/{sn}/srv 单片大小 256B 是协议常量,不接受协商;hex 512 字符 + JSON 外壳 ≈ 640B < 设备接收缓冲 1024B。

请求:

{
  "msg_id": 402,
  "cmd": "ota_data",
  "ts": 1719000002,
  "data": {
    "target": "loop",
    "offset": 0,
    "crc32": 2524764894,
    "data": "6a6173646f6e...512 hex 字符 = 256B"
  }
}
data 字段 类型 说明
target string ota_begin
offset uint32 本片在镜像中的绝对偏移(256 对齐,首片 0
crc32 uint32 本片 256B 的 CRC32(十进制)
data string 256B 原始字节小写 hex512 字符

设备行为:

  1. offset == received(顺序片)→ 单片 CRC32 校验 → 写 W25Qxx 暂存(256B 页对齐)→ received += 256(≥size 截断为 size)→ code=0
  2. offset < received(重复片,平台重发)→ 幂等回 code=0,不重写
  3. offset > received(缺片/乱序)→ code=1, err_code=1 + data.offset=received(指示平台从该处续传;协议不要求乱序重组)
  4. 单片 CRC 失败 → code=1, err_code=1(平台重发本片;连续失败平台可 ota_abort
  5. 会话未开始 / 状态非 downloading → code=3, err_code=2
  6. data 非 512 hex / offset 非 256 对齐 / 超 size → code=1 参数错误

响应: 标准成功/失败 + data 回显 {offset, received}


4.21 结束下载 ota_end

Topic: dld960/{sn}/srv

请求:

{
  "msg_id": 403,
  "cmd": "ota_end",
  "ts": 1719000003,
  "data": {
    "target": "loop",
    "crc32": 305419896
  }
}

设备行为:

  1. received != sizecode=1 + data.offset=received(不完整,续传)
  2. received == size → 读回暂存区全镜像计算 CRC32,与 ota_begin 声明值比对
    • 一致 → 元数据 state=ready, last_result=0code=0, data={crc_ok:true}
    • 不一致 → 元数据 state=downloading(保留已下载数据,可重发错片)→ code=5, err_code=5, data={crc_ok:false}

4.22 中止会话 ota_abort

Topic: dld960/{sn}/srv

{
  "msg_id": 404,
  "cmd": "ota_abort",
  "ts": 1719000004,
  "data": { "target": "loop" }
}

设备行为:元数据 state=aborted,Slot 标记可覆盖;正在刷写时 abort → 停止发送后续 A7 块(Loop 端由 bootloader 超时复位回 APP 兜底)。响应标准成功。


4.23 触发刷写 ota_flash

Topic: dld960/{sn}/srv会车安全关键命令:平台应确认现场允许(无车压线圈、非高峰)再下发。

请求:

{
  "msg_id": 405,
  "cmd": "ota_flash",
  "ts": 1719000005,
  "data": {
    "target": "loop",
    "slot": "a",
    "force": false
  }
}
data 字段 类型 说明
slot string a / b(缺省 = 当前 ready 的槽)
force bool true = 跳过安全窗口检查(高风险,平台授权)

设备行为(同步检查 → 异步刷写):

  1. 安全窗口检查force=false 时):4 通道 Loop 有车(VD_FLAG 任一置位)→ code=3, err_code=3(有车,拒绝;平台提示"车辆离开后重试")。刷写期间会阻断检测与继电器控制 → 平台建议低峰执行
  2. 元数据非 ready → code=3(先 ota_end 完成校验)
  3. 通过 → 立即回 code=0(异步),进入刷写:
    • 写 offlog 事件日志:固件升级开始target/slot/version/size
    • 暂停事件上报与脱机日志(会话期间)MQTT event_report 暂停发送(入队积压,16 深溢出丢最旧,会话结束恢复后补发);offlog/快照落盘暂停("升级开始"日志在暂停前写入、"升级结果"在恢复后补记)
    • 维持 Loop 现状:复位进 bootloader 后的 GPIO/继电器状态与现网 BLE OTA 升级一致,不额外干预
    • 9F 01 00 01 A5 A7 启动帧 → Loop APP 写 flag 复位 → bootloader 回 pre_ok
    • 发 A6 地址帧(0x08003400 4 字节大端)→ addr_ok
    • 从 W25Qxx 暂存读镜像,按 ≤254B/块发 A7(非阻塞 tick 驱动:每轮主循环发送 1~2 块并检查 ACK,绝不阻塞主循环,保证刷写窗口内 MQTT PINGREQ/心跳/IWDG 喂狗正常);停等 ACK,1s 超时重发 ×3
    • 末块(sub_amount=1)→ bootloader 写剩余 → 清 flag → 复位跑新 APP
  4. 进度经 ota_report 上行(§5.5);失败重试 ×3 仍失败 → 元数据 state=flash_failed + event_report{type:ota_error} 告警(平台必答)
  5. 刷写结果(设备侧,V1.09 明确)
    • 成功(末块 ACK)→ 元数据 state=idle镜像保留size/crc32/version/slot 不变,last_result=0flash_cnt+1)→ 立即上报 ota_report{stage:done}(重发 3 次×5s,同 msg_id/ts)→ 恢复 event_report 发送与 offlog/快照落盘
    • 失败 → 元数据 state=flash_failed + ota_report{stage:failed} + event_report{type:ota_error}done/failed 同重发策略)
    • 状态语义:刷写完成后 DBN 侧状态回 idle("本轮刷写已结束"),与"下载完成待刷(ready"严格区分——平台不得把 state=ready 判为"刷写未启动":本地刷写 70 块(17224B)实测 <1s,平台轮询间隔可能错过 flashing 中间态

响应: code=0 仅表示已启动,不代表刷写成功——结果以 ota_report / ota_status 为准。


4.24 查询状态 ota_status

Topic: dld960/{sn}/srv

请求:

{ "msg_id": 406, "cmd": "ota_status", "ts": 1719000006 }

响应 data

{
  "target": "loop",
  "state": "flashing",
  "slot": "a",
  "size": 46864,
  "received": 46864,
  "crc32": 305419896,
  "version": "1.1.0",
  "progress": { "sent": 42112, "total": 46864 },
  "last_result": 0,
  "last_error": 0
}
data 字段 类型 说明
state string idle / downloading / ready / flashing / flash_failed / abortedidlesize>0 = 已刷写完成(镜像保留可重刷)
progress.sent uint32 刷写阶段已送 Loop 的字节数
last_result uint32 上次刷写结果:0=无/成功,非 0=错误码
last_error uint32 上次失败细分错误码

设备状态机:

        ota_begin(新会话)          ota_data×N          ota_end(CRC✓)
 IDLE ─────────────────▶ DOWNLOADING ────────────▶ READY
  ▲                        │  ▲                        │
  │  ota_abort             │  │ ota_end(CRC✗)          │ ota_flash(安全检查✓)
  │  / flash_failed        │  └──────────────┐         ▼
  └────────────────────────┴─────────────────┴───── FLASHING ──成功──▶ IDLE(镜像保留, last_result=0)
                                                      │
                                                      └──失败×3──▶ FLASH_FAILED ──ota_abort/ota_begin──▶ IDLE

平台判定指引(V1.09):

  • 主依据ota_report 主动上报(stage=done 成功 / stage=failed 失败,§5.5)——ota_flash 后平台应优先等该信号
  • 兜底ota_status 查询——state=idle 且 size>0 且 last_result=0 = 刷写成功(镜像保留,可重刷);state=flash_failed = 失败;state=ready = 下载完成待刷(不是"刷写未启动"
  • 刷写窗口极短(<1s),轮询可能捕捉不到 flashing 中间态,不得以此判失败

4.25 查询地感版本 loop_version_query

Topic: dld960/{sn}/srv 用途:实时查询地感 Loop MCU 固件/硬件版本(V1.10,配合远程 OTA 做升级前后版本核对)。

请求:

{ "msg_id": 407, "cmd": "loop_version_query", "ts": 1719000007 }

设备行为(异步):

  1. 经 UART2 向 Loop MCU 发 0x4A 查询帧(7F 00 01 4A ...,见 DLD960Loop_串口通信协议.md §3.01
  2. 收到 Loop 响应后解析(Soft/Hard 各三段)→ 更新本地缓存 → 立即回包
  3. Loop 无响应/超时 → 回 code=5loop_ver 保持缓存值(可为空)

响应:

{
  "msg_id": 407,
  "cmd": "loop_version_query",
  "ts": 1719000008,
  "code": 0,
  "msg": "success",
  "data": {
    "loop_ver": "1.2.3",
    "loop_hw_ver": "1.0.0",
    "version_str": "V1.2.3 (HW:1.0.0)"
  }
}
data 字段 类型 说明
loop_ver string 地感固件版本,格式 "主.次.次"Soft_Main.Sub.SSub
loop_hw_ver string 地感硬件版本,格式 "主.次.次"Hard_Main.Sub.SSub
version_str string 人类可读串,"V{soft} (HW:{hard})"Loop 无响应时可能为空

时序与缓存:设备上电后自动查询一次并缓存;ota_report stage=done(Loop 复位跑新固件)后自动刷新缓存。平台升级流程建议:ota_flashloop_version_query 记录旧版本 → 刷写完成后再次查询核对新版本是否生效。


5 设备主动上报

5.1 设备上电登陆信息 initialize

Topic: dld960/{sn}/dev QoS: 0/1

{
  "msg_id": 1,
  "cmd": "initialize",
  "ts": 1719000000,
  "data": {
    "dev_serial":"A1B2C3D4E5F6",
    "model": "DLD960",
    "hard_ver": "1.0",
    "soft_ver": "1.0",
    "loop_ver": "1.2.3",
    "loop_hw_ver": "1.0.0",
    "extra_info": {
      "code": "869756049404948",
      "csq": "21",
      "location": "113.9237976,022.6400375",
    }
  }
}
字段 类型 说明
data.dev_serial string 设备序列码
data.model string 产品型号
data.hard_ver string 硬件版本(整机)
data.soft_ver string 固件版本(整机 DBN MCU
data.loop_ver string 地感 Loop MCU 固件版本V1.10),格式 "主.次.次";来自 0x4A 缓存,查询未完成/失败则为空字符串
data.loop_hw_ver string 地感 Loop MCU 硬件版本V1.10),格式 "主.次.次";同上可为空
data.extra_info.code string 可选,设备代码如IMSI/ICCID
data.extra_info.csq string 可选,当前信号强度
data.extra_info.location string 可选,经纬度(经度,纬度)

V1.10 说明loop_ver/loop_hw_ver尽力携带(上电后异步 0x4A 查询,initialize 发出时可能未就绪 → 回空);平台核对版本请用 loop_version_query(§4.25)。

5.2 线圈传感数据 loop_data

Topic: dld960/{sn}/dev QoS: 0/1 上报节奏(三档):① 任一通道 iscar 翻转(进/出车)→ 立即上报,不受间隔限制;② 任一通道 |diff| 超阈值 → 300ms 快速档;③ 平稳 → 按 report_config.interval(默认 60s)。

{
  "msg_id": 100,
  "cmd": "loop_data",
  "ts": 1719000100,
  "data": {
    "channels": [
      {
        "ch": 1,
        "level": "high",
        "iscar": false,
        "loop_ok": true,
        "freq": 105280,
        "diff": 20,
        "sens": 7,
        "cndtn": 0,
        "misc": {
          "type": "time",
          "value": 0
        }
      },
      {
        "ch": 2,
        "level": "mid_high",
        "iscar": true,
        "loop_ok": true,
        "freq": 98700,
        "diff": 1500,
        "sens": 7,
        "cndtn": 2,
        "misc": {
          "type": "time",
          "value": 350
        }
      },
      {
        "ch": 3,
        "level": "mid_low",
        "iscar": false,
        "loop_ok": false,
        "freq": 0,
        "diff": 0,
        "sens": 5,
        "cndtn": 0,
        "misc": {
          "type": "cut_count",
          "value": 3
        }
      },
      {
        "ch": 4,
        "level": "low",
        "iscar": false,
        "loop_ok": true,
        "freq": 62100,
        "diff": 5,
        "sens": 8,
        "cndtn": 0,
        "misc": {
          "type": "flow_count",
          "value": 128
        }
      }
    ]
  }
}
通道字段 类型 说明
ch uint8 通道号 1~4
level string freq_level 线圈高低频档位
iscar bool 是否有车
loop_ok bool 线圈是否正常(false=断开)
freq uint32 当前频率 (Hz)
diff uint32 变化量
sens uint8 sensitivity, 当前灵敏度等级
cndtn uint8 condition, 环境状态评估值(越大干扰越大)
misc.type string "time"(时间量) / "cut_count"(断开次数) / "flow_count"(车流量)
misc.value uint32 杂项数值(时间量单位 50ms

5.3 事件上报 event_report

上报 Topic: dld960/{sn}/dev 应答 Topic: dld960/{sn}/srv QoS: 1(建议) ⚠️ 本指令要求平台必须应答(区别于 loop_data / heartbeat 的单向上报)

设备检测到事件时主动上报,非周期性。事件(进车/出车/线圈断开等)是不可再生的关键数据MQTT QoS 仅保证 broker 收到,不代表平台业务层已处理入库,因此引入应用层应答 + 设备重发机制闭环确认。

event_report 不受 report_config.enable 门控——设备上电即检测并上报事件。report_configenable/interval 仅控制 loop_data 周期上报,与事件上报解耦。

设备上报(dev topic):

{
  "msg_id": 101,
  "cmd": "event_report",
  "ts": 1719000200,
  "data": {
    "events": [
      {
        "type": "car_enter",
        "ch": 2,
        "value": 0
      },
      {
        "type": "car_leave",
        "ch": 2,
        "value": 350
      },
      {
        "type": "loop_cut",
        "ch": 3,
        "value": 0
      }
    ]
  }
}
事件类型 type 说明 value 含义
car_enter 车辆进入 0
car_leave 车辆离开 通过时间 (×50ms)
loop_cut 线圈断开 0
loop_restore 线圈恢复 断开持续时长 (×50ms)
ota_error OTA 刷写失败告警(重试×3 仍失败,V1.08) 0x1001=启动帧无响应 / 0x1002=地址帧错误 / 0x1003=数据块 ACK 超限 / 0x1004=全镜像校验失败 / 0x1005=安全窗口拒绝后强制失败

平台应答(srv topic,收到后必须立即回复):

{
  "msg_id": 101,
  "cmd": "event_report",
  "ts": 1719000201,
  "code": 0,
  "msg": "success"
}
字段 说明
msg_id 必须回显设备上报的 msg_id,设备以此匹配确认
cmd 固定 "event_report"
code 0 = 已接收并处理;非 0 = 处理失败(设备视同未确认,进入重发)

设备端确认与重发机制:

  1. 发出 event_report 后启动确认定时器,5s 内未收到 msg_id 匹配且 code=0 的应答 → 重发;
  2. 重发使用相同的 msg_id 与原始 ts(首次事件发生时间,不随重发刷新),平台以此去重;
  3. 最多重发 3 次(含首发共 4 次)。仍未确认 → 事件保留在待发队列,待 MQTT 重连成功或下一次事件触发时合并上报;
  4. 等待确认期间新产生的事件,合并进下一包的 events 数组(使用新 msg_id)。单包长度受发布缓冲限制(≤500B),超出时拆包分批;
  5. 待发队列建议深度 ≥16 条事件,溢出时丢弃最旧事件(先保新鲜数据)。

平台端要求:

  1. 收到 event_report 先落库、后应答,应答即承诺数据已持久化;
  2. (dev_serial, msg_id) 在近期窗口(建议 10 分钟)内去重——重复包为设备未收到应答所致,直接应答 code=0,不重复入库(设备重启后 msg_id 从头递增,去重必须限定时间窗口);
  3. 应答报文应控制在最小集(不带 data),减轻设备下行解析负担。

时序(正常 / 应答丢失重发):

设备                        平台
 │── event_report(id=101) ──▶│  落库
 │◀─ ack(id=101, code=0) ────│  确认, 事件出队
 │
 │── event_report(id=102) ──▶│  落库
 │      ✗ 应答丢失            │
 │  [5s 超时]                 │
 │── event_report(id=102) ──▶│  (dev_serial,102) 命中去重 → 不入库
 │◀─ ack(id=102, code=0) ────│  确认, 事件出队

5.4 设备心跳 heartbeat

Topic: dld960/{sn}/dev 周期:默认 60 秒

{
  "msg_id": 200,
  "cmd": "heartbeat",
  "ts": 1719000060,
  "data": {
    "uptime": 3600,
    "loop_status": [true, true, false, true],
    "net_status": true,
    "iot_status": true
  }
}
data 字段 类型 说明
uptime uint32 设备运行时长(秒)
loop_status bool[4] 各路线圈是否正常
net_status bool 以太网连接状态
iot_status bool MQTT 连接状态

5.5 OTA 进度/结果上报 ota_report

Topic: dld960/{sn}/dev · QoS 1 用途:OTA 会话与刷写进度/结果主动推送(V1.08)。进度可丢,结果可经 ota_status 兜底查询(设备侧元数据持久化 last_result)。

上报:

{
  "msg_id": 501,
  "cmd": "ota_report",
  "ts": 1719000007,
  "data": {
    "target": "loop",
    "stage": "flashing",
    "progress": { "sent": 42112, "total": 46864 },
    "code": 0,
    "msg": ""
  }
}
data 字段 说明
target 目标:loop(当前支持)
stage begin(会话开启)/ downloading(片落盘)/ ready(校验通过)/ flashing(刷写中)/ done(刷写成功,Loop 已重启)/ failed(刷写失败)
progress sent/total 字节(刷写阶段)
code stage 相关结果码

上报节奏begin/ready/done/failed 各 1 次;flashing 阶段按进度节流(建议每 64 块或每 8KB 一次,避免刷写期间消息风暴)。done/failed 设备侧重发 3 次(间隔 5s,同 msg_id/ts),平台去重窗口建议 10 分钟(与 event_report 同策略)。

平台判定(V1.09 明确)ota_report 是刷写结果的主依据——收到 stage=done 判成功、stage=failed 判失败,无需再轮询 ota_status。设备 ota_flash 响应 code=0 仅表示已启动(异步),不得以"轮询 ota_status 未见 flashing"或"状态持续为 ready"判"刷写未启动"——本地刷写 <1s 完成,轮询大概率错过中间态;兜底判定见 §4.24。

会话期间静默V1.08):OTA 会话期间(ota_begin ~ 结束)设备暂停 event_report 发送(入队积压,结束后补发)与 offlog/快照落盘("升级开始"日志暂停前写入、"升级结果"恢复后补记),保证刷写窗口内 MQTT 保活与 IWDG 喂狗不受影响。


6 4G 通道适配(方案 B:原始帧透传 + hex 封装,V1.11 新增)

适用场景:vd960DBN 有线网络失效时,经 Air8781P 整板(Air780EPM 4G 模组,LuatOS vd960Air 工程) 兜底上报。 4G 通道与有线通道命令面不同(见 §6.2),平台须按通道区分解析。

6.1 通道架构

上行: vd960Loop --0x7F帧(UART2)--> vd960DBN --原样转发(UART1)--> Air780 --MQTT frame_report--> 平台
下行: 平台 --MQTT frame_cmd--> Air780 --原样(UART1)--> vd960DBN --0x7F帧(UART2)--> vd960Loop
  • 魔数分流(在 vd960DBN 侧)0x7F 帧 → 转发 UART2vd960Loop 地感);0x8F 帧 → DBN 本地处理(BLE/串口配置命令同源)
  • Air780 职责:0x7F 帧切包(Lua 状态机,只切包不懂内容)+ MQTT 收发 + hex 封装
  • vd960DBN 职责UART2↔UART1 双向转发(复用 manage_dbn_ble_transparent 透传模式;列入 vd960DBN 开发计划,固件未实现
  • 链路层(UART:数据面 0x7F 帧字节流透传(0x7F 帧自带 LEN/校验,Air780 按帧切包);0x7D 帧仅用于 DBN↔Air780 配置同步下发/握手(详见 vd960Air 工程 readme §3

6.2 命令面差异

通道 上行(dev→srv 下行(srv→dev
有线(ETH MQTT initialize / loop_data / event_report / heartbeat / ota_report(标准 JSON dev_serial_set / ssc_net_* / iot_net_* / loop_param_* / report_config / log_* / ota_* / loop_version_query(标准 JSON
4GAir780 透传) frame_reporthex 帧,§6.3+ initializeJSON,§6.3.1 frame_cmdhex 帧,§6.4
  • 4G 通道不使用有线通道的标准 JSON 业务命令(loop_data / event_report 等)
  • 4G 通道不适用的网络配置类命令:ssc_net_set / ssc_net_query / iot_net_set / iot_net_query / iot_topic_set / iot_topic_query(4G 接入网是 SIM 运营商网络,APN/主题由 4G 侧管理;经 4G 通道下发返回 code=4
  • 其余命令(loop_param_、log_、ota_、pwd_、report_config、device_reset 等)可由平台经 frame_cmd 下发 0x8F 帧由 DBN 本地处理,或 0x7F 帧转发 vd960Loop——设备侧指令面不变

6.3 上行 frame_report

Topic: dld960/{sn}/dev · QoS 1 · 每个 0x7F 帧一条

{
  "msg_id": 123,
  "cmd": "frame_report",
  "ts": 1719000000,
  "data": { "frame": "7f00c0060102030405aabb" },
  "link": { "imei": "860012345678901", "iccid": "89860012345678901234", "imsi": "460001234567890", "msisdn": "", "csq": 23, "net": "4G" }
}
data 字段 类型 说明
frame string(hex) 0x7F 帧完整字节 hex(含魔数 0x7F/校验字节),小写;平台按《DLD960Loop_串口通信协议》解析(0xC0 传感 / 0x0C 响应 / 事件等)
  • link 为 4G 特有字段(§6.6),每条上行携带
  • msg_id / ts 由 Air780 生成;ts 校准遵循 §2.3(见 §6.3.1)

6.3.1 4G 通道 initializeJSON

4G 通道的 initializeAir780 在 MQTT 连接成功后发送(标准 JSON 形态 + link),用于:① 平台识别 4G 设备上线;② 平台下发 report_config 校准 ts(§2.3)。

{
  "msg_id": 1,
  "cmd": "initialize",
  "ts": 3,
  "data": { "loop_ver": "1.0.5", "loop_hw_ver": "1.0" },
  "link": { "imei": "860012345678901", "iccid": "89860012345678901234", "imsi": "460001234567890", "msisdn": "", "csq": 23, "net": "4G" }
}
  • loop_ver / loop_hw_ver:地感 Loop 版本(与 §4.25 同语义;vd960DBN 未实现 UART1 通道前可省略)
  • 后续传感/事件/响应数据一律经 frame_report

6.4 下行 frame_cmd

Topic: dld960/{sn}/srv · QoS 1

{
  "msg_id": 456,
  "cmd": "frame_cmd",
  "ts": 1719000100,
  "data": { "frame": "7f000809010203040506" }
}
data 字段 类型 说明
frame string(hex) 0x7F 帧(→ vd960Loop 地感指令)或 0x8F 帧(→ vd960DBN 本地配置)hex
  • Air780 收到 → hex 解码 → 原始字节 → UART1 → vd960DBN 魔数分流
  • 地感/设备响应经 frame_report 上行

6.5 4G 通道不支持的命令

cmd 说明
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 配置)

经 4G 通道下发以上命令:设备回 code=4 unsupported

字段 来源 说明
imei mobile.imei() 4G 模块 IMEI,设备唯一标识
iccid mobile.iccid() 流量卡卡号,物联网卡管理识别用(卡商未写入 → 空串)
imsi mobile.imsi() IMSI(部分卡返回空)
msisdn mobile.msisdn() 手机号(物联网卡通常拿不到 → 空串)
csq mobile.csq() 信号强度 0-31(31 最强,99/255 无信号)
net 固定 "4G" 网络制式(预留扩展)

6.7 平台侧要求

  1. 双通道区分:有线通道按标准 JSON 业务命令解析;4G 通道按 frame_report / frame_cmd 解析(可依 link.net 或报文形态区分)
  2. 新增解析依赖:《DLD960Loop_串口通信协议》(hex → 0x7F 帧 → 字段)
  3. 设备唯一标识dev_serial 与有线通道同一序列号(Topic 族一致,平台认同一台设备);link.imei / link.iccid 辅助 4G 设备/流量卡管理
  4. 时钟校准Air780 上线发 initialize(JSON + link)后,平台照常下发 report_config 校准 ts(§2.3

修订记录

版本 修订时间 修订说明 修订人
V1.00 2026-06-22 初始版本,基于串口协议 V1.01 wangfq
V1.01 2026-07-07 Topic 压缩为双主题({sn}/srv + {sn}/dev),消息类型由 cmd 字段区分 wangfq
V1.02 2026-07-09 缩写相关字段 wangfq
V1.03 2026-07-09 增加设备上电初始化指令 wangfq
V1.04 2026-07-15 event_report 增加平台必答机制:应答格式(回显 msg_id)、设备 5s 超时重发(同 msg_id/ts,最多 3 次)、待发队列合并上报、平台去重与先落库后应答要求 wangfq
V1.05 2026-07-15 增加设备时钟同步(§2.3,方案B):设备无 RTCinitialize 上线后平台经 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.11 2026-08-31 4G 通道适配(方案 B:原始帧透传 + hex 封装):新增 frame_reportdev→srv,§6.3/ frame_cmdsrv→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
V1.10 2026-08-20 网络上报携带地感版本(配合远程 OTA 升级前后版本核对):dev_info_query 响应 + initialize 上报新增 loop_ver/loop_hw_ver(地感 Loop MCU 固件/硬件版本,格式 "主.次.次",来自 0x4A 查询缓存,尽力携带可为空);新增命令 loop_version_query(§4.25,srv→dev 实时查询,设备经 UART2 0x4A 异步查询后回包,含 loop_ver/loop_hw_ver/version_str);版本语义:soft_ver=整机 DBN 固件(主.次),loop_ver=地感 Loop 固件(主.次.次),二者区分 wangfq
V1.09 2026-08-20 OTA 刷写结果判定修复(现场:刷写物理成功但平台误判"刷写未启动"):① 刷写成功后台侧状态回 idle(镜像保留:size/crc32/version 不变,last_result=0,可重刷),与"下载完成待刷 ready"严格区分;② ota_report 升级为刷写结果主依据stage=done/failed 设备必报,重发 3 次×5s),ota_status 仅兜底;③ 明确平台判定指引——idle+size>0+last_result=0=成功,不得以轮询未见 flashing 或状态持续 ready 判"刷写未启动"(本地刷写 <1s,轮询大概率错过中间态);④ ota_begin 兼容 idle+size/crc32 一致 → 免下载直接可刷(重刷) wangfq
V1.08 2026-08-20 增加 Loop MCU 远程 OTAROADMAP P1.4 ①,先存后刷):命令 ota_begin / ota_data / ota_end / ota_abort / ota_flash / ota_statussrv→dev+ ota_reportdev→srv);单片 256B + 单片/全镜像 CRC32ISO-HDLC,§4.19.1);断点续传(ota_begin 返回 offset);Slot A/B 双槽回滚(镜像 ≤96KB);ota_flash 安全窗口检查 + 非阻塞 tick 驱动刷写(刷写窗口内 MQTT 保活/IWDG 不受影响);会话期间暂停 event_report 发送与脱机日志落盘;event_report 扩展 type=ota_error 失败告警;§2.2 补 OTA 细分错误码(err_code);老固件兼容(ota_* 回 code=4 wangfq