# DLD960 TCP 接口协议(JSON) > 基于《DLD960 串口通信协议》V1.01,提供以太网 TCP 通道的 JSON 交互协议。 > 适用于上位机、调试工具等直接 TCP 连接场景。 --- # 1 通信说明 ## 1.1 连接参数 | 项目 | 说明 | |------|------| | 传输层 | TCP | | 默认端口 | 5960 | | 编码 | UTF-8 | | 序列化 | JSON | | 帧分隔 | 换行符 `\n`(每个 JSON 对象占一行) | | 最大帧长 | 4096 字节 | ## 1.2 通信模式 - **请求-响应**:客户端发送命令,设备返回响应(同步或异步) - **主动推送**:设备可主动推送数据(线圈数据、事件) - **连接保持**:支持长连接,60 秒无数据心跳超时断开 ## 1.3 连接流程 ``` Client Device (DLD960) | | |--- TCP Connect ------------------>| | | |--- pwd_verify (鉴权) ------------>| |<-- 鉴权成功 / 失败 ---------------| | | |--- 命令 1 ----------------------->| |<-- 响应 1 ------------------------| | | |--- 命令 2 ----------------------->| |<-- 响应 2 ------------------------| | | |<-- 主动推送 (loop_data/event) ----| | | |--- TCP Close / 超时 ------------->| ``` --- # 2 JSON 消息格式 ## 2.1 请求帧 ```json {"msg_id":1,"cmd":"dev_info_query","ts":1719000000,"data":{...}} ``` | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | `msg_id` | uint32 | 是 | 消息序列号,递增,用于请求-响应匹配 | | `cmd` | string | 是 | 命令标识符 | | `ts` | uint32 | 否 | Unix 时间戳(秒) | | `data` | object | 否 | 命令参数 | ## 2.2 响应帧 ```json {"msg_id":1,"cmd":"dev_info_query","ts":1719000001,"code":0,"msg":"success","data":{...}} ``` | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | `msg_id` | uint32 | 是 | 对应请求的 msg_id | | `cmd` | string | 是 | 命令标识符(与请求一致) | | `ts` | uint32 | 否 | 响应时间戳 | | `code` | int | 是 | 0=成功,非0=错误码 | | `msg` | string | 是 | 结果描述 | | `data` | object | 否 | 响应数据 | ## 2.3 主动推送帧 ```json {"msg_id":100,"cmd":"loop_data","ts":1719000100,"data":{...}} ``` > 设备主动推送无 `code`/`msg` 字段,由 `cmd` 区分类型。 ## 2.4 错误码 | code | 说明 | |------|------| | 0 | 成功 | | 1 | 参数错误(格式/范围不正确) | | 2 | 密码验证失败 / 未鉴权 | | 3 | 设备忙 | | 4 | 不支持的命令 | | 5 | 内部错误 | | 6 | 数据超长 | | 7 | 连接未鉴权(需先执行 pwd_verify) | --- # 3 命令详表 | cmd | 说明 | 需鉴权 | 对应串口 | |-----|------|--------|----------| | `pwd_verify` | 验证设备密码(连接鉴权) | 否 | 0x1C | | `dev_serial_set` | 更改设备序列码 | 是 | 0x09 | | `dev_info_query` | 查询设备信息 | 是 | 0x10 | | `ssc_net_set` | 设置 SSC 网络配置 | 是 | 0x11 | | `ssc_net_query` | 查询 SSC 网络配置 | 是 | 0x12 | | `iot_net_set` | 设置 IoT 网络配置 | 是 | 0x13 | | `iot_net_query` | 查询 IoT 网络配置 | 是 | 0x14 | | `iot_topic_set` | 设置设备 Topic | 是 | 0x15 | | `iot_topic_query` | 查询设备 Topic | 是 | 0x16 | | `pwd_set` | 设置设备密码 | 是 | 0x1D | | `factory_reset` | 设备出厂初始化 | 是 | 0x1E | | `device_reset` | 设备复位 | 是 | 0x1F | | `loop_param_set` | 设置车检器多路参数 | 是 | 0x63 | | `loop_param_query` | 读取车检器多路参数 | 是 | 0x64 | | `report_config` | 设置主动上报 | 是 | 0xC5 | | `log_stat` | 查询脱机事件日志统计 | 是 | — | | `log_query` | 分页拉取脱机事件日志 | 是 | — | | `log_clear` | 清除脱机事件日志(审计留痕) | 是 | — | | 主动推送 | | | | | `loop_data` | 线圈传感数据(设备→客户端) | — | 0xC0 | | `event_report` | 事件上报(设备→客户端,**客户端须应答**,见 §5.2) | — | — | --- # 4 命令详情 ## 4.1 验证设备密码 `pwd_verify` > 连接后首个命令,鉴权通过后才能执行其他命令。 > 60 秒内未鉴权则断开连接。 **请求:** ```json {"msg_id":1,"cmd":"pwd_verify","ts":1719000000,"data":{"password":"123456"}} ``` | data 字段 | 类型 | 说明 | |-----------|------|------| | `password` | string | 6 位数字密码 | **响应(成功):** ```json {"msg_id":1,"cmd":"pwd_verify","ts":1719000001,"code":0,"msg":"success"} ``` **响应(失败):** ```json {"msg_id":1,"cmd":"pwd_verify","ts":1719000001,"code":2,"msg":"password incorrect"} ``` 连续 3 次密码错误,设备断开连接并锁定 60 秒。 ## 4.2 更改设备序列码 `dev_serial_set` **请求:** ```json {"msg_id":2,"cmd":"dev_serial_set","ts":1719000002,"data":{"dev_serial":"A1B2C3D4E5F6"}} ``` **响应:** ```json {"msg_id":2,"cmd":"dev_serial_set","ts":1719000003,"code":0,"msg":"success"} ``` ## 4.3 查询设备信息 `dev_info_query` **请求:** ```json {"msg_id":3,"cmd":"dev_info_query","ts":1719000004} ``` **响应:** ```json {"msg_id":3,"cmd":"dev_info_query","ts":1719000005,"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}}} ``` ## 4.4 设置 SSC 网络配置 `ssc_net_set` **请求:** ```json {"msg_id":4,"cmd":"ssc_net_set","ts":1719000006,"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}} ``` **响应:** 标准成功/失败。 ## 4.5 查询 SSC 网络配置 `ssc_net_query` **请求:** ```json {"msg_id":5,"cmd":"ssc_net_query","ts":1719000008} ``` **响应:** `data` 字段同 4.4。 ## 4.6 设置 IoT 网络配置 `iot_net_set` **请求:** ```json {"msg_id":6,"cmd":"iot_net_set","ts":1719000010,"data":{"host":"mqtt.example.com","port":1883,"client_id":"dld960_A1B2C3D4E5F6","username":"admin","password":"secret"}} ``` ## 4.7 查询 IoT 网络配置 `iot_net_query` **请求:** ```json {"msg_id":7,"cmd":"iot_net_query","ts":1719000012} ``` ## 4.8 设置设备 Topic `iot_topic_set` **请求:** ```json {"msg_id":8,"cmd":"iot_topic_set","ts":1719000014,"data":{"client_id_enable":true,"topic_pub":"dld960/data/A1B2C3D4E5F6","topic_sub":"dld960/cmd/A1B2C3D4E5F6"}} ``` ## 4.9 查询设备 Topic `iot_topic_query` **请求:** ```json {"msg_id":9,"cmd":"iot_topic_query","ts":1719000016} ``` ## 4.10 设置设备密码 `pwd_set` **请求:** ```json {"msg_id":10,"cmd":"pwd_set","ts":1719000018,"data":{"old_password":"123456","new_password":"654321"}} ``` ## 4.11 设备出厂初始化 `factory_reset` **请求:** ```json {"msg_id":11,"cmd":"factory_reset","ts":1719000020} ``` > 执行后所有配置恢复出厂值,连接可能断开。 ## 4.12 设备复位 `device_reset` **请求:** ```json {"msg_id":12,"cmd":"device_reset","ts":1719000022} ``` > 无响应,设备立即复位。 ## 4.13 设置车检器多路参数 `loop_param_set` **请求:** ```json {"msg_id":13,"cmd":"loop_param_set","ts":1719000024,"data":{"auto_mode":false,"channels":[{"ch":1,"sensitivity":7,"freq_level":"high","loop_delay":0,"output_mode":"exist","exist_mode":0,"direction_mode":0,"safe_mode":0,"function_mode":0},{"ch":2,"sensitivity":7,"freq_level":"mid_high","loop_delay":5,"output_mode":"enter_pulse","exist_mode":10,"direction_mode":0,"safe_mode":0,"function_mode":0},{"ch":3,"sensitivity":5,"freq_level":"mid_low","loop_delay":0,"output_mode":"exist","exist_mode":0,"direction_mode":1,"safe_mode":5,"function_mode":0},{"ch":4,"sensitivity":8,"freq_level":"low","loop_delay":10,"output_mode":"leave_pulse","exist_mode":15,"direction_mode":0,"safe_mode":0,"function_mode":0}]}} ``` | `channels[]` 字段 | 类型 | 范围 | 说明 | |-------------------|------|------|------| | `ch` | uint8 | 1~4 | 通道号 | | `sensitivity` | uint8 | 0~9 | 灵敏度,默认 7 | | `freq_level` | string | 见下 | 线圈高低频档位 | | `loop_delay` | uint8 | 0~200 | 延时 ×0.1s,最大 20s | | `output_mode` | string | 见下 | 输出方式 | | `exist_mode` | uint8 | 0~255 | 0=永久,非0=分钟 | | `direction_mode` | uint8 | 0~6 | 0=触发,1~6=方向输出 | | `safe_mode` | uint8 | 0~255 | 0=关闭,非0=分钟 | | `function_mode` | uint8 | 0~15 | 功能模式 | **freq_level 枚举:** | 值 | 说明 | |----|------| | `"high"` | 高频 (33nF) | | `"mid_high"` | 中高频 (43nF) | | `"mid_low"` | 中低频 (66nF) | | `"low"` | 低频 (76nF) | **output_mode 枚举:** | 值 | 说明 | |----|------| | `"exist"` | 存在输出 | | `"enter_pulse"` | 进入脉冲 | | `"leave_pulse"` | 离开脉冲 | | `"direction"` | 方向判别 | ## 4.14 读取车检器多路参数 `loop_param_query` **请求:** ```json {"msg_id":14,"cmd":"loop_param_query","ts":1719000026} ``` **响应:** ```json {"msg_id":14,"cmd":"loop_param_query","ts":1719000027,"code":0,"msg":"success","data":{"auto_mode":false,"channels":[{"ch":1,"sensitivity":7,"freq_level":"high","loop_delay":0,"output_mode":"exist","exist_mode":0,"direction_mode":0,"safe_mode":0,"function_mode":0,"freq_initial":105300,"freq_current":105280,"freq_diff":20},{"ch":2,"sensitivity":7,"freq_level":"mid_high","loop_delay":5,"output_mode":"enter_pulse","exist_mode":10,"direction_mode":0,"safe_mode":0,"function_mode":0,"freq_initial":100200,"freq_current":98700,"freq_diff":1500},{"ch":3,"sensitivity":5,"freq_level":"mid_low","loop_delay":0,"output_mode":"exist","exist_mode":0,"direction_mode":1,"safe_mode":5,"function_mode":0,"freq_initial":78100,"freq_current":0,"freq_diff":0},{"ch":4,"sensitivity":8,"freq_level":"low","loop_delay":10,"output_mode":"leave_pulse","exist_mode":15,"direction_mode":0,"safe_mode":0,"function_mode":0,"freq_initial":62100,"freq_current":62095,"freq_diff":5}]}} ``` | 额外字段 | 类型 | 单位 | 说明 | |----------|------|------|------| | `freq_initial` | uint32 | Hz | 初始频率 | | `freq_current` | uint32 | Hz | 当前实时频率(线圈断开时为 0) | | `freq_diff` | uint32 | Hz | 频率变化量 | ## 4.15 设置主动上报 `report_config` **请求:** ```json {"msg_id":15,"cmd":"report_config","ts":1719000028,"data":{"sensor_type":12,"enable":true,"once":false,"env_eval":false,"interval":5,"ack_required":false,"timeout":0}} ``` | data 字段 | 类型 | 说明 | |-----------|------|------| | `sensor_type` | uint8 | 传感器类型,12=多路线圈(0x0C) | | `enable` | bool | 使能主动上报 | | `once` | bool | 仅上报一次 | | `env_eval` | bool | 环境评估模式 | | `interval` | uint8 | 上报间隔(秒),0=实时 | | `ack_required` | bool | 上报是否需要客户端确认 | | `timeout` | uint8 | 超时(分钟),0=无限制 | --- ## 4.16 查询脱机事件日志统计 `log_stat` > 设备本地 W25Q32 环形事件日志(256KB / 8064 条,掉电不丢)。用于日志拉取前的分页定位。 **请求:** ```json {"msg_id":16,"cmd":"log_stat","ts":1719000000,"data":{}} ``` **响应 data:** ```json {"stream":"event","enabled":true,"boot_seq":2,"count":1234,"capacity":8064,"seq_first":100,"seq_last":1333} ``` | data 字段 | 类型 | 说明 | |-----------|------|------| | `stream` | string | 日志流,当前仅 `event`(快照流预留) | | `enabled` | bool | 日志功能是否启用(Flash 初始化成功) | | `boot_seq` | uint16 | 当前启动序号(每次上电 +1,区分复位段) | | `count` | uint16 | 有效记录条数(0~8064) | | `capacity` | uint16 | 容量上限(8064) | | `seq_first` | uint32 | 逻辑首条记录全局序号(`seq_last - count + 1`) | | `seq_last` | uint32 | 最新一条记录全局序号(跨 boot 单调递增) | --- ## 4.17 分页拉取脱机事件日志 `log_query` > **分页按全局序号,不按时间**(未同步段时间不可靠)。`count` 上限 **4**(受响应帧 ≤800B 限制)。 **请求:** ```json {"msg_id":17,"cmd":"log_query","ts":1719000000,"data":{"start_seq":1330,"count":4}} ``` | data 字段 | 类型 | 说明 | |-----------|------|------| | `start_seq` | uint32 | 起始全局序号(含);越界(< `seq_first` 或 > `seq_last`)返回空 `records` | | `count` | uint8 | 拉取条数,**上限 4**,超限按 4 处理 | **响应 data:** ```json {"start_seq":1330,"records":[{"seq":1330,"boot_seq":2,"ts_ms":456789,"unix_ts":1784768575,"type":"iot_ready","data":null}]} ``` | 字段 | 类型 | 说明 | |------|------|------| | `seq` | uint32 | 全局序号 | | `boot_seq` | uint16 | 所属启动段 | | `ts_ms` | uint32 | boot 内相对时间(ms,断电归零) | | `unix_ts` | uint32 | 已同步 Unix 秒;**0 = 未同步**(回算见 §2.3) | | `type` | string | 事件类型名(下表) | | `data` | object/null | 类型相关参数 | **事件类型表:** | type | 含义 | data 字段 | |------|------|-----------| | `boot` | 上电/复位 | `{"rst": 复位原因寄存器原始值}`,位解析:bit31=IWDG(看门狗) bit30=WWDG bit29=LPWR bit26=NRST引脚 bit25=POR(真断电) bit24=软件复位 | | `iot_connect` | MQTT TCP 连接成功 | null | | `iot_ready` | MQTT 订阅完成 → 发 initialize | null | | `iot_disconnect` | MQTT 断连 | `{"reason": N}`,1=断开 2=超时 3=CONNACK拒绝 4=连接超时 | | `iot_reconn` | 重连退避 | `{"backoff_ms": 5000}` | | `evt_retry` | event_report ACK 超时重发 | `{"msg_id":5,"retry":2}` | | `evt_giveup` | event_report 重试耗尽挂起 | `{"msg_id":5}` | | `coil` | 线圈事件 | `{"sub":"car_enter","ch":1,"value":97}`;sub: car_enter/car_leave/loop_cut/loop_restore,value 为 50ms 单位时间量 | | `time_anchor` | 时钟同步锚点 | null(`unix_ts` 即平台下发值,严格一致) | | `log_clear` | 日志清除(审计) | null | --- ## 4.18 清除脱机事件日志 `log_clear` > ⚠ **高风险操作**:清除动作本身写入事件流(`log_clear` 审计——谁在何时清了日志,留痕不可清除)。客户端应做权限控制。 **请求:** ```json {"msg_id":18,"cmd":"log_clear","ts":1719000000,"data":{"stream":"event"}} ``` | data 字段 | 类型 | 说明 | |-----------|------|------| | `stream` | string | 日志流,当前仅 `event`;缺省等同 `event` | **响应:** 标准成功/失败。成功后 `log_stat` 的 `count` 归 1(仅剩审计记录),`seq_last` 继续递增(序号不复位)。 --- # 5 设备主动推送 主动推送帧与请求-响应共用同一条 TCP 连接,由设备在任意时刻发出。 ## 5.1 线圈传感数据 `loop_data` ```json {"msg_id":100,"cmd":"loop_data","ts":1719000100,"data":{"channels":[{"ch":1,"freq_level":"high","has_car":false,"loop_ok":true,"freq_current":105280,"freq_diff":20,"sensitivity":7,"condition":0,"misc":{"type":"time","value":0}},{"ch":2,"freq_level":"mid_high","has_car":true,"loop_ok":true,"freq_current":98700,"freq_diff":1500,"sensitivity":7,"condition":2,"misc":{"type":"time","value":350}},{"ch":3,"freq_level":"mid_low","has_car":false,"loop_ok":false,"freq_current":0,"freq_diff":0,"sensitivity":5,"condition":0,"misc":{"type":"cut_count","value":3}},{"ch":4,"freq_level":"low","has_car":false,"loop_ok":true,"freq_current":62095,"freq_diff":5,"sensitivity":8,"condition":0,"misc":{"type":"flow_count","value":128}}]}} ``` | 通道字段 | 类型 | 说明 | |----------|------|------| | `ch` | uint8 | 通道号 1~4 | | `freq_level` | string | 高低频档位 | | `has_car` | bool | 有车/无车 | | `loop_ok` | bool | 线圈正常/断开 | | `freq_current` | uint32 | 当前频率 (Hz) | | `freq_diff` | uint32 | 频率变化量 (Hz) | | `sensitivity` | uint8 | 灵敏度等级 | | `condition` | uint8 | 环境评估值 | | `misc.type` | string | `"time"` / `"cut_count"` / `"flow_count"` | | `misc.value` | uint32 | 对应数值 | ## 5.2 事件上报 `event_report` > 方向:设备 → 客户端;**⚠️ 客户端必须应答**(区别于 `loop_data` 的单向推送) 设备检测到事件时主动推送,非周期性。事件(进车/出车/线圈断开等)是**不可再生的关键数据**,TCP 送达仅保证客户端进程收到,不代表业务层已处理入库,因此引入**应用层应答 + 设备重发**机制闭环确认(与 IoT MQTT 协议 V1.04 §5.3 机制对称)。 > **注**:`event_report` **不受 `report_config.enable` 门控**——设备上电即检测并推送事件。`report_config` 的 `enable`/`interval` 仅控制 `loop_data` 周期推送,与事件上报解耦。 **设备推送:** ```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` | 说明 | `value` | |----------------|------|---------| | `car_enter` | 车辆进入 | 0 | | `car_leave` | 车辆离开 | 通过时间 (×50ms) | | `loop_cut` | 线圈断开 | 0 | | `loop_restore` | 线圈恢复 | 断开持续时长 (×50ms) | **客户端应答(收到后必须立即回复):** ```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 次)。仍未确认 → 事件保留在待发队列,待**连接恢复(重新鉴权通过)或下一次事件触发**时合并上报; 4. 连接断开期间产生的事件同样进入待发队列;等待确认期间新产生的事件合并进下一包 `events` 数组(使用新 `msg_id`),单帧不超过帧长限制(4096 字节); 5. 待发队列建议深度 **≥16 条事件**,溢出时丢弃最旧事件(先保新鲜数据)。 **客户端要求:** 1. 收到 `event_report` **先落库、后应答**,应答即承诺数据已持久化; 2. 按 `msg_id` 在近期窗口(建议 10 分钟)内去重——重复帧为设备未收到应答所致,**直接应答 `code=0`,不重复入库**(设备重启后 `msg_id` 从头递增,去重必须限定时间窗口;TCP 为点对点连接,无需 `dev_serial` 维度); 3. 应答帧保持最小集(不带 `data`),且与请求同为**单行紧凑 JSON + `\n` 结尾**。 --- # 6 交互示例 ## 6.1 完整会话 ``` >>> {"msg_id":1,"cmd":"pwd_verify","ts":1719000000,"data":{"password":"123456"}} <<< {"msg_id":1,"cmd":"pwd_verify","ts":1719000001,"code":0,"msg":"success"} >>> {"msg_id":2,"cmd":"dev_info_query","ts":1719000002} <<< {"msg_id":2,"cmd":"dev_info_query","ts":1719000003,"code":0,"msg":"success","data":{...}} >>> {"msg_id":3,"cmd":"loop_param_query","ts":1719000004} <<< {"msg_id":3,"cmd":"loop_param_query","ts":1719000005,"code":0,"msg":"success","data":{...}} >>> {"msg_id":4,"cmd":"report_config","ts":1719000006,"data":{"sensor_type":12,"enable":true,"interval":5}} <<< {"msg_id":4,"cmd":"report_config","ts":1719000007,"code":0,"msg":"success"} ... (5 秒后设备主动推送) ... <<< {"msg_id":100,"cmd":"loop_data","ts":1719000012,"data":{...}} <<< {"msg_id":101,"cmd":"loop_data","ts":1719000017,"data":{...}} <<< {"msg_id":102,"cmd":"event_report","ts":1719000020,"data":{"events":[{"type":"car_enter","ch":1,"value":0}]}} >>> {"msg_id":102,"cmd":"event_report","ts":1719000021,"code":0,"msg":"success"} ``` --- # 7 帧解析说明 - 每个 JSON 对象为一行,以 `\n` (0x0A) 结尾 - 解析时按行读取,提取完整 JSON 后解析 - JSON 内部不含换行符(紧凑格式) - 收到非 JSON 行则丢弃并返回错误 - 帧长超过 4096 字节则断开连接 --- # 修订记录 | 版本 | 修订时间 | 修订说明 | 修订人 | |------|----------|----------|--------| | V1.00 | 2026-06-22 | 初始版本,基于串口协议 V1.01 | wangfq | | V1.01 | 2026-07-15 | `event_report` 增加**客户端必答**机制:应答格式(回显 `msg_id`)、设备 5s 超时重发(同 `msg_id`/`ts`,最多 3 次)、待发队列合并上报、客户端去重与先落库后应答要求(与 MQTT 协议 V1.04 对称) | wangfq | | V1.02 | 2026-08-04 | 增加**脱机事件日志**命令:`log_stat`(统计/分页定位)、`log_query`(按全局序号分页,count≤4)、`log_clear`(清除+审计留痕);事件类型表与 MQTT 协议 V1.06 对齐 | wangfq |