设备无RTC/SNTP, 原上行 ts=mstick()/1000(上电秒数)。方案B: - net_srv.c 新增 dev_time_sync()/dev_time_now(): 门槛≥1600000000挡上电秒数, 已校准返回真Unix, 未校准退回上电秒数; 无符号相减处理mstick 49.7天回绕 - manage_mqtt_recv_message 从下行命令信封 ts 校准 (report_config主同步点) - 全部上行 ts (14处命令响应+initialize+loop_data+event_report) 改 dev_time_now() heartbeat uptime 字段保留 mstick()/1000 (运行时长非时间戳) - 协议 V1.05 §2.3 时间同步 + report_config 兼任同步点职责 - tests/test_dev_time_sync.c 6组全过 平台侧配合: 收到 initialize 后下发 report_config 时 ts 填当前 Unix 时间
896 lines
21 KiB
Markdown
896 lines
21 KiB
Markdown
# 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`
|
||
|
||
设备订阅此主题,接收服务器下发的所有命令(配置设置、查询、控制等)。
|
||
服务器发布到此主题。
|
||
|
||
对应串口 CMD:0x09 ~ 0x1F, 0x63, 0x64, 0xC5 等。
|
||
|
||
### 1.2.2 设备上报 Topic — `dld960/{sn}/dev`
|
||
|
||
设备发布到此主题,上报线圈数据、事件、心跳及命令响应。
|
||
服务器订阅此主题接收所有设备上行消息。
|
||
|
||
服务器端可订阅通配符 `dld960/+/dev` 监听所有设备。
|
||
|
||
---
|
||
|
||
# 2 JSON 消息格式
|
||
|
||
## 2.1 通用结构
|
||
|
||
```json
|
||
{
|
||
"msg_id": 12345,
|
||
"cmd": "dev_info_query",
|
||
"ts": 1719000000,
|
||
"data": { ... }
|
||
}
|
||
```
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `msg_id` | uint32 | 消息序列号,递增,用于请求-响应匹配(由发起方生成) |
|
||
| `cmd` | string | 命令标识符 |
|
||
| `ts` | uint32 | Unix 时间戳(秒),发起方填充 |
|
||
| `data` | object | 命令参数,结构依 cmd 而定 |
|
||
|
||
## 2.2 响应通用结构
|
||
|
||
```json
|
||
{
|
||
"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` 后都应下发一次带 `ts` 的 `report_config`。
|
||
|
||
---
|
||
|
||
# 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 |
|
||
| `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`
|
||
|
||
**请求:**
|
||
|
||
```json
|
||
{
|
||
"msg_id": 1,
|
||
"cmd": "dev_serial_set",
|
||
"ts": 1719000000,
|
||
"data": {
|
||
"dev_serial": "A1B2C3D4E5F6"
|
||
}
|
||
}
|
||
```
|
||
|
||
**响应:** Topic: `dld960/{sn}/dev`
|
||
|
||
```json
|
||
{
|
||
"msg_id": 1,
|
||
"cmd": "dev_serial_set",
|
||
"ts": 1719000001,
|
||
"code": 0,
|
||
"msg": "success"
|
||
}
|
||
```
|
||
|
||
## 4.2 查询设备信息 `dev_info_query`
|
||
|
||
> Topic: `dld960/{sn}/srv`
|
||
|
||
**请求:**
|
||
|
||
```json
|
||
{
|
||
"msg_id": 2,
|
||
"cmd": "dev_info_query",
|
||
"ts": 1719000000
|
||
}
|
||
```
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"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`
|
||
|
||
**请求:**
|
||
|
||
```json
|
||
{
|
||
"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 | 端口号 |
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"msg_id": 3,
|
||
"cmd": "ssc_net_set",
|
||
"ts": 1719000001,
|
||
"code": 0,
|
||
"msg": "success"
|
||
}
|
||
```
|
||
|
||
## 4.4 查询 SSC 网络配置 `ssc_net_query`
|
||
|
||
> Topic: `dld960/{sn}/srv`
|
||
|
||
**请求:**
|
||
|
||
```json
|
||
{
|
||
"msg_id": 4,
|
||
"cmd": "ssc_net_query",
|
||
"ts": 1719000000
|
||
}
|
||
```
|
||
|
||
**响应:** 返回字段同 4.3 的 `data`。
|
||
|
||
## 4.5 设置 IoT 网络配置 `iot_net_set`
|
||
|
||
> Topic: `dld960/{sn}/srv`
|
||
|
||
**请求:**
|
||
|
||
```json
|
||
{
|
||
"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`
|
||
|
||
**请求:**
|
||
|
||
```json
|
||
{
|
||
"msg_id": 6,
|
||
"cmd": "iot_net_query",
|
||
"ts": 1719000000
|
||
}
|
||
```
|
||
|
||
**响应:** 返回字段同 4.5 的 `data`。
|
||
|
||
## 4.7 设置设备 Topic `iot_topic_set`
|
||
|
||
> Topic: `dld960/{sn}/srv`
|
||
|
||
**请求:**
|
||
|
||
```json
|
||
{
|
||
"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`
|
||
|
||
**请求:**
|
||
|
||
```json
|
||
{
|
||
"msg_id": 8,
|
||
"cmd": "iot_topic_query",
|
||
"ts": 1719000000
|
||
}
|
||
```
|
||
|
||
**响应:** 返回字段同 4.7 的 `data`。
|
||
|
||
## 4.9 验证设备密码 `pwd_verify`
|
||
|
||
> Topic: `dld960/{sn}/srv`
|
||
|
||
**请求:**
|
||
|
||
```json
|
||
{
|
||
"msg_id": 9,
|
||
"cmd": "pwd_verify",
|
||
"ts": 1719000000,
|
||
"data": {
|
||
"password": "123456"
|
||
}
|
||
}
|
||
```
|
||
|
||
| data 字段 | 类型 | 说明 |
|
||
|-----------|------|------|
|
||
| `password` | string | 6 位数字密码 |
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"msg_id": 9,
|
||
"cmd": "pwd_verify",
|
||
"ts": 1719000001,
|
||
"code": 0,
|
||
"msg": "success"
|
||
}
|
||
```
|
||
|
||
## 4.10 设置设备密码 `pwd_set`
|
||
|
||
> Topic: `dld960/{sn}/srv`
|
||
|
||
**请求:**
|
||
|
||
```json
|
||
{
|
||
"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`
|
||
|
||
**请求:**
|
||
|
||
```json
|
||
{
|
||
"msg_id": 11,
|
||
"cmd": "factory_reset",
|
||
"ts": 1719000000
|
||
}
|
||
```
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"msg_id": 11,
|
||
"cmd": "factory_reset",
|
||
"ts": 1719000001,
|
||
"code": 0,
|
||
"msg": "success"
|
||
}
|
||
```
|
||
|
||
## 4.12 设备复位 `device_reset`
|
||
|
||
> Topic: `dld960/{sn}/srv`
|
||
|
||
**请求:**
|
||
|
||
```json
|
||
{
|
||
"msg_id": 12,
|
||
"cmd": "device_reset",
|
||
"ts": 1719000000
|
||
}
|
||
```
|
||
|
||
无响应(设备复位后断开连接)。
|
||
|
||
## 4.13 设置车检器多路参数 `loop_param_set`
|
||
|
||
> Topic: `dld960/{sn}/srv`
|
||
|
||
**请求:**
|
||
|
||
```json
|
||
{
|
||
"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, 功能模式 |
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"msg_id": 13,
|
||
"cmd": "loop_param_set",
|
||
"ts": 1719000001,
|
||
"code": 0,
|
||
"msg": "success"
|
||
}
|
||
```
|
||
|
||
## 4.14 读取车检器多路参数 `loop_param_query`
|
||
|
||
> Topic: `dld960/{sn}/srv`
|
||
|
||
**请求:**
|
||
|
||
```json
|
||
{
|
||
"msg_id": 14,
|
||
"cmd": "loop_param_query",
|
||
"ts": 1719000000
|
||
}
|
||
```
|
||
|
||
**响应:**
|
||
|
||
```json
|
||
{
|
||
"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` 后下发一次。
|
||
|
||
**请求:**
|
||
|
||
```json
|
||
{
|
||
"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=无限制 |
|
||
|
||
**响应:** 标准成功/失败。
|
||
|
||
---
|
||
|
||
# 5 设备主动上报
|
||
|
||
## 5.1 设备上电登陆信息 `initialize`
|
||
> Topic: `dld960/{sn}/dev`
|
||
> QoS: 0/1
|
||
|
||
```json
|
||
{
|
||
"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
|
||
|
||
```json
|
||
{
|
||
"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_config` 的 `enable`/`interval` 仅控制 `loop_data` 周期上报,与事件上报解耦。
|
||
|
||
**设备上报(dev topic):**
|
||
|
||
```json
|
||
{
|
||
"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,收到后必须立即回复):**
|
||
|
||
```json
|
||
{
|
||
"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 秒
|
||
|
||
```json
|
||
{
|
||
"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):设备无 RTC,`initialize` 上线后平台经 `report_config` 命令下发 Unix `ts`,设备据此校准,之后上行 `ts` 为真实 Unix 时间;校准前为上电秒数 | wangfq |
|
||
|