From 4fbda960789f8978b871fba49e37b699252b66ee Mon Sep 17 00:00:00 2001 From: wangfq Date: Thu, 2 Jul 2026 09:26:34 +0800 Subject: [PATCH] =?UTF-8?q?feat(vd960DBN):=20=E5=AE=9E=E7=8E=B0=20DLD960Lo?= =?UTF-8?q?op=20=E4=B8=B2=E5=8F=A3=E9=80=9A=E4=BF=A1=E5=8D=8F=E8=AE=AE=20(?= =?UTF-8?q?0x7F)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增: - docs/DLD960Loop_串口通信协议.md — 协议文档 V1.02 - loop_uart_proto.h/c — 协议实现: checksum/组包/解析/帧状态机/命令状态机 修改: - usart_biz.c: 使用 lup_feed_byte() 帧解析器替代 timeout heuristic; 波特率修正为 115200 - tcp_json_srv.c/h: loop_param_set/query 真实实现(0x63/0x64), 0xC0 传感器推流, 延迟响应机制 - peripheral_main.c: 添加 tcp_json_push_sensor() 调用, 帧解析器超时保护 校验验证: 5个协议例程 XOR+SUM 全部通过 --- .../APP/include/loop_uart_proto.h | 239 ++++++ .../APP/include/tcp_json_srv.h | 13 + .../APP/loop_uart_proto.c | 679 ++++++++++++++++++ .../APP/peripheral_main.c | 10 + .../APP/tcp_json_srv.c | 308 +++++++- .../OnlyUpdateApp_Peripheral/APP/usart_biz.c | 100 ++- vd960DBN/docs/DLD960Loop_串口通信协议.md | 237 ++++++ 7 files changed, 1522 insertions(+), 64 deletions(-) create mode 100644 vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/loop_uart_proto.h create mode 100644 vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/loop_uart_proto.c create mode 100644 vd960DBN/docs/DLD960Loop_串口通信协议.md diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/loop_uart_proto.h b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/loop_uart_proto.h new file mode 100644 index 0000000..12e84df --- /dev/null +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/loop_uart_proto.h @@ -0,0 +1,239 @@ +/** + ****************************************************************************** + * @file loop_uart_proto.h + * @author wangfq + * @version V1.0 + * @date 2026-07-02 + * @brief DLD960Loop 串口通信协议 (7F) — CH32V208 ↔ DLD960Loop MCU + * + * 帧格式: 7F | Addr | LEN | CMD | Data... | XOR | SUM + * UART: USART2, 115200bps, PA2(Tx)/PA3(Rx) + * + * 该协议是通信MCU(CH32V208)与地感MCU(DLD960Loop/AT32F421)之间的接口。 + * 网络接口(TCP JSON/5960)通过本模块向地感MCU下发命令并获取数据。 + ****************************************************************************** + */ +#ifndef __LOOP_UART_PROTO_H__ +#define __LOOP_UART_PROTO_H__ + +#include + +/*=========================================================================== + * Protocol Constants + *===========================================================================*/ +#define LUP_MAGIC 0x7F // 配置/查询协议 +#define LUP_MAGIC_OTA 0x9F // OTA + +#define LUP_ADDR_DEFAULT 0x00 // 默认地址 + +#define LUP_MAX_VALUE_LEN 64 // Value 字段最大长度 +#define LUP_MAX_PKG_LEN (1 + 3 + LUP_MAX_VALUE_LEN + 2) // 70 bytes 最大帧 + +/*=========================================================================== + * Command Bytes (CMD) + *===========================================================================*/ +#define LUP_CMD_GET_VERSION 0x4A // 获取设备版本号 +#define LUP_CMD_RESET 0x6D // 设备复位(无回复) +#define LUP_CMD_FACTORY_INIT 0x92 // 出厂初始化 +#define LUP_CMD_SENSITIVITY 0x8A // 读写线圈灵敏度列表 +#define LUP_CMD_SET_PARAM 0x63 // 设置多路参数 +#define LUP_CMD_GET_PARAM 0x64 // 读取多路参数 +#define LUP_CMD_SENSOR_REPORT 0xC0 // 设备主动上报传感信息 + +/*=========================================================================== + * Sensor Types (0xC0 SensType) + *===========================================================================*/ +#define LUP_SENS_TYPE_MULTI_COIL 0x0C // 多路线圈传感信息(四路) + +/*=========================================================================== + * R/W byte for sensitivity (0x8A) + *===========================================================================*/ +#define LUP_SENS_RW_READ 0x00 +#define LUP_SENS_RW_WRITE 0x01 + +/*=========================================================================== + * 线圈数 + *===========================================================================*/ +#define LUP_COIL_COUNT 4 + +/*=========================================================================== + * Data Structures + *===========================================================================*/ + +/* 线圈配置 (0x63 Set / 0x64 Get 中使用) */ +typedef struct { + uint8_t sensitivity; // 低4位灵敏度(0~9), 高4位频率等级[4]=高频,[5]=低频 + uint8_t loop_delay; // 延时时间 0~200, 0.1s/级 + uint8_t output_mode; // 低3位: 0=存在,1=进入脉冲,2=离开脉冲,3=方向 + // 高5位: SafeMode 分钟数(0=关闭) + uint8_t exist_mode; // 低4位: 0=永久, 非0=有限分钟 + uint8_t direction_mode; // 低3位: 0=触发, 1~6=方向模式 + // 高4位: Function_Mode +} LUP_CoilParam; + +/* 多路参数设置 (0x63) */ +typedef struct { + uint8_t auto_mode; // 0=不启用自动调频, 默认0 + uint8_t amount; // 线圈数量 + LUP_CoilParam params[LUP_COIL_COUNT]; +} LUP_ParamSet; + +/* 多路参数读取返回 (0x64 Response) */ +typedef struct { + uint8_t auto_mode; + uint8_t amount; + LUP_CoilParam params[LUP_COIL_COUNT]; + uint32_t freq[LUP_COIL_COUNT][3]; // [ch][0]=freq1, [1]=freq2, [2]=freq3 +} LUP_ParamGet; + +/* 单路线圈传感数据单元 (0xC0/0x0C 中的每一路线圈) */ +typedef struct { + uint8_t freq_level : 2; // 00=高频33nF, 01=中高43nF, 10=中低66nF, 11=低频76nF + uint8_t direction : 1; // 0=触发, 1=方向判别 + uint8_t freq_type : 1; // 0=初始频率, 1=实时频率 + uint8_t sensitivity : 4; // 灵敏度等级 + // 线圈评估条件 + uint8_t condition : 4; // 环境状态评估值(0=正常, 值越大干扰越大) + uint8_t loop_state : 1; // 0=正常, 1=断开 + uint8_t car_state : 1; // 0=无车, 1=有车 + uint8_t misc_type : 2; // 0=时间量, 1=断开次数, 2=车流量 + uint32_t freq; // 频率 (3 bytes, little-endian) + uint16_t variation; // 变化量 (2 bytes, little-endian) + union { + uint32_t passtime_ms5; // 通过时间/车间距, 5ms单位 (misc_type=0) + uint32_t cut_amount; // 线圈断开次数 (misc_type=1) + uint32_t flow_amount; // 车流量数 (misc_type=2) + } misc; +} LUP_CoilSensor; + +/* 主动上报传感信息 (0xC0/0x0C) */ +typedef struct { + uint8_t sens_type; // 0x0C + uint8_t sub_amount; // 分包数量(高4位), 0=无分包 + uint8_t sub_sequence; // 当前分包序号(低4位), 1-based + uint8_t coil_count; // 线圈数 (解析出的) + LUP_CoilSensor coils[LUP_COIL_COUNT]; +} LUP_SensorReport; + +/* 灵敏度读写 (0x8A) */ +typedef struct { + uint8_t rw; // 0=Read, 1=Write + uint8_t amount; // 灵敏度值数量 + uint16_t sens_in[LUP_COIL_COUNT]; // 触发灵敏度 + uint16_t sens_out[LUP_COIL_COUNT]; // 释放灵敏度 +} LUP_Sensitivity; + +/* 版本信息 (0x4A Response) */ +typedef struct { + uint8_t status; // Status byte + uint8_t hard_main; + uint8_t hard_sub; + uint8_t hard_ssub; + uint8_t soft_main; + uint8_t soft_sub; + uint8_t soft_ssub; + char version_str[32]; // 格式化字符串 +} LUP_VersionInfo; + +/* 命令状态机 */ +typedef enum { + LUP_STATE_IDLE = 0, // 空闲 + LUP_STATE_WAIT_RESPONSE, // 等待响应 + LUP_STATE_RESPONSE_READY, // 响应已就绪 + LUP_STATE_TIMEOUT // 超时 +} LUP_CmdState; + +/* 挂起的命令跟踪 */ +typedef struct { + uint8_t pending_cmd; // 当前等待响应的命令字节 + uint32_t send_tick; // 发送时刻 (ms) + uint32_t timeout_ms; // 超时时间 + LUP_CmdState state; + // 响应数据缓冲区 + uint8_t resp_buf[LUP_MAX_PKG_LEN]; + uint16_t resp_len; +} LUP_CmdTracker; + +/*=========================================================================== + * Global State + *===========================================================================*/ +extern LUP_CmdTracker g_lup_cmd; + +/*=========================================================================== + * Public API + *===========================================================================*/ + +/* --- Checksum --- */ +uint8_t lup_calc_xor(const uint8_t *data, uint16_t len); +uint8_t lup_calc_sum(const uint8_t *data, uint16_t len); +int lup_verify_checksum(const uint8_t *pkg, uint16_t len); +void lup_append_checksum(uint8_t *pkg); + +/* --- Packet Builders (returns total packet length) --- */ +uint16_t lup_build_get_version(uint8_t *buf); +uint16_t lup_build_reset(uint8_t *buf); +uint16_t lup_build_factory_init(uint8_t *buf); +uint16_t lup_build_sensitivity_read(uint8_t *buf); +uint16_t lup_build_sensitivity_write(uint8_t *buf, const LUP_Sensitivity *sens); +uint16_t lup_build_set_param(uint8_t *buf, const LUP_ParamSet *ps); +uint16_t lup_build_get_param(uint8_t *buf); + +/* --- Packet Parsers (returns 0=success, <0=error) --- */ +int lup_parse_version(const uint8_t *pkg, uint16_t len, LUP_VersionInfo *info); +int lup_parse_factory_init_resp(const uint8_t *pkg, uint16_t len, uint8_t *success); +int lup_parse_sensitivity_resp(const uint8_t *pkg, uint16_t len, LUP_Sensitivity *sens); +int lup_parse_param_get(const uint8_t *pkg, uint16_t len, LUP_ParamGet *pg); +int lup_parse_sensor_report(const uint8_t *pkg, uint16_t len, LUP_SensorReport *report); +int lup_parse_set_param_resp(const uint8_t *pkg, uint16_t len, uint8_t *success); + +/* --- Command State Machine --- */ +void lup_cmd_begin(uint8_t cmd, uint32_t timeout_ms); +void lup_cmd_done(void); +int lup_cmd_check_timeout(void); +void lup_cmd_on_response(const uint8_t *pkg, uint16_t len); + +/* --- High-level Commands (send + wait handled by state machine) --- */ +void lup_send_get_version(void); +void lup_send_reset(void); +void lup_send_factory_init(void); +void lup_send_sensitivity_read(void); +void lup_send_set_param(const LUP_ParamSet *ps); +void lup_send_get_param(void); + +/* --- Mid-packet ACK for 0xC0 multi-packet --- */ +uint16_t lup_build_sensor_ack(uint8_t *buf, uint8_t sens_type, + uint8_t seq, uint8_t sub_amount); + +/* --- UART Frame Parser (called from USART2 ISR context) --- */ +typedef enum { + LUP_FRAME_STATE_IDLE = 0, // 等待 0x7F + LUP_FRAME_STATE_HEADER, // 接收 Header (Addr, LEN, CMD) + LUP_FRAME_STATE_VALUE, // 接收 Value + LUP_FRAME_STATE_CHECK, // 接收 Checksum + LUP_FRAME_STATE_COMPLETE // 帧完成 +} LUP_FrameState; + +typedef struct { + LUP_FrameState state; + uint8_t buf[LUP_MAX_PKG_LEN]; + uint16_t idx; // 当前写入位置 + uint16_t value_len; // 期望的 Value 长度 (从 LEN 字段解析) + uint16_t value_idx; // 当前已接收的 Value 字节数 +} LUP_FrameParser; + +extern LUP_FrameParser g_lup_parser; + +/* 喂一个字节给帧解析器,返回 1 表示帧接收完成 */ +int lup_feed_byte(uint8_t byte); + +/* 取完整帧的指针和数据长度 */ +const uint8_t *lup_frame_data(void); +uint16_t lup_frame_len(void); + +/* 重置帧解析器 */ +void lup_frame_reset(void); + +/* --- Process a complete frame (call from main loop context) --- */ +void lup_process_frame(const uint8_t *pkg, uint16_t len); + +#endif /* __LOOP_UART_PROTO_H__ */ diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/tcp_json_srv.h b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/tcp_json_srv.h index 1e1d98a..5b855f1 100644 --- a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/tcp_json_srv.h +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/include/tcp_json_srv.h @@ -44,6 +44,17 @@ typedef enum { #define JSON_CODE_DATA_TOO_LONG 6 #define JSON_CODE_NOT_AUTHED 7 +/*=========================================================================== + * Pending Loop MCU Command Response (deferred) + *===========================================================================*/ +typedef struct { + uint8_t active; // 1 = pending response + uint8_t socket; // TCP socket to reply to + uint32_t msg_id; // Original msg_id + char cmd[32]; // Original command string + uint32_t deadline; // ms deadline for timeout +} TcpJsonPending; + /*=========================================================================== * Externs *===========================================================================*/ @@ -51,6 +62,7 @@ extern uint8_t g_json_socket_listen; // TCP socket ID (listen + data) extern TcpJsonAuthState g_json_auth_state; extern uint32_t g_json_auth_timer; // ms timer for auth timeout extern uint8_t g_json_pwd_retry; // password retry counter +extern TcpJsonPending g_json_pending; // Pending Loop MCU command state /*=========================================================================== * API @@ -58,5 +70,6 @@ extern uint8_t g_json_pwd_retry; // password retry counter void tcp_json_srv_init(void); // Create listen socket on port 5960 void tcp_json_handle_sock_int(uint8_t socketid, uint8_t intstat); // Socket interrupt handler void tcp_json_poll(void); // Periodic poll (auth timeout, etc.) +void tcp_json_push_sensor(void); // Push sensor data (0xC0) to TCP client #endif /* __TCP_JSON_SRV_H__ */ diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/loop_uart_proto.c b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/loop_uart_proto.c new file mode 100644 index 0000000..69ebd91 --- /dev/null +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/loop_uart_proto.c @@ -0,0 +1,679 @@ +/** + ****************************************************************************** + * @file loop_uart_proto.c + * @author wangfq + * @version V1.0 + * @date 2026-07-02 + * @brief DLD960Loop 串口通信协议实现 + * - 校验计算 (XOR + SUM) + * - 命令组包 / 响应解析 + * - 帧接收状态机 (替代 timeout heuristic) + * - 命令发送 + 超时跟踪 + ****************************************************************************** + */ +#include "CONFIG.h" +#include "loop_uart_proto.h" +#include "cmcng.h" +#include + +/*=========================================================================== + * Global State + *===========================================================================*/ +LUP_CmdTracker g_lup_cmd = {0, 0, 0, LUP_STATE_IDLE, {0}, 0}; + +/* --- Frame Parser --- */ +LUP_FrameParser g_lup_parser = { + .state = LUP_FRAME_STATE_IDLE, + .idx = 0, + .value_len = 0, + .value_idx = 0 +}; + +/*=========================================================================== + * Checksum + *===========================================================================*/ + +/* + * XOR 校验: 从 Addr 字节开始,到命令数据的最后一个字节结束 + */ +uint8_t lup_calc_xor(const uint8_t *data, uint16_t len) +{ + uint8_t x = 0; + uint16_t i; + for (i = 0; i < len; i++) { + x ^= data[i]; + } + return x; +} + +/* + * SUM 校验: 从 Addr 字节开始,到命令数据的最后一个字节结束 + */ +uint8_t lup_calc_sum(const uint8_t *data, uint16_t len) +{ + uint8_t s = 0; + uint16_t i; + for (i = 0; i < len; i++) { + s += data[i]; + } + return s; +} + +/* + * 校验整包: pkg = [7F] [Addr] [LEN] [CMD] [Data...] [XOR] [SUM] + * Checksum covers: Addr + LEN_field + Value (=CMD + Data) = 2 + LEN bytes + * starting at pkg[1] + */ +int lup_verify_checksum(const uint8_t *pkg, uint16_t len) +{ + if (len < 6) return -1; // 最小: 7F + 3 header + 2 checksum = 6 + + uint8_t pkg_len = pkg[2]; // LEN field + uint16_t check_len = 2 + pkg_len; // Addr(1)+LEN_field(1)+Value(LEN) + + if (len < check_len + 3) return -4; // Not enough data: Magic(1) + covered + Check(2) + + uint8_t expect_xor = lup_calc_xor(pkg + 1, check_len); + uint8_t expect_sum = lup_calc_sum(pkg + 1, check_len); + + uint8_t got_xor = pkg[1 + check_len]; + uint8_t got_sum = pkg[2 + check_len]; + + if (expect_xor != got_xor) return -2; + if (expect_sum != got_sum) return -3; + return 0; +} + +/* + * 为已填充好 Data 的 buf 追加 checksum。 + * buf 格式: [7F] [Addr] [LEN] [CMD] [Data...] + * Checksum 覆盖: Addr(1) + LEN(1) + Value(LEN) = 2 + LEN bytes + * Output: [7F][Addr][LEN][CMD][Data...][XOR][SUM] + */ +void lup_append_checksum(uint8_t *pkg) +{ + // pkg[0]=7F, pkg[1]=Addr, pkg[2]=LEN, pkg[3]=CMD, ... + uint8_t total_len = 1 + 3 + pkg[2] + 2; // Magic(1) + Header(3) + Value(LEN) + Check(2) + uint16_t check_len = 2 + pkg[2]; // Addr(1) + LEN_field(1) + Value(LEN) + + pkg[total_len - 2] = lup_calc_xor(pkg + 1, check_len); + pkg[total_len - 1] = lup_calc_sum(pkg + 1, check_len); +} + +/*=========================================================================== + * Packet Builders + *===========================================================================*/ + +/* + * 0x4A — 获取设备版本号 + * Req: 7F 00 01 4A 4B 4B + */ +uint16_t lup_build_get_version(uint8_t *buf) +{ + buf[0] = LUP_MAGIC; // 7F + buf[1] = LUP_ADDR_DEFAULT; // 0x00 + buf[2] = 0x01; // LEN = 1 (CMD byte) + buf[3] = LUP_CMD_GET_VERSION; // 0x4A + lup_append_checksum(buf); + return 6; // Magic(1) + Header(3) + Check(2) +} + +/* + * 0x6D — 设备复位 (无回复) + * Req: 7F 00 01 6D 6C 6E + */ +uint16_t lup_build_reset(uint8_t *buf) +{ + buf[0] = LUP_MAGIC; + buf[1] = LUP_ADDR_DEFAULT; + buf[2] = 0x01; + buf[3] = LUP_CMD_RESET; + lup_append_checksum(buf); + return 6; +} + +/* + * 0x92 — 出厂初始化 + * Req: 7F 00 01 92 93 93 + */ +uint16_t lup_build_factory_init(uint8_t *buf) +{ + buf[0] = LUP_MAGIC; + buf[1] = LUP_ADDR_DEFAULT; + buf[2] = 0x01; + buf[3] = LUP_CMD_FACTORY_INIT; + lup_append_checksum(buf); + return 6; +} + +/* + * 0x8A — 读灵敏度列表 + * Req: 7F 00 04 8A 00 00 ... + */ +uint16_t lup_build_sensitivity_read(uint8_t *buf) +{ + buf[0] = LUP_MAGIC; + buf[1] = LUP_ADDR_DEFAULT; + buf[2] = 0x04; // LEN = 4 (CMD + R/W + Amount + reserved?) + buf[3] = LUP_CMD_SENSITIVITY; // 0x8A + // Value: R/W + Amount (2 bytes, all zeros for read) + // 协议说 R/W: 1 byte, Amount: 1 byte, 后面是 Amount * (SensityIn + SensityOut) + // 对于 Read: R/W=0, Amount 忽略 + buf[4] = LUP_SENS_RW_READ; + buf[5] = 0x00; // Amount (ignored for read) + buf[6] = 0x00; // padding? + buf[7] = 0x00; // padding? + lup_append_checksum(buf); + return 10; // Magic(1)+Header(3)+Value(4)+Check(2) +} + +/* + * 0x8A — 写灵敏度列表 + */ +uint16_t lup_build_sensitivity_write(uint8_t *buf, const LUP_Sensitivity *sens) +{ + uint8_t i; + uint16_t idx = 0; + + buf[idx++] = LUP_MAGIC; + buf[idx++] = LUP_ADDR_DEFAULT; + // LEN field: 2 + 2*sens->amount + uint8_t val_len = 2 + 2 * sens->amount; + buf[idx++] = val_len; + buf[idx++] = LUP_CMD_SENSITIVITY; + + buf[idx++] = sens->rw; // 0x01 (Write) + buf[idx++] = sens->amount; + + for (i = 0; i < sens->amount; i++) { + buf[idx++] = (uint8_t)(sens->sens_in[i] & 0xFF); + buf[idx++] = (uint8_t)((sens->sens_in[i] >> 8) & 0xFF); + buf[idx++] = (uint8_t)(sens->sens_out[i] & 0xFF); + buf[idx++] = (uint8_t)((sens->sens_out[i] >> 8) & 0xFF); + } + lup_append_checksum(buf); + return idx + 2; // +2 for checksum bytes +} + +/* + * 0x63 — 设置多路参数 + * Value: AutoMode(1) + Amount(1) + Amount*(Param[5]) + */ +uint16_t lup_build_set_param(uint8_t *buf, const LUP_ParamSet *ps) +{ + uint8_t i; + uint16_t idx = 0; + + buf[idx++] = LUP_MAGIC; + buf[idx++] = LUP_ADDR_DEFAULT; + // LEN = 2 + 5 * Amount + uint8_t val_len = 2 + 5 * ps->amount; + buf[idx++] = val_len; + buf[idx++] = LUP_CMD_SET_PARAM; + + buf[idx++] = ps->auto_mode; + buf[idx++] = ps->amount; + + for (i = 0; i < ps->amount; i++) { + buf[idx++] = ps->params[i].sensitivity; + buf[idx++] = ps->params[i].loop_delay; + buf[idx++] = ps->params[i].output_mode; + buf[idx++] = ps->params[i].exist_mode; + buf[idx++] = ps->params[i].direction_mode; + } + lup_append_checksum(buf); + return idx + 2; +} + +/* + * 0x64 — 读取多路参数 + * Req: 7F 00 01 64 ... + */ +uint16_t lup_build_get_param(uint8_t *buf) +{ + buf[0] = LUP_MAGIC; + buf[1] = LUP_ADDR_DEFAULT; + buf[2] = 0x01; // LEN = 1 + buf[3] = LUP_CMD_GET_PARAM; // 0x64 + lup_append_checksum(buf); + return 6; +} + +/* + * 0xC0 mid-packet ACK + * Req: 7F | 00 | 07 | C0 | SensType | Seq | SubAmount | XOR | SUM + */ +uint16_t lup_build_sensor_ack(uint8_t *buf, uint8_t sens_type, + uint8_t seq, uint8_t sub_amount) +{ + buf[0] = LUP_MAGIC; + buf[1] = LUP_ADDR_DEFAULT; + buf[2] = 0x07; // LEN = 7 (CMD + SensType + Seq + SubAmount) + buf[3] = LUP_CMD_SENSOR_REPORT; + buf[4] = sens_type; + buf[5] = seq; + buf[6] = sub_amount; + buf[7] = 0x00; // padding + buf[8] = 0x00; + buf[9] = 0x00; + buf[10] = 0x00; + lup_append_checksum(buf); + return 13; // Magic(1)+Header(3)+Value(7)+Check(2) +} + +/*=========================================================================== + * Packet Parsers + *===========================================================================*/ + +/* + * Parse 0x4A Response: + * Header: 00 08 4A (Addr, LEN=8, CMD) + * Value: Status + Hard_Main + Hard_Sub + Hard_SSub + Soft_Main + Soft_Sub + Soft_SSub + */ +int lup_parse_version(const uint8_t *pkg, uint16_t len, LUP_VersionInfo *info) +{ + if (len < 10) return -1; // 1+3+7+2 = 13 min + + // pkg layout: [7F][Addr=0][LEN=8][CMD=0x4A][Status...7bytes][XOR][SUM] + if (pkg[0] != LUP_MAGIC || pkg[3] != LUP_CMD_GET_VERSION) return -2; + if (pkg[2] < 7) return -3; // LEN should be >= 7 + + const uint8_t *val = pkg + 4; // skip magic + header(3) + info->status = val[0]; + info->hard_main = val[1]; + info->hard_sub = val[2]; + info->hard_ssub = val[3]; + info->soft_main = val[4]; + info->soft_sub = val[5]; + info->soft_ssub = val[6]; + + return 0; +} + +/* + * Parse 0x92 Response: + * Value: 00/01 (00=success) + */ +int lup_parse_factory_init_resp(const uint8_t *pkg, uint16_t len, uint8_t *success) +{ + if (len < 7) return -1; + if (pkg[0] != LUP_MAGIC || pkg[3] != LUP_CMD_FACTORY_INIT) return -2; + *success = (pkg[4] == 0x00) ? 1 : 0; + return 0; +} + +/* + * Parse 0x8A Response: + * Value: Amount (1byte) + Amount * SensityValue (2bytes each) + */ +int lup_parse_sensitivity_resp(const uint8_t *pkg, uint16_t len, LUP_Sensitivity *sens) +{ + uint8_t i; + if (len < 6) return -1; + if (pkg[0] != LUP_MAGIC || pkg[3] != LUP_CMD_SENSITIVITY) return -2; + + const uint8_t *val = pkg + 4; + uint8_t amount = val[0]; + + if (amount > LUP_COIL_COUNT) amount = LUP_COIL_COUNT; + sens->amount = amount; + sens->rw = 0; // response, not request + + for (i = 0; i < amount; i++) { + uint16_t sv = val[1 + i * 2] | ((uint16_t)val[2 + i * 2] << 8); + // Response returns unified SensityValue (not split in/out) + sens->sens_in[i] = sv; + sens->sens_out[i] = sv; + } + return 0; +} + +/* + * Parse 0x64 Response: + * Value: AutoMode(1) + Amount(1) + Amount * [Sensitivity, Loop_Delay, Output_Mode, + * Exist_Mode, Direction_Mode, freq1(2), freq2(2), freq3(2)] + * Each coil = 8 bytes + */ +int lup_parse_param_get(const uint8_t *pkg, uint16_t len, LUP_ParamGet *pg) +{ + uint8_t i; + if (len < 6) return -1; + if (pkg[0] != LUP_MAGIC || pkg[3] != LUP_CMD_GET_PARAM) return -2; + + const uint8_t *val = pkg + 4; + pg->auto_mode = val[0]; + pg->amount = val[1]; + + if (pg->amount > LUP_COIL_COUNT) pg->amount = LUP_COIL_COUNT; + + for (i = 0; i < pg->amount; i++) { + const uint8_t *c = val + 2 + i * 8; + pg->params[i].sensitivity = c[0]; + pg->params[i].loop_delay = c[1]; + pg->params[i].output_mode = c[2]; + pg->params[i].exist_mode = c[3]; + pg->params[i].direction_mode = c[4]; + pg->freq[i][0] = c[5] | ((uint32_t)c[6] << 8); + pg->freq[i][1] = c[7] | ((uint32_t)c[8] << 8); + pg->freq[i][2] = c[9] | ((uint32_t)c[10] << 8); + } + + return 0; +} + +/* + * Parse 0x63 Response (Set Param): + * Value: Status(1 byte, 0x00=success) + */ +int lup_parse_set_param_resp(const uint8_t *pkg, uint16_t len, uint8_t *success) +{ + if (len < 7) return -1; + if (pkg[0] != LUP_MAGIC || pkg[3] != LUP_CMD_SET_PARAM) return -2; + *success = (pkg[4] == 0x00) ? 1 : 0; + return 0; +} + +/* + * Parse 0xC0/0x0C — 多路线圈传感信息 + * + * 每路线圈数据单元 = 11 bytes: + * [0] 配置1: freq_level(2) | Direction(1) | freq_type(1) | sens(4) + * [1] 评估条件: condition(4) | loop_state(1) | car_state(1) | misc_type(2) + * [2-4] 频率 (3 bytes, LE) + * [5-6] 变化量 (2 bytes, LE) + * [7-10] 杂项 (4 bytes) + */ +int lup_parse_sensor_report(const uint8_t *pkg, uint16_t len, LUP_SensorReport *report) +{ + uint8_t i; + if (len < 9) return -1; // 7F + 3 header + 2 sensType/SubPkgFlag + 2 checksum min + if (pkg[0] != LUP_MAGIC || pkg[3] != LUP_CMD_SENSOR_REPORT) return -2; + + uint8_t sens_type = pkg[4]; + if (sens_type != LUP_SENS_TYPE_MULTI_COIL) return -3; + + uint8_t sub_pkg = pkg[5]; + report->sens_type = sens_type; + report->sub_amount = (sub_pkg >> 4) & 0x0F; + report->sub_sequence = sub_pkg & 0x0F; + + // 计算线圈数: Value = sensType(1) + SubPkgFlag(1) + coil_count * 11 + uint16_t val_len = pkg[2]; // LEN field + uint16_t data_offset = 6; // 跳过 7F + Addr + LEN + CMD + SensType + SubPkgFlag + uint16_t data_len = val_len - 2; // 减去 SensType + SubPkgFlag + + uint8_t coil_count = data_len / 11; + if (coil_count > LUP_COIL_COUNT) coil_count = LUP_COIL_COUNT; + report->coil_count = coil_count; + + for (i = 0; i < coil_count; i++) { + const uint8_t *c = pkg + data_offset + i * 11; + + LUP_CoilSensor *cs = &report->coils[i]; + cs->freq_level = (c[0] >> 6) & 0x03; + cs->direction = (c[0] >> 5) & 0x01; + cs->freq_type = (c[0] >> 4) & 0x01; + cs->sensitivity = c[0] & 0x0F; + + cs->condition = (c[1] >> 4) & 0x0F; + cs->loop_state = (c[1] >> 3) & 0x01; + cs->car_state = (c[1] >> 2) & 0x01; + cs->misc_type = c[1] & 0x03; + + cs->freq = c[2] | ((uint32_t)c[3] << 8) | ((uint32_t)c[4] << 16); + cs->variation = c[5] | ((uint16_t)c[6] << 8); + cs->misc.passtime_ms5 = c[7] | ((uint32_t)c[8] << 8) + | ((uint32_t)c[9] << 16) | ((uint32_t)c[10] << 24); + } + + return 0; +} + +/*=========================================================================== + * Command State Machine + *===========================================================================*/ + +void lup_cmd_begin(uint8_t cmd, uint32_t timeout_ms) +{ + g_lup_cmd.pending_cmd = cmd; + g_lup_cmd.send_tick = mstick(); + g_lup_cmd.timeout_ms = timeout_ms; + g_lup_cmd.state = LUP_STATE_WAIT_RESPONSE; + g_lup_cmd.resp_len = 0; + memset(g_lup_cmd.resp_buf, 0, sizeof(g_lup_cmd.resp_buf)); +} + +void lup_cmd_done(void) +{ + g_lup_cmd.pending_cmd = 0; + g_lup_cmd.state = LUP_STATE_IDLE; + g_lup_cmd.resp_len = 0; +} + +int lup_cmd_check_timeout(void) +{ + if (g_lup_cmd.state != LUP_STATE_WAIT_RESPONSE) return 0; + if (mstick() - g_lup_cmd.send_tick > g_lup_cmd.timeout_ms) { + g_lup_cmd.state = LUP_STATE_TIMEOUT; + PRINT("LUP: cmd 0x%02X timeout\n", g_lup_cmd.pending_cmd); + return 1; + } + return 0; +} + +/* + * 收到响应时调用 — 如果当前有挂起命令且 CMD 匹配,则保存响应 + */ +void lup_cmd_on_response(const uint8_t *pkg, uint16_t len) +{ + if (g_lup_cmd.state != LUP_STATE_WAIT_RESPONSE) return; + if (len < 4) return; + + uint8_t resp_cmd = pkg[3]; + if (resp_cmd == g_lup_cmd.pending_cmd) { + // 保存响应数据 + uint16_t copy_len = (len < sizeof(g_lup_cmd.resp_buf)) ? len : sizeof(g_lup_cmd.resp_buf); + memcpy(g_lup_cmd.resp_buf, pkg, copy_len); + g_lup_cmd.resp_len = copy_len; + g_lup_cmd.state = LUP_STATE_RESPONSE_READY; + } +} + +/*=========================================================================== + * High-level Send Commands + *===========================================================================*/ + +void lup_send_get_version(void) +{ + uint8_t buf[LUP_MAX_PKG_LEN]; + uint16_t len = lup_build_get_version(buf); + UART2_SendString(buf, len); + lup_cmd_begin(LUP_CMD_GET_VERSION, 200); // 200ms timeout +} + +void lup_send_reset(void) +{ + uint8_t buf[LUP_MAX_PKG_LEN]; + uint16_t len = lup_build_reset(buf); + UART2_SendString(buf, len); + // No response expected +} + +void lup_send_factory_init(void) +{ + uint8_t buf[LUP_MAX_PKG_LEN]; + uint16_t len = lup_build_factory_init(buf); + UART2_SendString(buf, len); + lup_cmd_begin(LUP_CMD_FACTORY_INIT, 500); +} + +void lup_send_sensitivity_read(void) +{ + uint8_t buf[LUP_MAX_PKG_LEN]; + uint16_t len = lup_build_sensitivity_read(buf); + UART2_SendString(buf, len); + lup_cmd_begin(LUP_CMD_SENSITIVITY, 200); +} + +void lup_send_set_param(const LUP_ParamSet *ps) +{ + uint8_t buf[LUP_MAX_PKG_LEN]; + uint16_t len = lup_build_set_param(buf, ps); + UART2_SendString(buf, len); + lup_cmd_begin(LUP_CMD_SET_PARAM, 500); +} + +void lup_send_get_param(void) +{ + uint8_t buf[LUP_MAX_PKG_LEN]; + uint16_t len = lup_build_get_param(buf); + UART2_SendString(buf, len); + lup_cmd_begin(LUP_CMD_GET_PARAM, 500); +} + +/*=========================================================================== + * Frame Parser (called per-byte from USART2 ISR / timer context) + * + * 帧格式: [7F] [Addr] [LEN] [CMD] [Value: LEN bytes] [XOR] [SUM] + * LEN = Value 字段的长度,包括 CMD byte + * 所以: Value data bytes = LEN - 1 (减掉 CMD) + * + * 状态机: + * IDLE → 找 0x7F → HEADER + * HEADER → 收完 3 bytes Header → VALUE (若 LEN>1) + * VALUE → 收完 data_bytes → CHECK + * CHECK → 收完 2 bytes → COMPLETE + *===========================================================================*/ + +void lup_frame_reset(void) +{ + g_lup_parser.state = LUP_FRAME_STATE_IDLE; + g_lup_parser.idx = 0; + g_lup_parser.value_len = 0; + g_lup_parser.value_idx = 0; + memset(g_lup_parser.buf, 0, sizeof(g_lup_parser.buf)); +} + +const uint8_t *lup_frame_data(void) +{ + return g_lup_parser.buf; +} + +uint16_t lup_frame_len(void) +{ + return g_lup_parser.idx; +} + +/* + * 喂一个字节。返回 1 = 帧接收完成(在 buf 中) + */ +int lup_feed_byte(uint8_t byte) +{ + LUP_FrameParser *p = &g_lup_parser; + + switch (p->state) { + case LUP_FRAME_STATE_IDLE: + if (byte == LUP_MAGIC) { + p->buf[0] = byte; + p->idx = 1; + p->state = LUP_FRAME_STATE_HEADER; + } + break; + + case LUP_FRAME_STATE_HEADER: + p->buf[p->idx++] = byte; + if (p->idx == 4) { + // Header 收完: [7F][Addr][LEN][CMD] + // Value data bytes = LEN - 1 (CMD byte 已计入 LEN) + uint8_t len_field = p->buf[2]; + uint16_t data_bytes; // Value 中除 CMD 之外的 data 字节数 + if (len_field < 1) { + data_bytes = 0; + } else { + data_bytes = len_field - 1; + } + + if (data_bytes > LUP_MAX_VALUE_LEN) { + // 非法长度,丢弃 + lup_frame_reset(); + break; + } + + if (data_bytes > 0) { + p->value_len = data_bytes; + p->value_idx = 0; + p->state = LUP_FRAME_STATE_VALUE; + } else { + // 无 Data,直接等 Checksum + p->value_len = 0; + p->value_idx = 0; + p->state = LUP_FRAME_STATE_CHECK; + } + } + break; + + case LUP_FRAME_STATE_VALUE: + p->buf[p->idx++] = byte; + p->value_idx++; + if (p->value_idx >= p->value_len) { + p->state = LUP_FRAME_STATE_CHECK; + } + break; + + case LUP_FRAME_STATE_CHECK: + p->buf[p->idx++] = byte; + if (p->idx >= p->value_len + 1 + 3 + 2) { // Magic(1) + Header(3) + Value data + Check(2) + p->state = LUP_FRAME_STATE_COMPLETE; + return 1; + } + break; + + case LUP_FRAME_STATE_COMPLETE: + // 不应该到达这里,调用方应在 COMPLETE 后 reset + lup_frame_reset(); + break; + } + + return 0; +} + +/* + * 收到完整帧后的处理: + * 1. 校验 checksum + * 2. 如果是当前命令的响应 → 通知状态机 + * 3. 如果是主动上报 (0xC0) → 处理传感器数据 + */ +void lup_process_frame(const uint8_t *pkg, uint16_t len) +{ + int csr; + uint8_t i; + + if (len < 6) return; + + // Debug: print raw + PRINT("LUP Rx:"); + for (i = 0; i < len; i++) { + PRINT(" %02X", pkg[i]); + } + PRINT("\n"); + + // --- Checksum --- + csr = lup_verify_checksum(pkg, len); + if (csr != 0) { + PRINT("LUP: checksum fail (%d)\n", csr); + return; + } + + // --- Dispatch by CMD --- + uint8_t cmd = pkg[3]; + + // Active reports (0xC0) are NOT responses to commands + if (cmd == LUP_CMD_SENSOR_REPORT) { + // Sensor report — will be handled in upper layer (uart_srv + TCP JSON) + // Don't consume as command response + return; + } + + // Try matching with pending command + lup_cmd_on_response(pkg, len); +} diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/peripheral_main.c b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/peripheral_main.c index 716007d..e9e3179 100644 --- a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/peripheral_main.c +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/peripheral_main.c @@ -23,6 +23,7 @@ #include "net_srv.h" #include "storage.h" #include "tcp_json_srv.h" +#include "loop_uart_proto.h" /********************************************************************* * GLOBAL TYPEDEFS @@ -269,6 +270,8 @@ void Main_Circulation(void) poll_dbn_ble(); + tcp_json_push_sensor(); // Push 0xC0 sensor data to TCP JSON client + tcp_json_poll(); key_event_srv(); @@ -335,6 +338,13 @@ void TIM3_IRQHandler(void) } } } + // Safety: reset frame parser if stuck in non-IDLE state too long + if(g_lup_parser.state != LUP_FRAME_STATE_IDLE){ + if(g_pkg_uart_2.tick > 20){ + lup_frame_reset(); + g_pkg_uart_2.tick = 0; + } + } if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0){//HAL_PUSH_BUTTON()){ g_flag_counter_key.tick++; diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/tcp_json_srv.c b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/tcp_json_srv.c index 694cecb..533f770 100644 --- a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/tcp_json_srv.c +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/tcp_json_srv.c @@ -15,6 +15,7 @@ #include "wchnet.h" #include "net_srv.h" #include "cmcng.h" +#include "loop_uart_proto.h" #include "simple_json.h" #include "storage.h" #include @@ -29,6 +30,7 @@ TcpJsonAuthState g_json_auth_state = JSON_STATE_WAIT_AUTH; uint32_t g_json_auth_timer = 0; uint8_t g_json_pwd_retry = 0; uint32_t g_json_lockout_timer = 0; +TcpJsonPending g_json_pending = {0, 0, 0, "", 0}; /*=========================================================================== * Frame Receive Buffer (line-delimited JSON) @@ -464,46 +466,87 @@ static void handle_device_reset(uint8_t socket, uint32_t msg_id, const char *jso NVIC_SystemReset(); } -/* 4.13 loop_param_set */ +/* 4.13 loop_param_set — 设置地感线圈多路参数 (CMD 0x63) */ static void handle_loop_param_set(uint8_t socket, uint32_t msg_id, const char *json) { - // Forward to Loop MCU via UART2 - // Store params locally for query response char *data = json_get_data_str(json); if (!data) { json_send_error(socket, msg_id, "loop_param_set", JSON_CODE_PARAM_ERR, "missing data"); return; } + + LUP_ParamSet ps; + memset(&ps, 0, sizeof(ps)); + + ps.auto_mode = (uint8_t)json_get_uint_field(data, "\"auto_mode\""); + + // Parse channels array + // Use a simple approach: parse top-level fields for each known channel key + uint32_t amount = json_get_uint_field(data, "\"amount\""); + if (amount == 0 || amount > LUP_COIL_COUNT) { + amount = LUP_COIL_COUNT; + } + ps.amount = (uint8_t)amount; + + // Parse per-channel data + char ch_key[16]; + uint8_t ch; + for (ch = 0; ch < ps.amount; ch++) { + snprintf(ch_key, sizeof(ch_key), "\"ch%d\"", ch + 1); + + char ch_data[512] = {0}; + reset_tmp(); + simple_parse_json(data, ch_key, g_tmp_value); + if (strlen(g_tmp_value) == 0) continue; + strncpy(ch_data, g_tmp_value, sizeof(ch_data) - 1); + + LUP_CoilParam *cp = &ps.params[ch]; + + // sensitivity: 低4位=灵敏度, 高4位=freq_level + uint32_t sens_val = json_get_uint_field(ch_data, "\"sensitivity\""); + uint32_t freq_lev = json_get_uint_field(ch_data, "\"freq_level\""); // 0~3 + cp->sensitivity = ((uint8_t)(freq_lev & 0x03) << 4) | (uint8_t)(sens_val & 0x0F); + + cp->loop_delay = (uint8_t)json_get_uint_field(ch_data, "\"loop_delay\""); + + // output_mode: 低3位=输出方式, 高5位=SafeMode + uint32_t out_mode = json_get_uint_field(ch_data, "\"output_mode\""); + uint32_t safe_mode = json_get_uint_field(ch_data, "\"safe_mode\""); + cp->output_mode = ((uint8_t)(safe_mode & 0x1F) << 3) | (uint8_t)(out_mode & 0x07); + + cp->exist_mode = (uint8_t)json_get_uint_field(ch_data, "\"exist_mode\""); + + // direction_mode: 低3位=方向模式, 高4位=Function_Mode + uint32_t dir_mode = json_get_uint_field(ch_data, "\"direction_mode\""); + uint32_t func_mode = json_get_uint_field(ch_data, "\"function_mode\""); + cp->direction_mode = ((uint8_t)(func_mode & 0x0F) << 4) | (uint8_t)(dir_mode & 0x07); + } free(data); - // Build binary packet for Loop MCU (protocol 0x63 = CMD_SET_MCJQ_PARAM) - // For now, ack the command — full loop MCU integration deferred - json_send_ok(socket, msg_id, "loop_param_set", NULL); - PRINT("JSON: loop_param_set (Loop MCU relay pending)\n"); + // Send to Loop MCU and defer response + lup_send_set_param(&ps); + + // Save pending state for async response + g_json_pending.active = 1; + g_json_pending.socket = socket; + g_json_pending.msg_id = msg_id; + strncpy(g_json_pending.cmd, "loop_param_set", sizeof(g_json_pending.cmd) - 1); + g_json_pending.deadline = mstick() + 500; + + PRINT("JSON: loop_param_set sent (ch=%d)\n", ps.amount); } -/* 4.14 loop_param_query */ +/* 4.14 loop_param_query — 读取地感线圈多路参数 (CMD 0x64) */ static void handle_loop_param_query(uint8_t socket, uint32_t msg_id, const char *json) { - // Query Loop MCU via UART2 (protocol 0x64 = CMD_GET_MCJQ_PARAM) - // For now, return stub data — full loop MCU integration deferred - char data_json[600]; - snprintf(data_json, sizeof(data_json), - "{\"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\":0,\"freq_current\":0,\"freq_diff\":0}," - "{\"ch\":2,\"sensitivity\":7,\"freq_level\":\"mid_high\",\"loop_delay\":5," - "\"output_mode\":\"exist\",\"exist_mode\":0,\"direction_mode\":0," - "\"safe_mode\":0,\"function_mode\":0,\"freq_initial\":0,\"freq_current\":0,\"freq_diff\":0}," - "{\"ch\":3,\"sensitivity\":5,\"freq_level\":\"mid_low\",\"loop_delay\":0," - "\"output_mode\":\"exist\",\"exist_mode\":0,\"direction_mode\":0," - "\"safe_mode\":0,\"function_mode\":0,\"freq_initial\":0,\"freq_current\":0,\"freq_diff\":0}," - "{\"ch\":4,\"sensitivity\":8,\"freq_level\":\"low\",\"loop_delay\":0," - "\"output_mode\":\"exist\",\"exist_mode\":0,\"direction_mode\":0," - "\"safe_mode\":0,\"function_mode\":0,\"freq_initial\":0,\"freq_current\":0,\"freq_diff\":0}]}"); + // Send query to Loop MCU and defer response + lup_send_get_param(); - json_send_ok(socket, msg_id, "loop_param_query", data_json); - PRINT("JSON: loop_param_query (stub — Loop MCU relay pending)\n"); + g_json_pending.active = 1; + g_json_pending.socket = socket; + g_json_pending.msg_id = msg_id; + strncpy(g_json_pending.cmd, "loop_param_query", sizeof(g_json_pending.cmd) - 1); + g_json_pending.deadline = mstick() + 500; + + PRINT("JSON: loop_param_query sent\n"); } /* 4.15 report_config */ @@ -679,6 +722,169 @@ void tcp_json_handle_sock_int(uint8_t socketid, uint8_t intstat) { } } +/*=========================================================================== + * Deferred Response Helpers + *===========================================================================*/ + +/* + * Format coil param query response as JSON + */ +static int format_loop_param_json(char *buf, uint16_t buf_size, const LUP_ParamGet *pg) { + const char *freq_level_names[] = {"high", "mid_high", "mid_low", "low"}; + char *p = buf; + int remaining = buf_size; + int written; + uint8_t i; + + written = snprintf(p, remaining, + "{\"auto_mode\":%s,\"amount\":%d,\"channels\":[", + pg->auto_mode ? "true" : "false", pg->amount); + if (written < 0 || written >= remaining) return -1; + p += written; remaining -= written; + + for (i = 0; i < pg->amount; i++) { + const LUP_CoilParam *cp = &pg->params[i]; + uint8_t freq_lev = (cp->sensitivity >> 4) & 0x03; + uint8_t sens = cp->sensitivity & 0x0F; + uint8_t out_mode = cp->output_mode & 0x07; + uint8_t safe_mod = (cp->output_mode >> 3) & 0x1F; + uint8_t dir_mode = cp->direction_mode & 0x07; + uint8_t func_mod = (cp->direction_mode >> 4) & 0x0F; + + written = snprintf(p, remaining, + "%s{\"ch\":%d,\"sensitivity\":%d,\"freq_level\":\"%s\"," + "\"loop_delay\":%d,\"output_mode\":%d,\"safe_mode\":%d," + "\"exist_mode\":%d,\"direction_mode\":%d,\"function_mode\":%d," + "\"freq_initial\":%lu,\"freq_current\":%lu,\"freq_diff\":%lu}", + (i > 0) ? "," : "", + i + 1, sens, freq_level_names[freq_lev], + cp->loop_delay, out_mode, safe_mod, + cp->exist_mode, dir_mode, func_mod, + pg->freq[i][0], pg->freq[i][1], pg->freq[i][2]); + if (written < 0 || written >= remaining) return -1; + p += written; remaining -= written; + } + + written = snprintf(p, remaining, "]}"); + if (written < 0 || written >= remaining) return -1; + return 0; +} + +/* + * Format sensor report as JSON + */ +static int format_sensor_json(char *buf, uint16_t buf_size, const LUP_SensorReport *sr) { + const char *freq_level_names[] = {"high", "mid_high", "mid_low", "low"}; + char *p = buf; + int remaining = buf_size; + int written; + uint8_t i; + + written = snprintf(p, remaining, + "{\"sens_type\":\"multi_coil\",\"sub_amount\":%d,\"sub_seq\":%d,\"coils\":[", + sr->sub_amount, sr->sub_sequence); + if (written < 0 || written >= remaining) return -1; + p += written; remaining -= written; + + for (i = 0; i < sr->coil_count; i++) { + const LUP_CoilSensor *cs = &sr->coils[i]; + const char *misc_field; + uint32_t misc_val; + + switch (cs->misc_type) { + case 0: misc_field = "\"passtime_ms5\""; misc_val = cs->misc.passtime_ms5; break; + case 1: misc_field = "\"cut_amount\""; misc_val = cs->misc.cut_amount; break; + case 2: misc_field = "\"flow_amount\""; misc_val = cs->misc.flow_amount; break; + default: misc_field = "\"misc\""; misc_val = 0; break; + } + + written = snprintf(p, remaining, + "%s{\"ch\":%d,\"freq_level\":\"%s\",\"direction\":%d," + "\"freq_type\":\"%s\",\"sensitivity\":%d," + "\"condition\":%d,\"loop_state\":\"%s\",\"car_state\":\"%s\"," + "\"freq\":%lu,\"variation\":%d,%s:%lu}", + (i > 0) ? "," : "", + i + 1, + freq_level_names[cs->freq_level], + cs->direction, + cs->freq_type ? "current" : "initial", + cs->sensitivity, + cs->condition, + cs->loop_state ? "broken" : "normal", + cs->car_state ? "car" : "no_car", + cs->freq, + cs->variation, + misc_field, misc_val); + if (written < 0 || written >= remaining) return -1; + p += written; remaining -= written; + } + + written = snprintf(p, remaining, "]}"); + if (written < 0 || written >= remaining) return -1; + return 0; +} + +/* + * Check for and handle pending Loop MCU deferred responses + */ +static void json_check_pending(void) { + if (!g_json_pending.active) return; + + uint8_t cmd = g_lup_cmd.pending_cmd; + + if (g_lup_cmd.state == LUP_STATE_RESPONSE_READY) { + // Response received! + if (strcmp(g_json_pending.cmd, "loop_param_set") == 0) { + uint8_t success = 0; + int ret = lup_parse_set_param_resp(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &success); + if (ret == 0 && success) { + json_send_ok(g_json_pending.socket, g_json_pending.msg_id, + g_json_pending.cmd, NULL); + } else { + json_send_error(g_json_pending.socket, g_json_pending.msg_id, + g_json_pending.cmd, JSON_CODE_INTERNAL_ERR, + ret == 0 ? "Loop MCU returned failure" : "Parse error"); + } + } else if (strcmp(g_json_pending.cmd, "loop_param_query") == 0) { + LUP_ParamGet pg; + memset(&pg, 0, sizeof(pg)); + int ret = lup_parse_param_get(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &pg); + if (ret == 0) { + char data_json[2048]; + if (format_loop_param_json(data_json, sizeof(data_json), &pg) == 0) { + json_send_ok(g_json_pending.socket, g_json_pending.msg_id, + g_json_pending.cmd, data_json); + } else { + json_send_error(g_json_pending.socket, g_json_pending.msg_id, + g_json_pending.cmd, JSON_CODE_INTERNAL_ERR, + "Response too large"); + } + } else { + json_send_error(g_json_pending.socket, g_json_pending.msg_id, + g_json_pending.cmd, JSON_CODE_INTERNAL_ERR, + "Failed to parse Loop MCU response"); + } + } + + lup_cmd_done(); + g_json_pending.active = 0; + PRINT("JSON: deferred response sent for %s\n", g_json_pending.cmd); + } + else if (g_lup_cmd.state == LUP_STATE_TIMEOUT + || mstick() > g_json_pending.deadline) { + // Timeout + json_send_error(g_json_pending.socket, g_json_pending.msg_id, + g_json_pending.cmd, JSON_CODE_BUSY, + "Loop MCU no response"); + lup_cmd_done(); + g_json_pending.active = 0; + PRINT("JSON: deferred response timeout for %s\n", g_json_pending.cmd); + } +} + +/*=========================================================================== + * tcp_json_poll (updated) + *===========================================================================*/ void tcp_json_poll(void) { // Only poll if we have an active connection (socket configured) if (g_json_socket_listen == 0xFF) return; @@ -702,4 +908,50 @@ void tcp_json_poll(void) { g_json_pwd_retry = 0; } } + + // Check for pending Loop MCU deferred responses + json_check_pending(); +} + +/*=========================================================================== + * tcp_json_push_sensor — Push sensor data (0xC0) to connected TCP client + * + * Called from main loop when uart_srv() has a sensor report frame in + * g_pkg_uart_2 that hasn't been consumed by BLE. + *===========================================================================*/ +void tcp_json_push_sensor(void) { + // Check: socket active, authed, and data available + if (g_json_socket_listen == 0xFF) return; + if (g_json_auth_state != JSON_STATE_AUTHED) return; + + // Only proceed if g_pkg_uart_2 has data and it's a 0xC0 sensor report + if (g_pkg_uart_2.flag == 0) return; + if (g_pkg_uart_2.pkg[0] != 0x7F) return; + if (g_pkg_uart_2.pkg[3] != 0xC0) return; + + LUP_SensorReport sr; + memset(&sr, 0, sizeof(sr)); + int ret = lup_parse_sensor_report(g_pkg_uart_2.pkg, g_pkg_uart_2.offset, &sr); + if (ret != 0) { + PRINT("JSON: sensor parse failed (%d)\n", ret); + InitPkgUart(&g_pkg_uart_2); + return; + } + + char data_json[2048]; + if (format_sensor_json(data_json, sizeof(data_json), &sr) == 0) { + char *out = (char *)malloc(TCP_JSON_MAX_FRAME); + if (out) { + snprintf(out, TCP_JSON_MAX_FRAME, + "{\"msg_id\":0,\"cmd\":\"sensor_report\",\"ts\":%lu," + "\"code\":0,\"msg\":\"success\",\"data\":%s}\n", + (unsigned long)mstick(), data_json); + uint32_t slen = strlen(out); + WCHNET_SocketSend(g_json_socket_listen, (uint8_t *)out, &slen); + free(out); + } + } + + // Clear after processing + InitPkgUart(&g_pkg_uart_2); } diff --git a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/usart_biz.c b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/usart_biz.c index de86012..2bf7a0a 100644 --- a/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/usart_biz.c +++ b/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/usart_biz.c @@ -3,10 +3,12 @@ * * Created on: 2026-02-26 * Author: wangfq + * Updated: 2026-07-02 — 使用 loop_uart_proto 帧解析器替代 timeout heuristic */ #include "config.h" #include "cmcng.h" +#include "loop_uart_proto.h" #include #include "dbn_ble_srv.h" @@ -34,7 +36,7 @@ void uart_init(void){ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // Rx GPIO_Init(GPIOA, &GPIO_InitStructure); - USART_InitStructure.USART_BaudRate = 192000;//115200; + USART_InitStructure.USART_BaudRate = 115200; // 115200 (协议规定) USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; @@ -42,7 +44,7 @@ void uart_init(void){ USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; USART_Init(USART2, &USART_InitStructure); - // USART_ITConfig(USART2, USART_IT_IDLE, ENABLE); // DMAԲܽգԭδ֪ + // USART_ITConfig(USART2, USART_IT_IDLE, ENABLE); USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; @@ -55,11 +57,13 @@ void uart_init(void){ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//GPIO_Mode_IPU; // Key + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); -} + // 初始化帧解析器 + lup_frame_reset(); +} @@ -76,11 +80,11 @@ void USART1_IRQHandler(void) } - /********************************************************************* * @fn USART2_IRQHandler * - * @brief This function handles USART2 global interrupt request. + * @brief USART2 RX — 使用 lup_feed_byte() 帧解析器 + * 当解析出完整帧时,复制到 g_pkg_uart_2.pkg 并设置 flag * * @return none */ @@ -88,25 +92,24 @@ void USART2_IRQHandler(void) { if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) { - uint8_t _dat = USART_ReceiveData(USART2); - if(g_pkg_uart_2.offset == 0){ - if(_dat != 0){ - g_pkg_uart_2.pkg[g_pkg_uart_2.offset++] = _dat; - } - } - else{ - if(g_pkg_uart_2.offset < BUFF_STACK_SIZE){ - g_pkg_uart_2.pkg[g_pkg_uart_2.offset++] = _dat; - } - else{ - g_pkg_uart_2.flag = 1; - } - } - - if(g_pkg_uart_2.offset){ - g_pkg_uart_2.tick = 0; - } + uint8_t _dat = USART_ReceiveData(USART2); + // 喂给帧解析器 + if (lup_feed_byte(_dat)) { + // 帧接收完成,复制到 g_pkg_uart_2 + const uint8_t *frame = lup_frame_data(); + uint16_t frame_len = lup_frame_len(); + if (frame_len <= BUFF_STACK_SIZE) { + memcpy(g_pkg_uart_2.pkg, frame, frame_len); + g_pkg_uart_2.offset = frame_len; + g_pkg_uart_2.flag = 1; + g_pkg_uart_2.tick = 0; + } + lup_frame_reset(); + } else if (g_lup_parser.state != LUP_FRAME_STATE_IDLE) { + // 正在接收帧中,tick 归零 + g_pkg_uart_2.tick = 0; + } } } @@ -133,46 +136,67 @@ void UART1_SendString(uint8_t *buf, uint16_t len) } +/* + * uart_srv — 主循环中调用,处理已接收完整的 UART2 帧 + * + * 处理流程: + * 1. 0x7F + 0xC0/0x0C → 传感器数据上报 + * - 若无 BLE 连接 → 标记 `_report_flag`,在 TCP JSON 中处理 + * - 若有 BLE 连接且 acs_enable → 转 0x8F 前缀发给 BLE + * 2. 0x7F + 其他 CMD → 响应帧,交给 lup_process_frame() 匹配挂起命令 + * 3. 非 0x7F → 调试打印(可能是字符串等) + */ void uart_srv(void) { uint8_t i; uint8_t _report_flag = 0; + // 检查命令超时 + lup_cmd_check_timeout(); + if(g_pkg_uart_2.flag){ - + if(g_flag_counter_ota.flag == 0){ if(g_pkg_uart_2.pkg[0] == 0x7F){ - if(g_pkg_uart_2.pkg[3] == 0xC0 && g_pkg_uart_2.pkg[4] == 0x0C) + uint8_t cmd = g_pkg_uart_2.pkg[3]; + + // --- 传感器上报 (0xC0) --- + if(cmd == 0xC0 && g_pkg_uart_2.pkg[4] == 0x0C) { if(g_dbn_ble_state_acs_enable.flag == 0){ + // 无 BLE ACS 连接 → 标记为 TCP JSON 上报 _report_flag = 1; } else{ + // BLE ACS 已连接 → 改 Magic 为 0x8F 发给 BLE g_pkg_uart_2.pkg[0] = 0x8F; } } - + else { + // --- 命令响应 → 交给协议处理器 --- + lup_process_frame(g_pkg_uart_2.pkg, g_pkg_uart_2.offset); + } + + // 调试打印 for(i = 0; i < g_pkg_uart_2.offset; i++){ PRINT(" %02X", g_pkg_uart_2.pkg[i]); } PRINT("\n"); if(_report_flag){ - InitPkgUart(&g_pkg_uart_2); + // 传感器帧保留在 pkg 中供上层 (tcp_json_srv) 处理 + // 不 InitPkgUart — 由上层消费后再 InitPkgUart } } else { + // 非 0x7F 魔法字节 PRINT("Rcv_len:%d,dat: %s\n", g_pkg_uart_2.offset, g_pkg_uart_2.pkg); } } else{ - // PRINT("From_Loop: "); - // for(i = 0; i < g_pkg_uart_2.offset; i++){ - // PRINT(" %02X", g_pkg_uart_2.pkg[i]); - // } - // PRINT("\n"); + // OTA 模式 — 忽略 Loop MCU 数据 } - + if(g_flag_bt_state){ g_flag_notify_temp = set_response_tran_to_notify(g_pkg_uart_2.pkg, g_pkg_uart_2.offset, &g_notify_buftemp); @@ -180,7 +204,11 @@ void uart_srv(void) else{ g_dbn_ble_state_acs_enable.flag = 0; } - InitPkgUart(&g_pkg_uart_2); + + // 只有非 _report_flag 时才立即清空 + // _report_flag 的帧由 tcp_json 消费后清理 + if (!_report_flag) { + InitPkgUart(&g_pkg_uart_2); + } } } - diff --git a/vd960DBN/docs/DLD960Loop_串口通信协议.md b/vd960DBN/docs/DLD960Loop_串口通信协议.md new file mode 100644 index 0000000..b9a8023 --- /dev/null +++ b/vd960DBN/docs/DLD960Loop_串口通信协议.md @@ -0,0 +1,237 @@ +# DLD960Loop 串口通信协议 + +# 1 通信说明 + +## 1.1 通信方式 + +串口通信,波特率:115200 + +# 2 协议格式 + +Package + +| 1Byte | 3Bytes | 0~64Bytes | 2Bytes | +| --- | --- | --- | --- | +| Magic byte | Header | Value | Check Bytes | + +## 2.1 Magic Byte(1Byte) + +标识协议类型,也表示数据包开始信息。 + +| Magic Byte | 说明 | +| --- | --- | +| 0x7F | 屏、地感检测、探头的配置和查询协议,或者部分雷达的协议 | +| 0x8F | | +| 0x9F | OTA 用途 | + +## 2.2 Header(3Bytes) + +当Magic Byte为 0x7F时,Header字节定义如下: + +| 1Byte | 1Byte | 1Byte | +| --- | --- | --- | +| Addr 地址 | LEN 数据长度 | CMD 命令字节 | + +* Addr 默认0 + +* LEN数据长度,表示数据包的有效数据(Value)长度,包括命令(CMD Byte)和数据(Data Bytes); + +* CMD 命令字节; + + +## 2.3 校验字节(Check Bytes:2Bytes) + +当Magic Byte为0x7F时,校验字节实现如下: + +| 1Byte | 1Byte | +| --- | --- | +| XOR 异或校验 | SUM 和校验 | + +> XOR 异或校验,校验从Addr字节开始,到命令数据的最后一个字节结束; + +> SUM 和校验,校验从Addr字节开始,到命令数据的最后一个字节结束。 + +# 3 Cmd详解 + +## 3.01 获取设备版本号(0x4A) + +| | Header | Data | +| --- | --- | --- | +| **Value(Hex)** | 00 01 4A | | +| **Length** | 3 Byte | 0 Byte | + +> eg: `7F 00 01 4A 4B 4B` + +返回: + +| | Header | Status + Hard\_Main + Hard\_Sub + Hard\_SSub + Soft\_Main + Soft\_Sub + Soft\_SSub | +| --- | --- | --- | +| **Value(Hex)** | 00 08 4A | xx | +| **Length** | 3 Byte | 7 Byte | + +## 3.02 设备复位(0x6D) + +| | Header | Data | +| --- | --- | --- | +| **Value(Hex)** | 00 01 6D | | +| **Length** | 3 Byte | 0 Byte | + +设备无回复。 + +> eg:  `7F 00 01 6D 6C 6E` + +## 3.03 出厂初始化(0x92) + +| | Header | Data | +| --- | --- | --- | +| **Value(Hex)** | 00 01 92 | | +| **Length** | 3 Byte | 0 Byte | + +> eg: `7F 00 01 92 93 93` + +返回: + +| | Header | Data | +| --- | --- | --- | +| **Value(Hex)** | 00 02 92 | 00/01(00 success) | +| **Length** | 3 Byte | 1 Byte | + +> eg: `7F 00 02 92 00 90 94` + +## 3.04 读写线圈灵敏度列表(0x8A) + +| | Header | R/W,Amount, (Amount \* (SensityIn + SensityOut)) | +| --- | --- | --- | +| **Value(Hex)** | 00 04 8A | xx | +| **Length** | 3 Byte | (2 + 2\*x )Byte | + +> R/W: 1字节,0 Read, 1 Write + +> Amount:1字节,灵敏度值的数量, 当R/W为 Write时有效,最小3 + +> SensityIn:两字节,触发灵敏度值,当R/W为 Write时有效 + +> SensityOut:两字节,释放灵敏度值,当R/W为 Write时有效 + +返回 + +| | Header | Amount,  (Amount \* SensityValue) | +| --- | --- | --- | +| **Value(Hex)** | ADDR 03 8A | xx | +| **Length** | 3 Byte | (1 + 2\*x) Byte | + +## 3.05 设置车检器多路参数(0x63) + +| | Header | AutoMode + Amount + Param\[Sensitivity, Loop_Delay, Output_Mode, Exist_Mode, Direction_Mode\] | +| --- | --- | --- | +| **Value(Hex)** | 00 xx 63 | xx | +| **Length** | 3 Byte | (2 + 5 \* Amount) Byte | + +* AutoMode: 是否处于自动模式(自动调频),0表示不启用,默认0。 + +* Sensitivity: 低四位表示灵敏度,0~9级,值越大,灵敏度越高,默认7;高四位表示高低频,其中,\[4,5\]表示线圈的高低频。 + +* Loop\_Delay: 地感延时时间,0~200, 0.1s为一级,延时时间范围:\[0, 20秒\]; + +* Output\_Mode: 低三位有效,地感输出方式,0 - 存在输出,1-进入脉冲,2-离开脉冲,3-方向判别;高5位表示SafeMode:是否开启安全模式, 0 关闭,非 0 开启安全模式,分钟为单位。 + +* Exist\_Mode:低四位表示地感存在方式,0-永久存在,非0-有限存在的分钟数; + +* Direction\_Mode: + + * 低三位表示方向判别模式,0 表示触发模式, 1-6 表示方向判别的输出模式,对应以前的 0-5,   update 2023-02-24; + + * 高四位表示Function\_Mode,功能模式,是否启用特殊的模式 + + +| | Header | Status | +| --- | --- | --- | +| **Value(Hex)** | 00 02 63 | 00/01 | +| **Length** | 3 Byte | 1 Byte | + +## 3.06 读取车检器多路参数(0x64) + +| | Header | Param | +| --- | --- | --- | +| **Value(Hex)** | 00 01 64 | | +| **Length** | 3 Byte | | + +返回: + +| | Header | AutoMode+Amount + Data\[Sensitivity, Loop_Delay, Output_Mode, Exist_Mode, Direction_Mode, freq1,  freq2, freq3\] | +| --- | --- | --- | +| **Value(Hex)** | 00 xx 64 | XX | +| **Length** | 3 Byte | (2 + 8 \* Amount) Byte | + +## 3.07 设备主动上报传感信息(0xC0) + +设备主动上报传感信息指令,具体传感数据需要根据不同的类型进行解析。 + +| 7Fh | ADDR | Len | C0 | SensType(1Byte) | SubPkgFlag(1Byte) | SensData | XOR | SUM | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | + +SensType: 传感数据类型 + +SubPkgFlag: 分包标记 SubPkgFlag,高四位表示分包数量 SubAmount,低四位表示为当前分包中第几个分包 SubSequence,其中 SubSequence = \[1,SubAmount\]: + +* 00 表示无分包,是一条数据完整的记录; + +* 当SubAmount不为0时,SubSequence从1开始到SubAmount结束,整合为一条完整的数据包。 + +* 当SensType为0x05时,SubPkgFlag作为传感数据使用。 + +* 当SensType为0x06、0x07、0x09时,SubPkgFlag的Seq和SubAmount分开各用一个字节表示。 + + +SensData:传感数据 + +| SensType | 类型说明 | SensData | 传感数据说明 | +| --- | --- | --- | --- | +| 0x0C | 多线圈传感信息 | | 四路线圈 | +| | | | | + +上报线圈传感数据单元(多路线圈,每一路线圈单元的配置) + +| 字段 | 字节数 | 内容 | 详情 | +| --- | --- | --- | --- | +| 配置 1 | 1 | freq\_level(2bit), Direction(1bit),freq\_type(1bit), sens(4bit) | 线圈 1 配置freq\_level:高低频,两个比特位,00 表示高频(33nF),01表示中高(43nF), 10表示中低(66nF), 11表示低频(76nF)。Direction: 0 表示触发,1 表示方向判别,freq\_type: 0 表示初始频率,1 表示当前实时频率。sens:低四位表示当前的灵敏度等级 | +| 线圈评估条件 | 1 | condition(4bit), loop\_state(1bit),
car\_state(1bit),
report\_msic(2bit) | loop\_condition 正常(0)有效,环境状态条件(环境状态评估值,值越大,干扰越大),高四位有效。低四位中 ,其中第3位表示线圈状态,0 表示正常,1 表示线圈断开;第 2 位表示有无车,0 表示无车,1 表示有车;低两位表示**杂项类型**,0b00表示时间量,0b01表示线圈断开次数;0b10表示车流量数 | +| 频率 1 | 3 | frequent | 低字节在前 | +| 变化量 1 | 2 | variation | 低字节在前 | +| 杂项 | 4 | in\_out\_passtime/cut\_amount/flow\_amount | **杂项类型**,可能是时间量,也可能是线圈断开次数、车流量数,说明:
1、通过时间或车间距,5ms为单位,最大计时()
2、线圈断开次数
3、车流量数 | + +eg: + +`7F 00 2F C0 0C 00 12 00 2A 0F 01 07 00 00 00 00 00 D2 00 75 B7 00 01 00 00 00 00 00 12 00 EC 0C 01 02 00 00 00 00 00 92 00 83 C3 00 02 00 00 00 00 00 E2 34` + +指令解释 + +```python +7F Magic Byte +00 Addr,0 default +2F Len, 0x2F=47个字节(Cmd + Data) +C0 Cmd +0C SensType,传感数据类型,0x0C为 多线圈传感信息,960Loop为 四路线圈车检器,会有4个传感数据单元 +00 SubPkgFlag,分包标记,00 表示无分包,是一条数据完整的记录。 +12 00 2A 0F 01 07 00 00 00 00 00:线圈1传感数据单元。0b00 高频,0b0 触发模式,0b1实时频率,0b0010 表示2级灵敏度;0b0000环境正常,0b0线圈正常,0b0当前无车,0b00 表示当前杂项参数为时间量;0x010F2A,频率为69418Hz;0x0007,变化量为7;0x00000000,时间量为为0 +D2 00 75 B7 00 01 00 00 00 00 00:线圈2传感数据单元。0b11 低频,0b0 触发模式,0b1实时频率,0b0010 表示2级灵敏度;0b0000环境正常,0b0线圈正常,0b0当前无车,0b00 表示当前杂项参数为时间量;0x00B775,频率为46965Hz;0x0001,变化量为1;0x00000000,时间量为为0 +12 00 EC 0C 01 02 00 00 00 00 00:线圈3传感数据单元。0b00 高频,0b0 触发模式,0b1实时频率,0b0010 表示2级灵敏度;0b0000环境正常,0b0线圈正常,0b0当前无车,0b00 表示当前杂项参数为时间量;0x010CEC,频率为68844Hz;0x0002,变化量为2;0x00000000,时间量为为0 +92 00 83 C3 00 02 00 00 00 00 00:线圈4传感数据单元。0b10 中低,0b0 触发模式,0b1实时频率,0b0010 表示2级灵敏度;0b0000环境正常,0b0线圈正常,0b0当前无车,0b00 表示当前杂项参数为时间量;0x00C383,频率为50051Hz;0x0002,变化量为2;0x00000000,时间量为为0 +E2 34 校验字节 +``` + +**注意,如果有续包,最后一包上位机无返回**。中间续包上位机返回传感类型,当前收到包的序列和子包的数量: + +| 7Fh | ADDR | 07 | C0 | SensType | Seq(1Byte) | SubAmount(1Byte) | XOR | SUM | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | + +--- + +# 修订记录 + +| 版本 | 修订时间 | 修订说明 | 修订人 | +| --- | --- | --- | --- | +| | | | | +| V1.00 | 2026-02-11 | 创建协议初始版本 | wangfq | +| V1.01 | 2026-06-22 | 补充遗漏的接口说明 | wangfq | +| V1.02 | 2026-07-02 | 查缺补漏,补充例程说明。 | wangfq | \ No newline at end of file