up to build for console work

This commit is contained in:
Robert McMahon 2025-12-16 11:56:31 -08:00
parent 954075ea6e
commit fa3e8509fb
12 changed files with 319 additions and 778 deletions

View File

@ -1,4 +1,3 @@
idf_component_register(SRCS "app_console.c"
INCLUDE_DIRS "."
REQUIRES console
PRIV_REQUIRES wifi_controller csi_manager status_led gps_sync esp_wifi iperf)
PRIV_REQUIRES console wifi_cfg iperf)

View File

@ -2,111 +2,161 @@
#include "esp_console.h"
#include "esp_log.h"
#include "argtable3/argtable3.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// Dependencies
#include "wifi_controller.h"
#include "status_led.h"
#include "gps_sync.h"
#include "wifi_cfg.h"
#include "iperf.h"
#include <string.h>
#ifdef CONFIG_ESP_WIFI_CSI_ENABLED
#include "csi_manager.h"
#endif
// --- Command Handlers ---
// ============================================================================
// COMMAND: iperf
// ============================================================================
static struct {
struct arg_lit *start;
struct arg_lit *stop;
struct arg_lit *status;
struct arg_int *pps;
struct arg_lit *help;
struct arg_end *end;
} iperf_args;
static int cmd_iperf(int argc, char **argv) {
if (argc < 2) {
printf("Usage: iperf <start|stop|pps|status>\n");
int nerrors = arg_parse(argc, argv, (void **)&iperf_args);
if (nerrors > 0) {
arg_print_errors(stderr, iperf_args.end, argv[0]);
return 1;
}
if (strcmp(argv[1], "start") == 0) {
iperf_cfg_t cfg = { .time = 0 }; // Infinite
iperf_start(&cfg);
// iperf_start already logs "IPERF_STARTED" via printf in iperf.c,
// but keeping it here is fine/redundant.
// To be safe and clean, we rely on iperf.c's output or just return success.
if (iperf_args.help->count > 0) {
printf("Usage: iperf [start|stop|status] [--pps <n>]\n");
return 0;
}
} else if (strcmp(argv[1], "stop") == 0) {
if (iperf_args.stop->count > 0) {
iperf_stop();
return 0;
}
} else if (strcmp(argv[1], "pps") == 0) {
// Syntax: iperf pps 100
if (argc < 3) {
printf("Error: Missing value. Usage: iperf pps <rate>\n");
return 1;
if (iperf_args.pps->count > 0) {
int val = iperf_args.pps->ival[0];
if (val > 0) {
iperf_set_pps((uint32_t)val);
} else {
printf("Error: PPS must be > 0\n");
}
int pps = atoi(argv[2]);
if (pps <= 0) {
printf("Error: Invalid PPS.\n");
return 1;
}
iperf_set_pps((uint32_t)pps);
// iperf_set_pps prints "IPERF_PPS_UPDATED: ..."
return 0;
}
} else if (strcmp(argv[1], "status") == 0) {
// [FIXED] Use the new API to print detailed stats
if (iperf_args.status->count > 0) {
iperf_print_status();
return 0;
}
printf("Error: Unknown subcommand '%s'.\n", argv[1]);
return 1;
if (iperf_args.start->count > 0) {
// Start using saved NVS config
iperf_cfg_t cfg = { .time = 0 };
iperf_start(&cfg);
return 0;
}
return 0;
}
static int cmd_mode_monitor(int argc, char **argv) {
int channel = wifi_ctl_get_monitor_channel();
if (argc > 1) channel = atoi(argv[1]);
if (wifi_ctl_switch_to_monitor(channel, WIFI_BW_HT20) != ESP_OK) {
printf("Failed to switch to monitor mode\n");
static void register_iperf_cmd(void) {
iperf_args.start = arg_lit0(NULL, "start", "Start iperf traffic");
iperf_args.stop = arg_lit0(NULL, "stop", "Stop iperf traffic");
iperf_args.status = arg_lit0(NULL, "status", "Show current statistics");
iperf_args.pps = arg_int0(NULL, "pps", "<n>", "Set packets per second");
iperf_args.help = arg_lit0(NULL, "help", "Show help");
iperf_args.end = arg_end(20);
const esp_console_cmd_t cmd = {
.command = "iperf",
.help = "Control iperf traffic generator",
.hint = NULL,
.func = &cmd_iperf,
.argtable = &iperf_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
// ============================================================================
// COMMAND: wifi_config
// ============================================================================
static struct {
struct arg_str *ssid;
struct arg_str *pass;
struct arg_str *ip;
struct arg_lit *dhcp;
struct arg_lit *help;
struct arg_end *end;
} wifi_args;
static int cmd_wifi_config(int argc, char **argv) {
int nerrors = arg_parse(argc, argv, (void **)&wifi_args);
if (nerrors > 0) {
arg_print_errors(stderr, wifi_args.end, argv[0]);
return 1;
}
return 0;
}
static int cmd_mode_sta(int argc, char **argv) {
if (wifi_ctl_switch_to_sta(WIFI_BAND_MODE_AUTO) != ESP_OK) {
printf("Failed to switch to STA mode\n");
if (wifi_args.help->count > 0) {
printf("Usage: wifi_config -s <ssid> -p <pass> [-i <ip>] [-d]\n");
return 0;
}
if (wifi_args.ssid->count == 0) {
printf("Error: SSID is required (-s)\n");
return 1;
}
const char* ssid = wifi_args.ssid->sval[0];
const char* pass = (wifi_args.pass->count > 0) ? wifi_args.pass->sval[0] : "";
const char* ip = (wifi_args.ip->count > 0) ? wifi_args.ip->sval[0] : NULL;
bool dhcp = (wifi_args.dhcp->count > 0);
printf("Saving WiFi Config: SSID='%s' DHCP=%d\n", ssid, dhcp);
wifi_cfg_set_credentials(ssid, pass);
if (ip) {
char mask[] = "255.255.255.0";
char gw[32];
// FIXED: Use strlcpy instead of strncpy to prevent truncation warnings
strlcpy(gw, ip, sizeof(gw));
char *last_dot = strrchr(gw, '.');
if (last_dot) strcpy(last_dot, ".1");
wifi_cfg_set_static_ip(ip, mask, gw);
wifi_cfg_set_dhcp(false);
} else {
wifi_cfg_set_dhcp(dhcp);
}
printf("Config saved. Rebooting to apply...\n");
esp_restart();
return 0;
}
static int cmd_mode_status(int argc, char **argv) {
wifi_ctl_mode_t mode = wifi_ctl_get_mode();
printf("\n=== WiFi Mode Status ===\n");
printf("Current mode: %s\n", mode == WIFI_CTL_MODE_STA ? "STA" : "MONITOR");
printf("LED state: %d\n", status_led_get_state());
printf("GPS synced: %s\n", gps_is_synced() ? "Yes" : "No");
return 0;
}
static void register_wifi_cmd(void) {
wifi_args.ssid = arg_str0("s", "ssid", "<ssid>", "WiFi SSID");
wifi_args.pass = arg_str0("p", "password", "<pass>", "WiFi Password");
wifi_args.ip = arg_str0("i", "ip", "<ip>", "Static IP");
wifi_args.dhcp = arg_lit0("d", "dhcp", "Enable DHCP");
wifi_args.help = arg_lit0("h", "help", "Show help");
wifi_args.end = arg_end(20);
static int cmd_csi_dump(int argc, char **argv) {
#ifdef CONFIG_ESP_WIFI_CSI_ENABLED
csi_mgr_schedule_dump();
#else
printf("Error: CSI feature is disabled in this firmware build.\n");
#endif
return 0;
const esp_console_cmd_t cmd = {
.command = "wifi_config",
.help = "Configure WiFi credentials",
.hint = NULL,
.func = &cmd_wifi_config,
.argtable = &wifi_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
void app_console_register_commands(void) {
const esp_console_cmd_t cmds[] = {
{ .command = "mode_monitor", .help = "Switch to monitor mode", .func = &cmd_mode_monitor },
{ .command = "mode_sta", .help = "Switch to STA mode", .func = &cmd_mode_sta },
{ .command = "mode_status", .help = "Show device status", .func = &cmd_mode_status },
{ .command = "csi_dump", .help = "Dump collected CSI data", .func = &cmd_csi_dump },
{ .command = "iperf", .help = "Control iperf (start, stop, pps, status)", .func = &cmd_iperf },
};
for (int i = 0; i < sizeof(cmds)/sizeof(cmds[0]); i++) {
ESP_ERROR_CHECK(esp_console_cmd_register(&cmds[i]));
}
register_iperf_cmd();
register_wifi_cmd();
}

View File

@ -5,8 +5,7 @@ extern "C" {
#endif
/**
* @brief Register all application-specific console commands
* (mode_monitor, mode_sta, mode_status, csi_dump)
* @brief Register application-specific console commands
*/
void app_console_register_commands(void);

View File

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

View File

@ -1,138 +0,0 @@
#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;
}
}
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;
}
}
// --- UPDATED DISPATCH LOGIC ---
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)
// Note: The listener (wifi_cfg) is now responsible for sending "OK" if it handles the line.
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
if (!handled && strlen(line) > 0) {
int ret;
esp_err_t err = esp_console_run(line, &ret);
if (err == ESP_ERR_NOT_FOUND) {
// Robustness: Always reply, even for unknown commands
if (reply_func) reply_func("ERROR: Unknown Command\n", reply_ctx);
} else if (err == ESP_OK) {
if (ret == 0) {
// Command Success -> Send ACK so Python knows to proceed
if (reply_func) reply_func("OK\n", reply_ctx);
} else {
// Command logic failed (e.g., iperf returned 1)
char buf[64];
snprintf(buf, sizeof(buf), "ERROR: Command failed (ret=%d)\n", ret);
if (reply_func) reply_func(buf, reply_ctx);
}
} else {
if (reply_func) reply_func("ERROR: System execution failed\n", reply_ctx);
}
}
}
// --- 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];
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;
xTaskCreatePinnedToCore(uart_listener_task, "cmd_uart", 4096, NULL, 5, NULL, tskNO_AFFINITY);
#if SOC_USB_SERIAL_JTAG_SUPPORTED
xTaskCreatePinnedToCore(usb_listener_task, "cmd_usb", 4096, NULL, 5, NULL, tskNO_AFFINITY);
#endif
}

View File

@ -1,37 +0,0 @@
#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

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

View File

@ -1,8 +1,6 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdatomic.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
@ -11,149 +9,98 @@
#include "esp_netif.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_check.h"
#include "wifi_cfg.h"
#include "cmd_transport.h"
#ifdef CONFIG_ESP_WIFI_CSI_ENABLED
#include "csi_manager.h"
#endif
// Removed unused TAG
static const char *TAG = "wifi_cfg";
static esp_netif_t *sta_netif = NULL;
static bool cfg_dhcp = true;
// --- NVS Helper ---
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);
}
// --- NVS Save Functions ---
// 1. Save Network Settings (Namespace: "netcfg")
static void save_net_cfg(const char* ssid, const char* pass, const char* ip, const char* mask, const char* gw, bool dhcp, const char* band, const char* bw, const char* powersave, const char* mode, uint8_t mon_ch){
// --- Helper: NVS Write ---
static void nvs_write_str(const char *key, const char *val) {
nvs_handle_t h;
if (nvs_open("netcfg", NVS_READWRITE, &h) != ESP_OK) return;
if (ssid) nvs_set_str2(h, "ssid", ssid);
if (pass) nvs_set_str2(h, "pass", pass);
if (ip) nvs_set_str2(h, "ip", ip);
if (mask) nvs_set_str2(h, "mask", mask);
if (gw) nvs_set_str2(h, "gw", gw);
if (band) nvs_set_str2(h, "band", band);
if (bw) nvs_set_str2(h, "bw", bw);
if (powersave) nvs_set_str2(h, "powersave", powersave);
if (mode) nvs_set_str2(h, "mode", mode);
nvs_set_u8(h, "mon_ch", mon_ch);
nvs_set_u8(h, "dhcp", dhcp ? 1 : 0);
nvs_commit(h);
nvs_close(h);
cfg_dhcp = dhcp;
ESP_LOGI(TAG, "Net Config Saved: SSID=%s IP=%s DHCP=%d", ssid?ssid:"", ip?ip:"", dhcp);
if (nvs_open("netcfg", NVS_READWRITE, &h) == ESP_OK) {
if (val) nvs_set_str(h, key, val);
else nvs_erase_key(h, key);
nvs_commit(h);
nvs_close(h);
}
}
// 2. Save Iperf Settings (Namespace: "storage") -> Matches iperf.c keys
static void save_iperf_cfg(const char* dst_ip, const char* role, const char* proto, uint32_t period, uint32_t burst, uint32_t len, uint32_t port, bool enable){
static void nvs_write_u8(const char *key, uint8_t val) {
nvs_handle_t h;
if (nvs_open("storage", NVS_READWRITE, &h) != ESP_OK) return;
// Note: Keys must match what iperf.c reads (NVS_KEY_IPERF_DST_IP etc)
if (dst_ip && dst_ip[0]) nvs_set_str(h, "iperf_dst_ip", dst_ip);
if (role && role[0]) nvs_set_str(h, "iperf_role", role);
if (proto && proto[0]) nvs_set_str(h, "iperf_proto", proto);
nvs_set_u32(h, "iperf_period", period);
nvs_set_u32(h, "iperf_burst", burst);
nvs_set_u32(h, "iperf_len", len);
nvs_set_u32(h, "iperf_port", port);
nvs_set_u8(h, "iperf_enabled", enable ? 1 : 0);
nvs_commit(h);
nvs_close(h);
ESP_LOGI(TAG, "Iperf Config Saved: Target=%s Role=%s Period=%lu", dst_ip?dst_ip:"", role?role:"", (unsigned long)period);
if (nvs_open("netcfg", NVS_READWRITE, &h) == ESP_OK) {
nvs_set_u8(h, key, val);
nvs_commit(h);
nvs_close(h);
}
}
// --- Public Setters ---
void wifi_cfg_set_credentials(const char* ssid, const char* pass) {
nvs_write_str("ssid", ssid);
nvs_write_str("pass", pass);
}
void wifi_cfg_set_static_ip(const char* ip, const char* mask, const char* gw) {
nvs_write_str("ip", ip);
nvs_write_str("mask", mask);
nvs_write_str("gw", gw);
}
void wifi_cfg_set_dhcp(bool enable) {
nvs_write_u8("dhcp", enable ? 1 : 0);
}
// --- Init & Load ---
void wifi_cfg_init(void) {
nvs_flash_init();
}
// --- Load Logic (Network Only - Iperf loads itself) ---
static bool load_cfg(char* ssid, size_t ssz, char* pass, size_t psz,
char* ip, size_t isz, char* mask, size_t msz, char* gw, size_t gsz,
char* band, size_t bsz, char* bw, size_t bwsz, char* powersave, size_t pssz,
char* mode, size_t modesz, uint8_t* mon_ch, bool* dhcp){
nvs_handle_t h;
if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) return false;
size_t len;
esp_err_t e;
if ((e = nvs_get_str(h, "ssid", NULL, &len)) != ESP_OK){ nvs_close(h); return false; }
if (len >= ssz){ nvs_close(h); return false; }
nvs_get_str(h, "ssid", ssid, &len);
len = psz; e = nvs_get_str(h, "pass", pass, &len); if (e!=ESP_OK) pass[0]=0;
len = isz; e = nvs_get_str(h, "ip", ip, &len); if (e!=ESP_OK) ip[0]=0;
len = msz; e = nvs_get_str(h, "mask", mask, &len); if (e!=ESP_OK) mask[0]=0;
len = gsz; e = nvs_get_str(h, "gw", gw, &len); if (e!=ESP_OK) gw[0]=0;
len = bsz; e = nvs_get_str(h, "band", band, &len); if (e!=ESP_OK) strcpy(band, "2.4G");
len = bwsz; e = nvs_get_str(h, "bw", bw, &len); if (e!=ESP_OK) strcpy(bw, "HT20");
len = pssz; e = nvs_get_str(h, "powersave", powersave, &len); if (e!=ESP_OK) strcpy(powersave, "NONE");
len = modesz; e = nvs_get_str(h, "mode", mode, &len); if (e!=ESP_OK) strcpy(mode, "STA");
// Load SSID (Mandatory)
len = ssz;
if (nvs_get_str(h, "ssid", ssid, &len) != ESP_OK) { nvs_close(h); return false; }
// Load Optionals
len = psz; if (nvs_get_str(h, "pass", pass, &len) != ESP_OK) pass[0]=0;
len = isz; if (nvs_get_str(h, "ip", ip, &len) != ESP_OK) ip[0]=0;
len = msz; if (nvs_get_str(h, "mask", mask, &len) != ESP_OK) mask[0]=0;
len = gsz; if (nvs_get_str(h, "gw", gw, &len) != ESP_OK) gw[0]=0;
// Defaults
len = bsz; if (nvs_get_str(h, "band", band, &len) != ESP_OK) strcpy(band, "2.4G");
len = bwsz; if (nvs_get_str(h, "bw", bw, &len) != ESP_OK) strcpy(bw, "HT20");
len = pssz; if (nvs_get_str(h, "powersave", powersave, &len) != ESP_OK) strcpy(powersave, "NONE");
len = modesz; if (nvs_get_str(h, "mode", mode, &len) != ESP_OK) strcpy(mode, "STA");
uint8_t ch=36; nvs_get_u8(h, "mon_ch", &ch); *mon_ch = ch;
uint8_t d=1; nvs_get_u8(h, "dhcp", &d); *dhcp = (d!=0);
nvs_close(h);
return true;
}
void wifi_cfg_force_dhcp(bool enable){ cfg_dhcp = enable; }
bool wifi_cfg_get_mode(char *mode, uint8_t *mon_ch) {
if (!mode || !mon_ch) return false;
nvs_handle_t h;
if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) {
strcpy(mode, "STA"); *mon_ch = 36; return false;
}
size_t len = 16;
if (nvs_get_str(h, "mode", mode, &len) != ESP_OK) strcpy(mode, "STA");
uint8_t ch = 36;
nvs_get_u8(h, "mon_ch", &ch);
*mon_ch = ch;
nvs_close(h);
return true;
}
static atomic_bool s_net_stack_ready = false;
static esp_err_t ensure_net_stack_once(void) {
bool expected = false;
if (atomic_compare_exchange_strong(&s_net_stack_ready, &expected, true)) {
ESP_RETURN_ON_ERROR(esp_netif_init(), TAG, "esp_netif_init");
esp_err_t err = esp_event_loop_create_default();
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
ESP_RETURN_ON_ERROR(err, TAG, "event loop create");
}
}
return ESP_OK;
}
static atomic_bool s_wifi_inited = false;
esp_err_t wifi_ensure_inited(void) {
bool expected = false;
if (!atomic_compare_exchange_strong(&s_wifi_inited, &expected, true)) return ESP_OK;
ESP_RETURN_ON_ERROR(ensure_net_stack_once(), TAG, "net stack");
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_err_t err = esp_wifi_init(&cfg);
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
atomic_store(&s_wifi_inited, false);
ESP_RETURN_ON_ERROR(err, TAG, "esp_wifi_init");
}
return ESP_OK;
}
static void apply_ip_static(const char* ip, const char* mask, const char* gw){
if (!sta_netif) return;
if (!ip || !ip[0] || !mask || !mask[0] || !gw || !gw[0]) return;
if (!ip || !ip[0]) return;
esp_netif_ip_info_t info = {0};
esp_netif_dhcpc_stop(sta_netif);
info.ip.addr = esp_ip4addr_aton(ip);
info.netmask.addr = esp_ip4addr_aton(mask);
info.gw.addr = esp_ip4addr_aton(gw);
ESP_ERROR_CHECK( esp_netif_set_ip_info(sta_netif, &info) );
info.netmask.addr = (mask && mask[0]) ? esp_ip4addr_aton(mask) : esp_ip4addr_aton("255.255.255.0");
info.gw.addr = (gw && gw[0]) ? esp_ip4addr_aton(gw) : 0;
esp_netif_set_ip_info(sta_netif, &info);
}
bool wifi_cfg_apply_from_nvs(void) {
@ -165,16 +112,11 @@ bool wifi_cfg_apply_from_nvs(void) {
band,sizeof(band), bw,sizeof(bw), powersave,sizeof(powersave), mode,sizeof(mode), &mon_ch, &dhcp)){
return false;
}
if (ssid[0] == '\0') return false;
static bool inited = false;
if (!inited){
nvs_flash_init();
ensure_net_stack_once();
if (sta_netif == NULL) sta_netif = esp_netif_create_default_wifi_sta();
wifi_ensure_inited();
inited = true;
}
if (sta_netif == NULL) sta_netif = esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&cfg);
wifi_config_t wcfg = {0};
strlcpy((char*)wcfg.sta.ssid, ssid, sizeof(wcfg.sta.ssid));
@ -183,200 +125,32 @@ bool wifi_cfg_apply_from_nvs(void) {
wcfg.sta.sae_pwe_h2e = WPA3_SAE_PWE_BOTH;
wcfg.sta.scan_method = WIFI_ALL_CHANNEL_SCAN;
if (strcmp(band, "5G") == 0) wcfg.sta.channel = 0;
else wcfg.sta.channel = 0;
esp_wifi_set_mode(WIFI_MODE_STA);
#if CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C6
wifi_protocols_t protocols = {
.ghz_2g = WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N,
.ghz_5g = WIFI_PROTOCOL_11A | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_11AC | WIFI_PROTOCOL_11AX,
};
esp_wifi_set_protocols(WIFI_IF_STA, &protocols);
#else
esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N);
#endif
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 CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C6
wifi_bandwidths_t bandwidths = {.ghz_2g = WIFI_BW_HT20, .ghz_5g = WIFI_BW_HT20};
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);
#else
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
else esp_netif_dhcpc_start(sta_netif);
esp_wifi_start();
wifi_ps_type_t ps_mode = WIFI_PS_NONE;
if (strcmp(powersave, "MIN") == 0) ps_mode = WIFI_PS_MIN_MODEM;
else if (strcmp(powersave, "MAX") == 0) ps_mode = WIFI_PS_MAX_MODEM;
esp_wifi_set_ps(ps_mode);
esp_wifi_connect();
return true;
}
// --- Parsing State ---
typedef struct {
// Network
char ssid[64];
char pass[64];
char ip[32];
char mask[32];
char gw[32];
char band[16];
char bw[16];
char powersave[16];
char mode[16];
uint8_t mon_ch;
bool dhcp;
bool csi_enable;
// Iperf
char iperf_dest[32];
char iperf_role[16];
char iperf_proto[8];
uint32_t iperf_period_us;
uint32_t iperf_burst;
uint32_t iperf_len;
uint32_t iperf_port;
bool iperf_enable;
} cfg_state_t;
static void on_cfg_line(const char *line, cfg_state_t *s){
// Network Parsing
if (strncmp(line, "SSID:",5)==0){ strncpy(s->ssid, line+5, 63); s->ssid[63]=0; return; }
if (strncmp(line, "PASS:",5)==0){ strncpy(s->pass, line+5, 63); s->pass[63]=0; return; }
if (strncmp(line, "IP:",3)==0){ strncpy(s->ip, line+3, 31); s->ip[31]=0; return; }
if (strncmp(line, "MASK:",5)==0){ strncpy(s->mask, line+5, 31); s->mask[31]=0; return; }
if (strncmp(line, "GW:",3)==0){ strncpy(s->gw, line+3, 31); s->gw[31]=0; return; }
if (strncmp(line, "BAND:",5)==0){ strncpy(s->band, line+5, 15); s->band[15]=0; return; }
if (strncmp(line, "BW:",3)==0){ strncpy(s->bw, line+3, 15); s->bw[15]=0; return; }
if (strncmp(line, "POWERSAVE:",10)==0){ strncpy(s->powersave, line+10, 15); s->powersave[15]=0; return; }
if (strncmp(line, "MODE:",5)==0){ strncpy(s->mode, line+5, 15); s->mode[15]=0; return; }
if (strncmp(line, "MON_CH:",7)==0){ s->mon_ch = atoi(line+7); return; }
if (strncmp(line, "DHCP:",5)==0){ s->dhcp = atoi(line+5) ? true:false; return; }
// Iperf Parsing (Matches Python Script Keys)
// Support both DEST and DST to be safe
if (strncmp(line, "IPERF_DEST_IP:", 14) == 0) { strncpy(s->iperf_dest, line+14, 31); s->iperf_dest[31]=0; return; }
if (strncmp(line, "IPERF_DST_IP:", 13) == 0) { strncpy(s->iperf_dest, line+13, 31); s->iperf_dest[31]=0; return; }
if (strncmp(line, "IPERF_ROLE:", 11) == 0) { strncpy(s->iperf_role, line+11, 15); s->iperf_role[15]=0; return; }
if (strncmp(line, "IPERF_PROTO:", 12) == 0) { strncpy(s->iperf_proto, line+12, 7); s->iperf_proto[7]=0; return; }
if (strncmp(line, "IPERF_PERIOD_US:", 16) == 0) { s->iperf_period_us = atoi(line+16); return; }
if (strncmp(line, "IPERF_BURST:", 12) == 0) { s->iperf_burst = atoi(line+12); return; }
if (strncmp(line, "IPERF_LEN:", 10) == 0) { s->iperf_len = atoi(line+10); return; }
if (strncmp(line, "IPERF_PORT:", 11) == 0) { s->iperf_port = atoi(line+11); return; }
if (strncmp(line, "IPERF_ENABLED:", 14) == 0) { s->iperf_enable = atoi(line+14) ? true:false; return; }
#ifdef CONFIG_ESP_WIFI_CSI_ENABLED
if (strncmp(line, "CSI:",4)==0){ s->csi_enable = atoi(line+4) ? true:false; return; }
#endif
}
static bool wifi_cfg_cmd_handler(const char *line, cmd_reply_func_t reply_func, void *reply_ctx) {
static bool in_cfg = false;
static cfg_state_t s;
if (!in_cfg) {
if (strcmp(line, "CFG") == 0) {
in_cfg = true;
// Clear all buffers
memset(&s, 0, sizeof(s));
// Set Defaults
s.mon_ch = 36;
s.dhcp = true;
s.csi_enable = false;
s.iperf_period_us = 10000;
s.iperf_burst = 1;
s.iperf_len = 1470;
s.iperf_port = 5001;
s.iperf_enable = true;
strcpy(s.iperf_dest, "192.168.1.50");
strcpy(s.iperf_role, "CLIENT");
strcpy(s.iperf_proto, "UDP");
// --- NEW: ACK Start of CFG ---
if (reply_func) reply_func("OK\n", reply_ctx);
return true;
}
return false;
}
if (strcmp(line, "END") == 0) {
// Apply Network Defaults if missing
if (!s.band[0]) strcpy(s.band, "2.4G");
if (!s.bw[0]) strcpy(s.bw, "HT20");
if (!s.powersave[0]) strcpy(s.powersave, "NONE");
if (!s.mode[0]) strcpy(s.mode, "STA");
// 1. Save Network
save_net_cfg(s.ssid, s.pass, s.ip, s.mask, s.gw, s.dhcp, s.band, s.bw, s.powersave, s.mode, s.mon_ch);
// 2. Save Iperf
save_iperf_cfg(s.iperf_dest, s.iperf_role, s.iperf_proto, s.iperf_period_us, s.iperf_burst, s.iperf_len, s.iperf_port, s.iperf_enable);
// 3. Save CSI
#ifdef CONFIG_ESP_WIFI_CSI_ENABLED
csi_mgr_save_enable_state(s.csi_enable);
#endif
// --- NEW: ACK End of CFG ---
if (reply_func) reply_func("OK Config Saved\n", reply_ctx);
// Apply changes immediately
wifi_cfg_apply_from_nvs();
in_cfg = false;
return true;
}
on_cfg_line(line, &s);
// --- NEW: ACK Intermediate Line ---
if (reply_func) reply_func("OK\n", reply_ctx);
return true;
}
void wifi_cfg_init(void){
nvs_flash_init();
cmd_transport_init();
cmd_transport_register_listener(wifi_cfg_cmd_handler);
}
wifi_ps_type_t wifi_cfg_get_power_save_mode(void) {
char powersave[16] = {0};
nvs_handle_t h;
if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) return WIFI_PS_NONE;
size_t len = sizeof(powersave);
nvs_get_str(h, "powersave", powersave, &len);
nvs_close(h);
if (strcmp(powersave, "MIN") == 0) return WIFI_PS_MIN_MODEM;
if (strcmp(powersave, "MAX") == 0) return WIFI_PS_MAX_MODEM;
return WIFI_PS_NONE;
}
bool wifi_cfg_get_bandwidth(char *buf, size_t buf_size) {
if (!buf || buf_size < 1) return false;
nvs_handle_t h;
if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) {
strncpy(buf, "Unknown", buf_size); return false;
}
size_t len = buf_size;
esp_err_t err = nvs_get_str(h, "bw", buf, &len);
nvs_close(h);
return (err == ESP_OK);
if (buf) strncpy(buf, "HT20", buf_size);
return true;
}
bool wifi_cfg_get_mode(char *mode, uint8_t *mon_ch) {
nvs_handle_t h;
if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) return false;
size_t len = 16;
if (nvs_get_str(h, "mode", mode, &len) != ESP_OK) strcpy(mode, "STA");
nvs_get_u8(h, "mon_ch", mon_ch);
nvs_close(h);
return true;
}

View File

@ -8,59 +8,19 @@
extern "C" {
#endif
/**
* @brief Initialize the WiFi configuration system
*
* Spawns listener tasks for both UART and USB-Serial/JTAG interfaces
* to receive configuration commands.
*/
// --- Initialization ---
void wifi_cfg_init(void);
/**
* @brief Apply WiFi configuration from NVS
*
* @return true if configuration was found and applied, false otherwise
*/
// --- Getters (Used by Controller) ---
bool wifi_cfg_apply_from_nvs(void);
/**
* @brief Force DHCP mode enable/disable
*
* @param enable true to enable DHCP, false to disable
*/
void wifi_cfg_force_dhcp(bool enable);
/**
* @brief Get the configured power save mode from NVS
*
* @return wifi_ps_type_t Power save mode (WIFI_PS_NONE, WIFI_PS_MIN_MODEM, or WIFI_PS_MAX_MODEM)
*/
wifi_ps_type_t wifi_cfg_get_power_save_mode(void);
/**
* @brief Get the configured bandwidth from NVS
*
* @param buf Buffer to store bandwidth string (e.g., "HT20", "HT40", "VHT80")
* @param buf_size Size of buffer
* @return true if bandwidth was retrieved, false otherwise
*/
bool wifi_cfg_get_bandwidth(char *buf, size_t buf_size);
/**
* @brief Get operating mode and monitor channel from NVS
*
* @param mode Output buffer for mode string (min 16 bytes)
* @param mon_ch Output pointer for monitor channel
* @return true if mode retrieved, false if no config in NVS
*/
bool wifi_cfg_get_mode(char *mode, uint8_t *mon_ch);
/**
* @brief Ensure WiFi driver is initialized (thread-safe, idempotent)
*
* @return ESP_OK on success, error code otherwise
*/
esp_err_t wifi_ensure_inited(void);
// --- Setters (Used by Console) ---
void wifi_cfg_set_credentials(const char* ssid, const char* pass);
void wifi_cfg_set_static_ip(const char* ip, const char* mask, const char* gw);
void wifi_cfg_set_dhcp(bool enable);
#ifdef __cplusplus
}

View File

@ -1,23 +1,3 @@
idf_component_register(
SRCS "main.c"
INCLUDE_DIRS "."
PRIV_REQUIRES
nvs_flash
esp_wifi
esp_netif
esp_event
lwip
console
driver # <--- Added (For GPIO definitions)
cmd_transport # <--- Added (For cmd_transport.h)
iperf
wifi_cfg
csi_log
wifi_monitor
gps_sync
led_strip
status_led
csi_manager
wifi_controller
app_console
)
idf_component_register(SRCS "main.c"
INCLUDE_DIRS "."
PRIV_REQUIRES nvs_flash esp_netif wifi_controller wifi_cfg app_console iperf status_led gps_sync console)

View File

@ -1,37 +1,45 @@
#pragma once
#include "driver/gpio.h"
#ifndef BOARD_CONFIG_H
#define BOARD_CONFIG_H
// --- Hardware Configuration ---
#include "sdkconfig.h"
#if defined(CONFIG_IDF_TARGET_ESP32S3)
// ESP32-S3
#define RGB_LED_GPIO 48
#define HAS_RGB_LED 1
#define GPS_TX_PIN GPIO_NUM_5
#define GPS_RX_PIN GPIO_NUM_4
#define GPS_PPS_PIN GPIO_NUM_6
#elif defined(CONFIG_IDF_TARGET_ESP32C5)
// ESP32-C5
#define RGB_LED_GPIO 27
#define HAS_RGB_LED 1
#define GPS_TX_PIN GPIO_NUM_24
#define GPS_RX_PIN GPIO_NUM_23
#define GPS_PPS_PIN GPIO_NUM_25
#elif defined(CONFIG_IDF_TARGET_ESP32)
// ESP32 (Original)
#define RGB_LED_GPIO 2 // Standard Blue LED
#define HAS_RGB_LED 0 // Not RGB
#define GPS_TX_PIN GPIO_NUM_17
#define GPS_RX_PIN GPIO_NUM_16
#define GPS_PPS_PIN GPIO_NUM_4
#else
// Fallback
#define RGB_LED_GPIO 8
#define HAS_RGB_LED 1
#define GPS_TX_PIN GPIO_NUM_1
#define GPS_RX_PIN GPIO_NUM_3
#define GPS_PPS_PIN GPIO_NUM_5
// ============================================================================
// ESP32-C5 (DevKitC-1)
// ============================================================================
#ifdef CONFIG_IDF_TARGET_ESP32C5
#define RGB_LED_GPIO 8 // Common addressable LED pin for C5
#define HAS_RGB_LED 1
#endif
// ============================================================================
// ESP32-S3 (DevKitC-1)
// ============================================================================
#ifdef CONFIG_IDF_TARGET_ESP32S3
// Most S3 DevKits use GPIO 48 for the addressable RGB LED.
// If yours uses GPIO 38, change this value.
#define RGB_LED_GPIO 48
#define HAS_RGB_LED 1
#endif
// ============================================================================
// ESP32 (Original / Standard)
// ============================================================================
#ifdef CONFIG_IDF_TARGET_ESP32
// Standard ESP32 DevKits usually have a single blue LED on GPIO 2.
// They rarely have an addressable RGB LED built-in.
#define RGB_LED_GPIO 2
#define HAS_RGB_LED 0
#endif
// ============================================================================
// Fallbacks (Prevent Compilation Errors)
// ============================================================================
#ifndef RGB_LED_GPIO
#define RGB_LED_GPIO 2
#endif
#ifndef HAS_RGB_LED
#define HAS_RGB_LED 0
#endif
#endif // BOARD_CONFIG_H

View File

@ -1,161 +1,110 @@
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_console.h"
#include "esp_vfs_dev.h"
#include "driver/uart.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "lwip/inet.h"
#include "esp_event.h"
// Components
#include "board_config.h"
#include "status_led.h"
#include "gps_sync.h"
#include "wifi_cfg.h"
#include "board_config.h"
#include "wifi_controller.h"
#include "wifi_cfg.h"
#include "app_console.h"
#include "iperf.h"
// GUARDED INCLUDE
#ifdef CONFIG_ESP_WIFI_CSI_ENABLED
#include "csi_log.h"
#include "csi_manager.h"
#endif
// --- VERSION DEFINITION ---
#define APP_VERSION "1.1.0"
#define APP_VERSION "2.0.0-SHELL"
static const char *TAG = "MAIN";
// --- Version Command -----------------------------------------------
static int cmd_version(int argc, char **argv) {
printf("APP_VERSION: %s\n", APP_VERSION);
// --- System Commands ---
static int cmd_restart(int argc, char **argv) {
ESP_LOGI(TAG, "Restarting...");
esp_restart();
return 0;
}
// --- Event Handler -------------------------------------------------
static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
if (event_base == WIFI_EVENT) {
if (event_id == WIFI_EVENT_STA_START) {
if (wifi_ctl_get_mode() == WIFI_CTL_MODE_STA) {
status_led_set_state(LED_STATE_WAITING);
}
}
else if (event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (wifi_ctl_get_mode() == WIFI_CTL_MODE_STA) {
status_led_set_state(LED_STATE_FAILED);
}
}
}
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
if (wifi_ctl_get_mode() != WIFI_CTL_MODE_STA) return;
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip));
status_led_set_state(LED_STATE_CONNECTED);
#ifdef CONFIG_ESP_WIFI_CSI_ENABLED
if (csi_mgr_should_enable()) {
ESP_LOGI(TAG, "CSI enabled in config - starting capture");
csi_mgr_enable_async();
csi_mgr_schedule_dump();
} else {
ESP_LOGI(TAG, "CSI disabled in config - skipping capture");
}
#endif
// iperf_start() will fill this from NVS (including Dest IP and Role)
iperf_cfg_t cfg = { 0 };
iperf_start(&cfg);
}
static int cmd_version(int argc, char **argv) {
printf("APP_VERSION: %s\n", APP_VERSION);
printf("IDF_VERSION: %s\n", esp_get_idf_version());
return 0;
}
// --- Main ----------------------------------------------------------
static void register_system_common(void) {
const esp_console_cmd_t restart_cmd = {
.command = "reset",
.help = "Software reset of the device",
.func = &cmd_restart
};
ESP_ERROR_CHECK(esp_console_cmd_register(&restart_cmd));
const esp_console_cmd_t version_cmd = {
.command = "version",
.help = "Get firmware version",
.func = &cmd_version
};
ESP_ERROR_CHECK(esp_console_cmd_register(&version_cmd));
}
// --- Main Application ---
void app_main(void) {
// 1. System Init
// 1. Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
// 2. Initialize Netif & Event Loop
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
// 2. Hardware/Driver Init
// 3. Hardware Init
status_led_init(RGB_LED_GPIO, HAS_RGB_LED);
#ifdef CONFIG_ESP_WIFI_CSI_ENABLED
ESP_ERROR_CHECK(csi_log_init());
#endif
status_led_init(RGB_LED_GPIO, HAS_RGB_LED);
const gps_sync_config_t gps_cfg = {
.uart_port = UART_NUM_1,
.tx_pin = GPS_TX_PIN,
.rx_pin = GPS_RX_PIN,
.pps_pin = GPS_PPS_PIN,
};
gps_sync_init(&gps_cfg, true);
// 3. Subsystem Init
#ifdef CONFIG_ESP_WIFI_CSI_ENABLED
csi_mgr_init();
#endif
// 4. Initialize WiFi Controller (Loads config from NVS automatically)
wifi_ctl_init();
wifi_cfg_init();
// 4. Console Registry Init
esp_console_config_t console_config = {
.max_cmdline_args = 8,
.max_cmdline_length = 256,
};
ESP_ERROR_CHECK(esp_console_init(&console_config));
esp_console_register_help_command();
// 5. Initialize Console
esp_console_repl_t *repl = NULL;
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
// Register App Commands
// This prompt is the anchor for your Python script
repl_config.prompt = "esp32> ";
repl_config.max_cmdline_length = 1024;
// Install UART driver for Console (Standard IO)
esp_console_dev_uart_config_t hw_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_console_new_repl_uart(&hw_config, &repl_config, &repl));
// 6. Register Commands
register_system_common();
app_console_register_commands();
// Register Version Command (Built-in)
const esp_console_cmd_t version_cmd_def = {
.command = "version",
.help = "Get firmware version",
.hint = NULL,
.func = &cmd_version,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&version_cmd_def));
// 7. Start Shell
printf("\n ==================================================\n");
printf(" | ESP32 iPerf Shell - Ready |\n");
printf(" | Type 'help' for commands |\n");
printf(" ==================================================\n");
// 5. Register Events
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, NULL));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, NULL));
// 6. Application Start
#ifdef CONFIG_ESP_WIFI_CSI_ENABLED
bool csi_enabled = csi_mgr_should_enable();
ESP_LOGI(TAG, "CSI Capture: %s", csi_enabled ? "ENABLED" : "DISABLED");
#endif
if (wifi_cfg_apply_from_nvs()) {
status_led_set_state(LED_STATE_WAITING);
char mode[16] = {0};
uint8_t mon_ch = 36;
if (wifi_cfg_get_mode(mode, &mon_ch) && strcmp(mode, "MONITOR") == 0) {
wifi_ctl_auto_monitor_start(mon_ch);
}
} else {
status_led_set_state(LED_STATE_NO_CONFIG);
ESP_LOGW(TAG, "No Config Found. Waiting for setup...");
}
// 7. Keep Main Task Alive
ESP_LOGI(TAG, "Initialization complete. Entering idle loop.");
ESP_LOGI(TAG, "Firmware Version: %s", APP_VERSION); // Log version on boot
while (true) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
// This function runs the REPL loop and does not return
ESP_ERROR_CHECK(esp_console_start_repl(repl));
}