more on support for esp32, C5 and S3

This commit is contained in:
Bob 2025-12-08 14:43:48 -08:00
parent a84f9a0db6
commit e350cc2a80
14 changed files with 444 additions and 748 deletions

View File

@ -0,0 +1,3 @@
idf_component_register(SRCS "cmd_transport.c"
INCLUDE_DIRS "."
PRIV_REQUIRES console driver soc)

View File

@ -0,0 +1,131 @@
#include "cmd_transport.h"
#include "esp_log.h"
#include "esp_console.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "soc/soc_caps.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#if SOC_USB_SERIAL_JTAG_SUPPORTED
#include "driver/usb_serial_jtag.h"
#endif
static const char *TAG = "CMD_TP";
#define MAX_LISTENERS 4
static cmd_line_handler_t s_listeners[MAX_LISTENERS] = {0};
static int s_listener_count = 0;
static bool s_inited = false;
void cmd_transport_register_listener(cmd_line_handler_t handler) {
if (s_listener_count < MAX_LISTENERS) {
s_listeners[s_listener_count++] = handler;
}
}
// Trim trailing whitespace (CR, LF)
static void trim_trailing(char *s) {
int n = strlen(s);
while (n > 0 && (s[n-1] == '\r' || s[n-1] == '\n' || isspace((unsigned char)s[n-1]))) {
s[--n] = 0;
}
}
// Dispatch line to listeners, then to ESP Console
static void dispatch_line(char *line, cmd_reply_func_t reply_func, void *reply_ctx) {
bool handled = false;
// 1. Offer to registered listeners (e.g. wifi_cfg)
for (int i = 0; i < s_listener_count; i++) {
if (s_listeners[i] && s_listeners[i](line, reply_func, reply_ctx)) {
handled = true;
break;
}
}
// 2. If not handled, pass to system console (for commands like 'mode_monitor')
if (!handled && strlen(line) > 0) {
int ret;
esp_err_t err = esp_console_run(line, &ret);
if (err == ESP_ERR_NOT_FOUND) {
// Unrecognized command - silent ignore or reply error
} else if (err != ESP_OK) {
ESP_LOGE(TAG, "Console run error: %s", esp_err_to_name(err));
}
}
}
// --- UART (stdin/stdout) Support ---
static void uart_reply(const char *msg, void *ctx) {
(void)ctx;
printf("%s", msg);
fflush(stdout);
}
static void uart_listener_task(void *arg) {
char line[256];
// Disable buffering
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
while (1) {
if (fgets(line, sizeof(line), stdin)) {
trim_trailing(line);
dispatch_line(line, uart_reply, NULL);
} else {
vTaskDelay(pdMS_TO_TICKS(20));
}
}
}
// --- USB Serial/JTAG Support ---
#if SOC_USB_SERIAL_JTAG_SUPPORTED
static void usb_reply(const char *msg, void *ctx) {
(void)ctx;
usb_serial_jtag_write_bytes((const uint8_t*)msg, strlen(msg), pdMS_TO_TICKS(50));
}
static void usb_listener_task(void *arg) {
usb_serial_jtag_driver_config_t d = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT();
if (usb_serial_jtag_driver_install(&d) != ESP_OK) {
ESP_LOGE(TAG, "Failed to install USB-Serial/JTAG driver");
vTaskDelete(NULL);
}
char buf[256];
size_t idx = 0;
uint8_t c;
while (1) {
int n = usb_serial_jtag_read_bytes(&c, 1, pdMS_TO_TICKS(20));
if (n > 0) {
if (c == '\n' || c == '\r') {
if (idx > 0) {
buf[idx] = 0;
dispatch_line(buf, usb_reply, NULL);
idx = 0;
}
} else {
if (idx < sizeof(buf) - 1) {
buf[idx++] = (char)c;
}
}
}
}
}
#endif
void cmd_transport_init(void) {
if (s_inited) return;
s_inited = true;
// Start UART Listener
xTaskCreatePinnedToCore(uart_listener_task, "cmd_uart", 4096, NULL, 5, NULL, tskNO_AFFINITY);
// Start USB Listener (if supported)
#if SOC_USB_SERIAL_JTAG_SUPPORTED
xTaskCreatePinnedToCore(usb_listener_task, "cmd_usb", 4096, NULL, 5, NULL, tskNO_AFFINITY);
#endif
}

View File

@ -0,0 +1,37 @@
#pragma once
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Function pointer structure for replying to the command source
*/
typedef void (*cmd_reply_func_t)(const char *msg, void *ctx);
/**
* @brief Callback for handling incoming lines
* * @param line The received line (null-terminated, trimmed of trailing CR/LF)
* @param reply_func Function to call to send a response back to the source
* @param reply_ctx Context pointer to pass to reply_func
* @return true if the line was consumed/handled
* @return false if the line should be passed to the next listener (or system console)
*/
typedef bool (*cmd_line_handler_t)(const char *line, cmd_reply_func_t reply_func, void *reply_ctx);
/**
* @brief Initialize the command transport (starts UART and USB listener tasks)
*/
void cmd_transport_init(void);
/**
* @brief Register a listener for console input
* @param handler The callback function
*/
void cmd_transport_register_listener(cmd_line_handler_t handler);
#ifdef __cplusplus
}
#endif

View File

@ -13,15 +13,11 @@
static const char *TAG = "GPS_SYNC"; static const char *TAG = "GPS_SYNC";
// --- INTERNAL MACROS ---
#define GPS_BAUD_RATE 9600 #define GPS_BAUD_RATE 9600
#define UART_BUF_SIZE 1024 #define UART_BUF_SIZE 1024
// --- GLOBAL STATE --- // --- GLOBAL STATE ---
// MUST be defined before gps_task uses it
static uart_port_t gps_uart_num = UART_NUM_1; static uart_port_t gps_uart_num = UART_NUM_1;
// GPS sync state
static int64_t monotonic_offset_us = 0; static int64_t monotonic_offset_us = 0;
static volatile int64_t last_pps_monotonic = 0; static volatile int64_t last_pps_monotonic = 0;
static volatile time_t next_pps_gps_second = 0; static volatile time_t next_pps_gps_second = 0;
@ -30,7 +26,7 @@ static bool use_gps_for_logs = false;
static SemaphoreHandle_t sync_mutex; static SemaphoreHandle_t sync_mutex;
static volatile bool force_sync_update = true; static volatile bool force_sync_update = true;
// PPS interrupt - captures exact monotonic time at second boundary // PPS interrupt
static void IRAM_ATTR pps_isr_handler(void* arg) { static void IRAM_ATTR pps_isr_handler(void* arg) {
static bool onetime = true; static bool onetime = true;
last_pps_monotonic = esp_timer_get_time(); last_pps_monotonic = esp_timer_get_time();
@ -40,43 +36,26 @@ static void IRAM_ATTR pps_isr_handler(void* arg) {
} }
} }
// Parse GPS time from NMEA sentence // Parse GPS time from NMEA
static bool parse_gprmc(const char* nmea, struct tm* tm_out, bool* valid) { static bool parse_gprmc(const char* nmea, struct tm* tm_out, bool* valid) {
if (strncmp(nmea, "$GPRMC", 6) != 0 && strncmp(nmea, "$GNRMC", 6) != 0) { if (strncmp(nmea, "$GPRMC", 6) != 0 && strncmp(nmea, "$GNRMC", 6) != 0) return false;
return false;
}
char *p = strchr(nmea, ','); char *p = strchr(nmea, ',');
if (!p) return false; if (!p) return false;
// Time field
p++; p++;
int hour, min, sec; int hour, min, sec;
if (sscanf(p, "%2d%2d%2d", &hour, &min, &sec) != 3) { if (sscanf(p, "%2d%2d%2d", &hour, &min, &sec) != 3) return false;
return false;
}
// Status field (A=valid, V=invalid)
p = strchr(p, ','); p = strchr(p, ',');
if (!p) return false; if (!p) return false;
p++; p++;
*valid = (*p == 'A'); *valid = (*p == 'A');
// Skip to date field (8 commas ahead from time)
for (int i = 0; i < 7; i++) { for (int i = 0; i < 7; i++) {
p = strchr(p, ','); p = strchr(p, ',');
if (!p) return false; if (!p) return false;
p++; p++;
} }
// Date field: ddmmyy
int day, month, year; int day, month, year;
if (sscanf(p, "%2d%2d%2d", &day, &month, &year) != 3) { if (sscanf(p, "%2d%2d%2d", &day, &month, &year) != 3) return false;
return false;
}
year += (year < 80) ? 2000 : 1900; year += (year < 80) ? 2000 : 1900;
tm_out->tm_sec = sec; tm_out->tm_sec = sec;
tm_out->tm_min = min; tm_out->tm_min = min;
tm_out->tm_hour = hour; tm_out->tm_hour = hour;
@ -84,72 +63,55 @@ static bool parse_gprmc(const char* nmea, struct tm* tm_out, bool* valid) {
tm_out->tm_mon = month - 1; tm_out->tm_mon = month - 1;
tm_out->tm_year = year - 1900; tm_out->tm_year = year - 1900;
tm_out->tm_isdst = 0; tm_out->tm_isdst = 0;
return true; return true;
} }
// Force the next GPS update to snap immediately (bypass filter)
void gps_force_next_update(void) { void gps_force_next_update(void) {
force_sync_update = true; force_sync_update = true;
ESP_LOGW(TAG, "Requesting forced GPS sync update"); ESP_LOGW(TAG, "Requesting forced GPS sync update");
} }
// GPS processing task
static void gps_task(void* arg) { static void gps_task(void* arg) {
// Buffer for UART reads (more efficient than reading 1 byte at a time)
uint8_t d_buf[64]; uint8_t d_buf[64];
char line[128]; char line[128];
int pos = 0; int pos = 0;
static int log_counter = 0; // Counter to throttle logs static int log_counter = 0;
while (1) { while (1) {
// Read up to 64 bytes with a 100ms timeout // Using dynamically stored port
int len = uart_read_bytes(gps_uart_num, d_buf, sizeof(d_buf), pdMS_TO_TICKS(100)); int len = uart_read_bytes(gps_uart_num, d_buf, sizeof(d_buf), pdMS_TO_TICKS(100));
if (len > 0) { if (len > 0) {
// Process all bytes received in this batch
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
uint8_t data = d_buf[i]; uint8_t data = d_buf[i];
if (data == '\n') { if (data == '\n') {
line[pos] = '\0'; line[pos] = '\0';
struct tm gps_tm; struct tm gps_tm;
bool valid; bool valid;
if (parse_gprmc(line, &gps_tm, &valid)) { if (parse_gprmc(line, &gps_tm, &valid)) {
if (valid) { if (valid) {
time_t gps_time = mktime(&gps_tm); time_t gps_time = mktime(&gps_tm);
xSemaphoreTake(sync_mutex, portMAX_DELAY); xSemaphoreTake(sync_mutex, portMAX_DELAY);
next_pps_gps_second = gps_time + 1; next_pps_gps_second = gps_time + 1;
xSemaphoreGive(sync_mutex); xSemaphoreGive(sync_mutex);
// Wait a bit to ensure PPS has likely fired if it was going to
vTaskDelay(pdMS_TO_TICKS(300)); vTaskDelay(pdMS_TO_TICKS(300));
xSemaphoreTake(sync_mutex, portMAX_DELAY); xSemaphoreTake(sync_mutex, portMAX_DELAY);
if (last_pps_monotonic > 0) { if (last_pps_monotonic > 0) {
int64_t gps_us = (int64_t)next_pps_gps_second * 1000000LL; int64_t gps_us = (int64_t)next_pps_gps_second * 1000000LL;
int64_t new_offset = gps_us - last_pps_monotonic; int64_t new_offset = gps_us - last_pps_monotonic;
if (monotonic_offset_us == 0 || force_sync_update) { if (monotonic_offset_us == 0 || force_sync_update) {
monotonic_offset_us = new_offset; monotonic_offset_us = new_offset;
if (force_sync_update) { if (force_sync_update) {
ESP_LOGW(TAG, "GPS sync SNAP: Offset forced to %lld us", monotonic_offset_us); ESP_LOGW(TAG, "GPS sync SNAP: Offset forced to %" PRIi64 " us", monotonic_offset_us);
force_sync_update = false; force_sync_update = false;
log_counter = 0; // Ensure we see the log immediately after a snap log_counter = 0;
} }
} else { } else {
// Low-pass filter: 90% old + 10% new
monotonic_offset_us = (monotonic_offset_us * 9 + new_offset) / 10; monotonic_offset_us = (monotonic_offset_us * 9 + new_offset) / 10;
} }
gps_has_fix = true; gps_has_fix = true;
// LOGGING THROTTLE: Only print every 60th update (approx 60 seconds)
if (log_counter == 0) { if (log_counter == 0) {
ESP_LOGI(TAG, "GPS sync: %04d-%02d-%02d %02d:%02d:%02d, offset=%lld us", ESP_LOGI(TAG, "GPS sync: %04d-%02d-%02d %02d:%02d:%02d, offset=%" PRIi64 " us",
gps_tm.tm_year + 1900, gps_tm.tm_mon + 1, gps_tm.tm_mday, gps_tm.tm_year + 1900, gps_tm.tm_mon + 1, gps_tm.tm_mday,
gps_tm.tm_hour, gps_tm.tm_min, gps_tm.tm_sec, gps_tm.tm_hour, gps_tm.tm_min, gps_tm.tm_sec,
monotonic_offset_us); monotonic_offset_us);
@ -162,7 +124,6 @@ static void gps_task(void* arg) {
gps_has_fix = false; gps_has_fix = false;
} }
} }
pos = 0; pos = 0;
} else if (pos < sizeof(line) - 1) { } else if (pos < sizeof(line) - 1) {
line[pos++] = data; line[pos++] = data;
@ -175,11 +136,9 @@ static void gps_task(void* arg) {
void gps_sync_init(const gps_sync_config_t *config, bool use_gps_log_timestamps) { void gps_sync_init(const gps_sync_config_t *config, bool use_gps_log_timestamps) {
ESP_LOGI(TAG, "Initializing GPS sync"); ESP_LOGI(TAG, "Initializing GPS sync");
// 1. Store the UART port for the task to use
gps_uart_num = config->uart_port; gps_uart_num = config->uart_port;
use_gps_for_logs = use_gps_log_timestamps; use_gps_for_logs = use_gps_log_timestamps;
// Ensure we start with a forced update
gps_force_next_update(); gps_force_next_update();
if (use_gps_log_timestamps) { if (use_gps_log_timestamps) {
@ -189,7 +148,6 @@ void gps_sync_init(const gps_sync_config_t *config, bool use_gps_log_timestamps)
sync_mutex = xSemaphoreCreateMutex(); sync_mutex = xSemaphoreCreateMutex();
// 2. Configure UART
uart_config_t uart_config = { uart_config_t uart_config = {
.baud_rate = GPS_BAUD_RATE, .baud_rate = GPS_BAUD_RATE,
.data_bits = UART_DATA_8_BITS, .data_bits = UART_DATA_8_BITS,
@ -202,14 +160,12 @@ void gps_sync_init(const gps_sync_config_t *config, bool use_gps_log_timestamps)
ESP_ERROR_CHECK(uart_driver_install(config->uart_port, UART_BUF_SIZE, 0, 0, NULL, 0)); ESP_ERROR_CHECK(uart_driver_install(config->uart_port, UART_BUF_SIZE, 0, 0, NULL, 0));
ESP_ERROR_CHECK(uart_param_config(config->uart_port, &uart_config)); ESP_ERROR_CHECK(uart_param_config(config->uart_port, &uart_config));
// 3. Set Pins (Dynamic Configuration)
ESP_ERROR_CHECK(uart_set_pin(config->uart_port, ESP_ERROR_CHECK(uart_set_pin(config->uart_port,
config->tx_pin, config->tx_pin,
config->rx_pin, config->rx_pin,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE,
UART_PIN_NO_CHANGE)); UART_PIN_NO_CHANGE));
// 4. Configure PPS GPIO
gpio_config_t io_conf = { gpio_config_t io_conf = {
.intr_type = GPIO_INTR_POSEDGE, .intr_type = GPIO_INTR_POSEDGE,
.mode = GPIO_MODE_INPUT, .mode = GPIO_MODE_INPUT,
@ -219,7 +175,6 @@ void gps_sync_init(const gps_sync_config_t *config, bool use_gps_log_timestamps)
}; };
ESP_ERROR_CHECK(gpio_config(&io_conf)); ESP_ERROR_CHECK(gpio_config(&io_conf));
// 5. Install ISR
gpio_install_isr_service(0); gpio_install_isr_service(0);
ESP_ERROR_CHECK(gpio_isr_handler_add(config->pps_pin, pps_isr_handler, NULL)); ESP_ERROR_CHECK(gpio_isr_handler_add(config->pps_pin, pps_isr_handler, NULL));
@ -229,6 +184,7 @@ void gps_sync_init(const gps_sync_config_t *config, bool use_gps_log_timestamps)
config->uart_port, config->rx_pin, config->tx_pin, config->pps_pin); config->uart_port, config->rx_pin, config->tx_pin, config->pps_pin);
} }
gps_timestamp_t gps_get_timestamp(void) { gps_timestamp_t gps_get_timestamp(void) {
gps_timestamp_t ts; gps_timestamp_t ts;

View File

@ -1,12 +1,9 @@
#pragma once #pragma once
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include "driver/gpio.h" #include "driver/gpio.h"
#include "driver/uart.h" #include "driver/uart.h"
#include "freertos/FreeRTOS.h" #include <stdbool.h>
#include "freertos/semphr.h" #include <stdint.h>
#include <time.h>
typedef struct { typedef struct {
uart_port_t uart_port; uart_port_t uart_port;
@ -16,21 +13,18 @@ typedef struct {
} gps_sync_config_t; } gps_sync_config_t;
typedef struct { typedef struct {
int64_t monotonic_us; // Microseconds - never jumps backward int64_t monotonic_us;
int64_t monotonic_ms; // Milliseconds - for easier logging int64_t monotonic_ms;
int64_t gps_us; // GPS UTC time in microseconds int64_t gps_us;
int64_t gps_ms; // GPS UTC time in milliseconds int64_t gps_ms;
struct timespec mono_ts; // POSIX timespec struct timespec mono_ts;
bool synced; // true if GPS has valid fix bool synced;
} gps_timestamp_t; } gps_timestamp_t;
void gps_sync_init(const gps_sync_config_t *config, bool use_gps_log_timestamps); void gps_sync_init(const gps_sync_config_t *config, bool use_gps_log_timestamps);
void gps_force_next_update(void); void gps_force_next_update(void);
gps_timestamp_t gps_get_timestamp(void); gps_timestamp_t gps_get_timestamp(void);
int64_t gps_get_monotonic_ms(void); int64_t gps_get_monotonic_ms(void);
bool gps_is_synced(void); bool gps_is_synced(void);
// Internal logging hooks
uint32_t gps_log_timestamp(void); uint32_t gps_log_timestamp(void);
int gps_log_vprintf(const char *fmt, va_list args); int gps_log_vprintf(const char *fmt, va_list args);

View File

@ -1,11 +1,3 @@
idf_component_register( idf_component_register(SRCS "wifi_cfg.c"
SRCS "wifi_cfg.c"
INCLUDE_DIRS "." INCLUDE_DIRS "."
REQUIRES PRIV_REQUIRES nvs_flash esp_wifi esp_netif driver cmd_transport)
esp_wifi
esp_netif
nvs_flash
esp_event
driver
esp_driver_usb_serial_jtag
)

View File

@ -1,21 +1,10 @@
// wifi_cfg_dualmode.c — ESP-IDF v5.3.x
// Listens for CFG/END WiFi config on BOTH UART(console/COM) and USBSerial/JTAG concurrently.
// - UART path uses stdio (fgets on stdin) — works with /dev/ttyUSB*
// - USB path uses driver API (usb_serial_jtag_read_bytes / write_bytes) — works with /dev/ttyACM*
// - Tolerates ESP_ERR_INVALID_STATE on repeated inits
// - Supports DHCP or static IP, persists to NVS, applies immediately
// - Bandwidth options: HT20 (20MHz), HT40 (40MHz), VHT80 (80MHz, 5GHz only)
// - Power save modes: NONE (default, best for CSI), MIN/MIN_MODEM, MAX/MAX_MODEM
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <ctype.h>
#include <stdatomic.h> #include <stdatomic.h>
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h" #include "esp_log.h"
#include "nvs_flash.h" #include "nvs_flash.h"
#include "nvs.h" #include "nvs.h"
@ -23,25 +12,15 @@
#include "esp_wifi.h" #include "esp_wifi.h"
#include "esp_event.h" #include "esp_event.h"
#include "esp_check.h" #include "esp_check.h"
#include "esp_event.h"
#include "driver/usb_serial_jtag.h" // direct read/write API (no VFS remap)
#include "wifi_cfg.h" #include "wifi_cfg.h"
#include "cmd_transport.h" // Now uses the transport component
// wifi_cfg.c
static const char *TAG = "wifi_cfg"; static const char *TAG = "wifi_cfg";
static esp_netif_t *sta_netif = NULL; static esp_netif_t *sta_netif = NULL;
static bool cfg_dhcp = true; static bool cfg_dhcp = true;
static void trim(char *s){ // --- Helper Functions (Preserved) ---
int n = strlen(s);
while(n>0 && (s[n-1]=='\r' || s[n-1]=='\n' || isspace((unsigned char)s[n-1]))) s[--n]=0;
while(*s && isspace((unsigned char)*s)){
memmove(s, s+1, strlen(s));
}
}
static esp_err_t nvs_set_str2(nvs_handle_t h, const char *key, const char *val){ static esp_err_t nvs_set_str2(nvs_handle_t h, const char *key, const char *val){
return val ? nvs_set_str(h, key, val) : nvs_erase_key(h, key); return val ? nvs_set_str(h, key, val) : nvs_erase_key(h, key);
@ -64,8 +43,7 @@ static void save_cfg(const char* ssid, const char* pass, const char* ip, const c
nvs_commit(h); nvs_commit(h);
nvs_close(h); nvs_close(h);
cfg_dhcp = dhcp; cfg_dhcp = dhcp;
ESP_LOGI(TAG, "Config saved to NVS: SSID=%s Mode=%s MonCh=%d Band=%s BW=%s PowerSave=%s", ESP_LOGI(TAG, "Config saved to NVS: SSID=%s Mode=%s MonCh=%d", ssid?ssid:"", mode?mode:"STA", mon_ch);
ssid?ssid:"", mode?mode:"STA", mon_ch, band?band:"", bw?bw:"", powersave?powersave:"NONE");
} }
static bool load_cfg(char* ssid, size_t ssz, char* pass, size_t psz, static bool load_cfg(char* ssid, size_t ssz, char* pass, size_t psz,
@ -97,36 +75,21 @@ void wifi_cfg_force_dhcp(bool enable){ cfg_dhcp = enable; }
bool wifi_cfg_get_mode(char *mode, uint8_t *mon_ch) { bool wifi_cfg_get_mode(char *mode, uint8_t *mon_ch) {
if (!mode || !mon_ch) return false; if (!mode || !mon_ch) return false;
nvs_handle_t h; nvs_handle_t h;
if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) { if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) {
ESP_LOGW(TAG, "Failed to open NVS for mode - defaulting to STA"); strcpy(mode, "STA"); *mon_ch = 36; return false;
strcpy(mode, "STA");
*mon_ch = 36;
return false;
} }
size_t len = 16; size_t len = 16;
esp_err_t e = nvs_get_str(h, "mode", mode, &len); if (nvs_get_str(h, "mode", mode, &len) != ESP_OK) strcpy(mode, "STA");
if (e != ESP_OK) {
strcpy(mode, "STA"); // Default to STA mode
}
uint8_t ch = 36; uint8_t ch = 36;
nvs_get_u8(h, "mon_ch", &ch); nvs_get_u8(h, "mon_ch", &ch);
*mon_ch = ch; *mon_ch = ch;
nvs_close(h); nvs_close(h);
ESP_LOGI(TAG, "Retrieved mode from NVS: %s (MonCh=%d)", mode, ch);
return true; return true;
} }
/* --- One-time net stack bring-up (thread-safe) --- */
static atomic_bool s_net_stack_ready = false; static atomic_bool s_net_stack_ready = false;
static esp_err_t ensure_net_stack_once(void) {
static esp_err_t ensure_net_stack_once(void)
{
bool expected = false; bool expected = false;
if (atomic_compare_exchange_strong(&s_net_stack_ready, &expected, true)) { if (atomic_compare_exchange_strong(&s_net_stack_ready, &expected, true)) {
ESP_RETURN_ON_ERROR(esp_netif_init(), TAG, "esp_netif_init"); ESP_RETURN_ON_ERROR(esp_netif_init(), TAG, "esp_netif_init");
@ -138,236 +101,104 @@ static esp_err_t ensure_net_stack_once(void)
return ESP_OK; return ESP_OK;
} }
/* --- One-time Wi-Fi driver init (thread-safe, idempotent) --- */
static atomic_bool s_wifi_inited = false; static atomic_bool s_wifi_inited = false;
esp_err_t wifi_ensure_inited(void) {
esp_err_t wifi_ensure_inited(void)
{
bool expected = false; bool expected = false;
if (!atomic_compare_exchange_strong(&s_wifi_inited, &expected, true)) { if (!atomic_compare_exchange_strong(&s_wifi_inited, &expected, true)) return ESP_OK;
// someone else already initialized (or is initializing and finished)
return ESP_OK;
}
ESP_RETURN_ON_ERROR(ensure_net_stack_once(), TAG, "net stack"); ESP_RETURN_ON_ERROR(ensure_net_stack_once(), TAG, "net stack");
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_err_t err = esp_wifi_init(&cfg); esp_err_t err = esp_wifi_init(&cfg);
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) { if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
// roll back the flag so a later attempt can retry
atomic_store(&s_wifi_inited, false); atomic_store(&s_wifi_inited, false);
ESP_RETURN_ON_ERROR(err, TAG, "esp_wifi_init"); ESP_RETURN_ON_ERROR(err, TAG, "esp_wifi_init");
} }
// Optional: set default interface and mode now or let caller do it
// ESP_RETURN_ON_ERROR(esp_wifi_set_mode(WIFI_MODE_STA), TAG, "set mode");
return ESP_OK; return ESP_OK;
} }
static void apply_ip_static(const char* ip, const char* mask, const char* gw){ static void apply_ip_static(const char* ip, const char* mask, const char* gw){
if (!sta_netif) return; if (!sta_netif) return;
if (!ip || !ip[0] || !mask || !mask[0] || !gw || !gw[0]) return;
// Validate IP strings are not empty
if (!ip || !ip[0] || !mask || !mask[0] || !gw || !gw[0]) {
ESP_LOGW(TAG, "Invalid static IP config: IP=%s MASK=%s GW=%s",
ip ? ip : "NULL", mask ? mask : "NULL", gw ? gw : "NULL");
return;
}
esp_netif_ip_info_t info = {0}; esp_netif_ip_info_t info = {0};
esp_netif_dhcpc_stop(sta_netif); esp_netif_dhcpc_stop(sta_netif);
info.ip.addr = esp_ip4addr_aton(ip); info.ip.addr = esp_ip4addr_aton(ip);
info.netmask.addr = esp_ip4addr_aton(mask); info.netmask.addr = esp_ip4addr_aton(mask);
info.gw.addr = esp_ip4addr_aton(gw); info.gw.addr = esp_ip4addr_aton(gw);
ESP_LOGI(TAG, "Setting static IP: %s, netmask: %s, gateway: %s", ip, mask, gw);
ESP_ERROR_CHECK( esp_netif_set_ip_info(sta_netif, &info) ); ESP_ERROR_CHECK( esp_netif_set_ip_info(sta_netif, &info) );
} }
bool wifi_cfg_apply_from_nvs(void) { bool wifi_cfg_apply_from_nvs(void) {
char ssid[64]={0}, pass[64]={0}, ip[32]={0}, mask[32]={0}, gw[32]={0}; char ssid[64]={0}, pass[64]={0}, ip[32]={0}, mask[32]={0}, gw[32]={0};
char band[16]={0}, bw[16]={0}, powersave[16]={0}, mode[16]={0}; char band[16]={0}, bw[16]={0}, powersave[16]={0}, mode[16]={0};
uint8_t mon_ch = 36; uint8_t mon_ch = 36; bool dhcp = true;
bool dhcp = true;
if (!load_cfg(ssid,sizeof(ssid), pass,sizeof(pass), ip,sizeof(ip), mask,sizeof(mask), gw,sizeof(gw), if (!load_cfg(ssid,sizeof(ssid), pass,sizeof(pass), ip,sizeof(ip), mask,sizeof(mask), gw,sizeof(gw),
band,sizeof(band), bw,sizeof(bw), powersave,sizeof(powersave), mode,sizeof(mode), &mon_ch, &dhcp)){ band,sizeof(band), bw,sizeof(bw), powersave,sizeof(powersave), mode,sizeof(mode), &mon_ch, &dhcp)){
ESP_LOGW(TAG, "No WiFi config in NVS");
return false; return false;
} }
// Treat empty SSID as "no config" to avoid ESP_ERR_WIFI_SSID panics if (ssid[0] == '\0') return false;
if (ssid[0] == '\0') {
ESP_LOGW(TAG, "SSID in NVS is empty; treating as no Wi-Fi config");
return false;
}
ESP_LOGI(TAG, "Applying Wi-Fi config: SSID=%s DHCP=%d IP=%s MASK=%s GW=%s Band=%s BW=%s PowerSave=%s Mode=%s MonCh=%d",
ssid, dhcp, ip, mask, gw, band, bw, powersave, mode, mon_ch);
static bool inited = false; static bool inited = false;
if (!inited){ if (!inited){
// NVS (with recovery) nvs_flash_init();
esp_err_t err = nvs_flash_init(); ensure_net_stack_once();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { if (sta_netif == NULL) sta_netif = esp_netif_create_default_wifi_sta();
nvs_flash_erase(); wifi_ensure_inited();
err = nvs_flash_init();
}
if (err != ESP_OK) { ESP_ERROR_CHECK(err); }
// Netif + default event loop (tolerate already-initialized state)
do{ esp_err_t __e = esp_netif_init(); if(__e!=ESP_OK && __e!=ESP_ERR_INVALID_STATE){ ESP_ERROR_CHECK(__e);} }while(0);
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) { ESP_ERROR_CHECK(err); }
do{ esp_err_t __e = esp_event_loop_create_default(); if(__e!=ESP_OK && __e!=ESP_ERR_INVALID_STATE){ ESP_ERROR_CHECK(__e);} }while(0);
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) { ESP_ERROR_CHECK(err); }
if (sta_netif == NULL) {
sta_netif = esp_netif_create_default_wifi_sta();
}
ESP_ERROR_CHECK(err=wifi_ensure_inited());
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) { ESP_ERROR_CHECK(err); }
inited = true; inited = true;
} }
wifi_config_t wcfg = (wifi_config_t){0}; wifi_config_t wcfg = {0};
strncpy((char*)wcfg.sta.ssid, ssid, sizeof(wcfg.sta.ssid)-1); strncpy((char*)wcfg.sta.ssid, ssid, sizeof(wcfg.sta.ssid)-1);
strncpy((char*)wcfg.sta.password, pass, sizeof(wcfg.sta.password)-1); strncpy((char*)wcfg.sta.password, pass, sizeof(wcfg.sta.password)-1);
wcfg.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK; wcfg.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
wcfg.sta.sae_pwe_h2e = WPA3_SAE_PWE_BOTH; wcfg.sta.sae_pwe_h2e = WPA3_SAE_PWE_BOTH;
// Set scan method to search all channels
wcfg.sta.scan_method = WIFI_ALL_CHANNEL_SCAN; wcfg.sta.scan_method = WIFI_ALL_CHANNEL_SCAN;
// Log and configure band preference if (strcmp(band, "5G") == 0) wcfg.sta.channel = 0;
if (strcmp(band, "5G") == 0) { else wcfg.sta.channel = 0;
ESP_LOGI(TAG, "Configuring for 5GHz operation");
// For 5GHz preference, scan both but prefer 5GHz channels
wcfg.sta.channel = 0; // Scan all channels (both 2.4GHz and 5GHz)
} else {
ESP_LOGI(TAG, "Configuring for 2.4GHz operation (default)");
wcfg.sta.channel = 0; // Scan all channels
}
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); esp_wifi_set_mode(WIFI_MODE_STA);
// Configure WiFi protocols based on chip capabilities // Protocol selection based on target
#if CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C6 #if CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C6
// Dual-band chips (2.4GHz + 5GHz support)
wifi_protocols_t protocols = { wifi_protocols_t protocols = {
// 2.4 GHz: b/g/n (no ax for CSI compatibility) .ghz_2g = WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N,
.ghz_2g = WIFI_PROTOCOL_11B | .ghz_5g = WIFI_PROTOCOL_11A | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_11AC | WIFI_PROTOCOL_11AX,
WIFI_PROTOCOL_11G |
WIFI_PROTOCOL_11N,
// 5 GHz: a/n/ac/ax
.ghz_5g = WIFI_PROTOCOL_11A |
WIFI_PROTOCOL_11N |
WIFI_PROTOCOL_11AC |
WIFI_PROTOCOL_11AX,
}; };
ESP_ERROR_CHECK( esp_wifi_set_protocols(WIFI_IF_STA, &protocols) ); esp_wifi_set_protocols(WIFI_IF_STA, &protocols);
#else #else
// Single-band chips (2.4GHz only) - ESP32, ESP32-S2, ESP32-S3, ESP32-C3, etc. esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N);
// Use legacy API for 2.4GHz-only chips #endif
uint8_t protocol_bitmap = WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N;
ESP_ERROR_CHECK( esp_wifi_set_protocol(WIFI_IF_STA, protocol_bitmap) );
// Warn if user requested 5GHz on a 2.4GHz-only chip esp_wifi_set_config(WIFI_IF_STA, &wcfg);
if (strcmp(band, "5G") == 0) {
ESP_LOGW(TAG, "5GHz requested but this chip only supports 2.4GHz - using 2.4GHz");
}
#endif
ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wcfg) ); if (!dhcp && ip[0]) apply_ip_static(ip, mask, gw);
else if (sta_netif) esp_netif_dhcpc_start(sta_netif);
if (!dhcp && ip[0] && mask[0] && gw[0]){ // Bandwidth selection
apply_ip_static(ip, mask, gw); #if CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C6
} else { wifi_bandwidths_t bandwidths = {.ghz_2g = WIFI_BW_HT20, .ghz_5g = WIFI_BW_HT20};
if (sta_netif) esp_netif_dhcpc_start(sta_netif); if (strcmp(bw, "VHT80") == 0) { bandwidths.ghz_2g = WIFI_BW_HT40; bandwidths.ghz_5g = WIFI_BW80; }
} else if (strcmp(bw, "HT40") == 0) { bandwidths.ghz_2g = WIFI_BW_HT40; bandwidths.ghz_5g = WIFI_BW_HT40; }
esp_wifi_set_bandwidths(WIFI_IF_STA, &bandwidths);
// Set bandwidth BEFORE WiFi is started #else
#if CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C6
// Dual-band chips use struct-based API
wifi_bandwidths_t bandwidths = {
.ghz_2g = WIFI_BW_HT20,
.ghz_5g = WIFI_BW_HT20
};
if (strcmp(bw, "VHT80") == 0) {
// 80MHz only supported on 5GHz
bandwidths.ghz_2g = WIFI_BW_HT40; // 2.4GHz fallback to 40MHz
bandwidths.ghz_5g = WIFI_BW80; // Use WIFI_BW80, not WIFI_BW_HT80
ESP_LOGI(TAG, "Setting bandwidth to VHT80 (80MHz) for 5GHz, HT40 (40MHz) for 2.4GHz");
} else if (strcmp(bw, "HT40") == 0) {
bandwidths.ghz_2g = WIFI_BW_HT40;
bandwidths.ghz_5g = WIFI_BW_HT40;
ESP_LOGI(TAG, "Setting bandwidth to HT40 (40MHz) for both bands");
} else {
ESP_LOGI(TAG, "Setting bandwidth to HT20 (20MHz) for both bands");
}
esp_err_t bw_err = esp_wifi_set_bandwidths(WIFI_IF_STA, &bandwidths);
if (bw_err != ESP_OK) {
ESP_LOGW(TAG, "Failed to set bandwidths: %s", esp_err_to_name(bw_err));
}
#else
// Single-band chips (2.4GHz only) use legacy API
wifi_bandwidth_t bandwidth = WIFI_BW_HT20; wifi_bandwidth_t bandwidth = WIFI_BW_HT20;
if (strcmp(bw, "HT40") == 0) bandwidth = WIFI_BW_HT40;
esp_wifi_set_bandwidth(WIFI_IF_STA, bandwidth);
#endif
if (strcmp(bw, "VHT80") == 0) { esp_wifi_start();
ESP_LOGW(TAG, "VHT80 (80MHz) not supported on 2.4GHz-only chips - using HT40 (40MHz)");
bandwidth = WIFI_BW_HT40;
} else if (strcmp(bw, "HT40") == 0) {
bandwidth = WIFI_BW_HT40;
ESP_LOGI(TAG, "Setting bandwidth to HT40 (40MHz) for 2.4GHz");
} else {
ESP_LOGI(TAG, "Setting bandwidth to HT20 (20MHz) for 2.4GHz");
}
esp_err_t bw_err = esp_wifi_set_bandwidth(WIFI_IF_STA, bandwidth); // Power Save
if (bw_err != ESP_OK) { wifi_ps_type_t ps_mode = WIFI_PS_NONE;
ESP_LOGW(TAG, "Failed to set bandwidth: %s", esp_err_to_name(bw_err)); if (strcmp(powersave, "MIN") == 0) ps_mode = WIFI_PS_MIN_MODEM;
} else if (strcmp(powersave, "MAX") == 0) ps_mode = WIFI_PS_MAX_MODEM;
#endif esp_wifi_set_ps(ps_mode);
esp_err_t err2 = esp_wifi_start(); esp_wifi_connect();
if (err2 != ESP_OK && err2 != ESP_ERR_INVALID_STATE) { ESP_ERROR_CHECK(err2); }
// Set power save mode based on configuration
wifi_ps_type_t ps_mode = WIFI_PS_NONE; // Default: no power save (best for CSI)
if (strcmp(powersave, "MIN") == 0 || strcmp(powersave, "MIN_MODEM") == 0) {
ps_mode = WIFI_PS_MIN_MODEM;
ESP_LOGI(TAG, "Setting power save mode: MIN_MODEM");
} else if (strcmp(powersave, "MAX") == 0 || strcmp(powersave, "MAX_MODEM") == 0) {
ps_mode = WIFI_PS_MAX_MODEM;
ESP_LOGI(TAG, "Setting power save mode: MAX_MODEM");
} else {
ESP_LOGI(TAG, "Setting power save mode: NONE (best for CSI)");
}
err2 = esp_wifi_set_ps(ps_mode);
if (err2 != ESP_OK) {
ESP_LOGW(TAG, "Failed to set power save mode: %s", esp_err_to_name(err2));
}
err2 = esp_wifi_connect();
if (err2 != ESP_OK && err2 != ESP_ERR_WIFI_NOT_INIT && err2 != ESP_ERR_INVALID_STATE) { ESP_ERROR_CHECK(err2); }
return true; return true;
} }
// -------------------- Dual input paths -------------------- // --- Command Listener Logic ---
typedef struct {
// emit() should write a short line back to the same channel (optional)
void (*emit)(const char *s, void *ctx);
void *ctx;
// fetch_line() should fill buf with a single line (without trailing CR/LF) and return true if a line was read
bool (*fetch_line)(char *buf, size_t sz, void *ctx);
} cfg_io_t;
static void on_cfg_line(const char *line, char *ssid, char *pass, char *ip, char *mask, char *gw, char *band, char *bw, char *powersave, char *mode, uint8_t *mon_ch, bool *dhcp){ static void on_cfg_line(const char *line, char *ssid, char *pass, char *ip, char *mask, char *gw, char *band, char *bw, char *powersave, char *mode, uint8_t *mon_ch, bool *dhcp){
if (strncmp(line, "SSID:",5)==0){ strncpy(ssid, line+5, 63); ssid[63]=0; return; } if (strncmp(line, "SSID:",5)==0){ strncpy(ssid, line+5, 63); ssid[63]=0; return; }
@ -383,168 +214,77 @@ static void on_cfg_line(const char *line, char *ssid, char *pass, char *ip, char
if (strncmp(line, "DHCP:",5)==0){ *dhcp = atoi(line+5) ? true:false; return; } if (strncmp(line, "DHCP:",5)==0){ *dhcp = atoi(line+5) ? true:false; return; }
} }
static void cfg_worker(const cfg_io_t *io){ static bool wifi_cfg_cmd_handler(const char *line, cmd_reply_func_t reply_func, void *reply_ctx) {
char line[160]; static bool in_cfg = false;
char ssid[64]={0}, pass[64]={0}, ip[32]={0}, mask[32]={0}, gw[32]={0}; static char ssid[64]={0}, pass[64]={0}, ip[32]={0}, mask[32]={0}, gw[32]={0};
char band[16]={0}, bw[16]={0}, powersave[16]={0}, mode[16]={0}; static char band[16]={0}, bw[16]={0}, powersave[16]={0}, mode[16]={0};
uint8_t mon_ch = 36; static uint8_t mon_ch = 36;
bool dhcp = true; static bool dhcp = true;
bool in_cfg = false;
for(;;){ if (!in_cfg) {
if (!io->fetch_line(line, sizeof(line), io->ctx)){ if (strcmp(line, "CFG") == 0) {
vTaskDelay(pdMS_TO_TICKS(20));
continue;
}
trim(line);
if (!in_cfg){
if (strcmp(line, "CFG")==0){
in_cfg = true; in_cfg = true;
ssid[0]=pass[0]=ip[0]=mask[0]=gw[0]=0; // Reset buffers
band[0]=bw[0]=powersave[0]=mode[0]=0; ssid[0]=0; pass[0]=0; ip[0]=0; mask[0]=0; gw[0]=0;
mon_ch = 36; band[0]=0; bw[0]=0; powersave[0]=0; mode[0]=0;
dhcp = true; mon_ch = 36; dhcp = true;
return true; // Handled
} }
continue; return false; // Not handled
} }
if (strcmp(line, "END")==0){
// Set defaults if not specified // Inside CFG block
if (strcmp(line, "END") == 0) {
// Apply Defaults
if (!band[0]) strcpy(band, "2.4G"); if (!band[0]) strcpy(band, "2.4G");
if (!bw[0]) strcpy(bw, "HT20"); if (!bw[0]) strcpy(bw, "HT20");
if (!powersave[0]) strcpy(powersave, "NONE"); if (!powersave[0]) strcpy(powersave, "NONE");
if (!mode[0]) strcpy(mode, "STA"); if (!mode[0]) strcpy(mode, "STA");
save_cfg(ssid, pass, ip, mask, gw, dhcp, band, bw, powersave, mode, mon_ch); save_cfg(ssid, pass, ip, mask, gw, dhcp, band, bw, powersave, mode, mon_ch);
if (io->emit) io->emit("OK\n", io->ctx);
if (reply_func) reply_func("OK\n", reply_ctx);
wifi_cfg_apply_from_nvs(); wifi_cfg_apply_from_nvs();
in_cfg = false; in_cfg = false;
continue; return true;
} }
on_cfg_line(line, ssid, pass, ip, mask, gw, band, bw, powersave, mode, &mon_ch, &dhcp); on_cfg_line(line, ssid, pass, ip, mask, gw, band, bw, powersave, mode, &mon_ch, &dhcp);
}
}
// ---- UART(stdin) path ----
static bool uart_fetch_line(char *buf, size_t sz, void *ctx){
(void)ctx;
if (!fgets(buf, sz, stdin)) return false;
return true; return true;
} }
static void uart_emit(const char *s, void *ctx){
(void)ctx;
fputs(s, stdout);
fflush(stdout);
}
static void cfg_listener_uart_task(void *arg){
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
cfg_io_t io = {.emit = uart_emit, .ctx = NULL, .fetch_line = uart_fetch_line};
cfg_worker(&io);
}
// ---- USB-Serial/JTAG path ----
typedef struct { int dummy; } usb_ctx_t;
static bool usb_fetch_line(char *buf, size_t sz, void *ctx){
(void)ctx;
static char acc[256];
static size_t acc_len = 0;
uint8_t tmp[64];
int n = usb_serial_jtag_read_bytes(tmp, sizeof(tmp), pdMS_TO_TICKS(10));
if (n <= 0){
return false;
}
for (int i=0; i<n; ++i){
char c = (char)tmp[i];
if (c == '\n' || c == '\r'){
acc[acc_len] = 0;
strncpy(buf, acc, sz-1);
buf[sz-1]=0;
acc_len = 0;
return true;
}else if (acc_len < sizeof(acc)-1){
acc[acc_len++] = c;
}
}
return false;
}
static void usb_emit(const char *s, void *ctx){
(void)ctx;
usb_serial_jtag_write_bytes((const uint8_t*)s, strlen(s), pdMS_TO_TICKS(50));
}
static void cfg_listener_usb_task(void *arg){
// Install USB-Serial/JTAG driver (no VFS remap: stdio stays on UART)
usb_serial_jtag_driver_config_t d = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT();
ESP_ERROR_CHECK(usb_serial_jtag_driver_install(&d));
usb_ctx_t uctx = {0};
cfg_io_t io = {.emit = usb_emit, .ctx = &uctx, .fetch_line = usb_fetch_line};
cfg_worker(&io);
}
void wifi_cfg_init(void){ void wifi_cfg_init(void){
// Make sure NVS exists
nvs_flash_init(); nvs_flash_init();
// Spawn both listeners // Init the shared transport (starts UART/USB tasks)
xTaskCreatePinnedToCore(cfg_listener_uart_task, "cfg_uart", cmd_transport_init();
6144, NULL, 5, NULL, tskNO_AFFINITY);
xTaskCreatePinnedToCore(cfg_listener_usb_task, "cfg_usb", // Register our callback to catch "CFG" blocks
6144, NULL, 5, NULL, tskNO_AFFINITY); cmd_transport_register_listener(wifi_cfg_cmd_handler);
} }
wifi_ps_type_t wifi_cfg_get_power_save_mode(void) { wifi_ps_type_t wifi_cfg_get_power_save_mode(void) {
char powersave[16] = {0}; char powersave[16] = {0};
nvs_handle_t h; nvs_handle_t h;
if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) return WIFI_PS_NONE;
if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) {
ESP_LOGW(TAG, "Failed to open NVS for power save - defaulting to NONE");
return WIFI_PS_NONE;
}
size_t len = sizeof(powersave); size_t len = sizeof(powersave);
esp_err_t err = nvs_get_str(h, "powersave", powersave, &len); nvs_get_str(h, "powersave", powersave, &len);
nvs_close(h); nvs_close(h);
if (err != ESP_OK) { if (strcmp(powersave, "MIN") == 0) return WIFI_PS_MIN_MODEM;
ESP_LOGI(TAG, "No power save setting in NVS - defaulting to NONE"); if (strcmp(powersave, "MAX") == 0) return WIFI_PS_MAX_MODEM;
return WIFI_PS_NONE; return WIFI_PS_NONE;
}
// Parse the power save string
if (strcmp(powersave, "MIN") == 0 || strcmp(powersave, "MIN_MODEM") == 0) {
ESP_LOGI(TAG, "Power save mode: MIN_MODEM");
return WIFI_PS_MIN_MODEM;
} else if (strcmp(powersave, "MAX") == 0 || strcmp(powersave, "MAX_MODEM") == 0) {
ESP_LOGI(TAG, "Power save mode: MAX_MODEM");
return WIFI_PS_MAX_MODEM;
} else {
ESP_LOGI(TAG, "Power save mode: NONE (no power saving)");
return WIFI_PS_NONE;
}
} }
bool wifi_cfg_get_bandwidth(char *buf, size_t buf_size) { bool wifi_cfg_get_bandwidth(char *buf, size_t buf_size) {
if (!buf || buf_size < 1) { if (!buf || buf_size < 1) return false;
return false;
}
nvs_handle_t h; nvs_handle_t h;
if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) { if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) {
strncpy(buf, "Unknown", buf_size - 1); strncpy(buf, "Unknown", buf_size); return false;
buf[buf_size - 1] = '\0';
return false;
} }
size_t len = buf_size; size_t len = buf_size;
esp_err_t err = nvs_get_str(h, "bw", buf, &len); esp_err_t err = nvs_get_str(h, "bw", buf, &len);
nvs_close(h); nvs_close(h);
return (err == ESP_OK);
if (err != ESP_OK) {
strncpy(buf, "HT20", buf_size - 1); // Default
buf[buf_size - 1] = '\0';
return false;
}
return true;
} }

View File

@ -1,5 +1,6 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h>
#include <inttypes.h> #include <inttypes.h>
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
@ -25,12 +26,14 @@
#include "csi_log.h" #include "csi_log.h"
#include "wifi_monitor.h" #include "wifi_monitor.h"
#include "gps_sync.h" #include "gps_sync.h"
// Note: cmd_transport is initialized by wifi_cfg_init, so we don't need to include it directly here unless we use it.
static const char *TAG = "MAIN";
// --- BOARD CONFIGURATION ---
// --- Hardware Configuration --- // --- Hardware Configuration ---
#if defined(CONFIG_IDF_TARGET_ESP32S3) #if defined(CONFIG_IDF_TARGET_ESP32S3)
// ESP32-S3 Specific Wiring // ESP32-S3 Specific Wiring
#define RGB_LED_GPIO 48 // Standard S3 DevKit RGB pin #define RGB_LED_GPIO 48
#define GPS_TX_PIN GPIO_NUM_5 #define GPS_TX_PIN GPIO_NUM_5
#define GPS_RX_PIN GPIO_NUM_4 #define GPS_RX_PIN GPIO_NUM_4
#define GPS_PPS_PIN GPIO_NUM_6 #define GPS_PPS_PIN GPIO_NUM_6
@ -53,23 +56,17 @@
#else #else
// Fallback / Other Chips (C6, etc.) // Fallback / Other Chips (C6, etc.)
#define RGB_LED_GPIO 8 #define RGB_LED_GPIO 8
#define GPS_TX_PIN GPIO_NUM_24 #define GPS_TX_PIN GPIO_NUM_1
#define GPS_RX_PIN GPIO_NUM_23 #define GPS_RX_PIN GPIO_NUM_3
#define GPS_PPS_PIN GPIO_NUM_25 #define GPS_PPS_PIN GPIO_NUM_5
#endif #endif
static const char *TAG = "MAIN";
// --- WiFi Operation Mode --- // --- WiFi Operation Mode ---
typedef enum { typedef enum {
WIFI_MODE_STA_CSI, // STA mode: Connected to AP, CSI + iperf (DEFAULT) WIFI_MODE_STA_CSI, // STA mode: Connected to AP, CSI + iperf (DEFAULT)
WIFI_MODE_MONITOR // Monitor mode: Promiscuous, collapse detection WIFI_MODE_MONITOR // Monitor mode: Promiscuous, collapse detection
} wifi_operation_mode_t; } 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_operation_mode_t current_wifi_mode = WIFI_MODE_STA_CSI;
static wifi_band_mode_t preferred_band = WIFI_BAND_MODE_AUTO; static wifi_band_mode_t preferred_band = WIFI_BAND_MODE_AUTO;
static uint8_t monitor_channel = 6; // Default monitor channel static uint8_t monitor_channel = 6; // Default monitor channel
@ -150,9 +147,8 @@ static void led_task(void *arg) {
// --- GPS Logging Helper --- // --- GPS Logging Helper ---
void log_collapse_event(float nav_duration_us, int rssi, int retry) { void log_collapse_event(float nav_duration_us, int rssi, int retry) {
gps_timestamp_t ts = gps_get_timestamp(); gps_timestamp_t ts = gps_get_timestamp();
// Format: COLLAPSE,MonoMS,GpsMS,Synced,Duration,RSSI,Retry // Format: COLLAPSE,MonoMS,GpsMS,Synced,Duration,RSSI,Retry
printf("COLLAPSE,%lld,%lld,%d,%.2f,%d,%d\n", printf("COLLAPSE,%" PRIi64 ",%" PRIi64 ",%d,%.2f,%d,%d\n",
ts.monotonic_ms, ts.monotonic_ms,
ts.gps_ms, ts.gps_ms,
ts.synced ? 1 : 0, ts.synced ? 1 : 0,
@ -172,6 +168,7 @@ static void csi_cb(void *ctx, wifi_csi_info_t *info) {
ESP_LOGI("CSI", "Captured %lu CSI packets", (unsigned long)s_csi_packet_count); ESP_LOGI("CSI", "Captured %lu CSI packets", (unsigned long)s_csi_packet_count);
} }
} }
static void wifi_enable_csi_once(void) { static void wifi_enable_csi_once(void) {
if (s_csi_enabled) return; if (s_csi_enabled) return;
@ -257,7 +254,7 @@ static void monitor_stats_task(void *arg) {
wifi_collapse_stats_t stats; wifi_collapse_stats_t stats;
if (wifi_monitor_get_stats(&stats) == ESP_OK) { if (wifi_monitor_get_stats(&stats) == ESP_OK) {
ESP_LOGI("MONITOR", "--- Stats: %lu frames, Retry Rate: %.2f%%, Avg NAV: %u us ---", ESP_LOGI("MONITOR", "--- Stats: %lu frames, Retry Rate: %.2f%%, Avg NAV: %u us ---",
stats.total_frames, stats.retry_rate, stats.avg_nav); (unsigned long)stats.total_frames, stats.retry_rate, stats.avg_nav);
if (wifi_monitor_is_collapsed()) { if (wifi_monitor_is_collapsed()) {
ESP_LOGW("MONITOR", "⚠️ ⚠️ ⚠️ WiFi COLLAPSE DETECTED! ⚠️ ⚠️ ⚠️ "); ESP_LOGW("MONITOR", "⚠️ ⚠️ ⚠️ WiFi COLLAPSE DETECTED! ⚠️ ⚠️ ⚠️ ");
@ -274,22 +271,18 @@ esp_err_t switch_to_monitor_mode(uint8_t channel, wifi_bandwidth_t bandwidth) {
return ESP_OK; 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) { if (bandwidth != WIFI_BW_HT20) {
ESP_LOGW(TAG, "Monitor mode typically restricted to 20MHz capture width"); ESP_LOGW(TAG, "Monitor mode typically restricted to 20MHz capture width");
ESP_LOGW(TAG, "Forcing bandwidth to 20MHz (driver limitation)"); ESP_LOGW(TAG, "Forcing bandwidth to 20MHz (driver limitation)");
bandwidth = WIFI_BW_HT20; bandwidth = WIFI_BW_HT20;
} }
// Detect band for informational logging
const char* band_str = "2.4GHz"; const char* band_str = "2.4GHz";
if (channel >= 36 && channel <= 165) { if (channel >= 36 && channel <= 165) {
band_str = "5GHz"; band_str = "5GHz";
} }
const char* bw_str = "20MHz"; const char* bw_str = "20MHz";
// Note: Monitor mode typically limited to 20MHz by ESP-IDF drivers
ESP_LOGI(TAG, "========================================"); ESP_LOGI(TAG, "========================================");
ESP_LOGI(TAG, "Switching to MONITOR MODE"); ESP_LOGI(TAG, "Switching to MONITOR MODE");
@ -297,46 +290,31 @@ esp_err_t switch_to_monitor_mode(uint8_t channel, wifi_bandwidth_t bandwidth) {
ESP_LOGI(TAG, " Bandwidth: %s (monitor mode limitation)", bw_str); ESP_LOGI(TAG, " Bandwidth: %s (monitor mode limitation)", bw_str);
ESP_LOGI(TAG, "========================================"); ESP_LOGI(TAG, "========================================");
// 1. Stop iperf if running
ESP_LOGI(TAG, "Stopping iperf..."); ESP_LOGI(TAG, "Stopping iperf...");
iperf_stop(); iperf_stop();
vTaskDelay(pdMS_TO_TICKS(500)); vTaskDelay(pdMS_TO_TICKS(500));
// 2. Disable CSI
wifi_disable_csi(); wifi_disable_csi();
vTaskDelay(pdMS_TO_TICKS(500)); vTaskDelay(pdMS_TO_TICKS(500));
// 3. Disconnect from AP
ESP_LOGI(TAG, "Disconnecting from AP..."); ESP_LOGI(TAG, "Disconnecting from AP...");
esp_wifi_disconnect(); esp_wifi_disconnect();
vTaskDelay(pdMS_TO_TICKS(1000)); vTaskDelay(pdMS_TO_TICKS(1000));
// 4. Stop WiFi
ESP_LOGI(TAG, "Stopping WiFi..."); ESP_LOGI(TAG, "Stopping WiFi...");
esp_wifi_stop(); esp_wifi_stop();
vTaskDelay(pdMS_TO_TICKS(500)); vTaskDelay(pdMS_TO_TICKS(500));
// 5. Set to NULL mode
ESP_LOGI(TAG, "Setting WiFi mode to NULL..."); ESP_LOGI(TAG, "Setting WiFi mode to NULL...");
esp_wifi_set_mode(WIFI_MODE_NULL); esp_wifi_set_mode(WIFI_MODE_NULL);
vTaskDelay(pdMS_TO_TICKS(500)); 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..."); ESP_LOGI(TAG, "Starting monitor mode...");
if (wifi_monitor_init(channel, monitor_frame_callback) != ESP_OK) { if (wifi_monitor_init(channel, monitor_frame_callback) != ESP_OK) {
ESP_LOGE(TAG, "Failed to init monitor mode"); ESP_LOGE(TAG, "Failed to init monitor mode");
return ESP_FAIL; return ESP_FAIL;
} }
// Set bandwidth after init
esp_wifi_set_bandwidth(WIFI_IF_STA, bandwidth); esp_wifi_set_bandwidth(WIFI_IF_STA, bandwidth);
if (wifi_monitor_start() != ESP_OK) { if (wifi_monitor_start() != ESP_OK) {
@ -349,7 +327,6 @@ esp_err_t switch_to_monitor_mode(uint8_t channel, wifi_bandwidth_t bandwidth) {
current_led_state = LED_STATE_MONITORING; current_led_state = LED_STATE_MONITORING;
monitor_channel = channel; monitor_channel = channel;
// 8. Start stats task
if (s_monitor_stats_task_handle == NULL) { if (s_monitor_stats_task_handle == NULL) {
xTaskCreate(monitor_stats_task, "monitor_stats", 4096, NULL, 5, &s_monitor_stats_task_handle); xTaskCreate(monitor_stats_task, "monitor_stats", 4096, NULL, 5, &s_monitor_stats_task_handle);
} }
@ -384,13 +361,11 @@ esp_err_t switch_to_sta_mode(wifi_band_mode_t band_mode) {
preferred_band = band_mode; preferred_band = band_mode;
// 1. Stop monitor stats task
if (s_monitor_stats_task_handle != NULL) { if (s_monitor_stats_task_handle != NULL) {
vTaskDelete(s_monitor_stats_task_handle); vTaskDelete(s_monitor_stats_task_handle);
s_monitor_stats_task_handle = NULL; s_monitor_stats_task_handle = NULL;
} }
// 2. Stop monitor mode
if (s_monitor_enabled) { if (s_monitor_enabled) {
ESP_LOGI(TAG, "Stopping monitor mode..."); ESP_LOGI(TAG, "Stopping monitor mode...");
wifi_monitor_stop(); wifi_monitor_stop();
@ -398,27 +373,22 @@ esp_err_t switch_to_sta_mode(wifi_band_mode_t band_mode) {
vTaskDelay(pdMS_TO_TICKS(500)); vTaskDelay(pdMS_TO_TICKS(500));
} }
// 3. Set mode back to STA
ESP_LOGI(TAG, "Setting WiFi mode to STA..."); ESP_LOGI(TAG, "Setting WiFi mode to STA...");
esp_wifi_set_mode(WIFI_MODE_STA); esp_wifi_set_mode(WIFI_MODE_STA);
vTaskDelay(pdMS_TO_TICKS(500)); vTaskDelay(pdMS_TO_TICKS(500));
// 4. Configure band preference
wifi_config_t wifi_config; wifi_config_t wifi_config;
esp_wifi_get_config(WIFI_IF_STA, &wifi_config); esp_wifi_get_config(WIFI_IF_STA, &wifi_config);
if (band_mode == WIFI_BAND_MODE_2G_ONLY) { if (band_mode == WIFI_BAND_MODE_2G_ONLY) {
wifi_config.sta.channel = 0; // Scan all channels, but prefer 2.4GHz wifi_config.sta.channel = 0;
wifi_config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN; 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"); ESP_LOGI(TAG, "Configured for 2.4GHz band");
} else if (band_mode == WIFI_BAND_MODE_5G_ONLY) { } else if (band_mode == WIFI_BAND_MODE_5G_ONLY) {
wifi_config.sta.channel = 0; // Scan all channels wifi_config.sta.channel = 0;
wifi_config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN; 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"); ESP_LOGI(TAG, "Configured for 5GHz band preference");
} else { } else {
// Auto mode - let ESP-IDF choose best band
wifi_config.sta.channel = 0; wifi_config.sta.channel = 0;
wifi_config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN; wifi_config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN;
ESP_LOGI(TAG, "Configured for auto band selection"); ESP_LOGI(TAG, "Configured for auto band selection");
@ -426,12 +396,10 @@ esp_err_t switch_to_sta_mode(wifi_band_mode_t band_mode) {
esp_wifi_set_config(WIFI_IF_STA, &wifi_config); esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
// 5. Start WiFi
ESP_LOGI(TAG, "Starting WiFi..."); ESP_LOGI(TAG, "Starting WiFi...");
esp_wifi_start(); esp_wifi_start();
vTaskDelay(pdMS_TO_TICKS(500)); vTaskDelay(pdMS_TO_TICKS(500));
// 6. Reconnect to AP
ESP_LOGI(TAG, "Connecting to AP..."); ESP_LOGI(TAG, "Connecting to AP...");
esp_wifi_connect(); esp_wifi_connect();
@ -445,135 +413,67 @@ esp_err_t switch_to_sta_mode(wifi_band_mode_t band_mode) {
ESP_LOGI(TAG, " - CSI and iperf will start after connection"); ESP_LOGI(TAG, " - CSI and iperf will start after connection");
ESP_LOGI(TAG, "========================================"); ESP_LOGI(TAG, "========================================");
// Note: CSI and iperf will be started by event handler when IP is obtained
return ESP_OK; return ESP_OK;
} }
// --- Console Commands ---------------------------------------------- // --- Console Commands ----------------------------------------------
static int cmd_mode_monitor(int argc, char **argv) { static int cmd_mode_monitor(int argc, char **argv) {
int channel = monitor_channel; // Use last channel or default int channel = monitor_channel;
wifi_bandwidth_t bandwidth = WIFI_BW_HT20; // Default to 20MHz wifi_bandwidth_t bandwidth = WIFI_BW_HT20;
if (argc > 1) { if (argc > 1) {
// Parse channel/bandwidth format: "6/20" or "36/40"
char *slash = strchr(argv[1], '/'); char *slash = strchr(argv[1], '/');
if (slash != NULL) { if (slash != NULL) {
*slash = '\0'; // Split string at '/' *slash = '\0';
channel = atoi(argv[1]); channel = atoi(argv[1]);
int bw = atoi(slash + 1); int bw = atoi(slash + 1);
// Convert bandwidth to enum - ESP32-C5 only supports 20/40MHz
switch(bw) { switch(bw) {
case 20: case 20: bandwidth = WIFI_BW_HT20; break;
bandwidth = WIFI_BW_HT20; case 40: bandwidth = WIFI_BW_HT40; break;
break; default: printf("Error: Invalid bandwidth %d\n", bw); return 1;
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 { } else {
channel = atoi(argv[1]); channel = atoi(argv[1]);
// Monitor mode: always default to 20MHz (driver limitation)
bandwidth = WIFI_BW_HT20; 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; bool valid = false;
const char* band = "Unknown"; if (channel >= 1 && channel <= 14) valid = true;
else if (channel >= 36 && channel <= 165) valid = true; // Simplified check
// 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) { if (!valid) {
printf("Error: Invalid channel %d\n", channel); 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; return 1;
} }
printf("Monitoring channel %d\n", channel);
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 (switch_to_monitor_mode(channel, bandwidth) != ESP_OK) {
if (err != ESP_OK) {
printf("Failed to switch to monitor mode\n"); printf("Failed to switch to monitor mode\n");
return 1; return 1;
} }
return 0; return 0;
} }
static int cmd_mode_sta(int argc, char **argv) { static int cmd_mode_sta(int argc, char **argv) {
wifi_band_mode_t band_mode = WIFI_BAND_MODE_AUTO; // Default to auto wifi_band_mode_t band_mode = WIFI_BAND_MODE_AUTO;
if (argc > 1) { if (argc > 1) {
if (strcmp(argv[1], "2.4") == 0 || strcmp(argv[1], "2") == 0) { if (strcmp(argv[1], "2.4") == 0) band_mode = WIFI_BAND_MODE_2G_ONLY;
band_mode = WIFI_BAND_MODE_2G_ONLY; else if (strcmp(argv[1], "5") == 0) band_mode = WIFI_BAND_MODE_5G_ONLY;
printf("Forcing 2.4GHz band\n"); else if (strcmp(argv[1], "auto") == 0) band_mode = WIFI_BAND_MODE_AUTO;
} else if (strcmp(argv[1], "5") == 0 || strcmp(argv[1], "5.0") == 0) { else {
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("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; return 1;
} }
} }
esp_err_t err = switch_to_sta_mode(band_mode); if (switch_to_sta_mode(band_mode) != ESP_OK) {
if (err != ESP_OK) {
printf("Failed to switch to STA mode\n"); printf("Failed to switch to STA mode\n");
return 1; return 1;
} }
printf("Switching to STA mode...\n");
printf("Switching to STA mode (reconnecting to AP...)\n");
return 0; return 0;
} }
@ -581,140 +481,62 @@ static int cmd_mode_status(int argc, char **argv) {
printf("\n=== WiFi Mode Status ===\n"); printf("\n=== WiFi Mode Status ===\n");
printf("Current mode: %s\n", printf("Current mode: %s\n",
current_wifi_mode == WIFI_MODE_STA_CSI ? "STA (CSI + iperf)" : "MONITOR"); current_wifi_mode == WIFI_MODE_STA_CSI ? "STA (CSI + iperf)" : "MONITOR");
printf("LED state: "); printf("LED state: %d\n", current_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) { if (current_wifi_mode == WIFI_MODE_STA_CSI) {
printf("WiFi connected: %s\n", wifi_connected ? "Yes" : "No"); 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"); 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 { } else {
const char* band = (monitor_channel >= 36) ? "5GHz" : "2.4GHz"; printf("Monitor channel: %d\n", monitor_channel);
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("Frames captured: %lu\n", (unsigned long)s_monitor_frame_count);
} }
printf("GPS synced: %s\n", gps_is_synced() ? "Yes (+)" : "No (*)"); printf("GPS synced: %s\n", gps_is_synced() ? "Yes (+)" : "No (*)");
printf("\n"); printf("\n");
return 0; return 0;
} }
static int cmd_csi_dump(int argc, char **argv) { static int cmd_csi_dump(int argc, char **argv) {
if (current_wifi_mode != WIFI_MODE_STA_CSI) { if (current_wifi_mode != WIFI_MODE_STA_CSI) {
printf("Error: CSI only available in STA mode\n"); printf("Error: CSI only available in STA mode\n");
printf("Use 'mode_sta' to switch to STA mode first\n");
return 1; 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"); printf("Dumping CSI data...\n");
csi_log_dump_over_uart(); csi_log_dump_over_uart();
printf("CSI dump complete\n");
return 0; return 0;
} }
static void register_mode_commands(void) { static void register_mode_commands(void) {
const esp_console_cmd_t mode_monitor = { const esp_console_cmd_t mode_monitor = {
.command = "mode_monitor", .command = "mode_monitor",
.help = "Switch to monitor mode (collapse detection)\n" .help = "Switch to monitor mode",
" 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, .func = &cmd_mode_monitor,
}; };
ESP_ERROR_CHECK(esp_console_cmd_register(&mode_monitor)); ESP_ERROR_CHECK(esp_console_cmd_register(&mode_monitor));
const esp_console_cmd_t mode_sta = { const esp_console_cmd_t mode_sta = {
.command = "mode_sta", .command = "mode_sta",
.help = "Switch to STA mode (CSI + iperf)\n" .help = "Switch to STA mode",
" 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, .func = &cmd_mode_sta,
}; };
ESP_ERROR_CHECK(esp_console_cmd_register(&mode_sta)); ESP_ERROR_CHECK(esp_console_cmd_register(&mode_sta));
const esp_console_cmd_t mode_status = { const esp_console_cmd_t mode_status = {
.command = "mode_status", .command = "mode_status",
.help = "Show current WiFi mode and status", .help = "Show status",
.func = &cmd_mode_status, .func = &cmd_mode_status,
}; };
ESP_ERROR_CHECK(esp_console_cmd_register(&mode_status)); ESP_ERROR_CHECK(esp_console_cmd_register(&mode_status));
const esp_console_cmd_t csi_dump = { const esp_console_cmd_t csi_dump = {
.command = "csi_dump", .command = "csi_dump",
.help = "Dump CSI data to UART (STA mode only)", .help = "Dump CSI data",
.func = &cmd_csi_dump, .func = &cmd_csi_dump,
}; };
ESP_ERROR_CHECK(esp_console_cmd_register(&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) ------------------------------ // --- Event Handler -------------------------------------------------
static void event_handler(void* arg, esp_event_base_t event_base, static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data) { int32_t event_id, void* event_data) {
if (event_base == WIFI_EVENT) { if (event_base == WIFI_EVENT) {
@ -733,102 +555,62 @@ static void event_handler(void* arg, esp_event_base_t event_base,
} }
} }
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
if (current_wifi_mode != WIFI_MODE_STA_CSI) { if (current_wifi_mode != WIFI_MODE_STA_CSI) return;
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; 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, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip));
ESP_LOGI(TAG, "========================================");
wifi_connected = true; wifi_connected = true;
current_led_state = LED_STATE_CONNECTED; 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..."); ESP_LOGI(TAG, "Enabling CSI...");
wifi_enable_csi_once(); wifi_enable_csi_once();
// 2. Start iperf server
vTaskDelay(pdMS_TO_TICKS(1000)); vTaskDelay(pdMS_TO_TICKS(1000));
iperf_cfg_t cfg; iperf_cfg_t cfg;
memset(&cfg, 0, sizeof(cfg)); memset(&cfg, 0, sizeof(cfg));
cfg.flag = IPERF_FLAG_SERVER | IPERF_FLAG_TCP; cfg.flag = IPERF_FLAG_SERVER | IPERF_FLAG_TCP;
cfg.sport = 5001; cfg.sport = 5001;
iperf_start(&cfg); iperf_start(&cfg);
ESP_LOGI(TAG, "iperf TCP server started on port 5001"); ESP_LOGI(TAG, "iperf server started");
// 3. Optional: Schedule CSI dump for later
xTaskCreate(csi_dump_task, "csi_dump_task", 4096, NULL, 5, NULL); 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 ---------------------------------------- // --- Main ----------------------------------------------------------
void app_main(void) { void app_main(void) {
// 1. Initialize Non-Volatile Storage (needed for WiFi config)
ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(nvs_flash_init());
// 2. Initialize Netif (TCP/IP stack)
ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default()); ESP_ERROR_CHECK(esp_event_loop_create_default());
// 3. Initialize Custom Logging & LED // Init Logging & LED
ESP_ERROR_CHECK(csi_log_init()); ESP_ERROR_CHECK(csi_log_init());
rgb_led_init(); rgb_led_init();
xTaskCreate(led_task, "led_task", 4096, NULL, 5, NULL); xTaskCreate(led_task, "led_task", 4096, NULL, 5, NULL);
// 4. Initialize GPS (Enable GPS-timestamped logs) // Init GPS
ESP_LOGI(TAG, "========================================");
ESP_LOGI(TAG, "Initializing GPS sync..."); ESP_LOGI(TAG, "Initializing GPS sync...");
// CONFIGURATION STRUCT: Maps the correct pins based on the chip type
const gps_sync_config_t gps_cfg = { const gps_sync_config_t gps_cfg = {
.uart_port = UART_NUM_1, .uart_port = UART_NUM_1,
.tx_pin = GPS_TX_PIN, .tx_pin = GPS_TX_PIN,
.rx_pin = GPS_RX_PIN, .rx_pin = GPS_RX_PIN,
.pps_pin = GPS_PPS_PIN, .pps_pin = GPS_PPS_PIN,
}; };
// Pass the config and enable GPS timestamps in logs
gps_sync_init(&gps_cfg, true); gps_sync_init(&gps_cfg, true);
ESP_LOGI(TAG, "GPS initialized on UART1 (TX:%d, RX:%d, PPS:%d)", ESP_LOGI(TAG, "GPS init (TX:%d, RX:%d, PPS:%d)", GPS_TX_PIN, GPS_RX_PIN, GPS_PPS_PIN);
GPS_TX_PIN, GPS_RX_PIN, GPS_PPS_PIN);
ESP_LOGI(TAG, " - Waiting for GPS lock...");
ESP_LOGI(TAG, " - Timestamps: (*) = not synced, (+) = GPS synced");
ESP_LOGI(TAG, "========================================");
// 5. Register WiFi Events // Register Events
ESP_ERROR_CHECK(esp_event_handler_instance_register( ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, NULL));
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));
ESP_ERROR_CHECK(esp_event_handler_instance_register(
IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, NULL));
// 6. Initialize WiFi Configuration // Init WiFi Config (This starts the cmd_transport listener)
wifi_cfg_init(); wifi_cfg_init();
// 7. Initialize Serial Console (CRITICAL for console commands) // Init Console
ESP_LOGI(TAG, "Initializing console..."); ESP_LOGI(TAG, "Initializing console...");
/* Disable buffering on stdin */
setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdin, NULL, _IONBF, 0);
/* Initialize the console */
esp_console_config_t console_config = { esp_console_config_t console_config = {
.max_cmdline_args = 8, .max_cmdline_args = 8,
.max_cmdline_length = 256, .max_cmdline_length = 256,
@ -837,90 +619,51 @@ void app_main(void) {
#endif #endif
}; };
ESP_ERROR_CHECK(esp_console_init(&console_config)); ESP_ERROR_CHECK(esp_console_init(&console_config));
/* Configure linenoise line completion library */
linenoiseSetMultiLine(1); linenoiseSetMultiLine(1);
linenoiseSetCompletionCallback(NULL); linenoiseSetCompletionCallback(NULL);
linenoiseSetHintsCallback(NULL); linenoiseSetHintsCallback(NULL);
linenoiseHistorySetMaxLen(100); linenoiseHistorySetMaxLen(100);
/* Register help command */
esp_console_register_help_command(); esp_console_register_help_command();
ESP_LOGI(TAG, "✓ Console initialized");
// 8. Register Console Commands for Mode Switching
register_mode_commands(); register_mode_commands();
// 9. Apply WiFi config and connect // Apply Config
if (wifi_cfg_apply_from_nvs()) { if (wifi_cfg_apply_from_nvs()) {
has_config = true; has_config = true;
current_led_state = LED_STATE_WAITING; current_led_state = LED_STATE_WAITING;
ESP_LOGI(TAG, "========================================"); ESP_LOGI(TAG, "WiFi config loaded.");
ESP_LOGI(TAG, "WiFi config loaded. Connecting...");
// Check if device is configured for MONITOR mode
char mode[16] = {0}; char mode[16] = {0};
uint8_t mon_ch = 36; uint8_t mon_ch = 36;
if (wifi_cfg_get_mode(mode, &mon_ch)) { if (wifi_cfg_get_mode(mode, &mon_ch)) {
if (strcmp(mode, "MONITOR") == 0) { if (strcmp(mode, "MONITOR") == 0) {
ESP_LOGI(TAG, "MODE: MONITOR (collapse detection)"); ESP_LOGI(TAG, "MODE: MONITOR (Channel %d)", mon_ch);
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)); uint8_t *ch_param = malloc(sizeof(uint8_t));
*ch_param = mon_ch; *ch_param = mon_ch;
// Create task to switch to monitor mode after connection
xTaskCreate(auto_monitor_task, "auto_monitor", 4096, ch_param, 5, NULL); xTaskCreate(auto_monitor_task, "auto_monitor", 4096, ch_param, 5, NULL);
} else { } else {
ESP_LOGI(TAG, "MODE: STA (CSI + iperf)"); ESP_LOGI(TAG, "MODE: STA");
} }
} else {
ESP_LOGI(TAG, "DEFAULT MODE: STA (CSI + iperf)");
} }
ESP_LOGI(TAG, "========================================");
} else { } else {
has_config = false; has_config = false;
current_led_state = LED_STATE_NO_CONFIG; current_led_state = LED_STATE_NO_CONFIG;
ESP_LOGI(TAG, "========================================");
ESP_LOGI(TAG, "No WiFi config found."); 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) { static void auto_monitor_task(void *arg) {
uint8_t channel = *(uint8_t*)arg; uint8_t channel = *(uint8_t*)arg;
free(arg); // Free the allocated channel parameter free(arg);
// Wait for WiFi connection (LED will be green)
ESP_LOGI(TAG, "Waiting for WiFi connection before switching to monitor mode..."); ESP_LOGI(TAG, "Waiting for WiFi connection before switching to monitor mode...");
while (current_led_state != LED_STATE_CONNECTED) { while (current_led_state != LED_STATE_CONNECTED) {
vTaskDelay(pdMS_TO_TICKS(500)); vTaskDelay(pdMS_TO_TICKS(500));
} }
// Wait additional 2 seconds for GPS sync
ESP_LOGI(TAG, "WiFi connected, waiting for GPS sync..."); ESP_LOGI(TAG, "WiFi connected, waiting for GPS sync...");
vTaskDelay(pdMS_TO_TICKS(2000)); vTaskDelay(pdMS_TO_TICKS(2000));
ESP_LOGI(TAG, "Auto-switching to MONITOR mode on channel %d...", channel); ESP_LOGI(TAG, "Auto-switching to MONITOR mode on channel %d...", channel);
esp_err_t err = switch_to_monitor_mode(channel, WIFI_BW_HT20); 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); vTaskDelete(NULL);
} }

6
partitions_c5.csv Normal file
View File

@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, , 0x4000,
otadata, data, ota, , 0x2000,
phy_init, data, phy, , 0x1000,
factory, app, factory, , 2M,
storage, data, spiffs, , 5M,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x4000
3 otadata data ota 0x2000
4 phy_init data phy 0x1000
5 factory app factory 2M
6 storage data spiffs 5M

6
partitions_esp32.csv Normal file
View File

@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, , 0x4000,
otadata, data, ota, , 0x2000,
phy_init, data, phy, , 0x1000,
factory, app, factory, , 1536K,
storage, data, spiffs, , 0x250000,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x4000
3 otadata data ota 0x2000
4 phy_init data phy 0x1000
5 factory app factory 1536K
6 storage data spiffs 0x250000

6
partitions_s3.csv Normal file
View File

@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, , 0x4000,
otadata, data, ota, , 0x2000,
phy_init, data, phy, , 0x1000,
factory, app, factory, , 2M,
storage, data, spiffs, , 13M,
1 # Name Type SubType Offset Size Flags
2 nvs data nvs 0x4000
3 otadata data ota 0x2000
4 phy_init data phy 0x1000
5 factory app factory 2M
6 storage data spiffs 13M

34
sdkconfig.defaults.c5 Normal file
View File

@ -0,0 +1,34 @@
# ESP32-C5 - 8MB Flash
# --- Hardware & Partitions ---
CONFIG_IDF_TARGET="esp32c5"
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_c5.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions_c5.csv"
# --- Wi-Fi & CSI ---
CONFIG_ESP_WIFI_CSI_ENABLED=y
# Ensure we have enough RX buffers for promiscuous mode/CSI
CONFIG_ESP_WIFI_RX_BA_WIN=32
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=64
# --- System Stability ---
# Optimize for size to leave more room for CSV logs
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
# Increase stack sizes to prevent overflows during heavy CSI traffic
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=6144
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
CONFIG_FREERTOS_ISR_STACKSIZE=2048
# 1000Hz (1ms) is standard high-performance. 10kHz is usually overkill.
CONFIG_FREERTOS_HZ=1000
# --- Console ---
# Increase console buffer for pasting large config blocks
CONFIG_CONSOLE_UART_RX_BUF_SIZE=1024
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_c5.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions_c5.csv"

24
sdkconfig.defaults.esp32 Normal file
View File

@ -0,0 +1,24 @@
# ESP32 - 4MB Flash
# --- Hardware & Partitions ---
CONFIG_IDF_TARGET="esp32"
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_esp32.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions_esp32.csv"
# --- Wi-Fi & CSI ---
CONFIG_ESP_WIFI_CSI_ENABLED=y
CONFIG_ESP_WIFI_RX_BA_WIN=32
# FIX: Increase static buffers to support BA_WIN=32 (Rule: BA_WIN <= 2 * STATIC)
CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=16
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=64
# --- System Stability ---
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=6144
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
CONFIG_FREERTOS_ISR_STACKSIZE=2048
CONFIG_FREERTOS_HZ=1000
# --- Console ---
CONFIG_CONSOLE_UART_RX_BUF_SIZE=1024

24
sdkconfig.defaults.s3 Normal file
View File

@ -0,0 +1,24 @@
# ESP32-S3 - 16MB Flash
# --- Hardware & Partitions ---
CONFIG_IDF_TARGET="esp32s3"
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_s3.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions_s3.csv"
# --- Wi-Fi & CSI ---
CONFIG_ESP_WIFI_CSI_ENABLED=y
CONFIG_ESP_WIFI_RX_BA_WIN=32
# FIX: Increase static buffers
CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=16
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=64
# --- System Stability ---
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=6144
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
CONFIG_FREERTOS_ISR_STACKSIZE=2048
CONFIG_FREERTOS_HZ=1000
# --- Console ---
CONFIG_CONSOLE_UART_RX_BUF_SIZE=1024