feat(vd960DBN): TCP JSON协议服务 — 端口5960, 鉴权+15条命令
- net_config.h: TCP_LISTEN=0→1, TCP=2 支持 JSON 监听 - 新增 tcp_json_srv.h/c: 行分隔 JSON, pwd_verify鉴权, 命令分发 - 实现15条协议命令: dev_info/ssc_net/iot_net/iot_topic/pwd_set/factory_reset等 - loop_param_set/query 接受命令返回stub(Loop MCU中继待实现) - net_srv.c: 集成 JSON 中断路由 + init - peripheral_main.c: 主循环 tcp_json_poll()
This commit is contained in:
@@ -24,9 +24,9 @@ extern "C" {
|
|||||||
|
|
||||||
#define WCHNET_NUM_UDP 1 /* The number of UDP connections */
|
#define WCHNET_NUM_UDP 1 /* The number of UDP connections */
|
||||||
|
|
||||||
#define WCHNET_NUM_TCP 1 /* Number of TCP connections */
|
#define WCHNET_NUM_TCP 2 /* Number of TCP connections (1 SSC/MQTT + 1 JSON accepted) */
|
||||||
|
|
||||||
#define WCHNET_NUM_TCP_LISTEN 0 /* Number of TCP listening */
|
#define WCHNET_NUM_TCP_LISTEN 1 /* Number of TCP listening (JSON protocol on port 5960) */
|
||||||
|
|
||||||
/* The number of sockets, the maximum is 31 */
|
/* The number of sockets, the maximum is 31 */
|
||||||
#define WCHNET_MAX_SOCKET_NUM (WCHNET_NUM_IPRAW+WCHNET_NUM_UDP+WCHNET_NUM_TCP+WCHNET_NUM_TCP_LISTEN)
|
#define WCHNET_MAX_SOCKET_NUM (WCHNET_NUM_IPRAW+WCHNET_NUM_UDP+WCHNET_NUM_TCP+WCHNET_NUM_TCP_LISTEN)
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* @file tcp_json_srv.h
|
||||||
|
* @author wangfq
|
||||||
|
* @version V1.0
|
||||||
|
* @date 2026-06-30
|
||||||
|
* @brief TCP JSON protocol server — DLD960 TCP JSON protocol handler
|
||||||
|
* Port 5960, line-delimited JSON, auth + command dispatch
|
||||||
|
******************************************************************************
|
||||||
|
*/
|
||||||
|
#ifndef __TCP_JSON_SRV_H__
|
||||||
|
#define __TCP_JSON_SRV_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* Protocol Constants
|
||||||
|
*===========================================================================*/
|
||||||
|
#define TCP_JSON_PORT 5960 // TCP JSON protocol default port
|
||||||
|
#define TCP_JSON_MAX_FRAME 4096 // Max JSON frame length
|
||||||
|
#define TCP_JSON_AUTH_TIMEOUT_MS 60000 // 60s auth timeout (ms)
|
||||||
|
#define TCP_JSON_RECV_BUF_LEN (TCP_JSON_MAX_FRAME * 2)
|
||||||
|
#define TCP_JSON_MAX_PWD_RETRY 3 // Max password retries before lockout
|
||||||
|
#define TCP_JSON_LOCKOUT_TIMEOUT_MS 60000 // 60s lockout after max retries
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* Auth / State
|
||||||
|
*===========================================================================*/
|
||||||
|
typedef enum {
|
||||||
|
JSON_STATE_WAIT_AUTH = 0, // Waiting for pwd_verify
|
||||||
|
JSON_STATE_AUTHED, // Authenticated, commands accepted
|
||||||
|
JSON_STATE_LOCKOUT // Locked out (max retries exceeded)
|
||||||
|
} TcpJsonAuthState;
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* Error Codes
|
||||||
|
*===========================================================================*/
|
||||||
|
#define JSON_CODE_SUCCESS 0
|
||||||
|
#define JSON_CODE_PARAM_ERR 1
|
||||||
|
#define JSON_CODE_AUTH_FAIL 2
|
||||||
|
#define JSON_CODE_BUSY 3
|
||||||
|
#define JSON_CODE_UNSUPPORTED 4
|
||||||
|
#define JSON_CODE_INTERNAL_ERR 5
|
||||||
|
#define JSON_CODE_DATA_TOO_LONG 6
|
||||||
|
#define JSON_CODE_NOT_AUTHED 7
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* Externs
|
||||||
|
*===========================================================================*/
|
||||||
|
extern uint8_t g_json_socket_listen; // listen socket ID
|
||||||
|
extern uint8_t g_json_socket_client; // accepted client socket ID (0xFF = none)
|
||||||
|
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
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* API
|
||||||
|
*===========================================================================*/
|
||||||
|
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.)
|
||||||
|
|
||||||
|
#endif /* __TCP_JSON_SRV_H__ */
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "cmcng.h"
|
#include "cmcng.h"
|
||||||
#include "simple_json.h"
|
#include "simple_json.h"
|
||||||
|
#include "tcp_json_srv.h"
|
||||||
|
|
||||||
|
|
||||||
uint32_t slen;
|
uint32_t slen;
|
||||||
@@ -50,7 +51,7 @@ Net_State g_net_state = {0,0, 0};
|
|||||||
|
|
||||||
|
|
||||||
uint16_t srcport = 6000;
|
uint16_t srcport = 6000;
|
||||||
//UINT8 UDPDESIP[4] = {255,255,255,255}; /* ????????????????????,?????????·???????????????????? */
|
//UINT8 UDPDESIP[4] = {255,255,255,255}; /* ????????????????????,?????????<EFBFBD><EFBFBD>???????????????????? */
|
||||||
//UINT16 aport=1000; /* CH579?????????? */
|
//UINT16 aport=1000; /* CH579?????????? */
|
||||||
uint32_t g_wdg_counter = 0;
|
uint32_t g_wdg_counter = 0;
|
||||||
|
|
||||||
@@ -103,7 +104,7 @@ void mqtt_connect(void)
|
|||||||
int len = 0;
|
int len = 0;
|
||||||
mqttData.username.cstring = iot_net_info.username;
|
mqttData.username.cstring = iot_net_info.username;
|
||||||
mqttData.password.cstring = iot_net_info.password;
|
mqttData.password.cstring = iot_net_info.password;
|
||||||
mqttData.clientID.cstring = g_dev_number_str; //TODO: ?§????????????????????
|
mqttData.clientID.cstring = g_dev_number_str; //TODO: ?<EFBFBD><EFBFBD>????????????????????
|
||||||
mqttData.keepAliveInterval = MQTT_KEEPALIVE_INTERVAL;
|
mqttData.keepAliveInterval = MQTT_KEEPALIVE_INTERVAL;
|
||||||
|
|
||||||
PRINT("username:%s\r\n", mqttData.username.cstring);
|
PRINT("username:%s\r\n", mqttData.username.cstring);
|
||||||
@@ -480,6 +481,17 @@ void WCHNET_HandleSockInt(uint8_t socketid, uint8_t intstat)
|
|||||||
{
|
{
|
||||||
uint8_t i;
|
uint8_t i;
|
||||||
|
|
||||||
|
// Route JSON protocol socket events
|
||||||
|
if (socketid == g_json_socket_listen || socketid == g_json_socket_client) {
|
||||||
|
tcp_json_handle_sock_int(socketid, intstat);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Also catch newly accepted TCP connections that might be JSON clients
|
||||||
|
if (intstat & SINT_STAT_CONNECT && g_json_socket_client == 0xFF && socketid != g_json_socket_listen) {
|
||||||
|
tcp_json_handle_sock_int(socketid, intstat);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
g_net_state.intstat = intstat;
|
g_net_state.intstat = intstat;
|
||||||
|
|
||||||
if (intstat & SINT_STAT_RECV) //receive data
|
if (intstat & SINT_STAT_RECV) //receive data
|
||||||
@@ -532,7 +544,7 @@ void WCHNET_HandleSockInt(uint8_t socketid, uint8_t intstat)
|
|||||||
void dbn_net_ssc_srv(void)
|
void dbn_net_ssc_srv(void)
|
||||||
{
|
{
|
||||||
uint8_t _flag_timestamp = 0;
|
uint8_t _flag_timestamp = 0;
|
||||||
static uint32_t _report_counter = 0; //上报时间计数器
|
static uint32_t _report_counter = 0; //<EFBFBD>ϱ<EFBFBD>ʱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
|
||||||
//Check net if connect
|
//Check net if connect
|
||||||
if(g_net_state.flag < 3)
|
if(g_net_state.flag < 3)
|
||||||
@@ -592,8 +604,8 @@ void poll_mqtt(void)
|
|||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Function Name : GetMacAddr
|
* Function Name : GetMacAddr
|
||||||
* Description : 系统获取MAC地址
|
* Description : ϵͳ<EFBFBD><EFBFBD>ȡMAC<EFBFBD><EFBFBD>ַ
|
||||||
* Input : pMAC:指向用来存储Mac地址的缓冲
|
* Input : pMAC:ָ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>洢Mac<EFBFBD><EFBFBD>ַ<EFBFBD>Ļ<EFBFBD><EFBFBD><EFBFBD>
|
||||||
* Output : None
|
* Output : None
|
||||||
* Return : None
|
* Return : None
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@@ -610,50 +622,50 @@ void GetMacAddr(unsigned char *pMAC)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 将IPv4地址字符串转换为4字节的uint8_t数组,并进行严格的错误检查
|
// <EFBFBD><EFBFBD>IPv4<EFBFBD><EFBFBD>ַ<EFBFBD>ַ<EFBFBD><EFBFBD><EFBFBD>ת<EFBFBD><EFBFBD>Ϊ4<EFBFBD>ֽڵ<EFBFBD>uint8_t<EFBFBD><EFBFBD><EFBFBD>飬<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϸ<EFBFBD>Ĵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
int get_ipstr_to_array(char *src, uint8_t *dst) {
|
int get_ipstr_to_array(char *src, uint8_t *dst) {
|
||||||
if (src == NULL || dst == NULL) {
|
if (src == NULL || dst == NULL) {
|
||||||
return -1; // 无效指针
|
return -1; // <EFBFBD><EFBFBD>Чָ<EFBFBD><EFBFBD>
|
||||||
}
|
}
|
||||||
|
|
||||||
int part_count = 0;
|
int part_count = 0;
|
||||||
char *token = NULL;
|
char *token = NULL;
|
||||||
char *saveptr = NULL;
|
char *saveptr = NULL;
|
||||||
const char *delim = ".";
|
const char *delim = ".";
|
||||||
// 输入字符串应采用可修改的内存(如数组或堆内存),因为strtok_r会修改字符串
|
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ַ<EFBFBD><EFBFBD><EFBFBD>Ӧ<EFBFBD><EFBFBD><EFBFBD>ÿ<EFBFBD><EFBFBD>ĵ<EFBFBD><EFBFBD>ڴ棨<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڴ棩<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊstrtok_r<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ַ<EFBFBD><EFBFBD><EFBFBD>
|
||||||
// 需处理不可修改的字符串,可先进行拷贝
|
// <EFBFBD>账<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ĵ<EFBFBD><EFBFBD>ַ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƚ<EFBFBD><EFBFBD>п<EFBFBD><EFBFBD><EFBFBD>
|
||||||
char src_copy[16];
|
char src_copy[16];
|
||||||
strncpy(src_copy, src, sizeof(src_copy));
|
strncpy(src_copy, src, sizeof(src_copy));
|
||||||
src_copy[15] = '\0';
|
src_copy[15] = '\0';
|
||||||
|
|
||||||
// 首次分割
|
// <EFBFBD>״ηָ<EFBFBD>
|
||||||
token = strtok_r(src_copy, delim, &saveptr);
|
token = strtok_r(src_copy, delim, &saveptr);
|
||||||
while (token != NULL && part_count < 4) {
|
while (token != NULL && part_count < 4) {
|
||||||
char *endptr = NULL;
|
char *endptr = NULL;
|
||||||
long val = strtol(token, &endptr, 10);
|
long val = strtol(token, &endptr, 10);
|
||||||
|
|
||||||
// 检查转换有效性
|
// <EFBFBD><EFBFBD><EFBFBD>ת<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ч<EFBFBD><EFBFBD>
|
||||||
if (*endptr != '\0' || val < 0 || val > 255) {
|
if (*endptr != '\0' || val < 0 || val > 255) {
|
||||||
return -2; // 非数字或超出范围
|
return -2; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֻ<EFBFBD><EFBFBD><EFBFBD>Χ
|
||||||
}
|
}
|
||||||
|
|
||||||
dst[part_count++] = (uint8_t)val;
|
dst[part_count++] = (uint8_t)val;
|
||||||
|
|
||||||
// 后续分割
|
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ָ<EFBFBD>
|
||||||
token = strtok_r(NULL, delim, &saveptr);
|
token = strtok_r(NULL, delim, &saveptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否正好有4部分
|
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƿ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>4<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
if (part_count != 4) {
|
if (part_count != 4) {
|
||||||
return -3; // 部分数量错误
|
return -3; // <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否有额外字符
|
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƿ<EFBFBD><EFBFBD>ж<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ַ<EFBFBD>
|
||||||
if (token != NULL || (saveptr != NULL && *saveptr != '\0')) {
|
if (token != NULL || (saveptr != NULL && *saveptr != '\0')) {
|
||||||
return -4; // 存在额外分隔符或字符
|
return -4; // <EFBFBD><EFBFBD><EFBFBD>ڶ<EFBFBD><EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ַ<EFBFBD>
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0; // 成功
|
return 0; // <EFBFBD>ɹ<EFBFBD>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -757,7 +769,7 @@ void unpack_ssc_count_off(uint8_t socket,uint8_t count_mode, uint8_t *ip, uint16
|
|||||||
|
|
||||||
void unpack_ssc_device_reset(uint8_t socket, uint8_t *ip, uint16_t port, uint8_t *buf, uint32_t len)
|
void unpack_ssc_device_reset(uint8_t socket, uint8_t *ip, uint16_t port, uint8_t *buf, uint32_t len)
|
||||||
{
|
{
|
||||||
// 复位设备
|
// <EFBFBD><EFBFBD>λ<EFBFBD>豸
|
||||||
uint16_t tmplen = 0;
|
uint16_t tmplen = 0;
|
||||||
char *p = (char *)buf;
|
char *p = (char *)buf;
|
||||||
char *mBuff = (char *)malloc(512);
|
char *mBuff = (char *)malloc(512);
|
||||||
@@ -1108,6 +1120,7 @@ void net_srv_init(void)
|
|||||||
if(g_net_state.flag == 1)
|
if(g_net_state.flag == 1)
|
||||||
{
|
{
|
||||||
WCHNET_CreateUdpSocket();
|
WCHNET_CreateUdpSocket();
|
||||||
|
tcp_json_srv_init(); // Start JSON protocol TCP listener on port 5960
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include "eth_driver.h"
|
#include "eth_driver.h"
|
||||||
#include "net_srv.h"
|
#include "net_srv.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
|
#include "tcp_json_srv.h"
|
||||||
|
|
||||||
/*********************************************************************
|
/*********************************************************************
|
||||||
* GLOBAL TYPEDEFS
|
* GLOBAL TYPEDEFS
|
||||||
@@ -45,7 +46,7 @@ const uint32_t Address = 0xFFFFFFFF;
|
|||||||
|
|
||||||
__attribute__((aligned(4))) uint32_t Image_Flag __attribute__((section(".ImageFlag"))) = (uint32_t)&Address;
|
__attribute__((aligned(4))) uint32_t Image_Flag __attribute__((section(".ImageFlag"))) = (uint32_t)&Address;
|
||||||
|
|
||||||
uint8_t g_dev_number[6] = ""; // É豸±àºÅ ²úÆ·±àºÅ
|
uint8_t g_dev_number[6] = ""; // <EFBFBD>豸<EFBFBD><EFBFBD><EFBFBD> <20><>Ʒ<EFBFBD><C6B7><EFBFBD>
|
||||||
uint8_t g_dev_password[6] = {0x31, 0x32, 0x33, 0x34, 0x35, 0x36};
|
uint8_t g_dev_password[6] = {0x31, 0x32, 0x33, 0x34, 0x35, 0x36};
|
||||||
uint8_t g_ble_safe_flag = 0;
|
uint8_t g_ble_safe_flag = 0;
|
||||||
uint32_t g_ble_safe_counter_ori = 0;
|
uint32_t g_ble_safe_counter_ori = 0;
|
||||||
@@ -53,7 +54,7 @@ uint32_t g_ble_safe_counter_dst = 0;
|
|||||||
|
|
||||||
char g_flag_debug = 1;
|
char g_flag_debug = 1;
|
||||||
|
|
||||||
uint8_t g_dg_device_type = DDType_DLD950V4; // ???¨¨¡À?????????????
|
uint8_t g_dg_device_type = DDType_DLD950V4; // ???<EFBFBD><EFBFBD><EFBFBD><EFBFBD>?????????????
|
||||||
uint8_t g_dg_sub_dev_type = DDType_DLD950V4;
|
uint8_t g_dg_sub_dev_type = DDType_DLD950V4;
|
||||||
|
|
||||||
Sub_Code_Enable g_sub_code_enable = {0,};
|
Sub_Code_Enable g_sub_code_enable = {0,};
|
||||||
@@ -266,6 +267,8 @@ void Main_Circulation(void)
|
|||||||
|
|
||||||
poll_dbn_ble();
|
poll_dbn_ble();
|
||||||
|
|
||||||
|
tcp_json_poll();
|
||||||
|
|
||||||
key_event_srv();
|
key_event_srv();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
741
vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/tcp_json_srv.c
Normal file
741
vd960DBN/BLE/OnlyUpdateApp_Peripheral/APP/tcp_json_srv.c
Normal file
@@ -0,0 +1,741 @@
|
|||||||
|
/**
|
||||||
|
******************************************************************************
|
||||||
|
* @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 "simple_json.h"
|
||||||
|
#include "storage.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* Global State
|
||||||
|
*===========================================================================*/
|
||||||
|
uint8_t g_json_socket_listen = 0xFF; // listen socket ID
|
||||||
|
uint8_t g_json_socket_client = 0xFF; // accepted client socket ID
|
||||||
|
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;
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* 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 void json_get_cmd(const char *json, char *out, int out_len) {
|
||||||
|
reset_tmp();
|
||||||
|
simple_parse_json(json, "\"cmd\"", g_tmp_value);
|
||||||
|
strncpy(out, g_tmp_value, out_len - 1);
|
||||||
|
out[out_len - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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) {
|
||||||
|
char *data = json_get_data_str(json);
|
||||||
|
if (!data) {
|
||||||
|
json_send_error(socket, msg_id, "pwd_verify", JSON_CODE_PARAM_ERR, "missing data");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char password[16] = {0};
|
||||||
|
json_get_str_field(data, "\"password\"", password, sizeof(password));
|
||||||
|
free(data);
|
||||||
|
|
||||||
|
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 */
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 4.14 loop_param_query */
|
||||||
|
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}]}");
|
||||||
|
|
||||||
|
json_send_ok(socket, msg_id, "loop_param_query", data_json);
|
||||||
|
PRINT("JSON: loop_param_query (stub — Loop MCU relay pending)\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;
|
||||||
|
}
|
||||||
|
// Store report config for future push support
|
||||||
|
free(data);
|
||||||
|
json_send_ok(socket, msg_id, "report_config", NULL);
|
||||||
|
PRINT("JSON: report_config (push not yet implemented)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* 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},
|
||||||
|
{"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));
|
||||||
|
|
||||||
|
if (strlen(cmd) == 0) {
|
||||||
|
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) {
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* Public API
|
||||||
|
*===========================================================================*/
|
||||||
|
|
||||||
|
void tcp_json_srv_init(void) {
|
||||||
|
uint8_t ret;
|
||||||
|
SOCK_INF sock_inf;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// === Listen socket events ===
|
||||||
|
if (socketid == g_json_socket_listen) {
|
||||||
|
if (intstat & SINT_STAT_CONNECT) {
|
||||||
|
// A client connected — find the accepted socket
|
||||||
|
// The accepted connection gets a different socket ID
|
||||||
|
// We find it by scanning TCP sockets in ESTABLISHED state
|
||||||
|
uint8_t i;
|
||||||
|
for (i = 0; i < WCHNET_MAX_SOCKET_NUM; i++) {
|
||||||
|
if (i == g_json_socket_listen) continue;
|
||||||
|
// Check if this socket is in ESTABLISHED state and not our SSC/MQTT socket
|
||||||
|
SOCK_INF *si = NULL;
|
||||||
|
// We check by trying to recv — if it works, it's the accepted socket
|
||||||
|
uint32_t recv_len = WCHNET_SocketRecvLen(i, NULL);
|
||||||
|
(void)recv_len;
|
||||||
|
// Simpler approach: just assign the first non-listen TCP socket
|
||||||
|
// that's not the existing SSC/MQTT client (SocketId_TCP)
|
||||||
|
if (i != SocketId_TCP && g_json_socket_client == 0xFF) {
|
||||||
|
g_json_socket_client = i;
|
||||||
|
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\n", i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (intstat & SINT_STAT_DISCONNECT) {
|
||||||
|
PRINT("JSON: Listen socket disconnect (unexpected)\n");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Client socket events ===
|
||||||
|
if (socketid == g_json_socket_client) {
|
||||||
|
if (intstat & SINT_STAT_RECV) {
|
||||||
|
// Read data into frame buffer
|
||||||
|
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;
|
||||||
|
WCHNET_SocketRecv(socketid, g_json_recv_buf + g_json_recv_len, &rd_len);
|
||||||
|
g_json_recv_len += (uint16_t)rd_len;
|
||||||
|
|
||||||
|
// Process complete frames
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Reset auth timer on activity
|
||||||
|
if (g_json_auth_state == JSON_STATE_WAIT_AUTH) {
|
||||||
|
g_json_auth_timer = mstick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buffer overflow protection
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intstat & SINT_STAT_CONNECT) {
|
||||||
|
PRINT("JSON: Client socket connected (id=%d)\n", socketid);
|
||||||
|
g_json_auth_state = JSON_STATE_WAIT_AUTH;
|
||||||
|
g_json_pwd_retry = 0;
|
||||||
|
g_json_auth_timer = mstick();
|
||||||
|
g_json_recv_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intstat & SINT_STAT_DISCONNECT) {
|
||||||
|
PRINT("JSON: Client disconnected (id=%d)\n", socketid);
|
||||||
|
g_json_socket_client = 0xFF;
|
||||||
|
g_json_auth_state = JSON_STATE_WAIT_AUTH;
|
||||||
|
g_json_pwd_retry = 0;
|
||||||
|
g_json_recv_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intstat & SINT_STAT_TIM_OUT) {
|
||||||
|
PRINT("JSON: Client timeout (id=%d)\n", socketid);
|
||||||
|
g_json_socket_client = 0xFF;
|
||||||
|
g_json_auth_state = JSON_STATE_WAIT_AUTH;
|
||||||
|
g_json_pwd_retry = 0;
|
||||||
|
g_json_recv_len = 0;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// === New socket connecting? (WCHNET assigns TCP PCB after listen accept) ===
|
||||||
|
if (intstat & SINT_STAT_CONNECT && g_json_socket_client == 0xFF && socketid != g_json_socket_listen) {
|
||||||
|
// This might be the newly accepted JSON client connection
|
||||||
|
g_json_socket_client = socketid;
|
||||||
|
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 accepted on socket %d\n", socketid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tcp_json_poll(void) {
|
||||||
|
// Auth timeout check
|
||||||
|
if (g_json_auth_state == JSON_STATE_WAIT_AUTH && g_json_socket_client != 0xFF) {
|
||||||
|
if (mstick() - g_json_auth_timer > TCP_JSON_AUTH_TIMEOUT_MS) {
|
||||||
|
PRINT("JSON: Auth timeout, closing connection\n");
|
||||||
|
WCHNET_SocketClose(g_json_socket_client, TCP_CLOSE_NORMAL);
|
||||||
|
g_json_socket_client = 0xFF;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user