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:
@@ -54,7 +54,7 @@
|
|||||||
- [x] mDNS auto-discovery (done in v0.2)
|
- [x] mDNS auto-discovery (done in v0.2)
|
||||||
- [x] Watchdog + auto-recovery (done in v0.2)
|
- [x] Watchdog + auto-recovery (done in v0.2)
|
||||||
- [ ] On-device CSI processing (send metrics, not raw)
|
- [ ] On-device CSI processing (send metrics, not raw)
|
||||||
- [ ] Configuration via UDP (WiFi SSID, target IP)
|
- [x] Configuration via UDP (`TARGET <ip> [port]`, NVS persisted)
|
||||||
- [ ] Comprehensive error handling
|
- [ ] Comprehensive error handling
|
||||||
|
|
||||||
## Future
|
## Future
|
||||||
|
|||||||
@@ -108,11 +108,18 @@ static uint8_t s_ble_own_addr_type;
|
|||||||
static int s_udp_socket = -1;
|
static int s_udp_socket = -1;
|
||||||
static struct sockaddr_in s_dest_addr;
|
static struct sockaddr_in s_dest_addr;
|
||||||
static char s_udp_buffer[2048];
|
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 --- */
|
/* --- NVS helpers --- */
|
||||||
|
|
||||||
static void config_load_nvs(void)
|
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;
|
nvs_handle_t h;
|
||||||
if (nvs_open("csi_config", NVS_READONLY, &h) == ESP_OK) {
|
if (nvs_open("csi_config", NVS_READONLY, &h) == ESP_OK) {
|
||||||
int32_t val;
|
int32_t val;
|
||||||
@@ -135,9 +142,16 @@ static void config_load_nvs(void)
|
|||||||
if (nvs_get_i8(h, "ble_scan", &ble) == ESP_OK) {
|
if (nvs_get_i8(h, "ble_scan", &ble) == ESP_OK) {
|
||||||
s_ble_enabled = (ble != 0);
|
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);
|
nvs_close(h);
|
||||||
ESP_LOGI(TAG, "NVS loaded: rate=%d tx_power=%d adaptive=%d threshold=%.6f ble=%d",
|
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_send_frequency, s_tx_power_dbm, s_adaptive, s_motion_threshold, s_ble_enabled,
|
||||||
|
s_target_ip, s_target_port);
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGI(TAG, "NVS: no saved config, using defaults");
|
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;
|
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 --- */
|
/* --- LED --- */
|
||||||
|
|
||||||
static void led_gpio_init(void)
|
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));
|
memset(&s_dest_addr, 0, sizeof(s_dest_addr));
|
||||||
s_dest_addr.sin_family = AF_INET;
|
s_dest_addr.sin_family = AF_INET;
|
||||||
s_dest_addr.sin_port = htons(CONFIG_CSI_UDP_TARGET_PORT);
|
s_dest_addr.sin_port = htons(s_target_port);
|
||||||
inet_pton(AF_INET, CONFIG_CSI_UDP_TARGET_IP, &s_dest_addr.sin_addr);
|
inet_pton(AF_INET, s_target_ip, &s_dest_addr.sin_addr);
|
||||||
|
|
||||||
ESP_LOGI(TAG, "UDP socket initialized, sending to %s:%d",
|
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 --- */
|
/* --- Ping --- */
|
||||||
@@ -699,11 +724,11 @@ static int cmd_handle(const char *cmd, char *reply, size_t reply_size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
snprintf(reply, 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,
|
uptime_str, (unsigned long)heap, rssi, (int)s_tx_power_dbm,
|
||||||
s_send_frequency, CONFIG_CSI_HOSTNAME, app_desc->version,
|
s_send_frequency, CONFIG_CSI_HOSTNAME, app_desc->version,
|
||||||
s_adaptive ? "on" : "off", s_motion_detected ? 1 : 0,
|
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);
|
return strlen(reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -740,6 +765,38 @@ static int cmd_handle(const char *cmd, char *reply, size_t reply_size)
|
|||||||
return strlen(reply);
|
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 */
|
/* BLE ON/OFF */
|
||||||
if (strncmp(cmd, "BLE ", 4) == 0) {
|
if (strncmp(cmd, "BLE ", 4) == 0) {
|
||||||
const char *arg = cmd + 4;
|
const char *arg = cmd + 4;
|
||||||
|
|||||||
Reference in New Issue
Block a user