diff --git a/ROADMAP.md b/ROADMAP.md index ab9f1fc..de3bc6c 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -54,7 +54,7 @@ - [x] mDNS auto-discovery (done in v0.2) - [x] Watchdog + auto-recovery (done in v0.2) - [ ] On-device CSI processing (send metrics, not raw) -- [ ] Configuration via UDP (WiFi SSID, target IP) +- [x] Configuration via UDP (`TARGET [port]`, NVS persisted) - [ ] Comprehensive error handling ## Future diff --git a/get-started/csi_recv_router/main/app_main.c b/get-started/csi_recv_router/main/app_main.c index 7d09077..5d5dffc 100644 --- a/get-started/csi_recv_router/main/app_main.c +++ b/get-started/csi_recv_router/main/app_main.c @@ -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 [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 [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;