ESP32/main/main.c

875 lines
31 KiB
C

#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#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 <ssid> <pass>");
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);
}