Files
vd_960/docs/DLD960_IoT_MQTT协议.md
T
wangfq 5a1893cd1c 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
2026-08-18 14:04:26 +08:00

28 KiB
Raw Blame History

DLD960 IoT 接口协议(MQTT + JSON

基于《DLD960 串口通信协议》V1.01,将设备管理、参数配置、数据上报映射到 MQTT 协议。 交互格式:JSON。


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 数据超长

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
initialize 设备上电初始化登陆 dev→srv
loop_data 线圈传感数据上报 dev→srv 0xC0
event_report 事件上报(平台须应答,见 §5.3 dev→srv
heartbeat 设备心跳 dev→srv

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",
    "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 软件版本,格式 "主.次"
model string 产品型号,1~10 字符
product_code string 产品编码,6 位数字字符串
sub_code.net bool 网络功能是否启用
sub_code.iot bool IoT/MQTT 功能是否启用
bus.bus1~4 uint8 各总线探头数

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(逻辑清除 + 当前写扇区擦除,其余扇区由环形写覆盖时自动擦)。

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",
    "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 固件版本
data.extra_info.code string 可选,设备代码如IMSI/ICCID
data.extra_info.csq string 可选,当前信号强度
data.extra_info.location string 可选,经纬度(经度,纬度)

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)

平台应答(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 连接状态

修订记录

版本 修订时间 修订说明 修订人
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