feat(vd960DBN): 实现 DLD960Loop 串口通信协议 (0x7F)
新增: - 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 全部通过
This commit is contained in:
@@ -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 <string.h>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user