refactor: 完成R8裸机lwIP移植并更新文档
This commit is contained in:
+231
-573
@@ -1,113 +1,60 @@
|
||||
/**
|
||||
* @file config.c
|
||||
* @brief AT command configuration module implementation
|
||||
* @brief Bare-metal AT command configuration module implementation.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "flash_param.h"
|
||||
#include "usart.h"
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* Private Definitions
|
||||
*---------------------------------------------------------------------------*/
|
||||
#define CONFIG_RX_BUFFER_SIZE 128u
|
||||
#define CONFIG_TX_BUFFER_SIZE 256u
|
||||
#define CONFIG_CMD_MAX_LEN 128u
|
||||
|
||||
#define CONFIG_RX_BUFFER_SIZE 128
|
||||
#define CONFIG_TX_BUFFER_SIZE 256
|
||||
#define CONFIG_CMD_MAX_LEN 128
|
||||
|
||||
/* AT command prefixes */
|
||||
#define AT_PREFIX "AT+"
|
||||
#define AT_QUERY "AT+?"
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* Private Variables
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
||||
/* Current device configuration */
|
||||
static device_config_t g_config;
|
||||
static uint8_t g_rx_buffer[CONFIG_RX_BUFFER_SIZE];
|
||||
static volatile uint16_t g_rx_index;
|
||||
static volatile bool g_rx_complete;
|
||||
static volatile bool g_reset_requested;
|
||||
|
||||
static uint32_t config_calc_crc(const device_config_t *cfg)
|
||||
{
|
||||
return flash_param_crc32(cfg, offsetof(device_config_t, crc));
|
||||
}
|
||||
|
||||
/* UART1 reception buffer */
|
||||
static uint8_t g_rx_buffer[CONFIG_RX_BUFFER_SIZE];
|
||||
static volatile uint16_t g_rx_index = 0;
|
||||
static volatile bool g_rx_complete = false;
|
||||
static volatile bool g_reset_requested = false;
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* Private Functions - String Utilities
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief Skip whitespace in string
|
||||
*/
|
||||
static const char *skip_whitespace(const char *str)
|
||||
{
|
||||
while (*str == ' ' || *str == '\t')
|
||||
{
|
||||
str++;
|
||||
while (*str == ' ' || *str == '\t') {
|
||||
++str;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Trim trailing whitespace and newlines
|
||||
*/
|
||||
static void trim_trailing(char *str)
|
||||
{
|
||||
int len = strlen(str);
|
||||
while (len > 0 && (str[len-1] == ' ' || str[len-1] == '\t' ||
|
||||
str[len-1] == '\r' || str[len-1] == '\n'))
|
||||
{
|
||||
int len = (int)strlen(str);
|
||||
|
||||
while (len > 0 && (str[len - 1] == ' ' || str[len - 1] == '\t' || str[len - 1] == '\r' || str[len - 1] == '\n')) {
|
||||
str[--len] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compare string prefix (case-insensitive)
|
||||
*/
|
||||
static bool starts_with(const char *str, const char *prefix)
|
||||
{
|
||||
while (*prefix)
|
||||
{
|
||||
char c1 = *str++;
|
||||
char c2 = *prefix++;
|
||||
|
||||
/* Convert to uppercase for comparison */
|
||||
if (c1 >= 'a' && c1 <= 'z') c1 -= 32;
|
||||
if (c2 >= 'a' && c2 <= 'z') c2 -= 32;
|
||||
|
||||
if (c1 != c2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool equals_ignore_case(const char *a, const char *b)
|
||||
{
|
||||
while (*a != '\0' && *b != '\0')
|
||||
{
|
||||
while (*a != '\0' && *b != '\0') {
|
||||
char c1 = *a++;
|
||||
char c2 = *b++;
|
||||
|
||||
if (c1 >= 'a' && c1 <= 'z') c1 -= 32;
|
||||
if (c2 >= 'a' && c2 <= 'z') c2 -= 32;
|
||||
|
||||
if (c1 != c2)
|
||||
{
|
||||
if (c1 != c2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -115,348 +62,93 @@ static bool equals_ignore_case(const char *a, const char *b)
|
||||
return (*a == '\0' && *b == '\0');
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* Private Functions - AT Command Handlers
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief Handle AT+IP command
|
||||
*/
|
||||
static at_result_t handle_ip(const char *value, char *response, uint16_t max_len)
|
||||
{
|
||||
uint8_t ip[4];
|
||||
|
||||
if (config_str_to_ip(value, ip) != 0)
|
||||
{
|
||||
snprintf(response, max_len, "ERROR: Invalid IP format\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
memcpy(g_config.ip, ip, 4);
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle AT+MASK command
|
||||
*/
|
||||
static at_result_t handle_mask(const char *value, char *response, uint16_t max_len)
|
||||
{
|
||||
uint8_t mask[4];
|
||||
|
||||
if (config_str_to_ip(value, mask) != 0)
|
||||
{
|
||||
snprintf(response, max_len, "ERROR: Invalid mask format\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
memcpy(g_config.mask, mask, 4);
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle AT+GW command
|
||||
*/
|
||||
static at_result_t handle_gw(const char *value, char *response, uint16_t max_len)
|
||||
{
|
||||
uint8_t gw[4];
|
||||
|
||||
if (config_str_to_ip(value, gw) != 0)
|
||||
{
|
||||
snprintf(response, max_len, "ERROR: Invalid gateway format\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
memcpy(g_config.gw, gw, 4);
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle AT+PORT command
|
||||
*/
|
||||
static at_result_t handle_port(const char *value, char *response, uint16_t max_len)
|
||||
{
|
||||
int port = atoi(value);
|
||||
|
||||
if (port < 1 || port > 65535)
|
||||
{
|
||||
snprintf(response, max_len, "ERROR: Invalid port (1-65535)\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
g_config.server_port = (uint16_t)port;
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle AT+RIP command
|
||||
*/
|
||||
static at_result_t handle_rip(const char *value, char *response, uint16_t max_len)
|
||||
{
|
||||
uint8_t ip[4];
|
||||
|
||||
if (config_str_to_ip(value, ip) != 0)
|
||||
{
|
||||
snprintf(response, max_len, "ERROR: Invalid remote IP format\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
memcpy(g_config.remote_ip, ip, 4);
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle AT+RPORT command
|
||||
*/
|
||||
static at_result_t handle_rport(const char *value, char *response, uint16_t max_len)
|
||||
{
|
||||
int port = atoi(value);
|
||||
|
||||
if (port < 1 || port > 65535)
|
||||
{
|
||||
snprintf(response, max_len, "ERROR: Invalid port (1-65535)\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
g_config.remote_port = (uint16_t)port;
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle AT+BAUD1 command (UART2)
|
||||
*/
|
||||
static at_result_t handle_baud1(const char *value, char *response, uint16_t max_len)
|
||||
{
|
||||
int baud = atoi(value);
|
||||
|
||||
/* Validate common baud rates */
|
||||
if (baud != 9600 && baud != 19200 && baud != 38400 &&
|
||||
baud != 57600 && baud != 115200 && baud != 230400 &&
|
||||
baud != 460800 && baud != 921600)
|
||||
{
|
||||
snprintf(response, max_len, "ERROR: Invalid baud rate\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
g_config.uart2_baudrate = (uint32_t)baud;
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle AT+BAUD2 command (UART3)
|
||||
*/
|
||||
static at_result_t handle_baud2(const char *value, char *response, uint16_t max_len)
|
||||
{
|
||||
int baud = atoi(value);
|
||||
|
||||
/* Validate common baud rates */
|
||||
if (baud != 9600 && baud != 19200 && baud != 38400 &&
|
||||
baud != 57600 && baud != 115200 && baud != 230400 &&
|
||||
baud != 460800 && baud != 921600)
|
||||
{
|
||||
snprintf(response, max_len, "ERROR: Invalid baud rate\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
g_config.uart3_baudrate = (uint32_t)baud;
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle AT+MAC command
|
||||
*/
|
||||
static at_result_t handle_mac(const char *value, char *response, uint16_t max_len)
|
||||
{
|
||||
uint8_t mac[6];
|
||||
|
||||
if (config_str_to_mac(value, mac) != 0)
|
||||
{
|
||||
snprintf(response, max_len, "ERROR: Invalid MAC format\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
memcpy(g_config.mac, mac, 6);
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle AT+DHCP command
|
||||
*/
|
||||
static at_result_t handle_dhcp(const char *value, char *response, uint16_t max_len)
|
||||
{
|
||||
int dhcp = atoi(value);
|
||||
|
||||
if (dhcp != 0 && dhcp != 1)
|
||||
{
|
||||
snprintf(response, max_len, "ERROR: Invalid value (0 or 1)\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
|
||||
g_config.dhcp_enable = (uint8_t)dhcp;
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle AT+SAVE command
|
||||
*/
|
||||
static at_result_t handle_save(char *response, uint16_t max_len)
|
||||
{
|
||||
if (config_save() != 0)
|
||||
{
|
||||
snprintf(response, max_len, "ERROR: Save failed\r\n");
|
||||
return AT_SAVE_FAILED;
|
||||
}
|
||||
|
||||
snprintf(response, max_len, "OK: Configuration saved\r\n");
|
||||
return AT_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle AT+RESET command
|
||||
*/
|
||||
static at_result_t handle_reset(char *response, uint16_t max_len)
|
||||
{
|
||||
snprintf(response, max_len, "OK: Resetting...\r\n");
|
||||
g_reset_requested = true;
|
||||
return AT_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle AT+DEFAULT command
|
||||
*/
|
||||
static at_result_t handle_default(char *response, uint16_t max_len)
|
||||
{
|
||||
config_set_defaults();
|
||||
snprintf(response, max_len, "OK: Defaults restored. Use AT+SAVE to save.\r\n");
|
||||
return AT_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle AT+? query command
|
||||
*/
|
||||
static at_result_t handle_query(char *response, uint16_t max_len)
|
||||
{
|
||||
char ip_str[16], mask_str[16], gw_str[16], rip_str[16], mac_str[18];
|
||||
|
||||
char ip_str[16];
|
||||
char mask_str[16];
|
||||
char gw_str[16];
|
||||
char rip_str[16];
|
||||
char mac_str[18];
|
||||
|
||||
config_ip_to_str(g_config.ip, ip_str);
|
||||
config_ip_to_str(g_config.mask, mask_str);
|
||||
config_ip_to_str(g_config.gw, gw_str);
|
||||
config_ip_to_str(g_config.remote_ip, rip_str);
|
||||
config_mac_to_str(g_config.mac, mac_str);
|
||||
|
||||
|
||||
snprintf(response, max_len,
|
||||
"=== TCP2UART Configuration ===\r\n"
|
||||
"MAC: %s\r\n"
|
||||
"DHCP: %s\r\n"
|
||||
"IP: %s\r\n"
|
||||
"MASK: %s\r\n"
|
||||
"GW: %s\r\n"
|
||||
"PORT: %u\r\n"
|
||||
"RIP: %s\r\n"
|
||||
"RPORT: %u\r\n"
|
||||
"BAUD1: %lu\r\n"
|
||||
"BAUD2: %lu\r\n"
|
||||
"==============================\r\n",
|
||||
mac_str,
|
||||
g_config.dhcp_enable ? "Enabled" : "Disabled",
|
||||
ip_str,
|
||||
mask_str,
|
||||
gw_str,
|
||||
g_config.server_port,
|
||||
rip_str,
|
||||
g_config.remote_port,
|
||||
g_config.uart2_baudrate,
|
||||
g_config.uart3_baudrate
|
||||
);
|
||||
|
||||
"MAC: %s\r\n"
|
||||
"DHCP: %u\r\n"
|
||||
"IP: %s\r\n"
|
||||
"MASK: %s\r\n"
|
||||
"GW: %s\r\n"
|
||||
"PORT: %u\r\n"
|
||||
"RIP: %s\r\n"
|
||||
"RPORT: %u\r\n"
|
||||
"BAUD1: %lu\r\n"
|
||||
"BAUD2: %lu\r\n",
|
||||
mac_str,
|
||||
g_config.dhcp_enable,
|
||||
ip_str,
|
||||
mask_str,
|
||||
gw_str,
|
||||
g_config.server_port,
|
||||
rip_str,
|
||||
g_config.remote_port,
|
||||
g_config.uart2_baudrate,
|
||||
g_config.uart3_baudrate);
|
||||
|
||||
return AT_OK;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* Public Functions
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief Initialize configuration module
|
||||
*/
|
||||
int config_init(void)
|
||||
{
|
||||
/* Load configuration from Flash */
|
||||
flash_param_init();
|
||||
return config_load();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Load configuration from Flash
|
||||
*/
|
||||
int config_load(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = flash_param_read(&g_config, sizeof(device_config_t));
|
||||
|
||||
if (ret != 0 ||
|
||||
if (flash_param_read(&g_config, sizeof(g_config)) != 0 ||
|
||||
g_config.magic != CONFIG_MAGIC ||
|
||||
g_config.version != CONFIG_VERSION ||
|
||||
g_config.crc != config_calc_crc(&g_config))
|
||||
{
|
||||
/* Invalid or corrupted configuration, use defaults */
|
||||
g_config.crc != config_calc_crc(&g_config)) {
|
||||
config_set_defaults();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Save configuration to Flash
|
||||
*/
|
||||
int config_save(void)
|
||||
{
|
||||
/* Update magic and CRC before saving */
|
||||
g_config.magic = CONFIG_MAGIC;
|
||||
g_config.version = CONFIG_VERSION;
|
||||
g_config.crc = config_calc_crc(&g_config);
|
||||
|
||||
return flash_param_write(&g_config, sizeof(device_config_t));
|
||||
return flash_param_write(&g_config, sizeof(g_config));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset configuration to factory defaults
|
||||
*/
|
||||
void config_set_defaults(void)
|
||||
{
|
||||
uint8_t default_ip[] = DEFAULT_IP;
|
||||
uint8_t default_mask[] = DEFAULT_MASK;
|
||||
uint8_t default_gw[] = DEFAULT_GW;
|
||||
uint8_t default_mac[] = DEFAULT_MAC;
|
||||
uint8_t default_rip[] = DEFAULT_REMOTE_IP;
|
||||
|
||||
memset(&g_config, 0, sizeof(device_config_t));
|
||||
|
||||
const uint8_t default_ip[] = DEFAULT_IP;
|
||||
const uint8_t default_mask[] = DEFAULT_MASK;
|
||||
const uint8_t default_gw[] = DEFAULT_GW;
|
||||
const uint8_t default_mac[] = DEFAULT_MAC;
|
||||
const uint8_t default_rip[] = DEFAULT_REMOTE_IP;
|
||||
|
||||
memset(&g_config, 0, sizeof(g_config));
|
||||
g_config.magic = CONFIG_MAGIC;
|
||||
g_config.version = CONFIG_VERSION;
|
||||
|
||||
memcpy(g_config.mac, default_mac, 6);
|
||||
memcpy(g_config.mac, default_mac, sizeof(g_config.mac));
|
||||
memcpy(g_config.ip, default_ip, sizeof(g_config.ip));
|
||||
memcpy(g_config.mask, default_mask, sizeof(g_config.mask));
|
||||
memcpy(g_config.gw, default_gw, sizeof(g_config.gw));
|
||||
memcpy(g_config.remote_ip, default_rip, sizeof(g_config.remote_ip));
|
||||
|
||||
g_config.dhcp_enable = DEFAULT_DHCP_ENABLE;
|
||||
memcpy(g_config.ip, default_ip, 4);
|
||||
memcpy(g_config.mask, default_mask, 4);
|
||||
memcpy(g_config.gw, default_gw, 4);
|
||||
|
||||
g_config.server_port = DEFAULT_SERVER_PORT;
|
||||
|
||||
memcpy(g_config.remote_ip, default_rip, 4);
|
||||
g_config.remote_port = DEFAULT_REMOTE_PORT;
|
||||
g_config.reconnect_interval = DEFAULT_RECONNECT_MS;
|
||||
|
||||
g_config.uart2_baudrate = DEFAULT_UART_BAUDRATE;
|
||||
g_config.uart3_baudrate = DEFAULT_UART_BAUDRATE;
|
||||
g_config.uart2_databits = DEFAULT_UART_DATABITS;
|
||||
@@ -468,305 +160,271 @@ void config_set_defaults(void)
|
||||
g_config.crc = config_calc_crc(&g_config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get current configuration (read-only)
|
||||
*/
|
||||
const device_config_t *config_get(void)
|
||||
{
|
||||
return &g_config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get mutable configuration
|
||||
*/
|
||||
device_config_t *config_get_mutable(void)
|
||||
{
|
||||
return &g_config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process AT command
|
||||
*/
|
||||
at_result_t config_process_at_cmd(const char *cmd, char *response, uint16_t max_len)
|
||||
{
|
||||
char cmd_copy[CONFIG_CMD_MAX_LEN];
|
||||
char *cmd_name;
|
||||
char *value;
|
||||
at_result_t result = AT_UNKNOWN_CMD;
|
||||
const char *p;
|
||||
|
||||
if (cmd == NULL || response == NULL || max_len == 0)
|
||||
{
|
||||
if (cmd == NULL || response == NULL || max_len == 0u) {
|
||||
return AT_ERROR;
|
||||
}
|
||||
|
||||
/* Make a copy for modification */
|
||||
strncpy(cmd_copy, cmd, CONFIG_CMD_MAX_LEN - 1);
|
||||
cmd_copy[CONFIG_CMD_MAX_LEN - 1] = '\0';
|
||||
|
||||
strncpy(cmd_copy, cmd, CONFIG_CMD_MAX_LEN - 1u);
|
||||
cmd_copy[CONFIG_CMD_MAX_LEN - 1u] = '\0';
|
||||
trim_trailing(cmd_copy);
|
||||
|
||||
/* Skip leading whitespace */
|
||||
const char *p = skip_whitespace(cmd_copy);
|
||||
|
||||
/* Check for AT+ prefix */
|
||||
if (!starts_with(p, "AT+"))
|
||||
{
|
||||
if (equals_ignore_case(p, "AT"))
|
||||
{
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_OK;
|
||||
}
|
||||
p = skip_whitespace(cmd_copy);
|
||||
|
||||
if ((p[0] != 'A' && p[0] != 'a') || (p[1] != 'T' && p[1] != 't')) {
|
||||
snprintf(response, max_len, "ERROR: Unknown command\r\n");
|
||||
return AT_UNKNOWN_CMD;
|
||||
}
|
||||
|
||||
/* Move past AT+ */
|
||||
|
||||
if (p[2] == '\0') {
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_OK;
|
||||
}
|
||||
|
||||
if (p[2] != '+') {
|
||||
snprintf(response, max_len, "ERROR: Unknown command\r\n");
|
||||
return AT_UNKNOWN_CMD;
|
||||
}
|
||||
|
||||
p += 3;
|
||||
|
||||
/* Find '=' separator if present */
|
||||
value = strchr((char *)p, '=');
|
||||
if (value != NULL)
|
||||
{
|
||||
*value = '\0'; /* Terminate command part */
|
||||
value++; /* Point to value */
|
||||
if (value != NULL) {
|
||||
*value = '\0';
|
||||
++value;
|
||||
value = (char *)skip_whitespace(value);
|
||||
}
|
||||
|
||||
cmd_name = (char *)p;
|
||||
trim_trailing(cmd_name);
|
||||
|
||||
/* Process commands */
|
||||
if (equals_ignore_case(cmd_name, "IP") && value != NULL)
|
||||
{
|
||||
result = handle_ip(value, response, max_len);
|
||||
|
||||
if ((equals_ignore_case(cmd_name, "?") || equals_ignore_case(cmd_name, "QUERY")) && value == NULL) {
|
||||
return handle_query(response, max_len);
|
||||
}
|
||||
else if (equals_ignore_case(cmd_name, "MASK") && value != NULL)
|
||||
{
|
||||
result = handle_mask(value, response, max_len);
|
||||
if (equals_ignore_case(cmd_name, "SAVE") && value == NULL) {
|
||||
if (config_save() != 0) {
|
||||
snprintf(response, max_len, "ERROR: Save failed\r\n");
|
||||
return AT_SAVE_FAILED;
|
||||
}
|
||||
snprintf(response, max_len, "OK: Configuration saved\r\n");
|
||||
return AT_OK;
|
||||
}
|
||||
else if (equals_ignore_case(cmd_name, "GW") && value != NULL)
|
||||
{
|
||||
result = handle_gw(value, response, max_len);
|
||||
if (equals_ignore_case(cmd_name, "RESET") && value == NULL) {
|
||||
g_reset_requested = true;
|
||||
snprintf(response, max_len, "OK: Resetting...\r\n");
|
||||
return AT_OK;
|
||||
}
|
||||
else if (equals_ignore_case(cmd_name, "PORT") && value != NULL)
|
||||
{
|
||||
result = handle_port(value, response, max_len);
|
||||
if (equals_ignore_case(cmd_name, "DEFAULT") && value == NULL) {
|
||||
config_set_defaults();
|
||||
snprintf(response, max_len, "OK: Defaults restored\r\n");
|
||||
return AT_OK;
|
||||
}
|
||||
else if (equals_ignore_case(cmd_name, "RIP") && value != NULL)
|
||||
{
|
||||
result = handle_rip(value, response, max_len);
|
||||
if (equals_ignore_case(cmd_name, "IP") && value != NULL) {
|
||||
if (config_str_to_ip(value, g_config.ip) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid IP format\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
else if (equals_ignore_case(cmd_name, "RPORT") && value != NULL)
|
||||
{
|
||||
result = handle_rport(value, response, max_len);
|
||||
if (equals_ignore_case(cmd_name, "MASK") && value != NULL) {
|
||||
if (config_str_to_ip(value, g_config.mask) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid mask format\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
else if (equals_ignore_case(cmd_name, "BAUD1") && value != NULL)
|
||||
{
|
||||
result = handle_baud1(value, response, max_len);
|
||||
if (equals_ignore_case(cmd_name, "GW") && value != NULL) {
|
||||
if (config_str_to_ip(value, g_config.gw) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid gateway format\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
else if (equals_ignore_case(cmd_name, "BAUD2") && value != NULL)
|
||||
{
|
||||
result = handle_baud2(value, response, max_len);
|
||||
if (equals_ignore_case(cmd_name, "RIP") && value != NULL) {
|
||||
if (config_str_to_ip(value, g_config.remote_ip) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid remote IP format\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
else if (equals_ignore_case(cmd_name, "MAC") && value != NULL)
|
||||
{
|
||||
result = handle_mac(value, response, max_len);
|
||||
if (equals_ignore_case(cmd_name, "MAC") && value != NULL) {
|
||||
if (config_str_to_mac(value, g_config.mac) != 0) {
|
||||
snprintf(response, max_len, "ERROR: Invalid MAC format\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
else if (equals_ignore_case(cmd_name, "DHCP") && value != NULL)
|
||||
{
|
||||
result = handle_dhcp(value, response, max_len);
|
||||
if (equals_ignore_case(cmd_name, "PORT") && value != NULL) {
|
||||
int port = atoi(value);
|
||||
if (port < 1 || port > 65535) {
|
||||
snprintf(response, max_len, "ERROR: Invalid port\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
g_config.server_port = (uint16_t)port;
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
else if (equals_ignore_case(cmd_name, "SAVE") && value == NULL)
|
||||
{
|
||||
result = handle_save(response, max_len);
|
||||
if (equals_ignore_case(cmd_name, "RPORT") && value != NULL) {
|
||||
int port = atoi(value);
|
||||
if (port < 1 || port > 65535) {
|
||||
snprintf(response, max_len, "ERROR: Invalid port\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
g_config.remote_port = (uint16_t)port;
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
else if (equals_ignore_case(cmd_name, "RESET") && value == NULL)
|
||||
{
|
||||
result = handle_reset(response, max_len);
|
||||
if (equals_ignore_case(cmd_name, "BAUD1") && value != NULL) {
|
||||
g_config.uart2_baudrate = (uint32_t)atoi(value);
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
else if (equals_ignore_case(cmd_name, "DEFAULT") && value == NULL)
|
||||
{
|
||||
result = handle_default(response, max_len);
|
||||
if (equals_ignore_case(cmd_name, "BAUD2") && value != NULL) {
|
||||
g_config.uart3_baudrate = (uint32_t)atoi(value);
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
else if ((equals_ignore_case(cmd_name, "?") || equals_ignore_case(cmd_name, "QUERY")) && value == NULL)
|
||||
{
|
||||
result = handle_query(response, max_len);
|
||||
if (equals_ignore_case(cmd_name, "DHCP") && value != NULL) {
|
||||
int dhcp = atoi(value);
|
||||
if (dhcp != 0 && dhcp != 1) {
|
||||
snprintf(response, max_len, "ERROR: Invalid value\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
if (dhcp != 0) {
|
||||
snprintf(response, max_len, "ERROR: DHCP disabled in this build\r\n");
|
||||
return AT_INVALID_PARAM;
|
||||
}
|
||||
g_config.dhcp_enable = (uint8_t)dhcp;
|
||||
snprintf(response, max_len, "OK\r\n");
|
||||
return AT_NEED_REBOOT;
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(response, max_len, "ERROR: Unknown command\r\n");
|
||||
result = AT_UNKNOWN_CMD;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
snprintf(response, max_len, "ERROR: Unknown command\r\n");
|
||||
return AT_UNKNOWN_CMD;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Format IP address to string
|
||||
*/
|
||||
void config_ip_to_str(const uint8_t *ip, char *str)
|
||||
{
|
||||
sprintf(str, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
|
||||
sprintf(str, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse IP address from string
|
||||
*/
|
||||
int config_str_to_ip(const char *str, uint8_t *ip)
|
||||
{
|
||||
int a, b, c, d;
|
||||
|
||||
if (sscanf(str, "%d.%d.%d.%d", &a, &b, &c, &d) != 4)
|
||||
{
|
||||
|
||||
if (sscanf(str, "%d.%d.%d.%d", &a, &b, &c, &d) != 4) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (a < 0 || a > 255 || b < 0 || b > 255 ||
|
||||
c < 0 || c > 255 || d < 0 || d > 255)
|
||||
{
|
||||
if (a < 0 || a > 255 || b < 0 || b > 255 || c < 0 || c > 255 || d < 0 || d > 255) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
ip[0] = (uint8_t)a;
|
||||
ip[1] = (uint8_t)b;
|
||||
ip[2] = (uint8_t)c;
|
||||
ip[3] = (uint8_t)d;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Format MAC address to string
|
||||
*/
|
||||
void config_mac_to_str(const uint8_t *mac, char *str)
|
||||
{
|
||||
sprintf(str, "%02X:%02X:%02X:%02X:%02X:%02X",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
sprintf(str, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse MAC address from string
|
||||
*/
|
||||
int config_str_to_mac(const char *str, uint8_t *mac)
|
||||
{
|
||||
int a[6];
|
||||
|
||||
if (sscanf(str, "%x:%x:%x:%x:%x:%x",
|
||||
&a[0], &a[1], &a[2], &a[3], &a[4], &a[5]) != 6)
|
||||
{
|
||||
/* Try alternate format with dashes */
|
||||
if (sscanf(str, "%x-%x-%x-%x-%x-%x",
|
||||
&a[0], &a[1], &a[2], &a[3], &a[4], &a[5]) != 6)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sscanf(str, "%x:%x:%x:%x:%x:%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]) != 6 &&
|
||||
sscanf(str, "%x-%x-%x-%x-%x-%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]) != 6) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
if (a[i] < 0 || a[i] > 255)
|
||||
{
|
||||
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
if (a[i] < 0 || a[i] > 255) {
|
||||
return -1;
|
||||
}
|
||||
mac[i] = (uint8_t)a[i];
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief UART1 IDLE interrupt handler
|
||||
*/
|
||||
void config_uart_idle_handler(void)
|
||||
{
|
||||
uint16_t dma_counter = __HAL_DMA_GET_COUNTER(huart1.hdmarx);
|
||||
uint16_t len = CONFIG_RX_BUFFER_SIZE - dma_counter;
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
uint16_t len = (uint16_t)(CONFIG_RX_BUFFER_SIZE - dma_counter);
|
||||
|
||||
if (len > 0u) {
|
||||
g_rx_index = len;
|
||||
g_rx_complete = true;
|
||||
}
|
||||
|
||||
/* Stop DMA and restart */
|
||||
|
||||
HAL_UART_DMAStop(&huart1);
|
||||
HAL_UART_Receive_DMA(&huart1, g_rx_buffer, CONFIG_RX_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start UART1 reception
|
||||
*/
|
||||
void config_start_reception(void)
|
||||
{
|
||||
/* Enable IDLE interrupt */
|
||||
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
|
||||
|
||||
/* Start DMA reception */
|
||||
HAL_UART_Receive_DMA(&huart1, g_rx_buffer, CONFIG_RX_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configuration task
|
||||
*/
|
||||
void config_task(void *argument)
|
||||
void config_poll(void)
|
||||
{
|
||||
char response[CONFIG_TX_BUFFER_SIZE];
|
||||
char cmd_buffer[CONFIG_CMD_MAX_LEN];
|
||||
at_result_t result;
|
||||
|
||||
(void)argument;
|
||||
|
||||
/* Initialize configuration */
|
||||
config_init();
|
||||
|
||||
/* Start UART1 reception */
|
||||
config_start_reception();
|
||||
|
||||
/* Send startup message */
|
||||
snprintf(response, sizeof(response),
|
||||
"\r\n=== TCP2UART v1.0 ===\r\n"
|
||||
"Type AT+? for configuration\r\n\r\n");
|
||||
HAL_UART_Transmit(&huart1, (uint8_t *)response, strlen(response), 1000);
|
||||
|
||||
while (1)
|
||||
{
|
||||
/* Wait for command */
|
||||
if (g_rx_complete)
|
||||
{
|
||||
/* Copy command and null-terminate */
|
||||
uint16_t len = g_rx_index;
|
||||
if (len >= CONFIG_CMD_MAX_LEN)
|
||||
{
|
||||
len = CONFIG_CMD_MAX_LEN - 1;
|
||||
}
|
||||
memcpy(cmd_buffer, g_rx_buffer, len);
|
||||
cmd_buffer[len] = '\0';
|
||||
|
||||
/* Reset reception state */
|
||||
g_rx_complete = false;
|
||||
g_rx_index = 0;
|
||||
|
||||
/* Process command */
|
||||
result = config_process_at_cmd(cmd_buffer, response, sizeof(response));
|
||||
|
||||
/* Send response */
|
||||
HAL_UART_Transmit(&huart1, (uint8_t *)response, strlen(response), 1000);
|
||||
uint16_t len;
|
||||
|
||||
if (g_reset_requested)
|
||||
{
|
||||
g_reset_requested = false;
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
/* Handle reboot needed */
|
||||
if (result == AT_NEED_REBOOT)
|
||||
{
|
||||
HAL_UART_Transmit(&huart1,
|
||||
(uint8_t *)"Note: Use AT+SAVE then AT+RESET to apply changes\r\n",
|
||||
51, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
if (!g_rx_complete) {
|
||||
return;
|
||||
}
|
||||
|
||||
len = g_rx_index;
|
||||
if (len >= CONFIG_CMD_MAX_LEN) {
|
||||
len = CONFIG_CMD_MAX_LEN - 1u;
|
||||
}
|
||||
|
||||
memcpy(cmd_buffer, g_rx_buffer, len);
|
||||
cmd_buffer[len] = '\0';
|
||||
g_rx_complete = false;
|
||||
g_rx_index = 0u;
|
||||
|
||||
result = config_process_at_cmd(cmd_buffer, response, sizeof(response));
|
||||
HAL_UART_Transmit(&huart1, (uint8_t *)response, (uint16_t)strlen(response), 1000u);
|
||||
|
||||
if (result == AT_NEED_REBOOT) {
|
||||
static const char hint[] = "Note: Use AT+SAVE then AT+RESET to apply changes\r\n";
|
||||
HAL_UART_Transmit(&huart1, (uint8_t *)hint, sizeof(hint) - 1u, 1000u);
|
||||
}
|
||||
}
|
||||
|
||||
bool config_is_reset_requested(void)
|
||||
{
|
||||
return g_reset_requested;
|
||||
}
|
||||
|
||||
void config_clear_reset_requested(void)
|
||||
{
|
||||
g_reset_requested = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user