#include #include #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.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" // Components #include "iperf.h" #include "wifi_cfg.h" #include "csi_log.h" #include "wifi_monitor.h" #include "gps_sync.h" #include "status_led.h" #include "board_config.h" static const char *TAG = "MAIN"; // --- WiFi Operation Mode --- typedef enum { WIFI_MODE_STA_CSI, WIFI_MODE_MONITOR } wifi_operation_mode_t; static wifi_operation_mode_t current_wifi_mode = WIFI_MODE_STA_CSI; static uint8_t monitor_channel = 6; static bool wifi_connected = false; static bool has_config = false; // --- Forward Declarations --- static void auto_monitor_task(void *arg); // --- 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,%" PRIi64 ",%" PRIi64 ",%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 = { 0 }; #if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32) csi_cfg.lltf_en = true; csi_cfg.htltf_en = true; csi_cfg.stbc_htltf2_en = true; csi_cfg.ltf_merge_en = true; csi_cfg.channel_filter_en = true; csi_cfg.manu_scale = false; csi_cfg.shift = false; #endif 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; } static void csi_dump_task(void *arg) { vTaskDelay(pdMS_TO_TICKS(20000)); 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++; // Check for Collapse if (frame->retry && frame->duration_id > 5000) { log_collapse_event((float)frame->duration_id, frame->rssi, frame->retry); } // 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)); wifi_collapse_stats_t stats; if (wifi_monitor_get_stats(&stats) == ESP_OK) { ESP_LOGI("MONITOR", "--- Stats: %lu frames, Retry: %.2f%%, Avg NAV: %u us ---", (unsigned long)stats.total_frames, stats.retry_rate, stats.avg_nav); if (wifi_monitor_is_collapsed()) { ESP_LOGW("MONITOR", "⚠️ ⚠️ 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) return ESP_OK; if (bandwidth != WIFI_BW_HT20) bandwidth = WIFI_BW_HT20; // Forced for monitor mode ESP_LOGI(TAG, "Switching to MONITOR MODE (Ch %d)", channel); iperf_stop(); vTaskDelay(pdMS_TO_TICKS(500)); wifi_disable_csi(); esp_wifi_disconnect(); esp_wifi_stop(); vTaskDelay(pdMS_TO_TICKS(500)); esp_wifi_set_mode(WIFI_MODE_NULL); if (wifi_monitor_init(channel, monitor_frame_callback) != ESP_OK) return ESP_FAIL; esp_wifi_set_bandwidth(WIFI_IF_STA, bandwidth); if (wifi_monitor_start() != ESP_OK) return ESP_FAIL; s_monitor_enabled = true; current_wifi_mode = WIFI_MODE_MONITOR; status_led_set_state(LED_STATE_MONITORING); monitor_channel = channel; if (s_monitor_stats_task_handle == NULL) { xTaskCreate(monitor_stats_task, "monitor_stats", 4096, NULL, 5, &s_monitor_stats_task_handle); } return ESP_OK; } esp_err_t switch_to_sta_mode(wifi_band_mode_t band_mode) { if (current_wifi_mode == WIFI_MODE_STA_CSI) return ESP_OK; ESP_LOGI(TAG, "Switching to STA MODE"); if (s_monitor_stats_task_handle != NULL) { vTaskDelete(s_monitor_stats_task_handle); s_monitor_stats_task_handle = NULL; } if (s_monitor_enabled) { wifi_monitor_stop(); s_monitor_enabled = false; vTaskDelay(pdMS_TO_TICKS(500)); } esp_wifi_set_mode(WIFI_MODE_STA); vTaskDelay(pdMS_TO_TICKS(500)); wifi_config_t wifi_config; esp_wifi_get_config(WIFI_IF_STA, &wifi_config); wifi_config.sta.channel = 0; // Auto band selection logic can be refined here esp_wifi_set_config(WIFI_IF_STA, &wifi_config); esp_wifi_start(); vTaskDelay(pdMS_TO_TICKS(500)); esp_wifi_connect(); current_wifi_mode = WIFI_MODE_STA_CSI; status_led_set_state(LED_STATE_WAITING); wifi_connected = false; return ESP_OK; } // --- Console Commands ---------------------------------------------- static int cmd_mode_monitor(int argc, char **argv) { int channel = monitor_channel; if (argc > 1) channel = atoi(argv[1]); if (switch_to_monitor_mode(channel, WIFI_BW_HT20) != 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; if (switch_to_sta_mode(band_mode) != ESP_OK) { printf("Failed to switch to STA mode\n"); return 1; } printf("Switching to STA mode...\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" : "MONITOR"); printf("LED state: %d\n", status_led_get_state()); printf("GPS synced: %s\n", gps_is_synced() ? "Yes" : "No"); 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"); return 1; } csi_log_dump_over_uart(); return 0; } static void register_mode_commands(void) { // Uses designated initializers to avoid missing field errors const esp_console_cmd_t cmds[] = { { .command = "mode_monitor", .help = "Switch to monitor mode", .func = &cmd_mode_monitor }, { .command = "mode_sta", .help = "Switch to STA mode", .func = &cmd_mode_sta }, { .command = "mode_status", .help = "Show status", .func = &cmd_mode_status }, { .command = "csi_dump", .help = "Dump CSI data", .func = &cmd_csi_dump }, }; for(int i=0; i<4; i++) ESP_ERROR_CHECK(esp_console_cmd_register(&cmds[i])); } // --- Event Handler ------------------------------------------------- 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) { status_led_set_state(LED_STATE_WAITING); } } else if (event_id == WIFI_EVENT_STA_DISCONNECTED) { wifi_connected = false; if (has_config && current_wifi_mode == WIFI_MODE_STA_CSI) { status_led_set_state(LED_STATE_FAILED); } } } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip)); wifi_connected = true; status_led_set_state(LED_STATE_CONNECTED); wifi_enable_csi_once(); iperf_cfg_t cfg = { .flag = IPERF_FLAG_SERVER | IPERF_FLAG_TCP, .sport = 5001 }; iperf_start(&cfg); xTaskCreate(csi_dump_task, "csi_dump_task", 4096, NULL, 5, NULL); } } // --- Main ---------------------------------------------------------- void app_main(void) { ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); // Init Subsystems ESP_ERROR_CHECK(csi_log_init()); status_led_init(RGB_LED_GPIO, HAS_RGB_LED); // Init GPS const gps_sync_config_t gps_cfg = { .uart_port = UART_NUM_1, .tx_pin = GPS_TX_PIN, .rx_pin = GPS_RX_PIN, .pps_pin = GPS_PPS_PIN, }; gps_sync_init(&gps_cfg, true); // Register 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)); // Init Config & Console wifi_cfg_init(); setvbuf(stdin, NULL, _IONBF, 0); esp_console_config_t console_config = { .max_cmdline_args = 8, .max_cmdline_length = 256, }; ESP_ERROR_CHECK(esp_console_init(&console_config)); linenoiseSetMultiLine(1); esp_console_register_help_command(); register_mode_commands(); // Apply Config if (wifi_cfg_apply_from_nvs()) { has_config = true; status_led_set_state(LED_STATE_WAITING); char mode[16] = {0}; uint8_t mon_ch = 36; if (wifi_cfg_get_mode(mode, &mon_ch) && strcmp(mode, "MONITOR") == 0) { uint8_t *ch_param = malloc(sizeof(uint8_t)); *ch_param = mon_ch; xTaskCreate(auto_monitor_task, "auto_monitor", 4096, ch_param, 5, NULL); } } else { has_config = false; status_led_set_state(LED_STATE_NO_CONFIG); } } static void auto_monitor_task(void *arg) { uint8_t channel = *(uint8_t*)arg; free(arg); while (status_led_get_state() != LED_STATE_CONNECTED) vTaskDelay(pdMS_TO_TICKS(500)); vTaskDelay(pdMS_TO_TICKS(2000)); switch_to_monitor_mode(channel, WIFI_BW_HT20); vTaskDelete(NULL); }