Files
vd_960/vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/tcp_json_srv.c
wangfq 9bab650c27 feat: 0xC0 时间量根据 car_state 区分通过时间/车间距
car_state=0(无车) → passtime_ms5 (通过时间)
car_state=1(有车) → gap_ms5 (车间距)
2026-07-02 17:48:05 +08:00

1209 lines
47 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
******************************************************************************
* @file tcp_json_srv.c
* @author wangfq
* @version V1.0
* @date 2026-06-30
* @brief TCP JSON protocol server implementation
* DLD960 TCP JSON protocol (port 5960) — auth + 15 commands
******************************************************************************
*/
#include "CONFIG.h"
#include "tcp_json_srv.h"
#include "eth_driver.h"
#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>
#include <stdlib.h>
#include <stdio.h>
/*===========================================================================
* Global State
*===========================================================================*/
uint8_t g_json_socket_listen = 0xFF; // TCP socket ID — handles both listen + client data
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};
uint8_t g_report_active = 0; // 0=不主动上报, 1=主动上报传感器数据
/*===========================================================================
* Frame Receive Buffer (line-delimited JSON)
*===========================================================================*/
static uint8_t g_json_recv_buf[TCP_JSON_RECV_BUF_LEN];
static uint16_t g_json_recv_len = 0;
static uint8_t g_json_wchnet_buf[RECE_BUF_LEN]; // WCHNET internal recv buffer
/*===========================================================================
* Internal helpers
*===========================================================================*/
static char g_tmp_value[256]; // reusable buffer for simple_parse_json
static void reset_tmp(void) {
memset(g_tmp_value, 0, sizeof(g_tmp_value));
}
static void json_send_error(uint8_t socket, uint32_t msg_id, const char *cmd,
int code, const char *msg) {
char *out = (char *)malloc(256);
if (!out) return;
snprintf(out, 256,
"{\"msg_id\":%lu,\"cmd\":\"%s\",\"ts\":%lu,\"code\":%d,\"msg\":\"%s\"}\n",
msg_id, cmd ? cmd : "", (unsigned long)mstick(), code, msg);
uint32_t slen = strlen(out);
WCHNET_SocketSend(socket, (uint8_t *)out, &slen);
free(out);
}
static void json_send_ok(uint8_t socket, uint32_t msg_id, const char *cmd,
const char *data_json) {
char *out = (char *)malloc(TCP_JSON_MAX_FRAME);
if (!out) return;
if (data_json && strlen(data_json) > 0) {
snprintf(out, TCP_JSON_MAX_FRAME,
"{\"msg_id\":%lu,\"cmd\":\"%s\",\"ts\":%lu,\"code\":0,\"msg\":\"success\",\"data\":%s}\n",
msg_id, cmd, (unsigned long)mstick(), data_json);
} else {
snprintf(out, TCP_JSON_MAX_FRAME,
"{\"msg_id\":%lu,\"cmd\":\"%s\",\"ts\":%lu,\"code\":0,\"msg\":\"success\"}\n",
msg_id, cmd, (unsigned long)mstick());
}
uint32_t slen = strlen(out);
WCHNET_SocketSend(socket, (uint8_t *)out, &slen);
free(out);
}
/* JSON field extractors — parse msg_id, cmd, and data object from raw JSON */
static uint32_t json_get_msg_id(const char *json) {
reset_tmp();
simple_parse_json(json, "\"msg_id\"", g_tmp_value);
if (strlen(g_tmp_value) == 0) return 0;
return (uint32_t)strtoul(g_tmp_value, NULL, 10);
}
static char *json_get_data_str(const char *json) {
char *data = (char *)malloc(TCP_JSON_MAX_FRAME);
if (!data) return NULL;
memset(data, 0, TCP_JSON_MAX_FRAME);
simple_parse_json(json, "\"data\"", data);
if (strlen(data) == 0) {
free(data);
return NULL;
}
return data;
}
static void json_get_str_field(const char *json, const char *key, char *out, int out_len) {
reset_tmp();
simple_parse_json(json, key, g_tmp_value);
// strip surrounding quotes if present
char *src = g_tmp_value;
if (src[0] == '"') src++;
int len = strlen(src);
if (len > 0 && src[len - 1] == '"') len--;
int copy_len = (len < out_len - 1) ? len : out_len - 1;
memcpy(out, src, copy_len);
out[copy_len] = '\0';
}
static void json_get_cmd(const char *json, char *out, int out_len) {
json_get_str_field(json, "\"cmd\"", out, out_len);
}
static uint32_t json_get_uint_field(const char *json, const char *key) {
reset_tmp();
simple_parse_json(json, key, g_tmp_value);
if (strlen(g_tmp_value) == 0) return 0;
return (uint32_t)strtoul(g_tmp_value, NULL, 10);
}
static uint8_t json_get_bool_field(const char *json, const char *key) {
reset_tmp();
simple_parse_json(json, key, g_tmp_value);
if (strstr(g_tmp_value, "true")) return 1;
return 0;
}
/*===========================================================================
* Frame parser: extract line-delimited JSON frames from receive buffer
*===========================================================================*/
static int json_extract_frame(uint8_t *buf, uint16_t *len, char *frame_out, uint16_t frame_size) {
uint16_t i;
for (i = 0; i < *len; i++) {
if (buf[i] == '\n') {
uint16_t frame_len = i;
if (frame_len >= frame_size) frame_len = frame_size - 1;
memcpy(frame_out, buf, frame_len);
frame_out[frame_len] = '\0';
// Remove consumed frame from buffer
uint16_t remaining = *len - i - 1;
if (remaining > 0) {
memmove(buf, buf + i + 1, remaining);
}
*len = remaining;
return 1;
}
}
return 0; // no complete frame yet
}
/*===========================================================================
* Command Handlers
*===========================================================================*/
/* 4.1 pwd_verify */
static void handle_pwd_verify(uint8_t socket, uint32_t msg_id, const char *json) {
PRINT("JSON: pwd_verify handler entered\n");
char *data = json_get_data_str(json);
if (!data) {
PRINT("JSON: pwd_verify — data is NULL\n");
json_send_error(socket, msg_id, "pwd_verify", JSON_CODE_PARAM_ERR, "missing data");
return;
}
PRINT("JSON: pwd_verify — data=[%s]\n", data);
char password[16] = {0};
json_get_str_field(data, "\"password\"", password, sizeof(password));
free(data);
PRINT("JSON: pwd_verify — password=[%s] len=%d\n", password, strlen(password));
PRINT("JSON: pwd_verify — dev_pwd=[%c%c%c%c%c%c]\n",
g_dev_password[0], g_dev_password[1], g_dev_password[2],
g_dev_password[3], g_dev_password[4], g_dev_password[5]);
if (strlen(password) == 0) {
json_send_error(socket, msg_id, "pwd_verify", JSON_CODE_PARAM_ERR, "missing password");
return;
}
// Compare password
if (memcmp(password, g_dev_password, 6) == 0 && strlen(password) == 6) {
g_json_auth_state = JSON_STATE_AUTHED;
g_json_pwd_retry = 0;
g_json_auth_timer = 0;
json_send_ok(socket, msg_id, "pwd_verify", NULL);
PRINT("JSON: Auth success\n");
} else {
g_json_pwd_retry++;
if (g_json_pwd_retry >= TCP_JSON_MAX_PWD_RETRY) {
g_json_auth_state = JSON_STATE_LOCKOUT;
g_json_lockout_timer = mstick();
PRINT("JSON: Auth locked out (%d retries)\n", g_json_pwd_retry);
json_send_error(socket, msg_id, "pwd_verify", JSON_CODE_AUTH_FAIL,
"password incorrect, locked out for 60s");
} else {
json_send_error(socket, msg_id, "pwd_verify", JSON_CODE_AUTH_FAIL, "password incorrect");
}
}
}
/* 4.2 dev_serial_set */
static void handle_dev_serial_set(uint8_t socket, uint32_t msg_id, const char *json) {
char *data = json_get_data_str(json);
if (!data) {
json_send_error(socket, msg_id, "dev_serial_set", JSON_CODE_PARAM_ERR, "missing data");
return;
}
char serial[32] = {0};
json_get_str_field(data, "\"dev_serial\"", serial, sizeof(serial));
free(data);
if (strlen(serial) != 12) {
json_send_error(socket, msg_id, "dev_serial_set", JSON_CODE_PARAM_ERR, "invalid serial length");
return;
}
// Convert hex string to bytes
uint8_t serial_bytes[6];
for (int i = 0; i < 6; i++) {
char hex[3] = {serial[i * 2], serial[i * 2 + 1], '\0'};
serial_bytes[i] = (uint8_t)strtoul(hex, NULL, 16);
}
alter_dev_serila(serial_bytes);
memcpy(g_dev_number, serial_bytes, 6);
sprintf(g_dev_number_str, "%02X%02X%02X%02X%02X%02X",
g_dev_number[0], g_dev_number[1], g_dev_number[2],
g_dev_number[3], g_dev_number[4], g_dev_number[5]);
json_send_ok(socket, msg_id, "dev_serial_set", NULL);
PRINT("JSON: dev_serial_set -> %s\n", g_dev_number_str);
}
/* 4.3 dev_info_query */
static void handle_dev_info_query(uint8_t socket, uint32_t msg_id, const char *json) {
char data_json[512];
snprintf(data_json, sizeof(data_json),
"{\"dev_serial\":\"%s\",\"hard_ver\":\"%s\",\"soft_ver\":\"%s\","
"\"model\":\"%s\",\"product_code\":\"960001\","
"\"sub_code\":{\"net\":%s,\"iot\":%s},"
"\"bus\":{\"bus1\":0,\"bus2\":0,\"bus3\":0,\"bus4\":0}}",
g_dev_number_str, HARDWARE_VER, FIRMWARE_VER,
PRODUCT_MODEL,
g_sub_code_enable.net_enable ? "true" : "false",
g_sub_code_enable.iot_enable ? "true" : "false");
json_send_ok(socket, msg_id, "dev_info_query", data_json);
}
/* 4.4 ssc_net_set */
static void handle_ssc_net_set(uint8_t socket, uint32_t msg_id, const char *json) {
char *data = json_get_data_str(json);
if (!data) {
json_send_error(socket, msg_id, "ssc_net_set", JSON_CODE_PARAM_ERR, "missing data");
return;
}
Local_Net_Cfg lcfg = local_net_cfg; // copy current
NET_CENTER_INFO ccfg = net_center_info;
char ip_str[16] = {0};
json_get_str_field(data, "\"dev_ip\"", ip_str, sizeof(ip_str));
if (strlen(ip_str) > 0) get_ipstr_to_array(ip_str, lcfg.lip);
char mask_str[16] = {0};
json_get_str_field(data, "\"subnet_mask\"", mask_str, sizeof(mask_str));
if (strlen(mask_str) > 0) get_ipstr_to_array(mask_str, lcfg.sub);
char gw_str[16] = {0};
json_get_str_field(data, "\"route_ip\"", gw_str, sizeof(gw_str));
if (strlen(gw_str) > 0) get_ipstr_to_array(gw_str, lcfg.gw);
char lssc_ip_str[16] = {0};
json_get_str_field(data, "\"lssc_ip\"", lssc_ip_str, sizeof(lssc_ip_str));
if (strlen(lssc_ip_str) > 0) get_ipstr_to_array(lssc_ip_str, ccfg.lssc_ip);
char dns_str[16] = {0};
json_get_str_field(data, "\"dns\"", dns_str, sizeof(dns_str));
if (strlen(dns_str) > 0) get_ipstr_to_array(dns_str, lcfg.dns);
uint32_t port = json_get_uint_field(data, "\"port\"");
if (port > 0 && port <= 65535) ccfg.tcp_port = (uint16_t)port;
free(data);
// Write to flash and update globals
write_net_config(&lcfg, &ccfg, &iot_net_info, &g_iot_topic);
local_net_cfg = lcfg;
net_center_info = ccfg;
json_send_ok(socket, msg_id, "ssc_net_set", NULL);
PRINT("JSON: ssc_net_set done\n");
}
/* 4.5 ssc_net_query */
static void handle_ssc_net_query(uint8_t socket, uint32_t msg_id, const char *json) {
char data_json[400];
snprintf(data_json, sizeof(data_json),
"{\"dev_ip\":\"%d.%d.%d.%d\",\"subnet_mask\":\"%d.%d.%d.%d\","
"\"route_ip\":\"%d.%d.%d.%d\",\"lssc_ip\":\"%d.%d.%d.%d\","
"\"dns\":\"%d.%d.%d.%d\",\"port\":%d}",
local_net_cfg.lip[0], local_net_cfg.lip[1], local_net_cfg.lip[2], local_net_cfg.lip[3],
local_net_cfg.sub[0], local_net_cfg.sub[1], local_net_cfg.sub[2], local_net_cfg.sub[3],
local_net_cfg.gw[0], local_net_cfg.gw[1], local_net_cfg.gw[2], local_net_cfg.gw[3],
net_center_info.lssc_ip[0], net_center_info.lssc_ip[1],
net_center_info.lssc_ip[2], net_center_info.lssc_ip[3],
local_net_cfg.dns[0], local_net_cfg.dns[1], local_net_cfg.dns[2], local_net_cfg.dns[3],
net_center_info.tcp_port);
json_send_ok(socket, msg_id, "ssc_net_query", data_json);
}
/* 4.6 iot_net_set */
static void handle_iot_net_set(uint8_t socket, uint32_t msg_id, const char *json) {
char *data = json_get_data_str(json);
if (!data) {
json_send_error(socket, msg_id, "iot_net_set", JSON_CODE_PARAM_ERR, "missing data");
return;
}
IOT_NET_INFO icfg = iot_net_info;
char host[64] = {0};
json_get_str_field(data, "\"host\"", host, sizeof(host));
if (strlen(host) > 0) strncpy((char *)icfg.remote_addr, host, 63);
uint32_t port = json_get_uint_field(data, "\"port\"");
if (port > 0 && port <= 65535) icfg.mqtt_port = (uint16_t)port;
char client_id[64] = {0};
json_get_str_field(data, "\"client_id\"", client_id, sizeof(client_id));
if (strlen(client_id) > 0) strncpy((char *)icfg.client_id, client_id, 63);
char username[64] = {0};
json_get_str_field(data, "\"username\"", username, sizeof(username));
if (strlen(username) > 0) strncpy((char *)icfg.username, username, 63);
char password[32] = {0};
json_get_str_field(data, "\"password\"", password, sizeof(password));
if (strlen(password) > 0) strncpy((char *)icfg.password, password, 31);
free(data);
write_net_config(&local_net_cfg, &net_center_info, &icfg, &g_iot_topic);
iot_net_info = icfg;
json_send_ok(socket, msg_id, "iot_net_set", NULL);
PRINT("JSON: iot_net_set done\n");
}
/* 4.7 iot_net_query */
static void handle_iot_net_query(uint8_t socket, uint32_t msg_id, const char *json) {
char data_json[400];
snprintf(data_json, sizeof(data_json),
"{\"host\":\"%s\",\"port\":%d,\"client_id\":\"%s\","
"\"username\":\"%s\",\"password\":\"%s\"}",
iot_net_info.remote_addr, iot_net_info.mqtt_port,
iot_net_info.client_id, iot_net_info.username, iot_net_info.password);
json_send_ok(socket, msg_id, "iot_net_query", data_json);
}
/* 4.8 iot_topic_set */
static void handle_iot_topic_set(uint8_t socket, uint32_t msg_id, const char *json) {
char *data = json_get_data_str(json);
if (!data) {
json_send_error(socket, msg_id, "iot_topic_set", JSON_CODE_PARAM_ERR, "missing data");
return;
}
IOT_Topic topic = g_iot_topic;
topic.clientid_enable = json_get_bool_field(data, "\"client_id_enable\"");
char topic_pub[64] = {0};
json_get_str_field(data, "\"topic_pub\"", topic_pub, sizeof(topic_pub));
if (strlen(topic_pub) > 0) strncpy((char *)topic.topic_pub, topic_pub, 63);
char topic_sub[64] = {0};
json_get_str_field(data, "\"topic_sub\"", topic_sub, sizeof(topic_sub));
if (strlen(topic_sub) > 0) strncpy((char *)topic.topic_sub, topic_sub, 63);
free(data);
write_net_config(&local_net_cfg, &net_center_info, &iot_net_info, &topic);
g_iot_topic = topic;
json_send_ok(socket, msg_id, "iot_topic_set", NULL);
PRINT("JSON: iot_topic_set done\n");
}
/* 4.9 iot_topic_query */
static void handle_iot_topic_query(uint8_t socket, uint32_t msg_id, const char *json) {
char data_json[300];
snprintf(data_json, sizeof(data_json),
"{\"client_id_enable\":%s,\"topic_pub\":\"%s\",\"topic_sub\":\"%s\"}",
g_iot_topic.clientid_enable ? "true" : "false",
g_iot_topic.topic_pub, g_iot_topic.topic_sub);
json_send_ok(socket, msg_id, "iot_topic_query", data_json);
}
/* 4.10 pwd_set */
static void handle_pwd_set(uint8_t socket, uint32_t msg_id, const char *json) {
char *data = json_get_data_str(json);
if (!data) {
json_send_error(socket, msg_id, "pwd_set", JSON_CODE_PARAM_ERR, "missing data");
return;
}
char old_pwd[16] = {0}, new_pwd[16] = {0};
json_get_str_field(data, "\"old_password\"", old_pwd, sizeof(old_pwd));
json_get_str_field(data, "\"new_password\"", new_pwd, sizeof(new_pwd));
free(data);
if (strlen(old_pwd) != 6 || strlen(new_pwd) != 6) {
json_send_error(socket, msg_id, "pwd_set", JSON_CODE_PARAM_ERR, "password must be 6 digits");
return;
}
if (memcmp(old_pwd, g_dev_password, 6) != 0) {
json_send_error(socket, msg_id, "pwd_set", JSON_CODE_AUTH_FAIL, "old password incorrect");
return;
}
memcpy(g_dev_password, new_pwd, 6);
set_ble_safe_pass((uint8_t *)new_pwd);
json_send_ok(socket, msg_id, "pwd_set", NULL);
PRINT("JSON: pwd_set done\n");
}
/* 4.11 factory_reset */
static void handle_factory_reset(uint8_t socket, uint32_t msg_id, const char *json) {
json_send_ok(socket, msg_id, "factory_reset", NULL);
PRINT("JSON: factory_reset — resetting...\n");
Delay_Ms(100);
factory_dev_info();
Delay_Ms(100);
NVIC_SystemReset();
}
/* 4.12 device_reset */
static void handle_device_reset(uint8_t socket, uint32_t msg_id, const char *json) {
// No response — device resets immediately
char *out = (char *)malloc(128);
if (out) {
snprintf(out, 128,
"{\"msg_id\":%lu,\"cmd\":\"device_reset\",\"ts\":%lu,\"code\":0,\"msg\":\"resetting\"}\n",
msg_id, (unsigned long)mstick());
uint32_t slen = strlen(out);
WCHNET_SocketSend(socket, (uint8_t *)out, &slen);
free(out);
}
Delay_Ms(100);
NVIC_SystemReset();
}
/* 4.13 loop_param_set — 设置地感线圈多路参数 (CMD 0x63) */
static void handle_loop_param_set(uint8_t socket, uint32_t msg_id, const char *json) {
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);
// 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 — 读取地感线圈多路参数 (CMD 0x64) */
static void handle_loop_param_query(uint8_t socket, uint32_t msg_id, const char *json) {
// Send query to Loop MCU and defer response
lup_send_get_param();
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 — 配置主动上报开关 */
static void handle_report_config(uint8_t socket, uint32_t msg_id, const char *json) {
char *data = json_get_data_str(json);
if (!data) {
json_send_error(socket, msg_id, "report_config", JSON_CODE_PARAM_ERR, "missing data");
return;
}
// Parse active_report flag
reset_tmp();
simple_parse_json(data, "\"active_report\"", g_tmp_value);
if (strlen(g_tmp_value) > 0) {
if (strstr(g_tmp_value, "true")) {
g_report_active = 1;
} else {
g_report_active = 0;
}
}
free(data);
json_send_ok(socket, msg_id, "report_config",
g_report_active ? "{\"active_report\":true}" : "{\"active_report\":false}");
PRINT("JSON: report_config active_report=%d\n", g_report_active);
}
/* 4.16 loop_version_query — 获取地感MCU版本号 (CMD 0x4A) */
static void handle_loop_version_query(uint8_t socket, uint32_t msg_id, const char *json) {
lup_send_get_version();
g_json_pending.active = 1;
g_json_pending.socket = socket;
g_json_pending.msg_id = msg_id;
strncpy(g_json_pending.cmd, "loop_version_query", sizeof(g_json_pending.cmd) - 1);
g_json_pending.deadline = mstick() + 300;
PRINT("JSON: loop_version_query sent\n");
}
/* 4.17 loop_reset — 复位地感MCU (CMD 0x6D, 无回复) */
static void handle_loop_reset(uint8_t socket, uint32_t msg_id, const char *json) {
lup_send_reset();
json_send_ok(socket, msg_id, "loop_reset", NULL);
PRINT("JSON: loop_reset sent (no response expected)\n");
}
/* 4.18 loop_factory_init — 地感MCU出厂初始化 (CMD 0x92) */
static void handle_loop_factory_init(uint8_t socket, uint32_t msg_id, const char *json) {
lup_send_factory_init();
g_json_pending.active = 1;
g_json_pending.socket = socket;
g_json_pending.msg_id = msg_id;
strncpy(g_json_pending.cmd, "loop_factory_init", sizeof(g_json_pending.cmd) - 1);
g_json_pending.deadline = mstick() + 600;
PRINT("JSON: loop_factory_init sent\n");
}
/* 4.19 loop_sens_read — 读取线圈灵敏度列表 (CMD 0x8A Read) */
static void handle_loop_sens_read(uint8_t socket, uint32_t msg_id, const char *json) {
lup_send_sensitivity_read();
g_json_pending.active = 1;
g_json_pending.socket = socket;
g_json_pending.msg_id = msg_id;
strncpy(g_json_pending.cmd, "loop_sens_read", sizeof(g_json_pending.cmd) - 1);
g_json_pending.deadline = mstick() + 300;
PRINT("JSON: loop_sens_read sent\n");
}
/* 4.20 loop_sens_write — 写入线圈灵敏度列表 (CMD 0x8A Write) */
static void handle_loop_sens_write(uint8_t socket, uint32_t msg_id, const char *json) {
char *data = json_get_data_str(json);
if (!data) {
json_send_error(socket, msg_id, "loop_sens_write", JSON_CODE_PARAM_ERR, "missing data");
return;
}
LUP_Sensitivity sens;
memset(&sens, 0, sizeof(sens));
sens.rw = LUP_SENS_RW_WRITE;
uint32_t amount = json_get_uint_field(data, "\"amount\"");
if (amount == 0 || amount > LUP_COIL_COUNT) amount = LUP_COIL_COUNT;
sens.amount = (uint8_t)amount;
char key[16];
uint8_t i;
for (i = 0; i < sens.amount; i++) {
snprintf(key, sizeof(key), "\"ch%d\"", i + 1);
char ch_data[256] = {0};
reset_tmp();
simple_parse_json(data, key, g_tmp_value);
if (strlen(g_tmp_value) == 0) continue;
strncpy(ch_data, g_tmp_value, sizeof(ch_data) - 1);
sens.sens_in[i] = (uint16_t)json_get_uint_field(ch_data, "\"sens_in\"");
sens.sens_out[i] = (uint16_t)json_get_uint_field(ch_data, "\"sens_out\"");
}
free(data);
uint8_t buf[LUP_MAX_PKG_LEN];
uint16_t len = lup_build_sensitivity_write(buf, &sens);
UART2_SendString(buf, len);
PRINT("LUP Tx:");
for (i = 0; i < len; i++) PRINT(" %02X", buf[i]);
PRINT("\n");
lup_cmd_begin(LUP_CMD_SENSITIVITY, 300);
g_json_pending.active = 1;
g_json_pending.socket = socket;
g_json_pending.msg_id = msg_id;
strncpy(g_json_pending.cmd, "loop_sens_write", sizeof(g_json_pending.cmd) - 1);
g_json_pending.deadline = mstick() + 300;
PRINT("JSON: loop_sens_write sent (ch=%d)\n", sens.amount);
}
/*===========================================================================
* Command Dispatch Table
*===========================================================================*/
typedef struct {
const char *cmd;
void (*handler)(uint8_t socket, uint32_t msg_id, const char *json);
uint8_t need_auth; // 1 = requires auth, 0 = pre-auth ok
} JsonCmdEntry;
static const JsonCmdEntry g_cmd_table[] = {
{"pwd_verify", handle_pwd_verify, 0},
{"dev_serial_set", handle_dev_serial_set, 1},
{"dev_info_query", handle_dev_info_query, 1},
{"ssc_net_set", handle_ssc_net_set, 1},
{"ssc_net_query", handle_ssc_net_query, 1},
{"iot_net_set", handle_iot_net_set, 1},
{"iot_net_query", handle_iot_net_query, 1},
{"iot_topic_set", handle_iot_topic_set, 1},
{"iot_topic_query", handle_iot_topic_query, 1},
{"pwd_set", handle_pwd_set, 1},
{"factory_reset", handle_factory_reset, 1},
{"device_reset", handle_device_reset, 1},
{"loop_param_set", handle_loop_param_set, 1},
{"loop_param_query", handle_loop_param_query, 1},
{"loop_version_query", handle_loop_version_query, 1},
{"loop_reset", handle_loop_reset, 1},
{"loop_factory_init", handle_loop_factory_init, 1},
{"loop_sens_read", handle_loop_sens_read, 1},
{"loop_sens_write", handle_loop_sens_write, 1},
{"report_config", handle_report_config, 1},
};
#define JSON_CMD_COUNT (sizeof(g_cmd_table) / sizeof(g_cmd_table[0]))
/*===========================================================================
* Frame Processing
*===========================================================================*/
static void json_process_frame(uint8_t socket, const char *frame) {
if (strlen(frame) == 0) return;
uint32_t msg_id = json_get_msg_id(frame);
char cmd[32] = {0};
json_get_cmd(frame, cmd, sizeof(cmd));
PRINT("JSON dispatch: msg_id=%lu cmd=[%s] len=%d\n", msg_id, cmd, strlen(cmd));
if (strlen(cmd) == 0) {
PRINT("JSON: empty cmd, sending error\n");
json_send_error(socket, msg_id, "", JSON_CODE_PARAM_ERR, "missing cmd field");
return;
}
// Dispatch
int handled = 0;
for (int i = 0; i < JSON_CMD_COUNT; i++) {
if (strcmp(cmd, g_cmd_table[i].cmd) == 0) {
PRINT("JSON: matched cmd[%d]=%s, need_auth=%d, auth_state=%d\n",
i, g_cmd_table[i].cmd, g_cmd_table[i].need_auth, g_json_auth_state);
// Check auth
if (g_cmd_table[i].need_auth && g_json_auth_state != JSON_STATE_AUTHED) {
json_send_error(socket, msg_id, cmd, JSON_CODE_NOT_AUTHED, "not authenticated");
handled = 1;
break;
}
PRINT("JSON: calling handler for %s\n", cmd);
g_cmd_table[i].handler(socket, msg_id, frame);
handled = 1;
break;
}
}
if (!handled) {
json_send_error(socket, msg_id, cmd, JSON_CODE_UNSUPPORTED, "unsupported command");
PRINT("JSON: unsupported cmd: [%s]\n", cmd);
}
}
/*===========================================================================
* json_sensor_callback — 注册到 lup_process_frame0xC0 帧到达时直接推送
*===========================================================================*/
/* Forward declarations */
static int format_sensor_json(char *buf, uint16_t buf_size, const LUP_SensorReport *sr);
static int format_loop_param_json(char *buf, uint16_t buf_size, const LUP_ParamGet *pg);
static void json_sensor_callback(const uint8_t *pkg, uint16_t len)
{
PRINT("JSON: sensor_cb enter socket=%d auth=%d report=%d\n",
g_json_socket_listen, g_json_auth_state, g_report_active);
// Check: socket active, authed, report enabled
if (g_json_socket_listen == 0xFF) { PRINT("JSON: sensor_cb skip: no socket\n"); return; }
if (g_json_auth_state != JSON_STATE_AUTHED) { PRINT("JSON: sensor_cb skip: not authed\n"); return; }
if (!g_report_active) { PRINT("JSON: sensor_cb skip: report disabled\n"); return; }
LUP_SensorReport sr;
memset(&sr, 0, sizeof(sr));
int ret = lup_parse_sensor_report(pkg, len, &sr);
if (ret != 0) {
PRINT("JSON: sensor callback parse failed (%d)\n", ret);
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 TCP: listen socket=N, data socket=N+1
WCHNET_SocketSend(g_json_socket_listen + 1, (uint8_t *)out, &slen);
free(out);
}
}
}
/*===========================================================================
* Public API
*===========================================================================*/
void tcp_json_srv_init(void) {
uint8_t ret;
SOCK_INF sock_inf;
static uint8_t _init_done = 0;
if (_init_done) return; // 防止 net_srv_init 反复调用
_init_done = 1;
// 注册传感器回调 — 必须在 socket 操作之前,
// 避免 socket 失败导致 return 而漏掉注册
lup_set_sensor_callback(json_sensor_callback);
memset(&sock_inf, 0, sizeof(SOCK_INF));
sock_inf.SourPort = TCP_JSON_PORT;
sock_inf.ProtoType = PROTO_TYPE_TCP;
sock_inf.RecvBufLen = RECE_BUF_LEN;
ret = WCHNET_SocketCreat(&g_json_socket_listen, &sock_inf);
if (ret != WCHNET_ERR_SUCCESS) {
PRINT("JSON: SocketCreat failed: 0x%02X\n", ret);
return;
}
ret = WCHNET_SocketListen(g_json_socket_listen);
if (ret != WCHNET_ERR_SUCCESS) {
PRINT("JSON: SocketListen failed: 0x%02X\n", ret);
return;
}
PRINT("JSON: TCP listen on port %d (socket %d)\n", TCP_JSON_PORT, g_json_socket_listen);
}
void tcp_json_handle_sock_int(uint8_t socketid, uint8_t intstat) {
// === CONNECT: client connected — configure recv buffer, init auth state ===
if (intstat & SINT_STAT_CONNECT) {
WCHNET_ModifyRecvBuf(socketid, (uint32_t)g_json_wchnet_buf, RECE_BUF_LEN);
g_json_auth_state = JSON_STATE_WAIT_AUTH;
g_json_pwd_retry = 0;
g_json_auth_timer = mstick();
g_json_recv_len = 0;
memset(g_json_recv_buf, 0, sizeof(g_json_recv_buf));
PRINT("JSON: Client connected on socket %d, recv buf configured\n", socketid);
}
// === RECV: read and process incoming data ===
if (intstat & SINT_STAT_RECV) {
uint32_t recv_len = WCHNET_SocketRecvLen(socketid, NULL);
if (recv_len > 0) {
uint16_t space = TCP_JSON_RECV_BUF_LEN - g_json_recv_len;
if (recv_len > space) recv_len = space;
uint32_t rd_len = recv_len;
uint8_t tmp_buf[RECE_BUF_LEN];
WCHNET_SocketRecv(socketid, tmp_buf, &rd_len);
memcpy(g_json_recv_buf + g_json_recv_len, tmp_buf, (uint16_t)rd_len);
g_json_recv_len += (uint16_t)rd_len;
char frame[TCP_JSON_MAX_FRAME];
while (json_extract_frame(g_json_recv_buf, &g_json_recv_len, frame, sizeof(frame))) {
PRINT("JSON recv: %s\n", frame);
json_process_frame(socketid, frame);
if (g_json_auth_state == JSON_STATE_WAIT_AUTH) {
g_json_auth_timer = mstick();
}
}
if (g_json_recv_len >= TCP_JSON_MAX_FRAME) {
PRINT("JSON: frame overflow, discarding buffer\n");
g_json_recv_len = 0;
memset(g_json_recv_buf, 0, sizeof(g_json_recv_buf));
}
}
}
// === DISCONNECT: client disconnected, reset state ===
if (intstat & SINT_STAT_DISCONNECT) {
PRINT("JSON: Client disconnected (id=%d)\n", socketid);
g_json_auth_state = JSON_STATE_WAIT_AUTH;
g_json_pwd_retry = 0;
g_json_recv_len = 0;
}
// === TIMEOUT: connection timed out ===
if (intstat & SINT_STAT_TIM_OUT) {
PRINT("JSON: Client timeout (id=%d)\n", socketid);
g_json_auth_state = JSON_STATE_WAIT_AUTH;
g_json_pwd_retry = 0;
g_json_recv_len = 0;
}
}
/*===========================================================================
* 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:
// car_state=0(无车)→通过时间, car_state=1(有车)→车间距
misc_field = cs->car_state ? "\"gap_ms5\"" : "\"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;
case 3: misc_field = "\"relay_count\""; misc_val = cs->misc.relay_count; 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;
if (g_lup_cmd.state == LUP_STATE_RESPONSE_READY) {
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");
}
} else if (strcmp(g_json_pending.cmd, "loop_version_query") == 0) {
LUP_VersionInfo info;
memset(&info, 0, sizeof(info));
int ret = lup_parse_version(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &info);
if (ret == 0) {
snprintf(info.version_str, sizeof(info.version_str),
"V%d.%d.%d (HW:%d.%d.%d)",
info.soft_main, info.soft_sub, info.soft_ssub,
info.hard_main, info.hard_sub, info.hard_ssub);
char data_json[256];
snprintf(data_json, sizeof(data_json),
"{\"soft_ver\":\"%d.%d.%d\",\"hard_ver\":\"%d.%d.%d\","
"\"version_str\":\"%s\"}",
info.soft_main, info.soft_sub, info.soft_ssub,
info.hard_main, info.hard_sub, info.hard_ssub,
info.version_str);
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,
"Failed to parse version");
}
} else if (strcmp(g_json_pending.cmd, "loop_factory_init") == 0) {
uint8_t success = 0;
int ret = lup_parse_factory_init_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 ? "Factory init failed" : "Parse error");
}
} else if (strcmp(g_json_pending.cmd, "loop_sens_read") == 0) {
LUP_Sensitivity sens;
memset(&sens, 0, sizeof(sens));
int ret = lup_parse_sensitivity_resp(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &sens);
if (ret == 0) {
char data_json[1024];
char *p = data_json;
int rem = sizeof(data_json);
int w = snprintf(p, rem,
"{\"ret\":\"%s\",\"amount\":%d,\"channels\":[",
(sens.rw == 0x10) ? "read" : (sens.rw == 0x11) ? "write" : "?",
sens.amount);
p += w; rem -= w;
uint8_t i;
for (i = 0; i < sens.amount; i++) {
w = snprintf(p, rem,
"%s{\"ch\":%d,\"sens_in\":%d,\"sens_out\":%d}",
(i > 0) ? "," : "", i + 1,
sens.sens_in[i], sens.sens_out[i]);
p += w; rem -= w;
}
snprintf(p, rem, "]}");
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,
"Failed to parse sensitivity");
}
} else if (strcmp(g_json_pending.cmd, "loop_sens_write") == 0) {
LUP_Sensitivity sens;
memset(&sens, 0, sizeof(sens));
int ret = lup_parse_sensitivity_resp(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &sens);
if (ret == 0) {
char data_json[1024];
char *p = data_json;
int rem = sizeof(data_json);
int w = snprintf(p, rem,
"{\"ret\":\"%s\",\"amount\":%d,\"channels\":[",
(sens.rw == 0x10) ? "read" : (sens.rw == 0x11) ? "write" : "?",
sens.amount);
p += w; rem -= w;
uint8_t i;
for (i = 0; i < sens.amount; i++) {
w = snprintf(p, rem,
"%s{\"ch\":%d,\"sens_in\":%d,\"sens_out\":%d}",
(i > 0) ? "," : "", i + 1,
sens.sens_in[i], sens.sens_out[i]);
p += w; rem -= w;
}
snprintf(p, rem, "]}");
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,
"Failed to parse sensitivity 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;
// Auth timeout check
if (g_json_auth_state == JSON_STATE_WAIT_AUTH) {
if (mstick() - g_json_auth_timer > TCP_JSON_AUTH_TIMEOUT_MS) {
PRINT("JSON: Auth timeout, closing connection\n");
WCHNET_SocketClose(g_json_socket_listen, TCP_CLOSE_NORMAL);
g_json_auth_state = JSON_STATE_WAIT_AUTH;
g_json_pwd_retry = 0;
g_json_recv_len = 0;
}
}
// Lockout timeout check
if (g_json_auth_state == JSON_STATE_LOCKOUT) {
if (mstick() - g_json_lockout_timer > TCP_JSON_LOCKOUT_TIMEOUT_MS) {
PRINT("JSON: Lockout expired\n");
g_json_auth_state = JSON_STATE_WAIT_AUTH;
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, report enabled
if (g_json_socket_listen == 0xFF) return;
if (g_json_auth_state != JSON_STATE_AUTHED) return;
if (!g_report_active) 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 TCP: listen socket=N, data socket=N+1
WCHNET_SocketSend(g_json_socket_listen + 1, (uint8_t *)out, &slen);
free(out);
}
}
// Clear after processing
InitPkgUart(&g_pkg_uart_2);
}