feat: Add TARGET command — runtime UDP destination config

- TARGET <ip> [port] command to change data destination live
- NVS persistence for target_ip and target_port
- Falls back to Kconfig defaults when no NVS config
- target= field added to STATUS reply
- config_save_str helper for NVS string storage
This commit is contained in:
user
2026-02-04 17:47:50 +01:00
parent 81c4337646
commit 92e65d8775
2 changed files with 65 additions and 8 deletions

View File

@@ -108,11 +108,18 @@ static uint8_t s_ble_own_addr_type;
static int s_udp_socket = -1;
static struct sockaddr_in s_dest_addr;
static char s_udp_buffer[2048];
static char s_target_ip[16]; /* runtime target IP (NVS or Kconfig default) */
static uint16_t s_target_port; /* runtime target port */
/* --- NVS helpers --- */
static void config_load_nvs(void)
{
/* Start with Kconfig defaults for target */
strncpy(s_target_ip, CONFIG_CSI_UDP_TARGET_IP, sizeof(s_target_ip) - 1);
s_target_ip[sizeof(s_target_ip) - 1] = '\0';
s_target_port = CONFIG_CSI_UDP_TARGET_PORT;
nvs_handle_t h;
if (nvs_open("csi_config", NVS_READONLY, &h) == ESP_OK) {
int32_t val;
@@ -135,9 +142,16 @@ static void config_load_nvs(void)
if (nvs_get_i8(h, "ble_scan", &ble) == ESP_OK) {
s_ble_enabled = (ble != 0);
}
size_t ip_len = sizeof(s_target_ip);
nvs_get_str(h, "target_ip", s_target_ip, &ip_len);
int32_t port;
if (nvs_get_i32(h, "target_port", &port) == ESP_OK && port > 0 && port <= 65535) {
s_target_port = (uint16_t)port;
}
nvs_close(h);
ESP_LOGI(TAG, "NVS loaded: rate=%d tx_power=%d adaptive=%d threshold=%.6f ble=%d",
s_send_frequency, s_tx_power_dbm, s_adaptive, s_motion_threshold, s_ble_enabled);
ESP_LOGI(TAG, "NVS loaded: rate=%d tx_power=%d adaptive=%d threshold=%.6f ble=%d target=%s:%d",
s_send_frequency, s_tx_power_dbm, s_adaptive, s_motion_threshold, s_ble_enabled,
s_target_ip, s_target_port);
} else {
ESP_LOGI(TAG, "NVS: no saved config, using defaults");
}
@@ -165,6 +179,17 @@ static esp_err_t config_save_i8(const char *key, int8_t value)
return err;
}
static esp_err_t config_save_str(const char *key, const char *value)
{
nvs_handle_t h;
esp_err_t err = nvs_open("csi_config", NVS_READWRITE, &h);
if (err != ESP_OK) return err;
err = nvs_set_str(h, key, value);
if (err == ESP_OK) err = nvs_commit(h);
nvs_close(h);
return err;
}
/* --- LED --- */
static void led_gpio_init(void)
@@ -401,11 +426,11 @@ static void udp_socket_init(void)
memset(&s_dest_addr, 0, sizeof(s_dest_addr));
s_dest_addr.sin_family = AF_INET;
s_dest_addr.sin_port = htons(CONFIG_CSI_UDP_TARGET_PORT);
inet_pton(AF_INET, CONFIG_CSI_UDP_TARGET_IP, &s_dest_addr.sin_addr);
s_dest_addr.sin_port = htons(s_target_port);
inet_pton(AF_INET, s_target_ip, &s_dest_addr.sin_addr);
ESP_LOGI(TAG, "UDP socket initialized, sending to %s:%d",
CONFIG_CSI_UDP_TARGET_IP, CONFIG_CSI_UDP_TARGET_PORT);
s_target_ip, s_target_port);
}
/* --- Ping --- */
@@ -699,11 +724,11 @@ static int cmd_handle(const char *cmd, char *reply, size_t reply_size)
}
snprintf(reply, reply_size,
"OK STATUS uptime=%s heap=%lu rssi=%d tx_power=%d rate=%d hostname=%s version=%s adaptive=%s motion=%d ble=%s",
"OK STATUS uptime=%s heap=%lu rssi=%d tx_power=%d rate=%d hostname=%s version=%s adaptive=%s motion=%d ble=%s target=%s:%d",
uptime_str, (unsigned long)heap, rssi, (int)s_tx_power_dbm,
s_send_frequency, CONFIG_CSI_HOSTNAME, app_desc->version,
s_adaptive ? "on" : "off", s_motion_detected ? 1 : 0,
s_ble_enabled ? "on" : "off");
s_ble_enabled ? "on" : "off", s_target_ip, s_target_port);
return strlen(reply);
}
@@ -740,6 +765,38 @@ static int cmd_handle(const char *cmd, char *reply, size_t reply_size)
return strlen(reply);
}
/* TARGET <ip> [port] */
if (strncmp(cmd, "TARGET ", 7) == 0) {
char ip_buf[16] = {0};
int port = s_target_port;
/* parse: "TARGET 192.168.1.10" or "TARGET 192.168.1.10 5500" */
if (sscanf(cmd + 7, "%15s %d", ip_buf, &port) < 1) {
snprintf(reply, reply_size, "ERR TARGET <ip> [port]");
return strlen(reply);
}
/* validate IP */
struct in_addr test_addr;
if (inet_pton(AF_INET, ip_buf, &test_addr) != 1) {
snprintf(reply, reply_size, "ERR TARGET invalid IP");
return strlen(reply);
}
if (port < 1 || port > 65535) {
snprintf(reply, reply_size, "ERR TARGET port range 1-65535");
return strlen(reply);
}
strncpy(s_target_ip, ip_buf, sizeof(s_target_ip) - 1);
s_target_ip[sizeof(s_target_ip) - 1] = '\0';
s_target_port = (uint16_t)port;
/* update live socket destination */
s_dest_addr.sin_port = htons(s_target_port);
inet_pton(AF_INET, s_target_ip, &s_dest_addr.sin_addr);
/* persist */
config_save_str("target_ip", s_target_ip);
config_save_i32("target_port", (int32_t)s_target_port);
snprintf(reply, reply_size, "OK TARGET %s:%d", s_target_ip, s_target_port);
return strlen(reply);
}
/* BLE ON/OFF */
if (strncmp(cmd, "BLE ", 4) == 0) {
const char *arg = cmd + 4;