#include #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" #include "esp_system.h" #include "esp_event.h" #include "esp_log.h" #include "esp_wifi.h" #include "esp_console.h" #include "linenoise/linenoise.h" #include "nvs_flash.h" #include "esp_netif.h" #include "lwip/inet.h" #include "led_strip.h" // Custom Components #include "iperf.h" #include "wifi_cfg.h" #include "csi_log.h" #include "wifi_monitor.h" #include "gps_sync.h" static const char *TAG = "MAIN"; // --- Hardware Configuration --- #if CONFIG_IDF_TARGET_ESP32C5 #define RGB_LED_GPIO 27 #else #define RGB_LED_GPIO 8 #endif // --- WiFi Operation Mode --- typedef enum { WIFI_MODE_STA_CSI, // STA mode: Connected to AP, CSI + iperf (DEFAULT) WIFI_MODE_MONITOR // Monitor mode: Promiscuous, collapse detection } wifi_operation_mode_t; // Note: wifi_band_mode_t is already defined in ESP-IDF's esp_wifi_types_generic.h // Values: WIFI_BAND_MODE_2G_ONLY, WIFI_BAND_MODE_5G_ONLY, WIFI_BAND_MODE_AUTO static wifi_operation_mode_t current_wifi_mode = WIFI_MODE_STA_CSI; static wifi_band_mode_t preferred_band = WIFI_BAND_MODE_AUTO; static uint8_t monitor_channel = 6; // Default monitor channel // --- LED State Machine --- static led_strip_handle_t led_strip; static bool wifi_connected = false; static bool has_config = false; typedef enum { LED_STATE_NO_CONFIG, // Yellow Solid LED_STATE_WAITING, // Blue Blink (Connecting) LED_STATE_CONNECTED, // Green Solid (Connected to AP) LED_STATE_FAILED, // Red Blink LED_STATE_MONITORING // Blue Solid (Sniffing Air) } led_state_t; static led_state_t current_led_state = LED_STATE_NO_CONFIG; // --- Forward Declarations --- static void auto_monitor_task(void *arg); static void rgb_led_init(void) { ESP_LOGI(TAG, "Initializing RGB LED on GPIO %d", RGB_LED_GPIO); led_strip_config_t strip_config = { .strip_gpio_num = RGB_LED_GPIO, .max_leds = 1, }; led_strip_rmt_config_t rmt_config = { .resolution_hz = 10 * 1000 * 1000, .flags.with_dma = false, }; ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip)); led_strip_clear(led_strip); } static void set_led_color(uint8_t r, uint8_t g, uint8_t b) { led_strip_set_pixel(led_strip, 0, r, g, b); led_strip_refresh(led_strip); } static void led_task(void *arg) { int blink_state = 0; while(1) { switch(current_led_state) { case LED_STATE_NO_CONFIG: set_led_color(25, 25, 0); // Yellow (Dimmed) vTaskDelay(pdMS_TO_TICKS(1000)); break; case LED_STATE_WAITING: if (blink_state) set_led_color(0, 0, 50); // Blue else set_led_color(0, 0, 0); blink_state = !blink_state; vTaskDelay(pdMS_TO_TICKS(500)); break; case LED_STATE_CONNECTED: set_led_color(0, 25, 0); // Green vTaskDelay(pdMS_TO_TICKS(1000)); break; case LED_STATE_MONITORING: set_led_color(0, 0, 50); // Blue Solid vTaskDelay(pdMS_TO_TICKS(1000)); break; case LED_STATE_FAILED: if (blink_state) set_led_color(50, 0, 0); // Red else set_led_color(0, 0, 0); blink_state = !blink_state; vTaskDelay(pdMS_TO_TICKS(200)); break; } } } // --- GPS Logging Helper --- void log_collapse_event(float nav_duration_us, int rssi, int retry) { gps_timestamp_t ts = gps_get_timestamp(); // Format: COLLAPSE,MonoMS,GpsMS,Synced,Duration,RSSI,Retry printf("COLLAPSE,%lld,%lld,%d,%.2f,%d,%d\n", ts.monotonic_ms, ts.gps_ms, ts.synced ? 1 : 0, nav_duration_us, rssi, retry); } // --- CSI Support --------------------------------------------------- static bool s_csi_enabled = false; static uint32_t s_csi_packet_count = 0; static void csi_cb(void *ctx, wifi_csi_info_t *info) { csi_log_append_record(info); s_csi_packet_count++; if ((s_csi_packet_count % 100) == 0) { ESP_LOGI("CSI", "Captured %lu CSI packets", (unsigned long)s_csi_packet_count); } } static void wifi_enable_csi_once(void) { if (s_csi_enabled) return; vTaskDelay(pdMS_TO_TICKS(2000)); wifi_csi_config_t csi_cfg; memset(&csi_cfg, 0, sizeof(csi_cfg)); csi_cfg.enable = true; ESP_LOGI("CSI", "Configuring CSI..."); if (esp_wifi_set_csi_config(&csi_cfg) != ESP_OK) { ESP_LOGE("CSI", "Failed to set CSI config"); return; } if (esp_wifi_set_csi_rx_cb(csi_cb, NULL) != ESP_OK) { ESP_LOGE("CSI", "Failed to set CSI callback"); return; } if (esp_wifi_set_csi(true) != ESP_OK) { ESP_LOGE("CSI", "Failed to enable CSI"); return; } ESP_LOGI("CSI", "CSI enabled!"); s_csi_enabled = true; } static void wifi_disable_csi(void) { if (!s_csi_enabled) return; ESP_LOGI("CSI", "Disabling CSI..."); esp_wifi_set_csi(false); s_csi_enabled = false; ESP_LOGI("CSI", "CSI disabled"); } static void csi_dump_task(void *arg) { vTaskDelay(pdMS_TO_TICKS(20000)); // Dump after 20 seconds ESP_LOGI("CSI", "Dumping CSI data..."); csi_log_dump_over_uart(); ESP_LOGI("CSI", "CSI dump complete"); vTaskDelete(NULL); } // --- WiFi Monitor Mode Support ------------------------------------- static bool s_monitor_enabled = false; static uint32_t s_monitor_frame_count = 0; static TaskHandle_t s_monitor_stats_task_handle = NULL; static void monitor_frame_callback(const wifi_frame_info_t *frame, const uint8_t *payload, uint16_t len) { s_monitor_frame_count++; // 1. Check for Collapse (High NAV + Retry) if (frame->retry && frame->duration_id > 5000) { log_collapse_event((float)frame->duration_id, frame->rssi, frame->retry); } // 2. Warn on extremely high NAV if (frame->duration_id > 30000) { ESP_LOGW("MONITOR", "⚠️ VERY HIGH NAV: %u us", frame->duration_id); } } static void monitor_stats_task(void *arg) { while (1) { vTaskDelay(pdMS_TO_TICKS(10000)); // Every 10 seconds wifi_collapse_stats_t stats; if (wifi_monitor_get_stats(&stats) == ESP_OK) { ESP_LOGI("MONITOR", "--- Stats: %lu frames, Retry Rate: %.2f%%, Avg NAV: %u us ---", stats.total_frames, stats.retry_rate, stats.avg_nav); if (wifi_monitor_is_collapsed()) { ESP_LOGW("MONITOR", "⚠️ ⚠️ ⚠️ WiFi COLLAPSE DETECTED! ⚠️ ⚠️ ⚠️ "); } } } } // --- Mode Switching Functions -------------------------------------- esp_err_t switch_to_monitor_mode(uint8_t channel, wifi_bandwidth_t bandwidth) { if (current_wifi_mode == WIFI_MODE_MONITOR) { ESP_LOGW(TAG, "Already in monitor mode"); return ESP_OK; } // CRITICAL: ESP-IDF monitor/promiscuous mode is typically restricted to 20MHz // even though the hardware supports 40MHz. Force 20MHz for monitor mode. if (bandwidth != WIFI_BW_HT20) { ESP_LOGW(TAG, "Monitor mode typically restricted to 20MHz capture width"); ESP_LOGW(TAG, "Forcing bandwidth to 20MHz (driver limitation)"); bandwidth = WIFI_BW_HT20; } // Detect band for informational logging const char* band_str = "2.4GHz"; if (channel >= 36 && channel <= 165) { band_str = "5GHz"; } const char* bw_str = "20MHz"; // Note: Monitor mode typically limited to 20MHz by ESP-IDF drivers ESP_LOGI(TAG, "========================================"); ESP_LOGI(TAG, "Switching to MONITOR MODE"); ESP_LOGI(TAG, " Channel: %d (%s)", channel, band_str); ESP_LOGI(TAG, " Bandwidth: %s (monitor mode limitation)", bw_str); ESP_LOGI(TAG, "========================================"); // 1. Stop iperf if running ESP_LOGI(TAG, "Stopping iperf..."); iperf_stop(); vTaskDelay(pdMS_TO_TICKS(500)); // 2. Disable CSI wifi_disable_csi(); vTaskDelay(pdMS_TO_TICKS(500)); // 3. Disconnect from AP ESP_LOGI(TAG, "Disconnecting from AP..."); esp_wifi_disconnect(); vTaskDelay(pdMS_TO_TICKS(1000)); // 4. Stop WiFi ESP_LOGI(TAG, "Stopping WiFi..."); esp_wifi_stop(); vTaskDelay(pdMS_TO_TICKS(500)); // 5. Set to NULL mode ESP_LOGI(TAG, "Setting WiFi mode to NULL..."); esp_wifi_set_mode(WIFI_MODE_NULL); vTaskDelay(pdMS_TO_TICKS(500)); // 6. Configure bandwidth before starting monitor mode ESP_LOGI(TAG, "Configuring bandwidth to %s...", bw_str); wifi_config_t wifi_config = {}; esp_wifi_get_config(WIFI_IF_STA, &wifi_config); // Set bandwidth in promiscuous mode config // Note: Bandwidth is set via wifi monitor init // 7. Start monitor mode ESP_LOGI(TAG, "Starting monitor mode..."); if (wifi_monitor_init(channel, monitor_frame_callback) != ESP_OK) { ESP_LOGE(TAG, "Failed to init monitor mode"); return ESP_FAIL; } // Set bandwidth after init esp_wifi_set_bandwidth(WIFI_IF_STA, bandwidth); if (wifi_monitor_start() != ESP_OK) { ESP_LOGE(TAG, "Failed to start monitor mode"); return ESP_FAIL; } s_monitor_enabled = true; current_wifi_mode = WIFI_MODE_MONITOR; current_led_state = LED_STATE_MONITORING; monitor_channel = channel; // 8. Start stats task if (s_monitor_stats_task_handle == NULL) { xTaskCreate(monitor_stats_task, "monitor_stats", 4096, NULL, 5, &s_monitor_stats_task_handle); } ESP_LOGI(TAG, "✓ Monitor mode active"); ESP_LOGI(TAG, " - Channel: %d (%s)", channel, band_str); ESP_LOGI(TAG, " - Bandwidth: %s", bw_str); ESP_LOGI(TAG, " - Logging GPS-timestamped collapse events"); ESP_LOGI(TAG, " - LED: Blue solid"); ESP_LOGI(TAG, "========================================"); return ESP_OK; } esp_err_t switch_to_sta_mode(wifi_band_mode_t band_mode) { if (current_wifi_mode == WIFI_MODE_STA_CSI) { ESP_LOGW(TAG, "Already in STA mode"); return ESP_OK; } const char* band_str = "Auto (2.4GHz or 5GHz)"; if (band_mode == WIFI_BAND_MODE_2G_ONLY) { band_str = "2.4GHz only"; } else if (band_mode == WIFI_BAND_MODE_5G_ONLY) { band_str = "5GHz only"; } ESP_LOGI(TAG, "========================================"); ESP_LOGI(TAG, "Switching to STA MODE (CSI + iperf)"); ESP_LOGI(TAG, " Band preference: %s", band_str); ESP_LOGI(TAG, "========================================"); preferred_band = band_mode; // 1. Stop monitor stats task if (s_monitor_stats_task_handle != NULL) { vTaskDelete(s_monitor_stats_task_handle); s_monitor_stats_task_handle = NULL; } // 2. Stop monitor mode if (s_monitor_enabled) { ESP_LOGI(TAG, "Stopping monitor mode..."); wifi_monitor_stop(); s_monitor_enabled = false; vTaskDelay(pdMS_TO_TICKS(500)); } // 3. Set mode back to STA ESP_LOGI(TAG, "Setting WiFi mode to STA..."); esp_wifi_set_mode(WIFI_MODE_STA); vTaskDelay(pdMS_TO_TICKS(500)); // 4. Configure band preference wifi_config_t wifi_config; esp_wifi_get_config(WIFI_IF_STA, &wifi_config); if (band_mode == WIFI_BAND_MODE_2G_ONLY) { wifi_config.sta.channel = 0; // Scan all channels, but prefer 2.4GHz wifi_config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN; // Note: ESP-IDF doesn't have direct band filtering, but we can set channel to force band ESP_LOGI(TAG, "Configured for 2.4GHz band"); } else if (band_mode == WIFI_BAND_MODE_5G_ONLY) { wifi_config.sta.channel = 0; // Scan all channels wifi_config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN; // The AP should be on 5GHz; connection will work if AP supports 5GHz ESP_LOGI(TAG, "Configured for 5GHz band preference"); } else { // Auto mode - let ESP-IDF choose best band wifi_config.sta.channel = 0; wifi_config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN; ESP_LOGI(TAG, "Configured for auto band selection"); } esp_wifi_set_config(WIFI_IF_STA, &wifi_config); // 5. Start WiFi ESP_LOGI(TAG, "Starting WiFi..."); esp_wifi_start(); vTaskDelay(pdMS_TO_TICKS(500)); // 6. Reconnect to AP ESP_LOGI(TAG, "Connecting to AP..."); esp_wifi_connect(); current_wifi_mode = WIFI_MODE_STA_CSI; current_led_state = LED_STATE_WAITING; wifi_connected = false; ESP_LOGI(TAG, "✓ Reconnecting to AP..."); ESP_LOGI(TAG, " - Band: %s", band_str); ESP_LOGI(TAG, " - Waiting for IP address"); ESP_LOGI(TAG, " - CSI and iperf will start after connection"); ESP_LOGI(TAG, "========================================"); // Note: CSI and iperf will be started by event handler when IP is obtained return ESP_OK; } // --- Console Commands ---------------------------------------------- static int cmd_mode_monitor(int argc, char **argv) { int channel = monitor_channel; // Use last channel or default wifi_bandwidth_t bandwidth = WIFI_BW_HT20; // Default to 20MHz if (argc > 1) { // Parse channel/bandwidth format: "6/20" or "36/40" char *slash = strchr(argv[1], '/'); if (slash != NULL) { *slash = '\0'; // Split string at '/' channel = atoi(argv[1]); int bw = atoi(slash + 1); // Convert bandwidth to enum - ESP32-C5 only supports 20/40MHz switch(bw) { case 20: bandwidth = WIFI_BW_HT20; break; case 40: bandwidth = WIFI_BW_HT40; printf("WARNING: Monitor mode typically restricted to 20MHz by ESP-IDF drivers\n"); printf(" 40MHz requested but will be forced to 20MHz\n"); break; default: printf("Error: Invalid bandwidth %d\n", bw); printf("ESP32-C5 hardware: 20MHz and 40MHz\n"); printf("Monitor mode driver: 20MHz only (typical limitation)\n"); return 1; } } else { channel = atoi(argv[1]); // Monitor mode: always default to 20MHz (driver limitation) bandwidth = WIFI_BW_HT20; } // Validate channel based on band // ESP32-C5 supports WiFi 6 on 2.4GHz and 5GHz only (NO 6GHz support) bool valid = false; const char* band = "Unknown"; // 2.4GHz: channels 1-14 if (channel >= 1 && channel <= 14) { valid = true; band = "2.4GHz"; // Validate bandwidth for 2.4GHz if (bandwidth != WIFI_BW_HT20 && bandwidth != WIFI_BW_HT40) { printf("Error: 2.4GHz only supports 20MHz and 40MHz bandwidth\n"); return 1; } } // 5GHz: specific valid channels only else if (channel >= 36 && channel <= 165) { // UNII-1 and UNII-2: 36-64 (every 4 channels) if ((channel >= 36 && channel <= 64 && (channel % 4 == 0)) || // UNII-2 Extended: 100-144 (every 4 channels) (channel >= 100 && channel <= 144 && (channel % 4 == 0)) || // UNII-3: 149,153,157,161,165 (channel >= 149 && channel <= 165 && (channel % 4 == 1))) { valid = true; band = "5GHz"; } } if (!valid) { printf("Error: Invalid channel %d\n", channel); printf("\nESP32-C5 supports WiFi 6 on 2.4GHz and 5GHz bands only:\n"); printf(" 2.4GHz: 1-14 (20MHz or 40MHz)\n"); printf(" 5GHz: 36,40,44,48,52,56,60,64,100,104,...,161,165 (20/40/80MHz)\n"); printf("\nExamples:\n"); printf(" mode_monitor 6/20 # 2.4GHz channel 6, 20MHz\n"); printf(" mode_monitor 6/40 # 2.4GHz channel 6, 40MHz\n"); printf(" mode_monitor 36/20 # 5GHz channel 36, 20MHz\n"); printf(" mode_monitor 36/40 # 5GHz channel 36, 40MHz\n"); printf(" mode_monitor 36/80 # 5GHz channel 36, 80MHz\n"); printf(" mode_monitor 149 # 5GHz channel 149, default 40MHz\n"); return 1; } const char* bw_str = (bandwidth == WIFI_BW_HT40) ? "40MHz" : "20MHz"; printf("Monitoring channel %d (%s band, %s)\n", channel, band, bw_str); } esp_err_t err = switch_to_monitor_mode(channel, bandwidth); if (err != ESP_OK) { printf("Failed to switch to monitor mode\n"); return 1; } return 0; } static int cmd_mode_sta(int argc, char **argv) { wifi_band_mode_t band_mode = WIFI_BAND_MODE_AUTO; // Default to auto if (argc > 1) { if (strcmp(argv[1], "2.4") == 0 || strcmp(argv[1], "2") == 0) { band_mode = WIFI_BAND_MODE_2G_ONLY; printf("Forcing 2.4GHz band\n"); } else if (strcmp(argv[1], "5") == 0 || strcmp(argv[1], "5.0") == 0) { band_mode = WIFI_BAND_MODE_5G_ONLY; printf("Forcing 5GHz band\n"); } else if (strcmp(argv[1], "auto") == 0) { band_mode = WIFI_BAND_MODE_AUTO; printf("Auto band selection (2.4GHz or 5GHz)\n"); } else { printf("Error: Invalid band '%s'\n", argv[1]); printf("Valid options: 2.4, 5, auto\n"); printf("Examples:\n"); printf(" mode_sta 2.4 # Connect on 2.4GHz only\n"); printf(" mode_sta 5 # Connect on 5GHz only\n"); printf(" mode_sta auto # Auto select (default)\n"); return 1; } } esp_err_t err = switch_to_sta_mode(band_mode); if (err != ESP_OK) { printf("Failed to switch to STA mode\n"); return 1; } printf("Switching to STA mode (reconnecting to AP...)\n"); return 0; } static int cmd_mode_status(int argc, char **argv) { printf("\n=== WiFi Mode Status ===\n"); printf("Current mode: %s\n", current_wifi_mode == WIFI_MODE_STA_CSI ? "STA (CSI + iperf)" : "MONITOR"); printf("LED state: "); switch(current_led_state) { case LED_STATE_NO_CONFIG: printf("Yellow (No config)\n"); break; case LED_STATE_WAITING: printf("Blue blink (Connecting)\n"); break; case LED_STATE_CONNECTED: printf("Green (Connected)\n"); break; case LED_STATE_MONITORING: printf("Blue solid (Monitoring)\n"); break; case LED_STATE_FAILED: printf("Red blink (Failed)\n"); break; } if (current_wifi_mode == WIFI_MODE_STA_CSI) { printf("WiFi connected: %s\n", wifi_connected ? "Yes" : "No"); // Show band preference const char* band_pref = "Auto"; if (preferred_band == WIFI_BAND_MODE_2G_ONLY) band_pref = "2.4GHz only"; else if (preferred_band == WIFI_BAND_MODE_5G_ONLY) band_pref = "5GHz only"; printf("Band preference: %s\n", band_pref); // Show actual connected band/channel if connected if (wifi_connected) { wifi_ap_record_t ap_info; if (esp_wifi_sta_get_ap_info(&ap_info) == ESP_OK) { const char* band = (ap_info.primary >= 36) ? "5GHz" : "2.4GHz"; printf("Connected band: %s (channel %d)\n", band, ap_info.primary); // Show bandwidth wifi_bandwidth_t bw; esp_wifi_get_bandwidth(WIFI_IF_STA, &bw); const char* bw_str = "Unknown"; if (bw == WIFI_BW_HT20) bw_str = "20MHz"; else if (bw == WIFI_BW_HT40) bw_str = "40MHz"; printf("Bandwidth: %s\n", bw_str); } } printf("CSI enabled: %s\n", s_csi_enabled ? "Yes" : "No"); if (s_csi_enabled) { printf("CSI packets captured: %lu\n", (unsigned long)s_csi_packet_count); } } else { const char* band = (monitor_channel >= 36) ? "5GHz" : "2.4GHz"; printf("Monitor channel: %d (%s)\n", monitor_channel, band); // Show monitor bandwidth wifi_bandwidth_t bw; esp_wifi_get_bandwidth(WIFI_IF_STA, &bw); const char* bw_str = "Unknown"; if (bw == WIFI_BW_HT20) bw_str = "20MHz"; else if (bw == WIFI_BW_HT40) bw_str = "40MHz"; printf("Monitor bandwidth: %s\n", bw_str); printf("Monitor enabled: %s\n", s_monitor_enabled ? "Yes" : "No"); printf("Frames captured: %lu\n", (unsigned long)s_monitor_frame_count); } printf("GPS synced: %s\n", gps_is_synced() ? "Yes (+)" : "No (*)"); printf("\n"); return 0; } static int cmd_csi_dump(int argc, char **argv) { if (current_wifi_mode != WIFI_MODE_STA_CSI) { printf("Error: CSI only available in STA mode\n"); printf("Use 'mode_sta' to switch to STA mode first\n"); return 1; } if (!s_csi_enabled) { printf("Error: CSI not enabled yet\n"); printf("Wait for WiFi connection, or reconnect with 'mode_sta'\n"); return 1; } printf("Dumping CSI data...\n"); csi_log_dump_over_uart(); printf("CSI dump complete\n"); return 0; } static void register_mode_commands(void) { const esp_console_cmd_t mode_monitor = { .command = "mode_monitor", .help = "Switch to monitor mode (collapse detection)\n" " ESP32-C5 Hardware: WiFi 6 on 2.4GHz/5GHz, 20/40MHz\n" " Monitor Driver: Typically restricted to 20MHz capture\n" " Usage: mode_monitor [channel[/bandwidth]]\n" " Note: 40MHz will be forced to 20MHz (driver limitation)\n" " Examples:\n" " mode_monitor 6 # 2.4GHz ch 6, 20MHz\n" " mode_monitor 6/20 # 2.4GHz ch 6, 20MHz (explicit)\n" " mode_monitor 36 # 5GHz ch 36, 20MHz\n" " mode_monitor 149/20 # 5GHz ch 149, 20MHz", .func = &cmd_mode_monitor, }; ESP_ERROR_CHECK(esp_console_cmd_register(&mode_monitor)); const esp_console_cmd_t mode_sta = { .command = "mode_sta", .help = "Switch to STA mode (CSI + iperf)\n" " STA mode supports 20MHz and 40MHz (full hardware support)\n" " Usage: mode_sta [band]\n" " Band: 2.4, 5, auto (default)\n" " Examples:\n" " mode_sta # Auto band selection\n" " mode_sta 2.4 # Connect on 2.4GHz only\n" " mode_sta 5 # Connect on 5GHz only", .func = &cmd_mode_sta, }; ESP_ERROR_CHECK(esp_console_cmd_register(&mode_sta)); const esp_console_cmd_t mode_status = { .command = "mode_status", .help = "Show current WiFi mode and status", .func = &cmd_mode_status, }; ESP_ERROR_CHECK(esp_console_cmd_register(&mode_status)); const esp_console_cmd_t csi_dump = { .command = "csi_dump", .help = "Dump CSI data to UART (STA mode only)", .func = &cmd_csi_dump, }; ESP_ERROR_CHECK(esp_console_cmd_register(&csi_dump)); ESP_LOGI(TAG, "Mode switch commands registered:"); ESP_LOGI(TAG, " mode_monitor [ch/bw] - Switch to monitor mode with bandwidth"); ESP_LOGI(TAG, " mode_sta [band] - Switch to STA mode with band preference"); ESP_LOGI(TAG, " mode_status - Show current mode"); ESP_LOGI(TAG, " csi_dump - Dump CSI data"); } // --- Event Handler (Connection Logic) ------------------------------ static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { if (event_base == WIFI_EVENT) { if (event_id == WIFI_EVENT_STA_START) { if (has_config && current_wifi_mode == WIFI_MODE_STA_CSI) { current_led_state = LED_STATE_WAITING; } } else if (event_id == WIFI_EVENT_STA_DISCONNECTED) { wifi_event_sta_disconnected_t* event = (wifi_event_sta_disconnected_t*) event_data; ESP_LOGW(TAG, "WiFi Disconnected (Reason: %d)", event->reason); if (!wifi_connected && has_config && current_wifi_mode == WIFI_MODE_STA_CSI) { current_led_state = LED_STATE_FAILED; } wifi_connected = false; } } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { if (current_wifi_mode != WIFI_MODE_STA_CSI) { ESP_LOGW(TAG, "Got IP but not in STA mode (mode changed during connection)"); return; } ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; ESP_LOGI(TAG, "========================================"); ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip)); ESP_LOGI(TAG, "========================================"); wifi_connected = true; current_led_state = LED_STATE_CONNECTED; // DEFAULT MODE: Start CSI + iperf (STA mode) ESP_LOGI(TAG, "Starting STA mode services..."); // 1. Enable CSI ESP_LOGI(TAG, "Enabling CSI..."); wifi_enable_csi_once(); // 2. Start iperf server vTaskDelay(pdMS_TO_TICKS(1000)); iperf_cfg_t cfg; memset(&cfg, 0, sizeof(cfg)); cfg.flag = IPERF_FLAG_SERVER | IPERF_FLAG_TCP; cfg.sport = 5001; iperf_start(&cfg); ESP_LOGI(TAG, "✓ iperf TCP server started on port 5001"); // 3. Optional: Schedule CSI dump for later xTaskCreate(csi_dump_task, "csi_dump_task", 4096, NULL, 5, NULL); ESP_LOGI(TAG, "✓ STA mode active"); ESP_LOGI(TAG, " - CSI capture enabled"); ESP_LOGI(TAG, " - iperf server running"); ESP_LOGI(TAG, " - LED: Green"); ESP_LOGI(TAG, "========================================"); ESP_LOGI(TAG, "Console commands available:"); ESP_LOGI(TAG, " mode_monitor [ch] - Switch to monitor mode"); ESP_LOGI(TAG, " mode_status - Show current status"); ESP_LOGI(TAG, " csi_dump - Dump CSI data now"); ESP_LOGI(TAG, "========================================"); } } // --- Main Application Entry ---------------------------------------- void app_main(void) { // 1. Initialize Non-Volatile Storage (needed for WiFi config) ESP_ERROR_CHECK(nvs_flash_init()); // 2. Initialize Netif (TCP/IP stack) ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); // 3. Initialize Custom Logging & LED ESP_ERROR_CHECK(csi_log_init()); rgb_led_init(); xTaskCreate(led_task, "led_task", 4096, NULL, 5, NULL); // 4. Initialize GPS (Enable GPS-timestamped logs) ESP_LOGI(TAG, "========================================"); ESP_LOGI(TAG, "Initializing GPS sync..."); gps_sync_init(true); // true = GPS timestamps for ESP_LOG ESP_LOGI(TAG, "GPS initialized"); ESP_LOGI(TAG, " - Waiting for GPS lock..."); ESP_LOGI(TAG, " - Timestamps: (*) = not synced, (+) = GPS synced"); ESP_LOGI(TAG, "========================================"); // 5. Register WiFi Events ESP_ERROR_CHECK(esp_event_handler_instance_register( WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, NULL)); ESP_ERROR_CHECK(esp_event_handler_instance_register( IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, NULL)); // 6. Initialize WiFi Configuration wifi_cfg_init(); // 7. Initialize Serial Console (CRITICAL for console commands) ESP_LOGI(TAG, "Initializing console..."); /* Disable buffering on stdin */ setvbuf(stdin, NULL, _IONBF, 0); /* Initialize the console */ esp_console_config_t console_config = { .max_cmdline_args = 8, .max_cmdline_length = 256, #if CONFIG_LOG_COLORS .hint_color = atoi(LOG_COLOR_CYAN) #endif }; ESP_ERROR_CHECK(esp_console_init(&console_config)); /* Configure linenoise line completion library */ linenoiseSetMultiLine(1); linenoiseSetCompletionCallback(NULL); linenoiseSetHintsCallback(NULL); linenoiseHistorySetMaxLen(100); /* Register help command */ esp_console_register_help_command(); ESP_LOGI(TAG, "✓ Console initialized"); // 8. Register Console Commands for Mode Switching register_mode_commands(); // 9. Apply WiFi config and connect if (wifi_cfg_apply_from_nvs()) { has_config = true; current_led_state = LED_STATE_WAITING; ESP_LOGI(TAG, "========================================"); ESP_LOGI(TAG, "WiFi config loaded. Connecting..."); // Check if device is configured for MONITOR mode char mode[16] = {0}; uint8_t mon_ch = 36; if (wifi_cfg_get_mode(mode, &mon_ch)) { if (strcmp(mode, "MONITOR") == 0) { ESP_LOGI(TAG, "MODE: MONITOR (collapse detection)"); ESP_LOGI(TAG, "Monitor Channel: %d", mon_ch); ESP_LOGI(TAG, "Will switch to monitor mode after WiFi connects..."); // Allocate channel parameter for task uint8_t *ch_param = malloc(sizeof(uint8_t)); *ch_param = mon_ch; // Create task to switch to monitor mode after connection xTaskCreate(auto_monitor_task, "auto_monitor", 4096, ch_param, 5, NULL); } else { ESP_LOGI(TAG, "MODE: STA (CSI + iperf)"); } } else { ESP_LOGI(TAG, "DEFAULT MODE: STA (CSI + iperf)"); } ESP_LOGI(TAG, "========================================"); } else { has_config = false; current_led_state = LED_STATE_NO_CONFIG; ESP_LOGI(TAG, "========================================"); ESP_LOGI(TAG, "No WiFi config found."); ESP_LOGI(TAG, "LED: Yellow"); ESP_LOGI(TAG, "Use CLI command: wifi_config_set "); ESP_LOGI(TAG, "========================================"); } ESP_LOGI(TAG, "========================================"); ESP_LOGI(TAG, "Initialization complete"); ESP_LOGI(TAG, "Console commands available (no interactive prompt)"); ESP_LOGI(TAG, "========================================"); // app_main() returns - device runs autonomously } // --- Auto-Monitor Mode Task (switches to monitor mode after WiFi connects) --- static void auto_monitor_task(void *arg) { uint8_t channel = *(uint8_t*)arg; free(arg); // Free the allocated channel parameter // Wait for WiFi connection (LED will be green) ESP_LOGI(TAG, "Waiting for WiFi connection before switching to monitor mode..."); while (current_led_state != LED_STATE_CONNECTED) { vTaskDelay(pdMS_TO_TICKS(500)); } // Wait additional 2 seconds for GPS sync ESP_LOGI(TAG, "WiFi connected, waiting for GPS sync..."); vTaskDelay(pdMS_TO_TICKS(2000)); ESP_LOGI(TAG, "Auto-switching to MONITOR mode on channel %d...", channel); esp_err_t err = switch_to_monitor_mode(channel, WIFI_BW_HT20); if (err == ESP_OK) { ESP_LOGI(TAG, "✓ Monitor mode activated"); } else { ESP_LOGE(TAG, "✗ Failed to switch to monitor mode: %s", esp_err_to_name(err)); } vTaskDelete(NULL); }