Compare commits

..

6 Commits

Author SHA1 Message Date
Robert McMahon 4ed4391068 final fixes for commands 2025-12-22 16:57:51 -08:00
Robert McMahon 2590a96b15 fix led so it works now 2025-12-22 15:52:02 -08:00
Robert McMahon 98b013569d some led work 2025-12-22 15:19:10 -08:00
Robert McMahon 64446be628 more cmd stuff 2025-12-22 15:00:54 -08:00
Robert McMahon b769dbc356 more on gps time 2025-12-22 13:06:05 -08:00
Robert McMahon 9974174d5b use sub commands for iperf major command 2025-12-22 12:27:12 -08:00
19 changed files with 1465 additions and 1481 deletions

View File

@ -1,11 +1,15 @@
idf_component_register(SRCS "app_console.c" idf_component_register(
"cmd_system.c" SRCS "app_console.c"
"cmd_wifi.c" "cmd_wifi.c"
"cmd_iperf.c" "cmd_iperf.c"
"cmd_system.c"
"cmd_monitor.c"
"cmd_nvs.c" "cmd_nvs.c"
"cmd_gps.c" "cmd_gps.c"
"cmd_ping.c" "cmd_ping.c"
"cmd_monitor.c"
"cmd_ip.c" "cmd_ip.c"
INCLUDE_DIRS "." INCLUDE_DIRS "."
PRIV_REQUIRES console iperf wifi_cfg wifi_controller nvs_flash gps_sync lwip driver) REQUIRES console wifi_cfg
wifi_controller iperf status_led gps_sync
esp_wifi esp_netif nvs_flash spi_flash
)

View File

@ -30,125 +30,116 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <time.h>
#include <inttypes.h> #include <inttypes.h>
#include <time.h>
#include <sys/time.h>
#include "esp_log.h" #include "esp_log.h"
#include "esp_console.h" #include "esp_console.h"
#include "argtable3/argtable3.h" #include "argtable3/argtable3.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "gps_sync.h" #include "gps_sync.h"
#include "app_console.h" #include "app_console.h"
// --- Forward Declarations ---
static int gps_do_status(int argc, char **argv);
// ============================================================================ // ============================================================================
// COMMAND: gps (Configure & Status) // COMMAND: gps (Dispatcher)
// ============================================================================ // ============================================================================
static struct { static void print_gps_usage(void) {
struct arg_lit *enable; printf("Usage: gps <subcommand> [args]\n");
struct arg_lit *disable; printf("Subcommands:\n");
struct arg_lit *status; printf(" status Show GPS lock status, time, and last NMEA message\n");
struct arg_lit *help; printf("\nType 'gps <subcommand> --help' for details.\n");
struct arg_end *end; }
} gps_args;
static int cmd_gps(int argc, char **argv) { static int cmd_gps(int argc, char **argv) {
int nerrors = arg_parse(argc, argv, (void **)&gps_args); if (argc < 2 || strcmp(argv[1], "help") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) {
if (nerrors > 0) { print_gps_usage();
arg_print_errors(stderr, gps_args.end, argv[0]);
return 1;
}
if (gps_args.help->count > 0) {
printf("Usage: gps [--enable|--disable|--status]\n");
return 0; return 0;
} }
nvs_handle_t h; if (strcmp(argv[1], "status") == 0) return gps_do_status(argc - 1, &argv[1]);
esp_err_t err = nvs_open("storage", NVS_READWRITE, &h); if (strcmp(argv[1], "info") == 0) return gps_do_status(argc - 1, &argv[1]); // Alias
if (err != ESP_OK) {
printf("Error opening NVS: %s\n", esp_err_to_name(err)); printf("Unknown subcommand '%s'.\n", argv[1]);
print_gps_usage();
return 1;
}
// ----------------------------------------------------------------------------
// Sub-command: status
// ----------------------------------------------------------------------------
static struct {
struct arg_lit *help;
struct arg_end *end;
} status_args;
static int gps_do_status(int argc, char **argv) {
status_args.help = arg_lit0("h", "help", "Help");
status_args.end = arg_end(1);
int nerrors = arg_parse(argc, argv, (void **)&status_args);
if (nerrors > 0) {
arg_print_errors(stderr, status_args.end, argv[0]);
return 1; return 1;
} }
// --- HANDLE SETTERS --- if (status_args.help->count > 0) {
bool changed = false; printf("Usage: gps status\n");
if (gps_args.enable->count > 0) { return 0;
nvs_set_u8(h, "gps_enabled", 1);
printf("GPS set to ENABLED. (Reboot required)\n");
changed = true;
} else if (gps_args.disable->count > 0) {
nvs_set_u8(h, "gps_enabled", 0);
printf("GPS set to DISABLED. (Reboot required)\n");
changed = true;
} }
if (changed) nvs_commit(h); // 1. Get GPS Data
// --- DISPLAY STATUS ---
// 1. NVS State
uint8_t val = 1;
if (nvs_get_u8(h, "gps_enabled", &val) != ESP_OK) val = 1; // Default true
printf("GPS NVS State: %s\n", val ? "ENABLED" : "DISABLED");
nvs_close(h);
// 2. Runtime Status
if (val) {
gps_timestamp_t ts = gps_get_timestamp(); gps_timestamp_t ts = gps_get_timestamp();
int64_t pps_age = gps_get_pps_age_ms(); int64_t pps_age = gps_get_pps_age_ms();
printf("Status: %s\n", ts.synced ? "SYNCED" : "SEARCHING"); char nmea_buf[128] = "<Empty>";
// Print Raw NMEA
char nmea_buf[128];
gps_get_last_nmea(nmea_buf, sizeof(nmea_buf)); gps_get_last_nmea(nmea_buf, sizeof(nmea_buf));
// Cleanup CR/LF for printing // Strip trailing newline
size_t len = strlen(nmea_buf); size_t len = strlen(nmea_buf);
if (len > 0 && (nmea_buf[len-1] == '\r' || nmea_buf[len-1] == '\n')) nmea_buf[len-1] = 0; if (len > 0 && (nmea_buf[len-1] == '\n' || nmea_buf[len-1] == '\r')) nmea_buf[len-1] = 0;
if (len > 1 && (nmea_buf[len-2] == '\r' || nmea_buf[len-2] == '\n')) nmea_buf[len-2] = 0;
printf("Last NMEA: [%s]\n", nmea_buf); // 2. Format Time
char time_str[64] = "Unknown";
if (pps_age < 0) { struct timespec ts_now = {0, 0};
printf("PPS Signal: NEVER DETECTED\n");
} else if (pps_age > 1100) {
printf("PPS Signal: LOST (Last seen %" PRId64 " ms ago)\n", pps_age);
} else {
printf("PPS Signal: ACTIVE (Age: %" PRId64 " ms)\n", pps_age);
}
if (ts.gps_us > 0) { if (ts.gps_us > 0) {
time_t now_sec = ts.gps_us / 1000000; // Get raw timespec for display
clock_gettime(CLOCK_REALTIME, &ts_now);
time_t now_sec = ts_now.tv_sec;
struct tm tm_info; struct tm tm_info;
gmtime_r(&now_sec, &tm_info); gmtime_r(&now_sec, &tm_info);
char time_buf[64]; strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S UTC", &tm_info);
strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S UTC", &tm_info);
printf("Current Time: %s\n", time_buf);
} else {
printf("Current Time: <Unknown> (System Epoch)\n");
}
} }
app_console_update_prompt(); // 3. Print
printf("GPS Status:\n");
printf(" Time: %s\n", time_str);
// UPDATED: Show raw timespec structure
printf(" Timespec: tv_sec=%" PRId64 " tv_nsec=%ld\n", (int64_t)ts_now.tv_sec, ts_now.tv_nsec);
printf(" PPS Locked: %s (Age: %" PRId64 " ms)\n", ts.synced ? "YES" : "NO", pps_age);
printf(" NMEA Valid: %s\n", ts.valid ? "YES" : "NO");
printf(" Last Message: %s\n", nmea_buf);
return 0; return 0;
} }
void register_gps_cmd(void) { // ----------------------------------------------------------------------------
gps_args.enable = arg_lit0(NULL, "enable", "Enable GPS"); // Registration
gps_args.disable = arg_lit0(NULL, "disable", "Disable GPS"); // ----------------------------------------------------------------------------
gps_args.status = arg_lit0(NULL, "status", "Show Status");
gps_args.help = arg_lit0("h", "help", "Help");
gps_args.end = arg_end(2);
void register_gps_cmd(void) {
const esp_console_cmd_t cmd = { const esp_console_cmd_t cmd = {
.command = "gps", .command = "gps",
.help = "Configure GPS", .help = "GPS Tool: status",
.hint = "<subcommand>",
.func = &cmd_gps, .func = &cmd_gps,
.argtable = &gps_args .argtable = NULL
}; };
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
} }

View File

@ -30,77 +30,161 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
/*
* cmd_ip.c
*
* Copyright (c) 2025 Umber Networks & Robert McMahon
* All rights reserved.
*/
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "esp_log.h"
#include "esp_console.h" #include "esp_console.h"
#include "argtable3/argtable3.h"
#include "esp_netif.h" #include "esp_netif.h"
#include "esp_mac.h" #include "esp_wifi.h"
#include "lwip/inet.h" #include "argtable3/argtable3.h"
#include "wifi_cfg.h"
#include "app_console.h" #include "app_console.h"
// --- Arguments ---
static struct { static struct {
struct arg_lit *addr; struct arg_str *ip;
struct arg_lit *help; struct arg_str *mask;
struct arg_str *gw;
struct arg_end *end; struct arg_end *end;
} ip_args; } set_args;
static void print_iface_details(const char* key) { static struct {
esp_netif_t *netif = esp_netif_get_handle_from_ifkey(key); struct arg_str *mode; // "on" or "off"
if (!netif) return; struct arg_end *end;
} dhcp_args;
const char *desc = esp_netif_get_desc(netif); static void print_if_info(esp_netif_t *netif, const char *name) {
bool is_up = esp_netif_is_netif_up(netif); if (netif == NULL) return;
uint8_t mac[6] = {0};
esp_netif_get_mac(netif, mac);
esp_netif_ip_info_t ip_info; esp_netif_ip_info_t ip_info;
esp_netif_get_ip_info(netif, &ip_info); if (esp_netif_get_ip_info(netif, &ip_info) == ESP_OK) {
printf("%s:\n", name);
printf(" IP: " IPSTR "\n", IP2STR(&ip_info.ip));
printf(" Mask: " IPSTR "\n", IP2STR(&ip_info.netmask));
printf(" GW: " IPSTR "\n", IP2STR(&ip_info.gw));
printf("%s (%s): <%s>\n", key, desc ? desc : "auth", is_up ? "UP" : "DOWN"); esp_netif_dhcp_status_t status;
printf(" link/ether %02x:%02x:%02x:%02x:%02x:%02x\n", esp_netif_dhcpc_get_status(netif, &status);
printf(" DHCP: %s\n", (status == ESP_NETIF_DHCP_STARTED) ? "ON" : "OFF");
uint8_t mac[6] = {0};
if (esp_netif_get_mac(netif, mac) == ESP_OK) {
printf(" MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
if (is_up && ip_info.ip.addr != 0) { printf("\n");
printf(" inet " IPSTR " netmask " IPSTR " broadcast " IPSTR "\n",
IP2STR(&ip_info.ip),
IP2STR(&ip_info.netmask),
IP2STR(&ip_info.gw));
} }
} }
static int cmd_ip(int argc, char **argv) { static int do_ip_addr(void) {
int nerrors = arg_parse(argc, argv, (void **)&ip_args); print_if_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), "Wi-Fi Station");
return 0;
}
static int do_ip_set(int argc, char **argv) {
int nerrors = arg_parse(argc, argv, (void **)&set_args);
if (nerrors > 0) { if (nerrors > 0) {
arg_print_errors(stderr, ip_args.end, argv[0]); arg_print_errors(stderr, set_args.end, argv[0]);
return 1; return 1;
} }
if (ip_args.help->count > 0) { const char *ip = set_args.ip->sval[0];
printf("Usage: ip [addr]\n"); const char *mask = set_args.mask->sval[0];
return 0; const char *gw = set_args.gw->sval[0];
// Validate and Convert (API Fix: esp_ip4addr_aton returns uint32_t)
esp_netif_ip_info_t info = {0};
info.ip.addr = esp_ip4addr_aton(ip);
info.netmask.addr = esp_ip4addr_aton(mask);
info.gw.addr = esp_ip4addr_aton(gw);
// Basic validation: 0 means conversion failed (or actual 0.0.0.0)
if (info.ip.addr == 0 || info.netmask.addr == 0) {
printf("Invalid IP format.\n");
return 1;
} }
// Explicitly check standard interfaces // Save
print_iface_details("WIFI_STA_DEF"); wifi_cfg_set_ipv4(ip, mask, gw);
print_iface_details("WIFI_AP_DEF"); wifi_cfg_set_dhcp(false); // Implicitly disable DHCP
print_iface_details("ETH_DEF");
// Apply
esp_netif_t *netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
if (netif) {
esp_netif_dhcpc_stop(netif);
esp_netif_set_ip_info(netif, &info);
printf("Static IP set. DHCP disabled.\n");
} else {
printf("Saved. Will apply on next init.\n");
}
return 0; return 0;
} }
static int do_ip_dhcp(int argc, char **argv) {
int nerrors = arg_parse(argc, argv, (void **)&dhcp_args);
if (nerrors > 0) {
arg_print_errors(stderr, dhcp_args.end, argv[0]);
return 1;
}
bool enable = (strcmp(dhcp_args.mode->sval[0], "on") == 0);
wifi_cfg_set_dhcp(enable);
esp_netif_t *netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
if (netif) {
if (enable) {
esp_netif_dhcpc_start(netif);
printf("DHCP enabled.\n");
} else {
esp_netif_dhcpc_stop(netif);
printf("DHCP disabled.\n");
}
}
return 0;
}
static void print_ip_usage(void) {
printf("Usage: ip <subcommand>\n");
printf("Subcommands:\n");
printf(" addr Show config\n");
printf(" set <ip> <mask> <gw> Set static IP (Disables DHCP)\n");
printf(" dhcp <on|off> Enable/Disable DHCP\n");
}
static int cmd_ip(int argc, char **argv) {
if (argc < 2) {
print_ip_usage();
return 0;
}
if (strcmp(argv[1], "addr") == 0) return do_ip_addr();
if (strcmp(argv[1], "set") == 0) return do_ip_set(argc - 1, &argv[1]);
if (strcmp(argv[1], "dhcp") == 0) return do_ip_dhcp(argc - 1, &argv[1]);
print_ip_usage();
return 1;
}
void register_ip_cmd(void) { void register_ip_cmd(void) {
ip_args.addr = arg_lit0("a", "addr", "Show addresses"); // Args
ip_args.help = arg_lit0("h", "help", "Help"); set_args.ip = arg_str1(NULL, NULL, "<ip>", "IP Address");
ip_args.end = arg_end(1); set_args.mask = arg_str1(NULL, NULL, "<mask>", "Netmask");
set_args.gw = arg_str1(NULL, NULL, "<gw>", "Gateway");
set_args.end = arg_end(3);
dhcp_args.mode = arg_str1(NULL, NULL, "<on|off>", "Mode");
dhcp_args.end = arg_end(1);
const esp_console_cmd_t cmd = { const esp_console_cmd_t cmd = {
.command = "ip", .command = "ip",
.help = "Show network interfaces", .help = "IP Config: addr, set, dhcp",
.hint = "<subcommand>",
.func = &cmd_ip, .func = &cmd_ip,
.argtable = &ip_args
}; };
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
} }

View File

@ -30,7 +30,6 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "esp_log.h" #include "esp_log.h"
@ -40,112 +39,201 @@
#include "iperf.h" #include "iperf.h"
#include "app_console.h" #include "app_console.h"
// --- Forward Declarations ---
static int iperf_do_start(int argc, char **argv);
static int iperf_do_stop(int argc, char **argv);
static int iperf_do_status(int argc, char **argv);
static int iperf_do_set(int argc, char **argv);
static int iperf_do_save(int argc, char **argv);
static int iperf_do_reload(int argc, char **argv);
static int iperf_do_clear(int argc, char **argv);
// ============================================================================ // ============================================================================
// COMMAND: iperf // COMMAND: iperf (Dispatcher)
// ============================================================================ // ============================================================================
static struct { static void print_iperf_usage(void) {
struct arg_lit *start, *stop, *status, *save, *reload; printf("Usage: iperf <subcommand> [args]\n");
struct arg_lit *clear_nvs; printf("Subcommands:\n");
struct arg_str *ip; printf(" start Start traffic generation\n");
struct arg_int *port, *pps, *len, *burst; printf(" stop Stop traffic generation\n");
struct arg_lit *help; printf(" status Show current statistics\n");
struct arg_end *end; printf(" set Configure parameters (IP, Port, PPS, etc.)\n");
} iperf_args; printf(" save Save configuration to NVS\n");
printf(" reload Reload configuration from NVS\n");
printf(" clear Reset configuration to defaults\n");
printf("\nType 'iperf <subcommand> --help' for details.\n");
}
static int cmd_iperf(int argc, char **argv) { static int cmd_iperf(int argc, char **argv) {
int nerrors = arg_parse(argc, argv, (void **)&iperf_args); if (argc < 2 || strcmp(argv[1], "help") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) {
print_iperf_usage();
return 0;
}
if (strcmp(argv[1], "start") == 0) return iperf_do_start(argc - 1, &argv[1]);
if (strcmp(argv[1], "stop") == 0) return iperf_do_stop(argc - 1, &argv[1]);
if (strcmp(argv[1], "status") == 0) return iperf_do_status(argc - 1, &argv[1]);
if (strcmp(argv[1], "set") == 0) return iperf_do_set(argc - 1, &argv[1]);
if (strcmp(argv[1], "save") == 0) return iperf_do_save(argc - 1, &argv[1]);
if (strcmp(argv[1], "reload") == 0) return iperf_do_reload(argc - 1, &argv[1]);
if (strcmp(argv[1], "clear") == 0) return iperf_do_clear(argc - 1, &argv[1]);
printf("Unknown subcommand '%s'.\n", argv[1]);
print_iperf_usage();
return 1;
}
// ----------------------------------------------------------------------------
// Sub-command: start
// ----------------------------------------------------------------------------
static int iperf_do_start(int argc, char **argv) {
iperf_start();
return 0;
}
// ----------------------------------------------------------------------------
// Sub-command: stop
// ----------------------------------------------------------------------------
static int iperf_do_stop(int argc, char **argv) {
iperf_stop();
return 0;
}
// ----------------------------------------------------------------------------
// Sub-command: status
// ----------------------------------------------------------------------------
static int iperf_do_status(int argc, char **argv) {
iperf_print_status();
return 0;
}
// ----------------------------------------------------------------------------
// Sub-command: set (Configuration)
// ----------------------------------------------------------------------------
static struct {
struct arg_str *ip;
struct arg_int *port;
struct arg_int *pps;
struct arg_int *len;
struct arg_int *burst;
struct arg_lit *help;
struct arg_end *end;
} set_args;
static int iperf_do_set(int argc, char **argv) {
// REVERTED: Now uses standard iPerf syntax ("client" / "-c")
set_args.ip = arg_str0("c", "client", "<ip>", "Destination IP");
set_args.port = arg_int0("p", "port", "<port>", "Destination Port");
set_args.pps = arg_int0(NULL, "pps", "<n>", "Packets Per Second");
set_args.len = arg_int0("l", "len", "<bytes>", "Packet Length");
set_args.burst = arg_int0("b", "burst", "<count>", "Burst Count");
set_args.help = arg_lit0("h", "help", "Help");
set_args.end = arg_end(20);
int nerrors = arg_parse(argc, argv, (void **)&set_args);
if (nerrors > 0) { if (nerrors > 0) {
arg_print_errors(stderr, iperf_args.end, argv[0]); // --- CUSTOM ERROR HINTING ---
// Check if user tried to use "--ip"
for (int i = 0; i < argc; i++) {
if (strcmp(argv[i], "--ip") == 0) {
printf("Error: Invalid option '--ip'. Did you mean '--client' (or -c) to set the destination IP?\n");
return 1;
}
}
// Fallback to standard error
arg_print_errors(stderr, set_args.end, argv[0]);
return 1; return 1;
} }
if (iperf_args.help->count > 0) { if (set_args.help->count > 0) {
printf("Usage: iperf [options]\n"); printf("Usage: iperf set [options]\n");
printf(" --start Start traffic\n"); arg_print_glossary(stdout, (void **)&set_args, " %-25s %s\n");
printf(" --stop Stop traffic\n");
printf(" --save Save config to NVS\n");
printf(" --reload Reload config from NVS\n");
printf(" --clear-nvs Reset to defaults\n");
printf(" -c <ip> Set Dest IP\n");
printf(" --pps <n> Set Packets Per Sec\n");
return 0; return 0;
} }
// --- Actions: NVS Management ---
if (iperf_args.clear_nvs->count > 0) {
iperf_param_clear();
printf("iPerf Configuration cleared (Reset to defaults).\n");
app_console_update_prompt();
return 0;
}
if (iperf_args.reload->count > 0) {
iperf_param_init(); // Force re-read
printf("Configuration reloaded from NVS.\n");
}
// --- Actions: Parameter Updates ---
bool config_changed = false;
iperf_cfg_t cfg; iperf_cfg_t cfg;
iperf_param_get(&cfg); // Get current staging iperf_param_get(&cfg);
bool changed = false;
if (iperf_args.ip->count > 0) { cfg.dip = inet_addr(iperf_args.ip->sval[0]); config_changed = true; } if (set_args.ip->count > 0) {
if (iperf_args.port->count > 0) { cfg.dport = (uint16_t)iperf_args.port->ival[0]; config_changed = true; } cfg.dip = inet_addr(set_args.ip->sval[0]);
if (iperf_args.len->count > 0) { cfg.send_len = (uint32_t)iperf_args.len->ival[0]; config_changed = true; } changed = true;
if (iperf_args.burst->count > 0) { cfg.burst_count = (uint32_t)iperf_args.burst->ival[0]; config_changed = true; } }
if (iperf_args.pps->count > 0) { if (set_args.port->count > 0) {
if (iperf_args.pps->ival[0] > 0) { cfg.dport = (uint16_t)set_args.port->ival[0];
cfg.target_pps = (uint32_t)iperf_args.pps->ival[0]; changed = true;
config_changed = true; }
if (set_args.len->count > 0) {
cfg.send_len = (uint32_t)set_args.len->ival[0];
changed = true;
}
if (set_args.burst->count > 0) {
cfg.burst_count = (uint32_t)set_args.burst->ival[0];
changed = true;
}
if (set_args.pps->count > 0) {
if (set_args.pps->ival[0] > 0) {
cfg.target_pps = (uint32_t)set_args.pps->ival[0];
changed = true;
} else {
printf("Error: PPS must be > 0.\n");
return 1;
} }
} }
if (config_changed) { if (changed) {
iperf_param_set(&cfg); iperf_param_set(&cfg);
printf("RAM configuration updated.\n"); printf("Configuration updated (RAM only). Run 'iperf save' to persist.\n");
} else {
printf("No changes specified.\n");
} }
if (iperf_args.save->count > 0) { return 0;
}
// ----------------------------------------------------------------------------
// Sub-command: save
// ----------------------------------------------------------------------------
static int iperf_do_save(int argc, char **argv) {
bool changed = false; bool changed = false;
if (iperf_param_save(&changed) == ESP_OK) { if (iperf_param_save(&changed) == ESP_OK) {
printf(changed ? "Configuration saved to NVS.\n" : "No changes to save (NVS matches RAM).\n"); printf(changed ? "Configuration saved to NVS.\n" : "No changes to save (NVS matches RAM).\n");
} else { } else {
printf("Error saving to NVS.\n"); printf("Error saving to NVS.\n");
} }
}
// --- Actions: Execution ---
if (iperf_args.stop->count > 0) iperf_stop();
if (iperf_args.start->count > 0) iperf_start();
if (iperf_args.status->count > 0) iperf_print_status();
app_console_update_prompt();
return 0; return 0;
} }
// ----------------------------------------------------------------------------
// Sub-command: reload
// ----------------------------------------------------------------------------
static int iperf_do_reload(int argc, char **argv) {
iperf_param_init(); // Force re-read from NVS
printf("Configuration reloaded from NVS.\n");
return 0;
}
// ----------------------------------------------------------------------------
// Sub-command: clear
// ----------------------------------------------------------------------------
static int iperf_do_clear(int argc, char **argv) {
iperf_param_clear();
printf("iPerf Configuration cleared (Reset to defaults).\n");
return 0;
}
// ----------------------------------------------------------------------------
// Registration
// ----------------------------------------------------------------------------
void register_iperf_cmd(void) { void register_iperf_cmd(void) {
iperf_args.start = arg_lit0(NULL, "start", "Start");
iperf_args.stop = arg_lit0(NULL, "stop", "Stop");
iperf_args.status = arg_lit0(NULL, "status", "Status");
iperf_args.save = arg_lit0(NULL, "save", "Save");
iperf_args.reload = arg_lit0(NULL, "reload", "Reload");
iperf_args.clear_nvs = arg_lit0(NULL, "clear-nvs", "Clear NVS");
iperf_args.ip = arg_str0("c", "client", "<ip>", "IP");
iperf_args.port = arg_int0("p", "port", "<port>", "Port");
iperf_args.pps = arg_int0(NULL, "pps", "<n>", "PPS");
iperf_args.len = arg_int0(NULL, "len", "<bytes>", "Len");
iperf_args.burst = arg_int0(NULL, "burst", "<count>", "Burst");
iperf_args.help = arg_lit0("h", "help", "Help");
iperf_args.end = arg_end(20);
const esp_console_cmd_t cmd = { const esp_console_cmd_t cmd = {
.command = "iperf", .command = "iperf",
.help = "Traffic Gen", .help = "Traffic Gen: start, stop, set, status",
.hint = "<subcommand> [args]",
.func = &cmd_iperf, .func = &cmd_iperf,
.argtable = &iperf_args .argtable = NULL
}; };
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
} }

View File

@ -30,105 +30,112 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <inttypes.h>
#include "esp_log.h" #include "esp_log.h"
#include "esp_console.h" #include "esp_console.h"
#include "argtable3/argtable3.h" #include "argtable3/argtable3.h"
#include "wifi_controller.h" #include "wifi_controller.h"
#include "iperf.h"
#include "app_console.h" #include "app_console.h"
// ============================================================================ // --- Subcommand Arguments ---
// COMMAND: monitor static struct {
// ============================================================================ struct arg_int *channel;
struct arg_end *end;
} start_args;
static struct { static struct {
struct arg_lit *start, *stop, *status, *save, *reload;
struct arg_lit *clear_nvs;
struct arg_int *channel; struct arg_int *channel;
struct arg_lit *help;
struct arg_end *end; struct arg_end *end;
} mon_args; } channel_args;
static int cmd_monitor(int argc, char **argv) { static void print_monitor_usage(void) {
int nerrors = arg_parse(argc, argv, (void **)&mon_args); printf("Usage: monitor <subcommand> [args]\n");
printf("Subcommands:\n");
printf(" start [-c <n>] Start Monitor Mode (optional: set channel)\n");
printf(" stop Stop Monitor Mode\n");
printf(" status Show current status\n");
printf(" channel <n> Switch channel (while running)\n");
printf(" save Save current config to NVS\n");
printf(" reload Reload config from NVS\n");
printf(" clear Clear NVS config\n");
}
// --- Subcommand Handlers ---
static int do_monitor_start(int argc, char **argv) {
start_args.channel = arg_int0("c", "channel", "<n>", "Channel (1-13)");
start_args.end = arg_end(1);
int nerrors = arg_parse(argc, argv, (void **)&start_args);
if (nerrors > 0) { if (nerrors > 0) {
arg_print_errors(stderr, mon_args.end, argv[0]); arg_print_errors(stderr, start_args.end, argv[0]);
return 1; return 1;
} }
if (mon_args.help->count > 0) { int ch = 0;
printf("Usage: monitor [--start|--stop] [-c <ch>] [--save|--reload]\n"); if (start_args.channel->count > 0) {
ch = start_args.channel->ival[0];
}
printf("Starting Monitor Mode%s...\n", ch ? " on specific channel" : "");
wifi_ctl_monitor_start(ch);
return 0;
}
static int do_monitor_channel(int argc, char **argv) {
channel_args.channel = arg_int1(NULL, NULL, "<n>", "Channel (1-13)");
channel_args.end = arg_end(1);
int nerrors = arg_parse(argc, argv, (void **)&channel_args);
if (nerrors > 0) {
arg_print_errors(stderr, channel_args.end, argv[0]);
return 1;
}
int ch = channel_args.channel->ival[0];
printf("Switching to Channel %d...\n", ch);
wifi_ctl_set_channel(ch);
return 0;
}
static int cmd_monitor(int argc, char **argv) {
if (argc < 2) {
print_monitor_usage();
return 0; return 0;
} }
// --- Actions: NVS Management --- if (strcmp(argv[1], "start") == 0) return do_monitor_start(argc - 1, &argv[1]);
if (mon_args.clear_nvs->count > 0) { if (strcmp(argv[1], "stop") == 0) { wifi_ctl_stop(); return 0; }
wifi_ctl_param_clear(); if (strcmp(argv[1], "status") == 0) { wifi_ctl_status(); return 0; }
printf("Monitor config cleared (Defaulting to Ch 6).\n"); if (strcmp(argv[1], "save") == 0) { wifi_ctl_param_save(NULL); printf("Saved.\n"); return 0; }
app_console_update_prompt(); if (strcmp(argv[1], "reload") == 0) { wifi_ctl_param_init(); printf("Reloaded.\n"); return 0; }
if (strcmp(argv[1], "clear") == 0) { wifi_ctl_param_clear(); printf("Cleared.\n"); return 0; }
if (strcmp(argv[1], "channel") == 0) return do_monitor_channel(argc - 1, &argv[1]);
if (strcmp(argv[1], "help") == 0 || strcmp(argv[1], "--help") == 0) {
print_monitor_usage();
return 0; return 0;
} }
if (mon_args.reload->count > 0) { printf("Unknown subcommand '%s'.\n", argv[1]);
wifi_ctl_param_reload(); print_monitor_usage();
printf("Config reloaded from NVS.\n"); return 1;
}
if (mon_args.channel->count > 0) {
wifi_ctl_param_set_monitor_channel((uint8_t)mon_args.channel->ival[0]);
printf("Channel set to %d (RAM).\n", mon_args.channel->ival[0]);
}
if (mon_args.save->count > 0) {
if (wifi_ctl_param_save()) printf("Configuration saved to NVS.\n");
else printf("No changes to save (NVS matches RAM).\n");
}
// --- Actions: Mode Switching ---
if (mon_args.stop->count > 0) {
wifi_ctl_switch_to_sta(WIFI_BW_HT20);
printf("Switched to Station Mode.\n");
}
if (mon_args.start->count > 0) {
if (wifi_ctl_switch_to_monitor(0, WIFI_BW_HT20) == ESP_OK) printf("Monitor Mode Started.\n");
else printf("Failed to start Monitor Mode.\n");
}
// --- Status ---
if (mon_args.status->count > 0) {
wifi_ctl_mode_t mode = wifi_ctl_get_mode();
printf("MONITOR STATUS:\n");
printf(" Mode: %s\n", (mode == WIFI_CTL_MODE_MONITOR) ? "MONITOR" : "STATION");
printf(" Active: Ch %d\n", (mode == WIFI_CTL_MODE_MONITOR) ? wifi_ctl_get_monitor_channel() : 0);
printf(" Staged: Ch %d\n", wifi_ctl_param_get_monitor_channel());
printf(" Frames: %" PRIu32 "\n", wifi_ctl_get_monitor_frame_count());
}
app_console_update_prompt();
return 0;
} }
void register_monitor_cmd(void) { void register_monitor_cmd(void) {
mon_args.start = arg_lit0(NULL, "start", "Start"); start_args.channel = arg_int0("c", "channel", "<n>", "Channel");
mon_args.stop = arg_lit0(NULL, "stop", "Stop"); start_args.end = arg_end(1);
mon_args.status = arg_lit0(NULL, "status", "Status");
mon_args.save = arg_lit0(NULL, "save", "Save"); channel_args.channel = arg_int1(NULL, NULL, "<n>", "Channel");
mon_args.reload = arg_lit0(NULL, "reload", "Reload"); channel_args.end = arg_end(1);
mon_args.clear_nvs = arg_lit0(NULL, "clear-nvs", "Clear NVS");
mon_args.channel = arg_int0("c", "channel", "<n>", "Chan");
mon_args.help = arg_lit0("h", "help", "Help");
mon_args.end = arg_end(20);
const esp_console_cmd_t cmd = { const esp_console_cmd_t cmd = {
.command = "monitor", .command = "monitor",
.help = "Monitor Mode", .help = "Monitor Mode: start, stop, channel, status",
.hint = "<subcommand>",
.func = &cmd_monitor, .func = &cmd_monitor,
.argtable = &mon_args
}; };
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
} }

View File

@ -30,130 +30,187 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
/*
* cmd_nvs.c
*
* Copyright (c) 2025 Umber Networks & Robert McMahon
* All rights reserved.
*/
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <inttypes.h> #include <inttypes.h>
#include "esp_log.h" #include "esp_log.h"
#include "esp_console.h" #include "esp_console.h"
#include "argtable3/argtable3.h"
#include "nvs.h"
#include "nvs_flash.h" #include "nvs_flash.h"
#include "wifi_cfg.h" #include "nvs.h"
#include "iperf.h" #include "argtable3/argtable3.h"
#include "app_console.h" #include "app_console.h"
// ============================================================================ static void print_nvs_usage(void) {
// COMMAND: nvs (Storage Management) printf("Usage: nvs <subcommand>\n");
// ============================================================================ printf("Subcommands:\n");
printf(" dump Dump 'storage' namespace keys and values\n");
printf(" clear Erase 'storage' namespace (Factory Reset)\n");
}
static struct { static void print_value(nvs_handle_t h, const char *key, nvs_type_t type) {
struct arg_lit *dump; esp_err_t err;
struct arg_lit *clear_all;
struct arg_lit *help;
struct arg_end *end;
} nvs_args;
// --- Helper Functions for Dumping --- switch (type) {
case NVS_TYPE_I8: {
static void print_nvs_key_str(nvs_handle_t h, const char *key, const char *label) { int8_t v = 0;
char buf[64] = {0}; err = nvs_get_i8(h, key, &v);
size_t len = sizeof(buf); if (err == ESP_OK) printf("Value: %d (I8)", v);
if (nvs_get_str(h, key, buf, &len) == ESP_OK) { break;
printf(" %-12s : %s\n", label, buf); }
} else { case NVS_TYPE_U8: {
printf(" %-12s : <empty>\n", label); uint8_t v = 0;
err = nvs_get_u8(h, key, &v);
if (err == ESP_OK) printf("Value: %u (U8)", v);
break;
}
case NVS_TYPE_I16: {
int16_t v = 0;
err = nvs_get_i16(h, key, &v);
if (err == ESP_OK) printf("Value: %d (I16)", v);
break;
}
case NVS_TYPE_U16: {
uint16_t v = 0;
err = nvs_get_u16(h, key, &v);
if (err == ESP_OK) printf("Value: %u (U16)", v);
break;
}
case NVS_TYPE_I32: {
int32_t v = 0;
err = nvs_get_i32(h, key, &v);
if (err == ESP_OK) printf("Value: %" PRIi32 " (I32)", v);
break;
}
case NVS_TYPE_U32: {
uint32_t v = 0;
err = nvs_get_u32(h, key, &v);
if (err == ESP_OK) printf("Value: %" PRIu32 " (U32)", v);
break;
}
case NVS_TYPE_I64: {
int64_t v = 0;
err = nvs_get_i64(h, key, &v);
if (err == ESP_OK) printf("Value: %" PRIi64 " (I64)", v);
break;
}
case NVS_TYPE_U64: {
uint64_t v = 0;
err = nvs_get_u64(h, key, &v);
if (err == ESP_OK) printf("Value: %" PRIu64 " (U64)", v);
break;
}
case NVS_TYPE_STR: {
size_t len = 0;
if (nvs_get_str(h, key, NULL, &len) == ESP_OK) {
char *str = malloc(len);
if (nvs_get_str(h, key, str, &len) == ESP_OK) {
printf("Value: \"%s\" (STR)", str);
}
free(str);
}
break;
}
case NVS_TYPE_BLOB: {
size_t len = 0;
if (nvs_get_blob(h, key, NULL, &len) == ESP_OK) {
printf("Value: [BLOB %u bytes]", (unsigned int)len);
}
break;
}
default:
printf("Value: [Unknown Type 0x%02x]", type);
break;
} }
} }
static void print_nvs_key_u32(nvs_handle_t h, const char *key, const char *label) { static int do_nvs_dump(int argc, char **argv) {
uint32_t val = 0; nvs_iterator_t it = NULL;
if (nvs_get_u32(h, key, &val) == ESP_OK) { nvs_handle_t h;
printf(" %-12s : %" PRIu32 "\n", label, val);
} else {
printf(" %-12s : <empty>\n", label);
}
}
static void print_nvs_key_u8(nvs_handle_t h, const char *key, const char *label) { // 1. Open Handle (Needed to read values)
uint8_t val = 0; esp_err_t err = nvs_open("storage", NVS_READONLY, &h);
if (nvs_get_u8(h, key, &val) == ESP_OK) { if (err != ESP_OK) {
printf(" %-12s : %u\n", label, val); printf("Error opening NVS handle: %s\n", esp_err_to_name(err));
} else {
printf(" %-12s : <empty>\n", label);
}
}
static int cmd_nvs(int argc, char **argv) {
int nerrors = arg_parse(argc, argv, (void **)&nvs_args);
if (nerrors > 0) {
arg_print_errors(stderr, nvs_args.end, argv[0]);
return 1; return 1;
} }
if (nvs_args.help->count > 0) { // 2. Start Iterator
printf("Usage: nvs [--dump] [--clear-all]\n"); esp_err_t res = nvs_entry_find("nvs", "storage", NVS_TYPE_ANY, &it);
if (res != ESP_OK) {
nvs_close(h);
if (res == ESP_ERR_NVS_NOT_FOUND) {
printf("No NVS entries found in 'storage'.\n");
} else {
printf("NVS Search Error: %s\n", esp_err_to_name(res));
}
return 0; return 0;
} }
// --- CLEAR ALL --- printf("NVS Dump (Namespace: storage):\n");
if (nvs_args.clear_all->count > 0) { printf("%-20s | %-12s | %s\n", "Key", "Type", "Value");
printf("Erasing ALL settings from NVS...\n"); printf("------------------------------------------------------------\n");
wifi_cfg_clear_credentials();
wifi_cfg_clear_monitor_channel();
iperf_param_clear();
printf("Done. Please reboot.\n");
app_console_update_prompt();
return 0;
}
// --- DUMP --- while (res == ESP_OK) {
// Note: NVS iterators are complex in ESP-IDF; explicit key listing is often simpler for known keys. nvs_entry_info_t info;
nvs_entry_info(it, &info);
printf("\n--- [WiFi Config (netcfg)] ---\n"); printf("%-20s | 0x%02x | ", info.key, info.type);
nvs_handle_t h; print_value(h, info.key, info.type);
if (nvs_open("netcfg", NVS_READONLY, &h) == ESP_OK) {
print_nvs_key_str(h, "ssid", "SSID");
print_nvs_key_str(h, "pass", "Password");
print_nvs_key_str(h, "ip", "Static IP");
print_nvs_key_str(h, "mask", "Netmask");
print_nvs_key_str(h, "gw", "Gateway");
print_nvs_key_u8 (h, "dhcp", "DHCP");
print_nvs_key_u8 (h, "mon_ch", "Monitor Ch");
nvs_close(h);
} else {
printf("Failed to open 'netcfg' namespace (not initialized?).\n");
}
printf("\n--- [iPerf Config (storage)] ---\n");
if (nvs_open("storage", NVS_READONLY, &h) == ESP_OK) {
print_nvs_key_str(h, "iperf_dst_ip", "Dest IP");
print_nvs_key_u32(h, "iperf_port", "Port");
print_nvs_key_u32(h, "iperf_pps", "Target PPS");
print_nvs_key_u32(h, "iperf_len", "Packet Len");
print_nvs_key_u32(h, "iperf_burst", "Burst");
print_nvs_key_u8 (h, "gps_enabled", "GPS En");
nvs_close(h);
} else {
printf("Failed to open 'storage' namespace.\n");
}
printf("\n"); printf("\n");
app_console_update_prompt(); res = nvs_entry_next(&it);
}
nvs_release_iterator(it);
nvs_close(h);
return 0; return 0;
} }
static int do_nvs_clear(int argc, char **argv) {
nvs_handle_t h;
esp_err_t err = nvs_open("storage", NVS_READWRITE, &h);
if (err != ESP_OK) {
printf("Error opening NVS: %s\n", esp_err_to_name(err));
return 1;
}
err = nvs_erase_all(h);
if (err == ESP_OK) {
nvs_commit(h);
printf("NVS 'storage' namespace erased.\n");
} else {
printf("Error erasing NVS: %s\n", esp_err_to_name(err));
}
nvs_close(h);
return (err == ESP_OK) ? 0 : 1;
}
static int cmd_nvs(int argc, char **argv) {
if (argc < 2) {
print_nvs_usage();
return 0;
}
if (strcmp(argv[1], "dump") == 0) return do_nvs_dump(argc - 1, &argv[1]);
if (strcmp(argv[1], "clear") == 0) return do_nvs_clear(argc - 1, &argv[1]);
print_nvs_usage();
return 1;
}
void register_nvs_cmd(void) { void register_nvs_cmd(void) {
nvs_args.dump = arg_lit0(NULL, "dump", "Show all");
nvs_args.clear_all = arg_lit0(NULL, "clear-all", "Factory Reset");
nvs_args.help = arg_lit0("h", "help", "Help");
nvs_args.end = arg_end(1);
const esp_console_cmd_t cmd = { const esp_console_cmd_t cmd = {
.command = "nvs", .command = "nvs",
.help = "Storage Management", .help = "Storage Tools: dump, clear",
.hint = "<subcommand>",
.func = &cmd_nvs, .func = &cmd_nvs,
.argtable = &nvs_args
}; };
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
} }

View File

@ -32,32 +32,22 @@
*/ */
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <inttypes.h>
#include "esp_log.h" #include "esp_log.h"
#include "esp_console.h" #include "esp_console.h"
#include "argtable3/argtable3.h"
#include "esp_system.h"
#include "esp_chip_info.h" #include "esp_chip_info.h"
#include "esp_mac.h" #include "esp_flash.h"
#include "esp_heap_caps.h" #include "esp_system.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "argtable3/argtable3.h"
#include "app_console.h" #include "app_console.h"
static const char *TAG = "CMD_SYS"; // Define default version if not passed by CMake
#ifndef APP_VERSION
// ============================================================================ #define APP_VERSION "2.0.0-SHELL"
// COMMAND: system (Reboot, Info, Heap) #endif
// ============================================================================
static struct {
struct arg_lit *reboot;
struct arg_lit *info;
struct arg_lit *heap;
struct arg_lit *help;
struct arg_end *end;
} sys_args;
// --- Helper: Convert Model ID to String ---
static const char* get_chip_model_string(esp_chip_model_t model) { static const char* get_chip_model_string(esp_chip_model_t model) {
switch (model) { switch (model) {
case CHIP_ESP32: return "ESP32"; case CHIP_ESP32: return "ESP32";
@ -67,98 +57,111 @@ static const char* get_chip_model_string(esp_chip_model_t model) {
case CHIP_ESP32C2: return "ESP32-C2"; case CHIP_ESP32C2: return "ESP32-C2";
case CHIP_ESP32C6: return "ESP32-C6"; case CHIP_ESP32C6: return "ESP32-C6";
case CHIP_ESP32H2: return "ESP32-H2"; case CHIP_ESP32H2: return "ESP32-H2";
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)) // Explicitly handle ID 23 for C5 if macro is missing
case CHIP_ESP32C5: return "ESP32-C5"; // Requires recent IDF case 23: return "ESP32-C5";
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)) && defined(CHIP_ESP32P4)
case CHIP_ESP32P4: return "ESP32-P4"; case CHIP_ESP32P4: return "ESP32-P4";
#endif
#ifdef CHIP_ESP32C5
case CHIP_ESP32C5: return "ESP32-C5";
#endif #endif
default: return "Unknown"; default: return "Unknown";
} }
} }
static int cmd_system(int argc, char **argv) { // --- Command Handlers ---
int nerrors = arg_parse(argc, argv, (void **)&sys_args);
if (nerrors > 0) {
arg_print_errors(stderr, sys_args.end, argv[0]);
return 1;
}
if (sys_args.help->count > 0) { static int do_system_reboot(int argc, char **argv) {
printf("Usage: system [--reboot|--info|--heap]\n"); printf("Rebooting...\n");
printf(" --reboot Restart the device immediately\n");
printf(" --info Show chip model, revision, and MAC\n");
printf(" --heap Show current memory statistics\n");
return 0;
}
// --- HEAP STATS ---
if (sys_args.heap->count > 0) {
uint32_t free_heap = esp_get_free_heap_size();
uint32_t min_heap = esp_get_minimum_free_heap_size();
printf("Heap Summary:\n");
printf(" Free Heap: %" PRIu32 " bytes\n", free_heap);
printf(" Min Free Ever: %" PRIu32 " bytes\n", min_heap);
uint32_t internal = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
uint32_t dma = heap_caps_get_free_size(MALLOC_CAP_DMA);
printf(" Internal (SRAM): %" PRIu32 " bytes\n", internal);
printf(" DMA Capable: %" PRIu32 " bytes\n", dma);
}
// --- CHIP INFO ---
if (sys_args.info->count > 0) {
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
printf("Hardware Info:\n");
printf(" Model: %s\n", get_chip_model_string(chip_info.model));
printf(" Cores: %d\n", chip_info.cores);
// Revision format is typically MXX (Major, Minor)
printf(" Revision: v%d.%02d\n", chip_info.revision / 100, chip_info.revision % 100);
printf(" Features: ");
if (chip_info.features & CHIP_FEATURE_WIFI_BGN) printf("WiFi-BGN ");
if (chip_info.features & CHIP_FEATURE_BLE) printf("BLE ");
if (chip_info.features & CHIP_FEATURE_BT) printf("BT ");
if (chip_info.features & CHIP_FEATURE_EMB_FLASH) printf("Emb-Flash ");
if (chip_info.features & CHIP_FEATURE_IEEE802154) printf("802.15.4 (Zigbee/Thread) ");
printf("\n");
uint8_t mac[6];
if (esp_read_mac(mac, ESP_MAC_WIFI_STA) == ESP_OK) {
printf(" MAC (STA): %02x:%02x:%02x:%02x:%02x:%02x\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
if (esp_read_mac(mac, ESP_MAC_WIFI_SOFTAP) == ESP_OK) {
printf(" MAC (AP): %02x:%02x:%02x:%02x:%02x:%02x\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
}
// --- REBOOT ---
if (sys_args.reboot->count > 0) {
ESP_LOGW(TAG, "Reboot requested by user.");
printf("Rebooting system in 1 second...\n");
vTaskDelay(pdMS_TO_TICKS(1000));
esp_restart(); esp_restart();
return 0;
}
static int do_version(int argc, char **argv) {
printf("APP_VERSION: %s\n", APP_VERSION);
printf("IDF_VERSION: %s\n", esp_get_idf_version());
return 0;
}
static int do_system_info(int argc, char **argv) {
esp_chip_info_t info;
esp_chip_info(&info);
const char *model_str = get_chip_model_string(info.model);
printf("IDF Version: %s\n", esp_get_idf_version());
if (strcmp(model_str, "Unknown") == 0) {
printf("Chip Info: Model=%s (ID=%d), Cores=%d, Revision=%d\n",
model_str, info.model, info.cores, info.revision);
} else {
printf("Chip Info: Model=%s, Cores=%d, Revision=%d\n",
model_str, info.cores, info.revision);
} }
uint32_t flash_size = 0;
if (esp_flash_get_size(NULL, &flash_size) == ESP_OK) {
printf("Flash Size: %" PRIu32 " MB\n", flash_size / (1024 * 1024));
} else {
printf("Flash Size: Unknown\n");
}
printf("Features: %s%s%s%s%s\n",
(info.features & CHIP_FEATURE_WIFI_BGN) ? "802.11bgn " : "",
(info.features & CHIP_FEATURE_BLE) ? "BLE " : "",
(info.features & CHIP_FEATURE_BT) ? "BT " : "",
(info.features & CHIP_FEATURE_IEEE802154) ? "802.15.4 " : "",
(info.features & CHIP_FEATURE_EMB_FLASH) ? "Embedded-Flash " : "");
return 0; return 0;
} }
void register_system_cmd(void) { static int do_system_heap(int argc, char **argv) {
sys_args.reboot = arg_lit0(NULL, "reboot", "Reboot device"); printf("Heap Info:\n");
sys_args.info = arg_lit0(NULL, "info", "Chip Info"); printf(" Free: %" PRIu32 " bytes\n", esp_get_free_heap_size());
sys_args.heap = arg_lit0(NULL, "heap", "Memory Info"); printf(" Min Free: %" PRIu32 " bytes\n", esp_get_minimum_free_heap_size());
sys_args.help = arg_lit0("h", "help", "Help"); return 0;
sys_args.end = arg_end(1); }
static int cmd_system(int argc, char **argv) {
if (argc < 2) {
printf("Usage: system <reboot|info|heap>\n");
return 0;
}
if (strcmp(argv[1], "reboot") == 0) return do_system_reboot(argc - 1, &argv[1]);
if (strcmp(argv[1], "info") == 0) return do_system_info(argc - 1, &argv[1]);
if (strcmp(argv[1], "heap") == 0) return do_system_heap(argc - 1, &argv[1]);
printf("Unknown subcommand '%s'.\n", argv[1]);
return 1;
}
// --- Registration ---
void register_system_cmd(void) {
const esp_console_cmd_t cmd = { const esp_console_cmd_t cmd = {
.command = "system", .command = "system",
.help = "System Management (Reboot, Info, Heap)", .help = "System Tools: reboot, info, heap",
.hint = "<subcommand>",
.func = &cmd_system, .func = &cmd_system,
.argtable = &sys_args };
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
void register_reset_cmd(void) {
const esp_console_cmd_t cmd = {
.command = "reset",
.help = "Software reset of the device",
.func = &do_system_reboot,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
void register_version_cmd(void) {
const esp_console_cmd_t cmd = {
.command = "version",
.help = "Get firmware version",
.func = &do_version,
}; };
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
} }

View File

@ -35,362 +35,185 @@
#include "esp_log.h" #include "esp_log.h"
#include "esp_console.h" #include "esp_console.h"
#include "argtable3/argtable3.h" #include "argtable3/argtable3.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_wifi.h" #include "esp_wifi.h"
#include "esp_netif.h"
#include "wifi_cfg.h" #include "wifi_cfg.h"
#include "wifi_controller.h" #include "wifi_controller.h"
#include "app_console.h" #include "app_console.h"
// --- Forward Declarations --- // --- Arguments ---
static int wifi_do_scan(int argc, char **argv); static struct {
static int wifi_do_connect(int argc, char **argv); struct arg_str *ssid;
static int wifi_do_disconnect(int argc, char **argv); struct arg_str *password;
static int wifi_do_link(int argc, char **argv); struct arg_end *end;
static int wifi_do_power(int argc, char **argv); } connect_args;
static int wifi_do_mode(int argc, char **argv);
// ============================================================================ static struct {
// COMMAND: wifi (Dispatcher) struct arg_str *mode; // "sta", "monitor", "ap"
// ============================================================================ struct arg_int *channel;
struct arg_end *end;
} mode_args;
static struct {
struct arg_int *tx_power;
struct arg_end *end;
} power_args;
static void print_wifi_usage(void) { static void print_wifi_usage(void) {
printf("Usage: wifi <subcommand> [args]\n"); printf("Usage: wifi <subcommand> [args]\n");
printf("Subcommands:\n"); printf("Subcommands:\n");
printf(" scan Scan for networks\n"); printf(" scan Scan for networks\n");
printf(" connect Connect to an AP (alias: join)\n"); printf(" connect <ssid> [<pass>] Connect to AP\n");
printf(" disconnect Disconnect from AP\n"); printf(" status Show connection info\n");
printf(" status Show connection status (alias: link)\n"); printf(" mode <sta|monitor> [-c <ch>] Switch Mode\n");
printf(" power Set power save (on/off)\n"); printf(" power <dBm> Set TX Power (8-84, 0.25dB units)\n");
printf(" mode Set mode (sta/monitor)\n");
printf("\nType 'wifi <subcommand> --help' for details.\n");
} }
static int cmd_wifi(int argc, char **argv) { // --- Handlers ---
if (argc < 2 || strcmp(argv[1], "help") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) {
print_wifi_usage();
return 0;
}
if (strcmp(argv[1], "scan") == 0) return wifi_do_scan(argc - 1, &argv[1]);
if (strcmp(argv[1], "connect") == 0) return wifi_do_connect(argc - 1, &argv[1]);
if (strcmp(argv[1], "join") == 0) return wifi_do_connect(argc - 1, &argv[1]);
if (strcmp(argv[1], "disconnect") == 0) return wifi_do_disconnect(argc - 1, &argv[1]);
if (strcmp(argv[1], "status") == 0) return wifi_do_link(argc - 1, &argv[1]);
if (strcmp(argv[1], "link") == 0) return wifi_do_link(argc - 1, &argv[1]);
if (strcmp(argv[1], "power") == 0) return wifi_do_power(argc - 1, &argv[1]);
if (strcmp(argv[1], "mode") == 0) return wifi_do_mode(argc - 1, &argv[1]);
printf("Unknown subcommand '%s'.\n", argv[1]);
print_wifi_usage();
return 1;
}
// ----------------------------------------------------------------------------
// Sub-command: wifi status / link
// ----------------------------------------------------------------------------
static struct {
struct arg_lit *help;
struct arg_end *end;
} link_args;
static int wifi_do_link(int argc, char **argv) {
link_args.help = arg_lit0("h", "help", "Help");
link_args.end = arg_end(1);
if (arg_parse(argc, argv, (void **)&link_args) == 0) {
if (link_args.help->count > 0) {
printf("Usage: wifi status\nDescription: Show connection status.\n");
goto exit;
}
}
wifi_ap_record_t ap_info;
if (esp_wifi_sta_get_ap_info(&ap_info) == ESP_OK) {
printf("Connected to %02x:%02x:%02x:%02x:%02x:%02x (on esp32)\n",
ap_info.bssid[0], ap_info.bssid[1], ap_info.bssid[2],
ap_info.bssid[3], ap_info.bssid[4], ap_info.bssid[5]);
printf("\tSSID: %s\n", ap_info.ssid);
printf("\tfreq: %d (Primary)\n", ap_info.primary);
printf("\tsignal: %d dBm\n", ap_info.rssi);
esp_netif_t *netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
esp_netif_ip_info_t ip;
if (netif && esp_netif_get_ip_info(netif, &ip) == ESP_OK) {
printf("\tIP: " IPSTR "\n", IP2STR(&ip.ip));
}
} else {
printf("Not connected.\n");
}
wifi_ps_type_t ps_type;
if (esp_wifi_get_ps(&ps_type) == ESP_OK) {
printf("\tPower Save: %s\n", (ps_type == WIFI_PS_NONE) ? "off" : "on");
}
exit:
return 0;
}
// ----------------------------------------------------------------------------
// Sub-command: wifi power
// ----------------------------------------------------------------------------
static struct {
struct arg_lit *on;
struct arg_lit *off;
struct arg_lit *help;
struct arg_end *end;
} power_args;
static int wifi_do_power(int argc, char **argv) {
power_args.on = arg_lit0(NULL, "on", "Enable Power Save");
power_args.off = arg_lit0(NULL, "off", "Disable Power Save");
power_args.help = arg_lit0("h", "help", "Help");
power_args.end = arg_end(1);
int nerrors = arg_parse(argc, argv, (void **)&power_args);
if (nerrors > 0) {
arg_print_errors(stderr, power_args.end, argv[0]);
return 1;
}
if (power_args.help->count > 0) {
printf("Usage: wifi power [on|off]\n");
return 0;
}
if (power_args.on->count > 0) {
esp_wifi_set_ps(WIFI_PS_MIN_MODEM);
printf("Power save enabled (WIFI_PS_MIN_MODEM).\n");
} else if (power_args.off->count > 0) {
esp_wifi_set_ps(WIFI_PS_NONE);
printf("Power save disabled (WIFI_PS_NONE).\n");
} else {
wifi_ps_type_t ps_type;
esp_wifi_get_ps(&ps_type);
printf("Current Power Save Mode: %s\n", (ps_type == WIFI_PS_NONE) ? "OFF" : "ON");
}
return 0;
}
// ----------------------------------------------------------------------------
// Sub-command: wifi mode
// ----------------------------------------------------------------------------
static struct {
struct arg_lit *sta;
struct arg_lit *monitor;
struct arg_lit *help;
struct arg_end *end;
} mode_args;
static int wifi_do_mode(int argc, char **argv) {
mode_args.sta = arg_lit0(NULL, "sta", "Station Mode");
mode_args.monitor = arg_lit0(NULL, "monitor", "Monitor Mode");
mode_args.help = arg_lit0("h", "help", "Help");
mode_args.end = arg_end(1);
int nerrors = arg_parse(argc, argv, (void **)&mode_args);
if (nerrors > 0) {
arg_print_errors(stderr, mode_args.end, argv[0]);
return 1;
}
if (mode_args.help->count > 0) {
printf("Usage: wifi mode [sta|monitor]\n");
return 0;
}
if (mode_args.sta->count > 0) {
wifi_ctl_switch_to_sta(WIFI_BW_HT20);
printf("Switched to STATION mode.\n");
} else if (mode_args.monitor->count > 0) {
wifi_ctl_switch_to_monitor(0, WIFI_BW_HT20);
printf("Switched to MONITOR mode.\n");
} else {
wifi_ctl_mode_t mode = wifi_ctl_get_mode();
printf("Current Mode: %s\n", (mode == WIFI_CTL_MODE_MONITOR) ? "MONITOR" : "STATION");
}
return 0;
}
// ----------------------------------------------------------------------------
// Sub-command: wifi scan
// ----------------------------------------------------------------------------
static struct {
struct arg_lit *help;
struct arg_end *end;
} scan_args;
static int wifi_do_scan(int argc, char **argv) { static int wifi_do_scan(int argc, char **argv) {
scan_args.help = arg_lit0("h", "help", "Help");
scan_args.end = arg_end(1);
if (arg_parse(argc, argv, (void **)&scan_args) == 0) {
if (scan_args.help->count > 0) {
printf("Usage: wifi scan\n");
return 0;
}
}
if (wifi_ctl_get_mode() == WIFI_CTL_MODE_MONITOR) {
printf("Error: Cannot scan while in Monitor Mode.\n");
return 1;
}
wifi_scan_config_t scan_config = {0};
scan_config.show_hidden = true;
printf("Scanning...\n"); printf("Scanning...\n");
esp_err_t err = esp_wifi_scan_start(&scan_config, true); wifi_scan_config_t scan_config = {0};
if (err == ESP_ERR_WIFI_STATE) { esp_wifi_scan_start(&scan_config, true); // Block until done
printf("WARN: Interface busy. Forcing disconnect for scan...\n");
esp_wifi_disconnect();
vTaskDelay(pdMS_TO_TICKS(100));
err = esp_wifi_scan_start(&scan_config, true);
}
if (err != ESP_OK) {
printf("Scan failed: %s\n", esp_err_to_name(err));
return 1;
}
uint16_t ap_count = 0;
esp_wifi_scan_get_ap_num(&ap_count);
if (ap_count == 0) {
printf("No networks found.\n");
return 0;
}
wifi_ap_record_t *ap_info = (wifi_ap_record_t *)malloc(sizeof(wifi_ap_record_t) * ap_count);
if (!ap_info) {
printf("Out of memory.\n");
return 1;
}
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_count, ap_info));
printf("Found %u unique BSSIDs\n", ap_count);
// FORMATTING: Using exact width specifiers for Header and Data to ensure alignment uint16_t ap_num = 0;
// SSID: 32 chars | BSSID: 17 chars | RSSI: 4 chars | Ch: 3 chars | Auth esp_wifi_scan_get_ap_num(&ap_num);
printf("%-32s | %-17s | %4s | %3s | %s\n", "SSID", "BSSID", "RSSI", "Ch", "Auth");
printf("--------------------------------------------------------------------------------------\n");
for (int i = 0; i < ap_count; i++) { wifi_ap_record_t *ap_list = (wifi_ap_record_t *)malloc(ap_num * sizeof(wifi_ap_record_t));
char *authmode = "UNK"; ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_num, ap_list));
switch (ap_info[i].authmode) {
case WIFI_AUTH_OPEN: authmode = "OPEN"; break;
case WIFI_AUTH_WEP: authmode = "WEP"; break;
case WIFI_AUTH_WPA_PSK: authmode = "WPA"; break;
case WIFI_AUTH_WPA2_PSK: authmode = "WPA2"; break;
case WIFI_AUTH_WPA_WPA2_PSK: authmode = "WPA/2"; break;
case WIFI_AUTH_WPA3_PSK: authmode = "WPA3"; break;
case WIFI_AUTH_WPA2_WPA3_PSK: authmode = "W2/W3"; break;
default: break;
}
printf("%-32.32s | %02x:%02x:%02x:%02x:%02x:%02x | %4d | %3d | %s\n", printf("Found %d APs:\n", ap_num);
ap_info[i].ssid, printf("%-32s | %-4s | %-4s | %-18s\n", "SSID", "RSSI", "CH", "BSSID");
ap_info[i].bssid[0], ap_info[i].bssid[1], ap_info[i].bssid[2], for (int i = 0; i < ap_num; i++) {
ap_info[i].bssid[3], ap_info[i].bssid[4], ap_info[i].bssid[5], printf("%-32s | %-4d | %-4d | %02x:%02x:%02x:%02x:%02x:%02x\n",
ap_info[i].rssi, ap_info[i].primary, authmode); ap_list[i].ssid, ap_list[i].rssi, ap_list[i].primary,
ap_list[i].bssid[0], ap_list[i].bssid[1], ap_list[i].bssid[2],
ap_list[i].bssid[3], ap_list[i].bssid[4], ap_list[i].bssid[5]);
} }
printf("--------------------------------------------------------------------------------------\n"); free(ap_list);
free(ap_info);
return 0; return 0;
} }
// ----------------------------------------------------------------------------
// Sub-command: wifi connect / join
// ----------------------------------------------------------------------------
static struct {
struct arg_str *ssid;
struct arg_str *password;
struct arg_str *bssid;
struct arg_lit *help;
struct arg_end *end;
} connect_args;
// ... [Keep existing includes and declarations] ...
static int wifi_do_connect(int argc, char **argv) { static int wifi_do_connect(int argc, char **argv) {
// Initialize argtable members
connect_args.ssid = arg_str1(NULL, NULL, "<ssid>", "SSID");
connect_args.password = arg_str0(NULL, NULL, "<pass>", "Password");
connect_args.bssid = arg_str0("b", "bssid", "<xx:xx...>", "Lock BSSID");
connect_args.help = arg_lit0("h", "help", "Help");
connect_args.end = arg_end(2);
int nerrors = arg_parse(argc, argv, (void **)&connect_args); int nerrors = arg_parse(argc, argv, (void **)&connect_args);
if (nerrors > 0) { if (nerrors > 0) {
arg_print_errors(stderr, connect_args.end, argv[0]); arg_print_errors(stderr, connect_args.end, argv[0]);
return 1; return 1;
} }
if (connect_args.help->count > 0) {
printf("Usage: wifi connect <ssid> [password] [-b <bssid>]\n");
return 0;
}
const char *ssid = connect_args.ssid->sval[0]; const char *ssid = connect_args.ssid->sval[0];
const char *pass = (connect_args.password->count > 0) ? connect_args.password->sval[0] : ""; const char *pass = (connect_args.password->count > 0) ? connect_args.password->sval[0] : "";
const char *bssid = (connect_args.bssid->count > 0) ? connect_args.bssid->sval[0] : "";
// 1. Ensure we are in Station Mode // Ensure we are in STA mode first
wifi_ctl_mode_t current_mode = wifi_ctl_get_mode(); wifi_ctl_mode_t current_mode = wifi_ctl_get_mode();
if (current_mode != WIFI_CTL_MODE_STA) { if (current_mode != WIFI_CTL_MODE_STA) {
printf("Switching to Station Mode first...\n"); printf("Switching to Station Mode first...\n");
wifi_ctl_switch_to_sta(WIFI_BW_HT20); wifi_ctl_switch_to_sta(); // Fixed: Removed argument
} }
printf("Connecting to '%s' (BSSID: %s)...\n", ssid, bssid[0] ? bssid : "Any"); printf("Connecting to '%s'...\n", ssid);
// 2. Save to NVS (Persistent) // Save to NVS
wifi_cfg_set_credentials(ssid, pass, bssid); wifi_cfg_set_ssid(ssid);
wifi_cfg_set_dhcp(true); wifi_cfg_set_password(pass);
// 3. Apply to Runtime Driver (Hot Reload) // Apply
wifi_config_t wifi_config = {0}; wifi_config_t wifi_config = {0};
strlcpy((char *)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid)); strncpy((char *)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid));
strlcpy((char *)wifi_config.sta.password, pass, sizeof(wifi_config.sta.password)); strncpy((char *)wifi_config.sta.password, pass, sizeof(wifi_config.sta.password));
// Default security settings ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK; ESP_ERROR_CHECK(esp_wifi_connect());
wifi_config.sta.pmf_cfg.capable = true;
wifi_config.sta.pmf_cfg.required = false;
// Handle BSSID Locking return 0;
if (bssid[0]) { }
unsigned int mac[6];
if (sscanf(bssid, "%02x:%02x:%02x:%02x:%02x:%02x", static int wifi_do_status(int argc, char **argv) {
&mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) == 6) { wifi_ctl_status();
for(int i=0; i<6; i++) wifi_config.sta.bssid[i] = (uint8_t)mac[i]; return 0;
wifi_config.sta.bssid_set = true; }
static int wifi_do_mode(int argc, char **argv) {
int nerrors = arg_parse(argc, argv, (void **)&mode_args);
if (nerrors > 0) {
arg_print_errors(stderr, mode_args.end, argv[0]);
return 1;
}
const char *mode_str = mode_args.mode->sval[0];
int channel = (mode_args.channel->count > 0) ? mode_args.channel->ival[0] : 0;
if (strcmp(mode_str, "sta") == 0) {
wifi_ctl_switch_to_sta(); // Fixed: Removed argument
} else if (strcmp(mode_str, "monitor") == 0) {
wifi_ctl_switch_to_monitor(channel, WIFI_BW_HT20);
} else { } else {
printf("Warning: Invalid BSSID format ignored.\n"); printf("Unknown mode '%s'. Use 'sta' or 'monitor'.\n", mode_str);
return 1;
} }
return 0;
}
static int wifi_do_power(int argc, char **argv) {
int nerrors = arg_parse(argc, argv, (void **)&power_args);
if (nerrors > 0) {
arg_print_errors(stderr, power_args.end, argv[0]);
return 1;
} }
// Force Disconnect -> Set Config -> Reconnect int pwr = power_args.tx_power->ival[0];
esp_wifi_disconnect(); esp_err_t err = esp_wifi_set_max_tx_power(pwr);
esp_err_t err = esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
if (err == ESP_OK) { if (err == ESP_OK) {
esp_wifi_connect(); printf("TX Power set to %d (approx %.2f dBm)\n", pwr, pwr * 0.25);
printf("Connection initiated (Check 'wifi status').\n");
} else { } else {
printf("Failed to apply configuration: %s\n", esp_err_to_name(err)); printf("Failed to set TX power: %s\n", esp_err_to_name(err));
}
return 0;
}
static int cmd_wifi(int argc, char **argv) {
if (argc < 2) {
print_wifi_usage();
return 0;
} }
return 0; if (strcmp(argv[1], "scan") == 0) return wifi_do_scan(argc - 1, &argv[1]);
} if (strcmp(argv[1], "connect") == 0) return wifi_do_connect(argc - 1, &argv[1]);
if (strcmp(argv[1], "status") == 0) return wifi_do_status(argc - 1, &argv[1]);
if (strcmp(argv[1], "mode") == 0) return wifi_do_mode(argc - 1, &argv[1]);
if (strcmp(argv[1], "power") == 0) return wifi_do_power(argc - 1, &argv[1]);
// ---------------------------------------------------------------------------- if (strcmp(argv[1], "help") == 0 || strcmp(argv[1], "--help") == 0) {
// Sub-command: wifi disconnect print_wifi_usage();
// ----------------------------------------------------------------------------
static int wifi_do_disconnect(int argc, char **argv) {
printf("Disconnecting...\n");
esp_wifi_disconnect();
return 0; return 0;
} }
// ---------------------------------------------------------------------------- printf("Unknown subcommand '%s'.\n", argv[1]);
// Registration print_wifi_usage();
// ---------------------------------------------------------------------------- return 1;
}
void register_wifi_cmd(void) { void register_wifi_cmd(void) {
// Connect Args
connect_args.ssid = arg_str1(NULL, NULL, "<ssid>", "SSID");
connect_args.password = arg_str0(NULL, NULL, "<pass>", "Password");
connect_args.end = arg_end(2);
// Mode Args
mode_args.mode = arg_str1(NULL, NULL, "<mode>", "sta | monitor");
mode_args.channel = arg_int0("c", "channel", "<n>", "Channel (Monitor only)");
mode_args.end = arg_end(2);
// Power Args
power_args.tx_power = arg_int1(NULL, NULL, "<dBm>", "Power (8-84)");
power_args.end = arg_end(1);
const esp_console_cmd_t cmd = { const esp_console_cmd_t cmd = {
.command = "wifi", .command = "wifi",
.help = "Wi-Fi Tool: scan, connect, status, power, mode", .help = "Wi-Fi Utils: scan, connect, mode, status, power",
.hint = "<subcommand> [args]", .hint = "<subcommand>",
.func = &cmd_wifi, .func = &cmd_wifi,
.argtable = NULL
}; };
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
} }

View File

@ -30,341 +30,230 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "gps_sync.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "esp_timer.h"
#include "esp_log.h"
#include "esp_rom_sys.h"
#include <string.h>
#include <time.h>
#include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <assert.h> #include <string.h>
#include <inttypes.h> #include <stdlib.h>
#include <sys/time.h>
#include <time.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "gps_sync.h"
static const char *TAG = "GPS_SYNC"; static const char *TAG = "GPS_SYNC";
#define GPS_BAUD_RATE 9600 #define GPS_BUF_SIZE 1024
#define UART_BUF_SIZE 1024
// --- GLOBAL STATE --- // --- Internal State ---
static uart_port_t gps_uart_num = UART_NUM_1; static gps_sync_config_t s_cfg;
static int64_t monotonic_offset_us = 0; static volatile int64_t s_last_pps_us = 0;
static volatile int64_t last_pps_monotonic = 0; static volatile int64_t s_nmea_epoch_us = 0;
static bool gps_has_fix = false; static volatile bool s_nmea_valid = false;
static bool use_gps_for_logs = false; static char s_last_nmea_msg[128] = {0};
static SemaphoreHandle_t sync_mutex; static bool s_time_set = false;
static volatile bool force_sync_update = true;
// Debug Buffer // --- PPS Handler ---
static char s_last_nmea_line[128] = "<WAITING FOR DATA>"; static void IRAM_ATTR pps_gpio_isr_handler(void* arg) {
s_last_pps_us = esp_timer_get_time();
// PPS interrupt: Captures the exact monotonic time of the rising edge
static void IRAM_ATTR pps_isr_handler(void* arg) {
int64_t now = esp_timer_get_time();
last_pps_monotonic = now;
} }
// Parse GPS time from NMEA (GPRMC or GNRMC) // --- Time Helper ---
static bool parse_gprmc(const char* nmea, struct tm* tm_out, bool* valid) { static void set_system_time(char *time_str, char *date_str) {
if (strncmp(nmea, "$GPRMC", 6) != 0 && strncmp(nmea, "$GNRMC", 6) != 0) return false; // time_str: HHMMSS.ss (e.g., 123519.00)
// date_str: DDMMYY (e.g., 230394)
char *p = strchr(nmea, ','); struct tm tm_info = {0};
if (!p) return false;
p++; // Move past comma
int hour, min, sec; // Parse Time
if (sscanf(p, "%2d%2d%2d", &hour, &min, &sec) != 3) return false; int h, m, s;
if (sscanf(time_str, "%2d%2d%2d", &h, &m, &s) != 3) return;
tm_info.tm_hour = h;
tm_info.tm_min = m;
tm_info.tm_sec = s;
p = strchr(p, ','); // Parse Date
if (!p) return false; int day, mon, year;
if (sscanf(date_str, "%2d%2d%2d", &day, &mon, &year) != 3) return;
tm_info.tm_mday = day;
tm_info.tm_mon = mon - 1; // 0-11
tm_info.tm_year = year + 100; // Years since 1900 (2025 -> 125)
time_t t = mktime(&tm_info);
if (t == -1) return;
struct timeval tv = { .tv_sec = t, .tv_usec = 0 };
// Simple sync: Only set if not set, or if drift is massive (>2s)
// In a real PTP/GPS app you'd use a PLL here, but this is a shell tool.
struct timeval now;
gettimeofday(&now, NULL);
if (!s_time_set || llabs(now.tv_sec - t) > 2) {
settimeofday(&tv, NULL);
s_time_set = true;
ESP_LOGI(TAG, "System Time Updated to GPS: %s", asctime(&tm_info));
}
}
// --- NMEA Parser ---
static void parse_nmea_line(char *line) {
strlcpy(s_last_nmea_msg, line, sizeof(s_last_nmea_msg));
// Support GPRMC and GNRMC
if (strncmp(line, "$GPRMC", 6) == 0 || strncmp(line, "$GNRMC", 6) == 0) {
char *p = line;
int field = 0;
char *time_ptr = NULL;
char *date_ptr = NULL;
char status = 'V';
// Walk fields
// $GPRMC,Time,Status,Lat,NS,Lon,EW,Spd,Trk,Date,...
// Field 1: Time
// Field 2: Status
// Field 9: Date
while ((p = strchr(p, ',')) != NULL) {
p++; p++;
*valid = (*p == 'A'); field++;
for (int i = 0; i < 7; i++) { if (field == 1) time_ptr = p;
p = strchr(p, ','); else if (field == 2) status = *p;
if (!p) return false; else if (field == 9) {
p++; date_ptr = p;
break; // We have what we need
}
} }
int day, month, year; s_nmea_valid = (status == 'A');
if (sscanf(p, "%2d%2d%2d", &day, &month, &year) != 3) return false;
year += (year < 80) ? 2000 : 1900; if (s_nmea_valid) {
s_nmea_epoch_us = esp_timer_get_time();
tm_out->tm_sec = sec; // Extract substrings for Time/Date (comma terminated)
tm_out->tm_min = min; if (time_ptr && date_ptr) {
tm_out->tm_hour = hour; char t_buf[16] = {0};
tm_out->tm_mday = day; char d_buf[16] = {0};
tm_out->tm_mon = month - 1;
tm_out->tm_year = year - 1900;
tm_out->tm_isdst = 0;
return true; char *end = strchr(time_ptr, ',');
if (end) {
int len = end - time_ptr;
if (len < sizeof(t_buf)) {
memcpy(t_buf, time_ptr, len);
t_buf[len] = 0;
}
}
end = strchr(date_ptr, ',');
if (end) {
int len = end - date_ptr;
if (len < sizeof(d_buf)) {
memcpy(d_buf, date_ptr, len);
d_buf[len] = 0;
}
}
// Update System Clock
if (t_buf[0] && d_buf[0]) {
set_system_time(t_buf, d_buf);
}
}
}
}
} }
void gps_force_next_update(void) { // --- UART Task ---
force_sync_update = true; static void gps_task(void *pvParameters) {
ESP_LOGW(TAG, "Requesting forced GPS sync update"); uint8_t *data = (uint8_t *)malloc(GPS_BUF_SIZE);
} if (!data) {
ESP_LOGE(TAG, "Failed to allocate GPS buffer");
vTaskDelete(NULL);
return;
}
static time_t timegm_impl(struct tm *tm) { char line_buf[128];
time_t t = mktime(tm); int line_pos = 0;
return t;
}
static void gps_task(void* arg) {
uint8_t d_buf[64];
char line[128];
int pos = 0;
static int log_counter = 0;
setenv("TZ", "UTC", 1);
tzset();
while (1) { while (1) {
int len = uart_read_bytes(gps_uart_num, d_buf, sizeof(d_buf), pdMS_TO_TICKS(100)); int len = uart_read_bytes(s_cfg.uart_port, data, GPS_BUF_SIZE, 20 / portTICK_PERIOD_MS);
if (len > 0) { if (len > 0) {
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
uint8_t data = d_buf[i]; char c = (char)data[i];
if (c == '\n' || c == '\r') {
if (data == '\n' || data == '\r') { if (line_pos > 0) {
if (pos > 0) { line_buf[line_pos] = 0;
line[pos] = '\0'; parse_nmea_line(line_buf);
line_pos = 0;
// Copy to debug buffer
xSemaphoreTake(sync_mutex, portMAX_DELAY);
strncpy(s_last_nmea_line, line, sizeof(s_last_nmea_line) - 1);
s_last_nmea_line[sizeof(s_last_nmea_line) - 1] = '\0';
xSemaphoreGive(sync_mutex);
struct tm gps_tm;
bool valid_fix;
if (parse_gprmc(line, &gps_tm, &valid_fix)) {
if (valid_fix) {
time_t gps_time_sec = timegm_impl(&gps_tm);
int64_t last_pps_snap = last_pps_monotonic;
int64_t now = esp_timer_get_time();
int64_t age_us = now - last_pps_snap;
if (last_pps_snap > 0 && age_us < 900000) {
int64_t gps_time_us = (int64_t)gps_time_sec * 1000000LL;
int64_t new_offset = gps_time_us - last_pps_snap;
xSemaphoreTake(sync_mutex, portMAX_DELAY);
if (monotonic_offset_us == 0 || force_sync_update) {
monotonic_offset_us = new_offset;
if (force_sync_update) {
ESP_LOGW(TAG, "GPS SNAP: Offset forced to %" PRIi64 " us", monotonic_offset_us);
force_sync_update = false;
log_counter = 0;
}
} else {
monotonic_offset_us = (monotonic_offset_us * 9 + new_offset) / 10;
}
gps_has_fix = true;
xSemaphoreGive(sync_mutex);
// Periodic Logging
if (log_counter <= 0) {
// CHANGED FROM ESP_LOGI TO ESP_LOGD (Hidden by default)
ESP_LOGD(TAG, "GPS Sync: %02d:%02d:%02d | Offset: %" PRIi64 " us | PPS Age: %" PRIi64 " ms",
gps_tm.tm_hour, gps_tm.tm_min, gps_tm.tm_sec,
monotonic_offset_us, age_us / 1000);
log_counter = 10;
}
log_counter--;
} else {
// Keep Warnings visible
if (log_counter <= 0) {
ESP_LOGW(TAG, "GPS valid but PPS missing/old (Age: %" PRIi64 " ms)", age_us / 1000);
log_counter = 10;
}
log_counter--;
}
} else {
gps_has_fix = false;
}
}
}
pos = 0;
} else {
if (pos < sizeof(line) - 1) {
line[pos++] = data;
} }
} else if (line_pos < sizeof(line_buf) - 1) {
line_buf[line_pos++] = c;
} }
} }
} }
} }
free(data);
vTaskDelete(NULL);
} }
void gps_get_last_nmea(char *buf, size_t max_len) { // --- API ---
if (!buf || max_len == 0) return;
xSemaphoreTake(sync_mutex, portMAX_DELAY);
strncpy(buf, s_last_nmea_line, max_len);
buf[max_len - 1] = '\0';
xSemaphoreGive(sync_mutex);
}
void gps_sync_init(const gps_sync_config_t *config, bool use_gps_log_timestamps) { void gps_sync_init(const gps_sync_config_t *cfg, bool force_enable) {
ESP_LOGI(TAG, "Initializing GPS Sync (UART %d, PPS GPIO %d)", config->uart_port, config->pps_pin); if (!cfg) return;
s_cfg = *cfg;
gpio_config_t pps_poll_conf = {
.pin_bit_mask = (1ULL << config->pps_pin),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
ESP_ERROR_CHECK(gpio_config(&pps_poll_conf));
bool pps_detected = false;
int start_level = gpio_get_level(config->pps_pin);
for (int i = 0; i < 2000; i++) {
if (gpio_get_level(config->pps_pin) != start_level) {
pps_detected = true;
break;
}
vTaskDelay(pdMS_TO_TICKS(1));
}
if (!pps_detected) {
ESP_LOGW(TAG, "No PPS signal detected on GPIO %d during boot check.", config->pps_pin);
} else {
ESP_LOGI(TAG, "PPS signal activity detected.");
}
gps_uart_num = config->uart_port;
use_gps_for_logs = use_gps_log_timestamps;
gps_force_next_update();
sync_mutex = xSemaphoreCreateMutex();
if (use_gps_log_timestamps) {
esp_log_set_vprintf(gps_log_vprintf);
}
uart_config_t uart_config = { uart_config_t uart_config = {
.baud_rate = GPS_BAUD_RATE, .baud_rate = 9600,
.data_bits = UART_DATA_8_BITS, .data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE, .parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1, .stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT, .source_clk = UART_SCLK_DEFAULT,
}; };
uart_driver_install(s_cfg.uart_port, GPS_BUF_SIZE * 2, 0, 0, NULL, 0);
uart_param_config(s_cfg.uart_port, &uart_config);
uart_set_pin(s_cfg.uart_port, s_cfg.tx_pin, s_cfg.rx_pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
ESP_ERROR_CHECK(uart_driver_install(config->uart_port, UART_BUF_SIZE, 0, 0, NULL, 0)); gpio_config_t io_conf = {};
ESP_ERROR_CHECK(uart_param_config(config->uart_port, &uart_config)); io_conf.intr_type = GPIO_INTR_POSEDGE;
ESP_ERROR_CHECK(uart_set_pin(config->uart_port, config->tx_pin, config->rx_pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); io_conf.pin_bit_mask = (1ULL << s_cfg.pps_pin);
io_conf.mode = GPIO_MODE_INPUT;
gpio_config_t pps_intr_conf = { io_conf.pull_up_en = 1;
.intr_type = GPIO_INTR_POSEDGE, gpio_config(&io_conf);
.mode = GPIO_MODE_INPUT,
.pin_bit_mask = (1ULL << config->pps_pin),
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
};
ESP_ERROR_CHECK(gpio_config(&pps_intr_conf));
gpio_install_isr_service(0); gpio_install_isr_service(0);
ESP_ERROR_CHECK(gpio_isr_handler_add(config->pps_pin, pps_isr_handler, NULL)); gpio_isr_handler_add(s_cfg.pps_pin, pps_gpio_isr_handler, NULL);
xTaskCreate(gps_task, "gps_task", 4096, NULL, 5, NULL); xTaskCreate(gps_task, "gps_task", 4096, NULL, 5, NULL);
ESP_LOGI(TAG, "Initialized (UART:%d, PPS:%d)", s_cfg.uart_port, s_cfg.pps_pin);
} }
gps_timestamp_t gps_get_timestamp(void) { gps_timestamp_t gps_get_timestamp(void) {
gps_timestamp_t ts; gps_timestamp_t ts = {0};
clock_gettime(CLOCK_MONOTONIC, &ts.mono_ts); int64_t now_boot = esp_timer_get_time(); // Boot time
ts.monotonic_us = (int64_t)ts.mono_ts.tv_sec * 1000000LL + ts.mono_ts.tv_nsec / 1000; // Check Flags
ts.monotonic_ms = ts.monotonic_us / 1000; ts.synced = (now_boot - s_last_pps_us < 1100000);
ts.valid = s_nmea_valid && (now_boot - s_nmea_epoch_us < 2000000);
xSemaphoreTake(sync_mutex, portMAX_DELAY); // Return WALL CLOCK time (Epoch), not boot time
ts.gps_us = ts.monotonic_us + monotonic_offset_us; struct timeval tv;
ts.synced = gps_has_fix; gettimeofday(&tv, NULL);
xSemaphoreGive(sync_mutex); ts.gps_us = (int64_t)tv.tv_sec * 1000000LL + (int64_t)tv.tv_usec;
ts.gps_ms = ts.gps_us / 1000;
return ts; return ts;
} }
int64_t gps_get_monotonic_ms(void) {
return esp_timer_get_time() / 1000;
}
bool gps_is_synced(void) {
return gps_has_fix;
}
// --- NEW: PPS Age Getter ---
int64_t gps_get_pps_age_ms(void) { int64_t gps_get_pps_age_ms(void) {
if (last_pps_monotonic == 0) return -1; if (s_last_pps_us == 0) return -1;
int64_t now = esp_timer_get_time(); return (esp_timer_get_time() - s_last_pps_us) / 1000;
return (now - last_pps_monotonic) / 1000;
} }
// ---------------- LOGGING SYSTEM INTERCEPTION ---------------- void gps_get_last_nmea(char *buf, size_t buf_len) {
if (buf && buf_len > 0) {
uint32_t gps_log_timestamp(void) { strlcpy(buf, s_last_nmea_msg, buf_len);
return (uint32_t)(esp_timer_get_time() / 1000ULL); }
}
int gps_log_vprintf(const char *fmt, va_list args) {
static char buffer[512];
int ret = vsnprintf(buffer, sizeof(buffer), fmt, args);
assert(ret >= 0);
if (use_gps_for_logs) {
char *timestamp_start = NULL;
for (int i = 0; buffer[i] != '\0' && i < sizeof(buffer) - 20; i++) {
if ((buffer[i] == 'I' || buffer[i] == 'W' || buffer[i] == 'E' ||
buffer[i] == 'D' || buffer[i] == 'V') &&
buffer[i+1] == ' ' && buffer[i+2] == '(') {
timestamp_start = &buffer[i+3];
break;
}
}
if (timestamp_start) {
char *timestamp_end = strchr(timestamp_start, ')');
if (timestamp_end) {
uint32_t monotonic_log_ms = 0;
if (sscanf(timestamp_start, "%lu", &monotonic_log_ms) == 1) {
char reformatted[512];
size_t prefix_len = timestamp_start - buffer;
if (prefix_len > sizeof(reformatted)) prefix_len = sizeof(reformatted);
memcpy(reformatted, buffer, prefix_len);
int decimal_len = 0;
if (gps_has_fix) {
int64_t log_mono_us = (int64_t)monotonic_log_ms * 1000;
int64_t log_gps_us = log_mono_us + monotonic_offset_us;
uint64_t gps_sec = log_gps_us / 1000000;
uint32_t gps_ms = (log_gps_us % 1000000) / 1000;
decimal_len = snprintf(reformatted + prefix_len,
sizeof(reformatted) - prefix_len,
"+%" PRIu64 ".%03lu", gps_sec, gps_ms);
} else {
uint32_t sec = monotonic_log_ms / 1000;
uint32_t ms = monotonic_log_ms % 1000;
decimal_len = snprintf(reformatted + prefix_len,
sizeof(reformatted) - prefix_len,
"*%lu.%03lu", (unsigned long)sec, (unsigned long)ms);
}
strcpy(reformatted + prefix_len + decimal_len, timestamp_end);
return printf("%s", reformatted);
}
}
}
}
return printf("%s", buffer);
} }

View File

@ -32,12 +32,18 @@
*/ */
#pragma once #pragma once
#include "driver/gpio.h"
#include "driver/uart.h"
#include <stdbool.h>
#include <stdint.h>
#include <time.h>
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#ifdef __cplusplus
extern "C" {
#endif
// --- Configuration Struct ---
typedef struct { typedef struct {
uart_port_t uart_port; uart_port_t uart_port;
gpio_num_t tx_pin; gpio_num_t tx_pin;
@ -45,27 +51,26 @@ typedef struct {
gpio_num_t pps_pin; gpio_num_t pps_pin;
} gps_sync_config_t; } gps_sync_config_t;
// --- Timestamp Struct ---
typedef struct { typedef struct {
int64_t monotonic_us; int64_t gps_us; // Current GPS time in microseconds
int64_t monotonic_ms; bool synced; // PPS signal is active and stable (Precision Lock)
int64_t gps_us; bool valid; // NMEA data indicates valid fix ('A' status) (Data Lock)
int64_t gps_ms;
struct timespec mono_ts;
bool synced;
} gps_timestamp_t; } gps_timestamp_t;
void gps_sync_init(const gps_sync_config_t *config, bool use_gps_log_timestamps); // --- Initialization ---
void gps_force_next_update(void); // Initializes the GPS task with specific hardware pins
void gps_sync_init(const gps_sync_config_t *cfg, bool force_enable);
// --- Getters --- // --- Getters ---
gps_timestamp_t gps_get_timestamp(void); gps_timestamp_t gps_get_timestamp(void);
int64_t gps_get_monotonic_ms(void);
bool gps_is_synced(void);
// Check health of the physical PPS signal // Returns milliseconds since the last PPS edge (Diagnostic)
int64_t gps_get_pps_age_ms(void); int64_t gps_get_pps_age_ms(void);
void gps_get_last_nmea(char *buf, size_t max_len);
// --- Logging Hooks --- // Copies the last received NMEA line into buffer (Diagnostic)
uint32_t gps_log_timestamp(void); void gps_get_last_nmea(char *buf, size_t buf_len);
int gps_log_vprintf(const char *fmt, va_list args);
#ifdef __cplusplus
}
#endif

View File

@ -43,6 +43,12 @@
* - GPS Timestamp integration for status reporting. * - GPS Timestamp integration for status reporting.
* @brief ESP32 iPerf Traffic Generator (UDP Client Only) with Trip-Time Support * @brief ESP32 iPerf Traffic Generator (UDP Client Only) with Trip-Time Support
*/ */
/*
* iperf.c
*
* Copyright (c) 2025 Umber Networks & Robert McMahon
* All rights reserved.
*/
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
@ -276,15 +282,16 @@ void iperf_print_status(void) {
iperf_get_stats(&s_stats); iperf_get_stats(&s_stats);
gps_timestamp_t ts = gps_get_timestamp(); gps_timestamp_t ts = gps_get_timestamp();
if (ts.synced && ts.gps_us > 0) { // Check both Synced (PPS) and Valid (NMEA)
if (ts.synced && ts.valid && ts.gps_us > 0) {
time_t now_sec = ts.gps_us / 1000000; time_t now_sec = ts.gps_us / 1000000;
struct tm tm_info; struct tm tm_info;
gmtime_r(&now_sec, &tm_info); gmtime_r(&now_sec, &tm_info);
char time_buf[64]; char time_buf[64];
strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S UTC", &tm_info); strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S UTC", &tm_info);
printf("TIME: %s\n", time_buf); printf("TIME: %s (GPS Locked)\n", time_buf);
} else { } else {
printf("TIME: <Not Synced>\n"); printf("TIME: <Not Synced - PPS:%d NMEA:%d>\n", ts.synced, ts.valid);
} }
char dst_ip[32] = "0.0.0.0"; char dst_ip[32] = "0.0.0.0";
@ -344,6 +351,10 @@ static void iperf_generate_headers(iperf_cfg_t *cfg, uint8_t *buffer, bool gps_s
udp_hdr->flags = htonl(HEADER_EXTEND | HEADER_SEQNO64B | HEADER_TRIPTIME); udp_hdr->flags = htonl(HEADER_EXTEND | HEADER_SEQNO64B | HEADER_TRIPTIME);
udp_hdr->start_tv_sec = htonl(start_time->tv_sec); udp_hdr->start_tv_sec = htonl(start_time->tv_sec);
udp_hdr->start_tv_usec = htonl(start_time->tv_nsec / 1000); udp_hdr->start_tv_usec = htonl(start_time->tv_nsec / 1000);
#if 0
ESP_LOGI(TAG, "TX Start Timestamp: %" PRIu32 ".%06" PRIu32,
(uint32_t)start_time.tv_sec, (uint32_t)(start_time.tv_nsec / 1000));
#endif
} else { } else {
udp_hdr->flags = htonl(HEADER_EXTEND | HEADER_SEQNO64B); udp_hdr->flags = htonl(HEADER_EXTEND | HEADER_SEQNO64B);
} }
@ -403,19 +414,18 @@ static esp_err_t iperf_start_udp_client(iperf_ctrl_t *ctrl) {
// --- CHECK GPS SYNC --- // --- CHECK GPS SYNC ---
gps_timestamp_t gps = gps_get_timestamp(); gps_timestamp_t gps = gps_get_timestamp();
bool gps_synced = gps.synced; // FIX: Must have valid NMEA (absolute time) AND PPS (precision)
bool gps_synced = gps.synced && gps.valid;
struct timespec start_ts = {0}; struct timespec start_ts = {0};
if (gps_synced) { if (gps_synced) {
ESP_LOGI(TAG, "GPS Synced. Enabling Trip-Times (OWD)."); ESP_LOGI(TAG, "GPS Locked (PPS + NMEA). Enabling Trip-Times.");
// Capture START TIME for session
clock_gettime(CLOCK_REALTIME, &start_ts); clock_gettime(CLOCK_REALTIME, &start_ts);
} else { } else {
ESP_LOGW(TAG, "GPS NOT Synced. Trip-Times disabled."); ESP_LOGW(TAG, "GPS NOT Fully Locked (PPS:%d, NMEA:%d). Trip-Times disabled.", gps.synced, gps.valid);
} }
// --- GENERATE HEADERS --- // --- GENERATE HEADERS ---
// We pass the start_ts so it can be written into the start_fq header
iperf_generate_headers(&ctrl->cfg, ctrl->buffer, gps_synced, &start_ts); iperf_generate_headers(&ctrl->cfg, ctrl->buffer, gps_synced, &start_ts);
s_stats.running = true; s_stats.running = true;

View File

@ -30,7 +30,6 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "status_led.h" #include "status_led.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
@ -38,7 +37,7 @@
#include "led_strip.h" #include "led_strip.h"
#include "esp_log.h" #include "esp_log.h"
static const char *TAG = "status_led"; static const char *TAG = "STATUS_LED"; // Added TAG for logging
static led_strip_handle_t s_led_strip = NULL; static led_strip_handle_t s_led_strip = NULL;
static bool s_is_rgb = false; static bool s_is_rgb = false;
@ -50,7 +49,6 @@ static void set_color(uint8_t r, uint8_t g, uint8_t b) {
led_strip_set_pixel(s_led_strip, 0, r, g, b); led_strip_set_pixel(s_led_strip, 0, r, g, b);
led_strip_refresh(s_led_strip); led_strip_refresh(s_led_strip);
} else if (!s_is_rgb && s_gpio_pin >= 0) { } else if (!s_is_rgb && s_gpio_pin >= 0) {
// Simple LED: On if any color component is > 0
gpio_set_level(s_gpio_pin, (r+g+b) > 0); gpio_set_level(s_gpio_pin, (r+g+b) > 0);
} }
} }
@ -59,40 +57,34 @@ static void led_task(void *arg) {
int toggle = 0; int toggle = 0;
while (1) { while (1) {
switch (s_current_state) { switch (s_current_state) {
case LED_STATE_NO_CONFIG: // Yellow (Solid for RGB, Blink for Single) case LED_STATE_NO_CONFIG: // Yellow
if (s_is_rgb) { set_color(20, 20, 0); vTaskDelay(pdMS_TO_TICKS(1000)); } if (s_is_rgb) { set_color(25, 25, 0); vTaskDelay(pdMS_TO_TICKS(1000)); }
else { set_color(1,1,1); vTaskDelay(pdMS_TO_TICKS(100)); set_color(0,0,0); vTaskDelay(pdMS_TO_TICKS(100)); } else { set_color(1,1,1); vTaskDelay(100); set_color(0,0,0); vTaskDelay(100); }
break; break;
case LED_STATE_WAITING: // Blue Blink (Searching) // ... rest of cases identical to your code ...
set_color(0, 0, toggle ? 50 : 0); case LED_STATE_WAITING:
toggle = !toggle; set_color(0, 0, toggle ? 50 : 0); toggle = !toggle;
vTaskDelay(pdMS_TO_TICKS(500)); vTaskDelay(pdMS_TO_TICKS(500));
break; break;
case LED_STATE_CONNECTED: // Green Solid (Idle) case LED_STATE_CONNECTED:
set_color(0, 20, 0); set_color(0, 25, 0); vTaskDelay(pdMS_TO_TICKS(1000));
vTaskDelay(pdMS_TO_TICKS(1000));
break; break;
case LED_STATE_MONITORING: // Blue Solid (Sniffer Mode) case LED_STATE_MONITORING:
set_color(0, 0, 50); set_color(0, 0, 50); vTaskDelay(pdMS_TO_TICKS(1000));
vTaskDelay(pdMS_TO_TICKS(1000));
break; break;
case LED_STATE_TRANSMITTING: // Fast Purple Flash (Busy) case LED_STATE_TRANSMITTING:
set_color(toggle ? 50 : 0, 0, toggle ? 50 : 0); set_color(toggle ? 50 : 0, 0, toggle ? 50 : 0); toggle = !toggle;
toggle = !toggle; vTaskDelay(pdMS_TO_TICKS(50));
vTaskDelay(pdMS_TO_TICKS(200));
break; break;
case LED_STATE_TRANSMITTING_SLOW: // Slow Purple Pulse (Pacing) case LED_STATE_TRANSMITTING_SLOW:
set_color(toggle ? 50 : 0, 0, toggle ? 50 : 0); set_color(toggle ? 50 : 0, 0, toggle ? 50 : 0); toggle = !toggle;
toggle = !toggle;
vTaskDelay(pdMS_TO_TICKS(250)); vTaskDelay(pdMS_TO_TICKS(250));
break; break;
case LED_STATE_STALLED: // Purple Solid (Stalled) case LED_STATE_STALLED:
set_color(50, 0, 50); set_color(50, 0, 50); vTaskDelay(pdMS_TO_TICKS(1000));
vTaskDelay(pdMS_TO_TICKS(1000));
break; break;
case LED_STATE_FAILED: // Red Blink (Error) case LED_STATE_FAILED:
set_color(toggle ? 50 : 0, 0, 0); set_color(toggle ? 50 : 0, 0, 0); toggle = !toggle;
toggle = !toggle;
vTaskDelay(pdMS_TO_TICKS(200)); vTaskDelay(pdMS_TO_TICKS(200));
break; break;
} }
@ -103,40 +95,27 @@ void status_led_init(int gpio_pin, bool is_rgb_strip) {
s_gpio_pin = gpio_pin; s_gpio_pin = gpio_pin;
s_is_rgb = is_rgb_strip; s_is_rgb = is_rgb_strip;
ESP_LOGI(TAG, "Initializing LED on GPIO %d (RGB=%d)", gpio_pin, is_rgb_strip); // --- DIAGNOSTIC LOG ---
ESP_LOGW(TAG, "Initializing LED on GPIO %d (RGB: %d)", gpio_pin, is_rgb_strip);
if (s_is_rgb) { if (s_is_rgb) {
led_strip_config_t s_cfg = { led_strip_config_t s_cfg = { .strip_gpio_num = gpio_pin, .max_leds = 1 };
.strip_gpio_num = gpio_pin, led_strip_rmt_config_t r_cfg = { .resolution_hz = 10 * 1000 * 1000 };
.max_leds = 1,
.led_pixel_format = LED_PIXEL_FORMAT_GRB, // Standard for WS2812
.led_model = LED_MODEL_WS2812, // Specific model
.flags.invert_out = false,
};
led_strip_rmt_config_t r_cfg = {
.resolution_hz = 10 * 1000 * 1000, // 10MHz
.flags.with_dma = false,
};
esp_err_t ret = led_strip_new_rmt_device(&s_cfg, &r_cfg, &s_led_strip); esp_err_t ret = led_strip_new_rmt_device(&s_cfg, &r_cfg, &s_led_strip);
if (ret != ESP_OK) { if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to create RMT LED strip: %s", esp_err_to_name(ret)); ESP_LOGE(TAG, "RMT Device Init Failed: %s", esp_err_to_name(ret));
return; } else {
} ESP_LOGI(TAG, "RMT Device Init Success");
led_strip_clear(s_led_strip); led_strip_clear(s_led_strip);
}
} else { } else {
gpio_reset_pin(gpio_pin); gpio_reset_pin(gpio_pin);
gpio_set_direction(gpio_pin, GPIO_MODE_OUTPUT); gpio_set_direction(gpio_pin, GPIO_MODE_OUTPUT);
gpio_set_level(gpio_pin, 0);
} }
xTaskCreate(led_task, "led_task", 2048, NULL, 5, NULL); xTaskCreate(led_task, "led_task", 2048, NULL, 5, NULL);
} }
void status_led_set_state(led_state_t state) { // ... Setters/Getters ...
s_current_state = state; void status_led_set_state(led_state_t state) { s_current_state = state; }
} led_state_t status_led_get_state(void) { return s_current_state; }
led_state_t status_led_get_state(void) {
return s_current_state;
}

View File

@ -37,235 +37,179 @@
* All rights reserved. * All rights reserved.
*/ */
#include <stdio.h> #include "wifi_cfg.h"
#include <string.h> #include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_wifi.h"
#include "nvs_flash.h" #include "nvs_flash.h"
#include "nvs.h" #include "nvs.h"
#include "esp_netif.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "wifi_cfg.h"
static esp_netif_t *sta_netif = NULL; static const char *TAG = "WIFI_CFG";
static const char *NVS_NS = "storage"; // Shared namespace for all settings
// --- Helper: NVS Write --- // --- Defaults ---
static void nvs_write_str(const char *key, const char *val) { #define DEFAULT_MONITOR_CHANNEL 6
nvs_handle_t h;
if (nvs_open("netcfg", NVS_READWRITE, &h) == ESP_OK) { // --- Initialization ---
if (val && strlen(val) > 0) nvs_set_str(h, key, val);
else nvs_erase_key(h, key); void wifi_cfg_init(void) {
nvs_commit(h); // NVS is usually initialized in app_main, but we can double check here
nvs_close(h); // or just leave it empty if no specific module init is needed.
}
} }
static void nvs_write_u8(const char *key, uint8_t val) { // --- Apply Configuration ---
nvs_handle_t h;
if (nvs_open("netcfg", NVS_READWRITE, &h) == ESP_OK) {
nvs_set_u8(h, key, val);
nvs_commit(h);
nvs_close(h);
}
}
// --- Helper: MAC Parser --- bool wifi_cfg_apply_from_nvs(void) {
static bool parse_bssid(const char *str, uint8_t *out_bssid) { nvs_handle_t h;
if (!str || strlen(str) != 17) return false; if (nvs_open(NVS_NS, NVS_READONLY, &h) != ESP_OK) {
unsigned int bytes[6]; return false; // No config found
int count = sscanf(str, "%02x:%02x:%02x:%02x:%02x:%02x", }
&bytes[0], &bytes[1], &bytes[2], &bytes[3], &bytes[4], &bytes[5]);
if (count == 6) { // 1. Load SSID/Pass
for (int i = 0; i < 6; i++) out_bssid[i] = (uint8_t)bytes[i]; size_t len = 0;
if (nvs_get_str(h, "wifi_ssid", NULL, &len) == ESP_OK && len > 0) {
char *ssid = malloc(len);
char *pass = NULL;
nvs_get_str(h, "wifi_ssid", ssid, &len);
if (nvs_get_str(h, "wifi_pass", NULL, &len) == ESP_OK && len > 0) {
pass = malloc(len);
nvs_get_str(h, "wifi_pass", pass, &len);
}
wifi_config_t wifi_config = {0};
strncpy((char *)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid));
if (pass) {
strncpy((char *)wifi_config.sta.password, pass, sizeof(wifi_config.sta.password));
}
ESP_LOGI(TAG, "Applying WiFi Config: SSID=%s", ssid);
esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
free(ssid);
if (pass) free(pass);
}
nvs_close(h);
return true; return true;
} }
// --- Getters ---
bool wifi_cfg_get_mode(char *mode_out, uint8_t *channel_out) {
// This function seems to be used to retrieve saved "mode" strings
// For now, we default to whatever is implicit, or implement saving "wifi_mode" later.
// Returning false implies default behavior.
return false; return false;
} }
// --- Public Setters --- // --- Setters (Credentials) ---
void wifi_cfg_set_credentials(const char* ssid, const char* pass, const char* bssid) { bool wifi_cfg_set_ssid(const char *ssid) {
nvs_write_str("ssid", ssid);
nvs_write_str("pass", pass);
nvs_write_str("bssid", bssid); // Save BSSID to NVS
}
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);
}
// --- Clearing ---
void wifi_cfg_clear_credentials(void) {
nvs_handle_t h; nvs_handle_t h;
if (nvs_open("netcfg", NVS_READWRITE, &h) == ESP_OK) { if (nvs_open(NVS_NS, NVS_READWRITE, &h) != ESP_OK) return false;
nvs_erase_key(h, "ssid");
nvs_erase_key(h, "pass"); esp_err_t err = nvs_set_str(h, "wifi_ssid", ssid);
nvs_erase_key(h, "bssid"); if (err == ESP_OK) nvs_commit(h);
nvs_erase_key(h, "ip");
nvs_erase_key(h, "mask");
nvs_erase_key(h, "gw");
nvs_erase_key(h, "dhcp");
nvs_commit(h);
nvs_close(h); nvs_close(h);
return (err == ESP_OK);
}
bool wifi_cfg_set_password(const char *password) {
nvs_handle_t h;
if (nvs_open(NVS_NS, NVS_READWRITE, &h) != ESP_OK) return false;
esp_err_t err;
if (password && strlen(password) > 0) {
err = nvs_set_str(h, "wifi_pass", password);
} else {
err = nvs_erase_key(h, "wifi_pass"); // Clear if empty
} }
if (err == ESP_OK) nvs_commit(h);
nvs_close(h);
return (err == ESP_OK);
}
// --- Monitor Channel Settings ---
bool wifi_cfg_set_monitor_channel(uint8_t channel) {
nvs_handle_t h;
if (nvs_open(NVS_NS, NVS_READWRITE, &h) != ESP_OK) return false;
esp_err_t err = nvs_set_u8(h, "mon_chan", channel);
if (err == ESP_OK) nvs_commit(h);
nvs_close(h);
return (err == ESP_OK);
} }
void wifi_cfg_clear_monitor_channel(void) { void wifi_cfg_clear_monitor_channel(void) {
nvs_handle_t h; nvs_handle_t h;
if (nvs_open("netcfg", NVS_READWRITE, &h) == ESP_OK) { if (nvs_open(NVS_NS, NVS_READWRITE, &h) == ESP_OK) {
nvs_erase_key(h, "mon_ch"); nvs_erase_key(h, "mon_chan");
nvs_commit(h); nvs_commit(h);
nvs_close(h); nvs_close(h);
} }
} }
// --- Init & Load --- bool wifi_cfg_monitor_channel_is_unsaved(uint8_t current_val) {
nvs_handle_t h;
uint8_t saved_val = DEFAULT_MONITOR_CHANNEL;
void wifi_cfg_init(void) { if (nvs_open(NVS_NS, NVS_READONLY, &h) == ESP_OK) {
nvs_flash_init(); nvs_get_u8(h, "mon_chan", &saved_val);
nvs_close(h);
}
return (saved_val != current_val);
} }
static bool load_cfg(char* ssid, size_t ssz, char* pass, size_t psz, char* bssid, size_t bsz, // ... existing code ...
char* ip, size_t isz, char* mask, size_t msz, char* gw, size_t gsz,
char* band, size_t bsz_band, char* bw, size_t bwsz, char* powersave, size_t pssz, // --- IP Configuration ---
char* mode, size_t modesz, uint8_t* mon_ch, bool* dhcp){
bool wifi_cfg_set_ipv4(const char *ip, const char *mask, const char *gw) {
nvs_handle_t h; nvs_handle_t h;
if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) return false; if (nvs_open(NVS_NS, NVS_READWRITE, &h) != ESP_OK) return false;
size_t len; nvs_set_str(h, "static_ip", ip);
// Load SSID (Mandatory) nvs_set_str(h, "static_mask", mask);
len = ssz; nvs_set_str(h, "static_gw", gw);
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 = bsz; if (nvs_get_str(h, "bssid", bssid, &len) != ESP_OK) bssid[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_band; 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_commit(h);
nvs_close(h); nvs_close(h);
return true; return true;
} }
static void apply_ip_static(const char* ip, const char* mask, const char* gw){ bool wifi_cfg_get_ipv4(char *ip, char *mask, char *gw) {
if (!sta_netif) 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 = (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) {
char ssid[64]={0}, pass[64]={0}, bssid_str[20]={0}, ip[32]={0}, mask[32]={0}, gw[32]={0};
char band[16]={0}, bw[16]={0}, powersave[16]={0}, mode[16]={0};
uint8_t mon_ch = 36; bool dhcp = true;
if (!load_cfg(ssid,sizeof(ssid), pass,sizeof(pass), bssid_str,sizeof(bssid_str),
ip,sizeof(ip), mask,sizeof(mask), gw,sizeof(gw),
band,sizeof(band), bw,sizeof(bw), powersave,sizeof(powersave),
mode,sizeof(mode), &mon_ch, &dhcp)){
return false;
}
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));
strlcpy((char*)wcfg.sta.password, pass, sizeof(wcfg.sta.password));
wcfg.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
wcfg.sta.sae_pwe_h2e = WPA3_SAE_PWE_BOTH;
wcfg.sta.scan_method = WIFI_ALL_CHANNEL_SCAN;
// Apply BSSID Lock if present
if (bssid_str[0] != 0) {
if (parse_bssid(bssid_str, wcfg.sta.bssid)) {
wcfg.sta.bssid_set = true;
ESP_LOGI("WIFI_CFG", "Locking to BSSID: %s", bssid_str);
} else {
ESP_LOGW("WIFI_CFG", "Invalid BSSID format in NVS: %s", bssid_str);
}
}
esp_wifi_set_mode(WIFI_MODE_STA);
esp_wifi_set_config(WIFI_IF_STA, &wcfg);
if (!dhcp && ip[0]) apply_ip_static(ip, mask, gw);
else esp_netif_dhcpc_start(sta_netif);
esp_wifi_start();
esp_wifi_connect();
return true;
}
wifi_ps_type_t wifi_cfg_get_power_save_mode(void) {
return WIFI_PS_NONE; // Default implementation
}
bool wifi_cfg_get_bandwidth(char *buf, size_t buf_size) {
if (buf) strncpy(buf, "HT20", buf_size);
return true;
}
bool wifi_cfg_get_mode(char *mode, uint8_t *mon_ch) {
nvs_handle_t h; nvs_handle_t h;
if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) return false; if (nvs_open(NVS_NS, NVS_READONLY, &h) != ESP_OK) return false;
size_t len = 16; size_t len = 16;
if (nvs_get_str(h, "mode", mode, &len) != ESP_OK) strcpy(mode, "STA"); bool exists = (nvs_get_str(h, "static_ip", ip, &len) == ESP_OK);
nvs_get_u8(h, "mon_ch", mon_ch); len = 16; nvs_get_str(h, "static_mask", mask, &len);
len = 16; nvs_get_str(h, "static_gw", gw, &len);
nvs_close(h); nvs_close(h);
return true; return exists;
} }
bool wifi_cfg_monitor_channel_is_unsaved(uint8_t ram_value) { bool wifi_cfg_set_dhcp(bool enable) {
nvs_handle_t h; nvs_handle_t h;
if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) return true; if (nvs_open(NVS_NS, NVS_READWRITE, &h) != ESP_OK) return false;
uint8_t nvs_val = 0; nvs_set_u8(h, "dhcp_en", enable ? 1 : 0);
esp_err_t err = nvs_get_u8(h, "mon_ch", &nvs_val);
nvs_close(h);
if (err != ESP_OK) return true;
return (nvs_val != ram_value);
}
bool wifi_cfg_set_monitor_channel(uint8_t channel) {
nvs_handle_t h;
if (nvs_open("netcfg", NVS_READWRITE, &h) != ESP_OK) return false;
uint8_t current = 0;
if (nvs_get_u8(h, "mon_ch", &current) == ESP_OK) {
if (current == channel) {
nvs_close(h);
return false;
}
}
nvs_set_u8(h, "mon_ch", channel);
nvs_commit(h); nvs_commit(h);
nvs_close(h); nvs_close(h);
return true; return true;
} }
bool wifi_cfg_get_dhcp(void) {
nvs_handle_t h;
uint8_t val = 1; // Default to Enabled
if (nvs_open(NVS_NS, NVS_READONLY, &h) == ESP_OK) {
nvs_get_u8(h, "dhcp_en", &val);
nvs_close(h);
}
return (val != 0);
}

View File

@ -30,42 +30,39 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#ifndef WIFI_CFG_H // IP Configuration
#define WIFI_CFG_H #pragma once
#include "esp_wifi.h"
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
// --- Initialization --- // Init
void wifi_cfg_init(void); void wifi_cfg_init(void);
// --- Getters --- // Apply
bool wifi_cfg_apply_from_nvs(void); bool wifi_cfg_apply_from_nvs(void);
wifi_ps_type_t wifi_cfg_get_power_save_mode(void);
bool wifi_cfg_get_bandwidth(char *buf, size_t buf_size);
bool wifi_cfg_get_mode(char *mode, uint8_t *mon_ch);
// --- State Checkers --- // Getters
bool wifi_cfg_monitor_channel_is_unsaved(uint8_t ram_value); bool wifi_cfg_get_mode(char *mode_out, uint8_t *channel_out);
// --- Setters --- // Setters (These were missing)
// UPDATED: Now accepts optional bssid (pass NULL if not used) bool wifi_cfg_set_ssid(const char *ssid);
void wifi_cfg_set_credentials(const char* ssid, const char* pass, const char* bssid); bool wifi_cfg_set_password(const char *password);
void wifi_cfg_set_static_ip(const char* ip, const char* mask, const char* gw); // Monitor Specific
void wifi_cfg_set_dhcp(bool enable);
bool wifi_cfg_set_monitor_channel(uint8_t channel); bool wifi_cfg_set_monitor_channel(uint8_t channel);
// --- Clearing ---
void wifi_cfg_clear_credentials(void);
void wifi_cfg_clear_monitor_channel(void); void wifi_cfg_clear_monitor_channel(void);
bool wifi_cfg_monitor_channel_is_unsaved(uint8_t current_val);
bool wifi_cfg_set_ipv4(const char *ip, const char *mask, const char *gw);
bool wifi_cfg_get_ipv4(char *ip_out, char *mask_out, char *gw_out); // buffers must be 16 bytes
bool wifi_cfg_set_dhcp(bool enable);
bool wifi_cfg_get_dhcp(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#endif // WIFI_CFG_H

View File

@ -30,13 +30,13 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "wifi_controller.h" #include "wifi_controller.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_wifi.h" #include "esp_wifi.h"
#include "esp_event.h" // Added #include "esp_event.h"
#include "esp_netif.h"
#include "inttypes.h" #include "inttypes.h"
#include "wifi_cfg.h" #include "wifi_cfg.h"
@ -53,7 +53,7 @@
static const char *TAG = "WIFI_CTL"; static const char *TAG = "WIFI_CTL";
static wifi_ctl_mode_t s_current_mode = WIFI_CTL_MODE_STA; static wifi_ctl_mode_t s_current_mode = WIFI_CTL_MODE_STA;
static uint8_t s_monitor_channel = 6; static uint8_t s_monitor_channel_active = 6;
static uint8_t s_monitor_channel_staging = 6; static uint8_t s_monitor_channel_staging = 6;
static bool s_monitor_enabled = false; static bool s_monitor_enabled = false;
@ -66,26 +66,25 @@ static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t e
ESP_LOGI(TAG, "Got IP -> LED Connected"); ESP_LOGI(TAG, "Got IP -> LED Connected");
status_led_set_state(LED_STATE_CONNECTED); status_led_set_state(LED_STATE_CONNECTED);
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
status_led_set_state(LED_STATE_NO_CONFIG); // Or WAITING if retrying if (s_current_mode == WIFI_CTL_MODE_STA) {
status_led_set_state(LED_STATE_NO_CONFIG);
}
} }
} }
// ... [Helper: Log Collapse Events (Same as before)] ... // ... [Log Collapse / Monitor Callback Logic] ...
static void log_collapse_event(float nav_duration_us, int rssi, int retry) { static void log_collapse_event(uint32_t nav_duration_us, int rssi, int retry) {
gps_timestamp_t ts = gps_get_timestamp(); gps_timestamp_t ts = gps_get_timestamp();
printf("COLLAPSE,%" PRIi64 ",%" PRIi64 ",%d,%.2f,%d,%d\n", int64_t now_ms = ts.gps_us / 1000;
ts.monotonic_ms, ts.gps_ms, ts.synced ? 1 : 0, nav_duration_us, rssi, retry); ESP_LOGI(TAG, "COLLAPSE: Time=%" PRId64 "ms, Sync=%d, Dur=%lu us, RSSI=%d, Retry=%d",
now_ms, ts.synced ? 1 : 0, nav_duration_us, rssi, retry);
} }
// ... [Monitor Callbacks (Same as before)] ...
static void monitor_frame_callback(const wifi_frame_info_t *frame, const uint8_t *payload, uint16_t len) { static void monitor_frame_callback(const wifi_frame_info_t *frame, const uint8_t *payload, uint16_t len) {
s_monitor_frame_count++; s_monitor_frame_count++;
if (frame->retry && frame->duration_id > 5000) { if (frame->retry && frame->duration_id > 5000) {
log_collapse_event((float)frame->duration_id, frame->rssi, frame->retry); log_collapse_event((float)frame->duration_id, frame->rssi, frame->retry);
} }
if (frame->duration_id > 30000) {
ESP_LOGW("MONITOR", "⚠️ VERY HIGH NAV: %u us", frame->duration_id);
}
} }
static void monitor_stats_task(void *arg) { static void monitor_stats_task(void *arg) {
@ -95,41 +94,71 @@ static void monitor_stats_task(void *arg) {
if (wifi_monitor_get_stats(&stats) == ESP_OK) { if (wifi_monitor_get_stats(&stats) == ESP_OK) {
ESP_LOGI("MONITOR", "--- Stats: %lu frames, Retry: %.2f%%, Avg NAV: %u us ---", ESP_LOGI("MONITOR", "--- Stats: %lu frames, Retry: %.2f%%, Avg NAV: %u us ---",
(unsigned long)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()) ESP_LOGW("MONITOR", "⚠️ ⚠️ COLLAPSE DETECTED! ⚠️ ⚠️"); if (wifi_monitor_is_collapsed()) ESP_LOGW("MONITOR", "⚠️ COLLAPSE DETECTED! ⚠️");
} }
} }
} }
static void auto_monitor_task_func(void *arg) { // --- Helper to apply IP settings ---
uint8_t channel = (uint8_t)(uintptr_t)arg; static void apply_ip_settings(void) {
ESP_LOGI(TAG, "Waiting for WiFi connection before switching to monitor mode..."); esp_netif_t *netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
while (status_led_get_state() != LED_STATE_CONNECTED) { if (!netif) return;
vTaskDelay(pdMS_TO_TICKS(500));
if (wifi_cfg_get_dhcp()) {
esp_netif_dhcpc_start(netif);
} else {
esp_netif_dhcpc_stop(netif);
char ip[16], mask[16], gw[16];
if (wifi_cfg_get_ipv4(ip, mask, gw)) {
esp_netif_ip_info_t info = {0};
// API Fix: esp_ip4addr_aton returns uint32_t
info.ip.addr = esp_ip4addr_aton(ip);
info.netmask.addr = esp_ip4addr_aton(mask);
info.gw.addr = esp_ip4addr_aton(gw);
esp_netif_set_ip_info(netif, &info);
ESP_LOGI(TAG, "Static IP applied: %s", ip);
}
} }
ESP_LOGI(TAG, "WiFi connected, waiting for GPS sync (2s)...");
vTaskDelay(pdMS_TO_TICKS(2000));
ESP_LOGI(TAG, "Auto-switching to MONITOR mode on channel %d...", channel);
wifi_ctl_switch_to_monitor(channel, WIFI_BW_HT20);
vTaskDelete(NULL);
} }
// --- API Implementation --- // ============================================================================
// PUBLIC API IMPLEMENTATION
// ============================================================================
void wifi_ctl_init(void) { void wifi_ctl_init(void) {
s_current_mode = WIFI_CTL_MODE_STA; s_current_mode = WIFI_CTL_MODE_STA;
s_monitor_enabled = false; s_monitor_enabled = false;
s_monitor_frame_count = 0; s_monitor_frame_count = 0;
// Register Event Handlers for LED feedback // 1. Initialize Network Interface
esp_netif_create_default_wifi_sta();
// 2. Apply IP Settings (Static vs DHCP)
apply_ip_settings();
// 3. Initialize Wi-Fi Driver
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
// 4. Register Events
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL, NULL)); ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL, NULL));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &wifi_event_handler, NULL, NULL)); ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &wifi_event_handler, NULL, NULL));
// 5. Configure Storage & Mode
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_start());
// 6. Apply Saved Config
if (!wifi_cfg_apply_from_nvs()) { if (!wifi_cfg_apply_from_nvs()) {
ESP_LOGW(TAG, "No saved WiFi config found, driver initialized in defaults."); ESP_LOGW(TAG, "No saved WiFi config found, driver initialized in defaults.");
status_led_set_state(LED_STATE_NO_CONFIG); status_led_set_state(LED_STATE_NO_CONFIG);
} else { } else {
ESP_LOGI(TAG, "WiFi driver initialized from NVS."); ESP_LOGI(TAG, "WiFi driver initialized from NVS.");
status_led_set_state(LED_STATE_WAITING); // Waiting for connection status_led_set_state(LED_STATE_WAITING);
esp_wifi_connect();
} }
// Load Staging Params // Load Staging Params
@ -138,63 +167,20 @@ void wifi_ctl_init(void) {
if (s_monitor_channel_staging == 0) s_monitor_channel_staging = 6; if (s_monitor_channel_staging == 0) s_monitor_channel_staging = 6;
} }
// --- Parameter Management --- // --- Mode Control (Core) ---
void wifi_ctl_param_set_monitor_channel(uint8_t channel) {
if (channel >= 1 && channel <= 14) s_monitor_channel_staging = channel;
}
uint8_t wifi_ctl_param_get_monitor_channel(void) { esp_err_t wifi_ctl_switch_to_monitor(uint8_t channel, wifi_bandwidth_t bw) {
return s_monitor_channel_staging; if (channel == 0) channel = s_monitor_channel_staging;
}
bool wifi_ctl_param_save(void) { if (s_current_mode == WIFI_CTL_MODE_MONITOR && s_monitor_channel_active == channel) {
bool changed = wifi_cfg_set_monitor_channel(s_monitor_channel_staging); ESP_LOGW(TAG, "Already in monitor mode (Ch %d)", channel);
if (changed) ESP_LOGI(TAG, "Monitor channel (%d) saved to NVS", s_monitor_channel_staging);
return changed;
}
void wifi_ctl_param_reload(void) {
char mode_ignored[16];
uint8_t ch = 0;
wifi_cfg_get_mode(mode_ignored, &ch);
if (ch > 0) s_monitor_channel_staging = ch;
ESP_LOGI(TAG, "Reloaded monitor channel: %d", s_monitor_channel_staging);
}
void wifi_ctl_param_clear(void) {
// 1. Erase NVS
wifi_cfg_clear_monitor_channel();
// 2. Reset RAM Staging to Default (6)
s_monitor_channel_staging = 6;
ESP_LOGI(TAG, "Monitor configuration cleared (Defaulting to Ch 6).");
}
bool wifi_ctl_param_is_unsaved(void) {
return wifi_cfg_monitor_channel_is_unsaved(s_monitor_channel_staging);
}
// --- Actions ---
esp_err_t wifi_ctl_switch_to_monitor(uint8_t channel_override, wifi_bandwidth_t bandwidth) {
uint8_t channel = (channel_override > 0) ? channel_override : s_monitor_channel_staging;
if (s_current_mode == WIFI_CTL_MODE_MONITOR && s_monitor_channel == channel) {
ESP_LOGW(TAG, "Already in monitor mode");
return ESP_OK; return ESP_OK;
} }
if (bandwidth != WIFI_BW_HT20) {
ESP_LOGW(TAG, "Forcing bandwidth to 20MHz for monitor mode");
bandwidth = WIFI_BW_HT20;
}
ESP_LOGI(TAG, "Switching to MONITOR MODE (Ch %d)", channel); ESP_LOGI(TAG, "Switching to MONITOR MODE (Ch %d)", channel);
iperf_stop(); iperf_stop();
vTaskDelay(pdMS_TO_TICKS(500)); vTaskDelay(pdMS_TO_TICKS(500));
#ifdef CONFIG_ESP_WIFI_CSI_ENABLED #ifdef CONFIG_ESP_WIFI_CSI_ENABLED
csi_mgr_disable(); csi_mgr_disable();
#endif #endif
@ -204,13 +190,12 @@ esp_err_t wifi_ctl_switch_to_monitor(uint8_t channel_override, wifi_bandwidth_t
vTaskDelay(pdMS_TO_TICKS(500)); vTaskDelay(pdMS_TO_TICKS(500));
esp_wifi_set_mode(WIFI_MODE_NULL); esp_wifi_set_mode(WIFI_MODE_NULL);
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;
} }
esp_wifi_set_bandwidth(WIFI_IF_STA, bandwidth); esp_wifi_set_bandwidth(WIFI_IF_STA, bw);
if (wifi_monitor_start() != ESP_OK) { if (wifi_monitor_start() != ESP_OK) {
ESP_LOGE(TAG, "Failed to start monitor mode"); ESP_LOGE(TAG, "Failed to start monitor mode");
@ -219,7 +204,7 @@ esp_err_t wifi_ctl_switch_to_monitor(uint8_t channel_override, wifi_bandwidth_t
s_monitor_enabled = true; s_monitor_enabled = true;
s_current_mode = WIFI_CTL_MODE_MONITOR; s_current_mode = WIFI_CTL_MODE_MONITOR;
s_monitor_channel = channel; s_monitor_channel_active = channel;
status_led_set_state(LED_STATE_MONITORING); status_led_set_state(LED_STATE_MONITORING);
if (s_monitor_stats_task_handle == NULL) { if (s_monitor_stats_task_handle == NULL) {
@ -229,9 +214,9 @@ esp_err_t wifi_ctl_switch_to_monitor(uint8_t channel_override, wifi_bandwidth_t
return ESP_OK; return ESP_OK;
} }
esp_err_t wifi_ctl_switch_to_sta(wifi_band_mode_t band_mode) { esp_err_t wifi_ctl_switch_to_sta(void) {
if (s_current_mode == WIFI_CTL_MODE_STA) { if (s_current_mode == WIFI_CTL_MODE_STA) {
ESP_LOGW(TAG, "Already in STA mode"); ESP_LOGI(TAG, "Already in STA mode");
return ESP_OK; return ESP_OK;
} }
@ -251,13 +236,7 @@ esp_err_t wifi_ctl_switch_to_sta(wifi_band_mode_t band_mode) {
esp_wifi_set_mode(WIFI_MODE_STA); esp_wifi_set_mode(WIFI_MODE_STA);
vTaskDelay(pdMS_TO_TICKS(500)); vTaskDelay(pdMS_TO_TICKS(500));
wifi_config_t wifi_config;
esp_wifi_get_config(WIFI_IF_STA, &wifi_config);
wifi_config.sta.channel = 0;
esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
esp_wifi_start(); esp_wifi_start();
vTaskDelay(pdMS_TO_TICKS(500));
esp_wifi_connect(); esp_wifi_connect();
s_current_mode = WIFI_CTL_MODE_STA; s_current_mode = WIFI_CTL_MODE_STA;
@ -266,18 +245,101 @@ esp_err_t wifi_ctl_switch_to_sta(wifi_band_mode_t band_mode) {
return ESP_OK; return ESP_OK;
} }
// --- Wrappers for cmd_monitor.c ---
void wifi_ctl_monitor_start(int channel) {
wifi_ctl_switch_to_monitor((uint8_t)channel, WIFI_BW_HT20);
}
void wifi_ctl_stop(void) {
wifi_ctl_switch_to_sta();
}
void wifi_ctl_start_station(void) {
wifi_ctl_switch_to_sta();
}
void wifi_ctl_start_ap(void) {
ESP_LOGW(TAG, "AP Mode not fully implemented, using STA");
wifi_ctl_switch_to_sta();
}
// --- Settings ---
void wifi_ctl_set_channel(int channel) {
if (channel < 1 || channel > 14) {
ESP_LOGE(TAG, "Invalid channel %d", channel);
return;
}
s_monitor_channel_staging = (uint8_t)channel;
if (s_current_mode == WIFI_CTL_MODE_MONITOR) {
ESP_LOGI(TAG, "Switching live channel to %d", channel);
esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
s_monitor_channel_active = (uint8_t)channel;
}
}
void wifi_ctl_status(void) {
const char *mode_str = (s_current_mode == WIFI_CTL_MODE_MONITOR) ? "MONITOR" :
(s_current_mode == WIFI_CTL_MODE_AP) ? "AP" : "STATION";
printf("WiFi Status:\n");
printf(" Mode: %s\n", mode_str);
if (s_current_mode == WIFI_CTL_MODE_MONITOR) {
printf(" Channel: %d\n", s_monitor_channel_active);
printf(" Frames: %lu\n", (unsigned long)s_monitor_frame_count);
}
printf(" Staging Ch: %d\n", s_monitor_channel_staging);
}
// --- Params (NVS) ---
bool wifi_ctl_param_is_unsaved(void) {
return wifi_cfg_monitor_channel_is_unsaved(s_monitor_channel_staging);
}
void wifi_ctl_param_save(const char *dummy) {
(void)dummy;
if (wifi_cfg_set_monitor_channel(s_monitor_channel_staging)) {
ESP_LOGI(TAG, "Monitor channel (%d) saved to NVS", s_monitor_channel_staging);
} else {
ESP_LOGI(TAG, "No changes to save.");
}
}
void wifi_ctl_param_init(void) {
char mode_ignored[16];
uint8_t ch = 0;
wifi_cfg_get_mode(mode_ignored, &ch);
if (ch > 0) s_monitor_channel_staging = ch;
ESP_LOGI(TAG, "Reloaded monitor channel: %d", s_monitor_channel_staging);
}
void wifi_ctl_param_clear(void) {
wifi_cfg_clear_monitor_channel();
s_monitor_channel_staging = 6;
ESP_LOGI(TAG, "Monitor config cleared (Defaulting to Ch 6).");
}
// --- Getters ---
wifi_ctl_mode_t wifi_ctl_get_mode(void) { return s_current_mode; }
int wifi_ctl_get_channel(void) { return s_monitor_channel_active; }
// --- Deprecated ---
static void auto_monitor_task_func(void *arg) {
uint8_t channel = (uint8_t)(uintptr_t)arg;
ESP_LOGI(TAG, "Waiting for WiFi connection before switching to monitor mode...");
while (status_led_get_state() != LED_STATE_CONNECTED) {
vTaskDelay(pdMS_TO_TICKS(500));
}
ESP_LOGI(TAG, "WiFi connected, waiting for GPS sync (2s)...");
vTaskDelay(pdMS_TO_TICKS(2000));
wifi_ctl_switch_to_monitor(channel, WIFI_BW_HT20);
vTaskDelete(NULL);
}
void wifi_ctl_auto_monitor_start(uint8_t channel) { void wifi_ctl_auto_monitor_start(uint8_t channel) {
xTaskCreate(auto_monitor_task_func, "auto_monitor", 4096, (void*)(uintptr_t)channel, 5, NULL); xTaskCreate(auto_monitor_task_func, "auto_monitor", 4096, (void*)(uintptr_t)channel, 5, NULL);
} }
wifi_ctl_mode_t wifi_ctl_get_mode(void) {
return s_current_mode;
}
uint8_t wifi_ctl_get_monitor_channel(void) {
return s_monitor_channel;
}
uint32_t wifi_ctl_get_monitor_frame_count(void) {
return s_monitor_frame_count;
}

View File

@ -30,41 +30,52 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#pragma once #pragma once
#include "esp_err.h"
#include "esp_wifi.h"
#include <stdbool.h> #include <stdbool.h>
#include "esp_err.h"
#include "esp_wifi_types.h" // Needed for wifi_bandwidth_t
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
// Types
typedef enum { typedef enum {
WIFI_CTL_MODE_STA, WIFI_CTL_MODE_STA, // Changed from _STATION to _STA to match cmd_wifi.c
WIFI_CTL_MODE_AP,
WIFI_CTL_MODE_MONITOR WIFI_CTL_MODE_MONITOR
} wifi_ctl_mode_t; } wifi_ctl_mode_t;
// Init
void wifi_ctl_init(void); void wifi_ctl_init(void);
// --- Parameter Management --- // Mode Control (Advanced)
void wifi_ctl_param_set_monitor_channel(uint8_t channel); esp_err_t wifi_ctl_switch_to_sta(void);
uint8_t wifi_ctl_param_get_monitor_channel(void); esp_err_t wifi_ctl_switch_to_monitor(uint8_t channel, wifi_bandwidth_t bw);
bool wifi_ctl_param_save(void);
void wifi_ctl_param_reload(void); // Simple Wrappers (for cmd_monitor.c)
void wifi_ctl_start_station(void);
void wifi_ctl_start_ap(void);
void wifi_ctl_monitor_start(int channel);
void wifi_ctl_stop(void);
// Settings
void wifi_ctl_set_channel(int channel);
void wifi_ctl_status(void);
// Params (NVS)
bool wifi_ctl_param_is_unsaved(void); bool wifi_ctl_param_is_unsaved(void);
void wifi_ctl_param_save(const char *dummy);
void wifi_ctl_param_init(void);
void wifi_ctl_param_clear(void); void wifi_ctl_param_clear(void);
// --- Actions --- // Getters
esp_err_t wifi_ctl_switch_to_monitor(uint8_t channel_override, wifi_bandwidth_t bandwidth);
esp_err_t wifi_ctl_switch_to_sta(wifi_band_mode_t band_mode);
void wifi_ctl_auto_monitor_start(uint8_t channel);
// --- Getters ---
wifi_ctl_mode_t wifi_ctl_get_mode(void); wifi_ctl_mode_t wifi_ctl_get_mode(void);
uint8_t wifi_ctl_get_monitor_channel(void); int wifi_ctl_get_channel(void);
uint32_t wifi_ctl_get_monitor_frame_count(void);
// Deprecated / Compatibility
void wifi_ctl_auto_monitor_start(uint8_t channel);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -42,7 +42,7 @@
// ============================================================================ // ============================================================================
// ESP32-C5 (DevKitC-1) 3.3V VCC Pin 1 GND PIN 15 // ESP32-C5 (DevKitC-1) 3.3V VCC Pin 1 GND PIN 15
// ============================================================================ // ============================================================================
#define RGB_LED_GPIO 8 // Common addressable LED pin for C5 #define RGB_LED_GPIO 27 // Common addressable LED pin for C5
#define HAS_RGB_LED 1 #define HAS_RGB_LED 1
#define GPS_TX_PIN GPIO_NUM_24 #define GPS_TX_PIN GPIO_NUM_24
#define GPS_RX_PIN GPIO_NUM_23 #define GPS_RX_PIN GPIO_NUM_23

View File

@ -33,6 +33,8 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <sys/time.h> // Added for gettimeofday
#include <time.h> // Added for time structs
#include "sdkconfig.h" #include "sdkconfig.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
@ -42,7 +44,7 @@
#include "esp_vfs_dev.h" #include "esp_vfs_dev.h"
#include "driver/uart.h" #include "driver/uart.h"
#include "nvs_flash.h" #include "nvs_flash.h"
#include "nvs.h" // Added for NVS read #include "nvs.h"
#include "esp_netif.h" #include "esp_netif.h"
#include "esp_event.h" #include "esp_event.h"
@ -67,6 +69,18 @@ static const char *TAG = "MAIN";
// --- Global Prompt Buffer (Mutable) --- // --- Global Prompt Buffer (Mutable) ---
static char s_cli_prompt[32] = "esp32> "; static char s_cli_prompt[32] = "esp32> ";
// --- Custom Log Formatter (Epoch Timestamp) ---
// This ensures logs match tcpdump/wireshark format: [Seconds.Microseconds]
static int custom_log_vprintf(const char *fmt, va_list args) {
struct timeval tv;
gettimeofday(&tv, NULL);
// Print [1766437791.123456] prefix
printf("[%ld.%06ld] ", (long)tv.tv_sec, (long)tv.tv_usec);
return vprintf(fmt, args);
}
// --- Prompt Updater --- // --- Prompt Updater ---
void app_console_update_prompt(void) { void app_console_update_prompt(void) {
bool dirty = false; bool dirty = false;
@ -136,7 +150,11 @@ void app_main(void) {
} }
ESP_ERROR_CHECK(ret); ESP_ERROR_CHECK(ret);
// 2. Initialize Netif & Event Loop // 2. Register Custom Log Formatter (Epoch Time)
// Must be done before any logs are printed to ensure consistency
esp_log_set_vprintf(custom_log_vprintf);
// 3. Initialize Netif & Event Loop
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());
@ -155,18 +173,19 @@ void app_main(void) {
ESP_LOGW(TAG, "GPS initialization skipped (Disabled in NVS)"); ESP_LOGW(TAG, "GPS initialization skipped (Disabled in NVS)");
} }
// 3. Hardware Init // 4. Hardware Init
status_led_init(RGB_LED_GPIO, HAS_RGB_LED); status_led_init(RGB_LED_GPIO, HAS_RGB_LED);
status_led_set_state(LED_STATE_FAILED); // Force Red Blink
#ifdef CONFIG_ESP_WIFI_CSI_ENABLED #ifdef CONFIG_ESP_WIFI_CSI_ENABLED
ESP_ERROR_CHECK(csi_log_init()); ESP_ERROR_CHECK(csi_log_init());
csi_mgr_init(); csi_mgr_init();
#endif #endif
// 4. Initialize WiFi Controller & iPerf // 5. Initialize WiFi Controller & iPerf
wifi_ctl_init(); wifi_ctl_init();
iperf_param_init(); iperf_param_init();
// 5. Initialize Console // 6. Initialize Console (REPL)
esp_console_repl_t *repl = NULL; esp_console_repl_t *repl = NULL;
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
@ -176,14 +195,14 @@ void app_main(void) {
esp_console_dev_uart_config_t hw_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); 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)); ESP_ERROR_CHECK(esp_console_new_repl_uart(&hw_config, &repl_config, &repl));
// 6. Register Commands // 7. Register Commands
register_system_common(); register_system_common();
app_console_register_commands(); app_console_register_commands();
// 7. Initial Prompt State Check // 8. Initial Prompt State Check
app_console_update_prompt(); app_console_update_prompt();
// 8. Start Shell // 9. Start Shell
printf("\n ==================================================\n"); printf("\n ==================================================\n");
printf(" | ESP32 iPerf Shell - Ready |\n"); printf(" | ESP32 iPerf Shell - Ready |\n");
printf(" | Type 'help' for commands |\n"); printf(" | Type 'help' for commands |\n");

View File

@ -7,3 +7,14 @@ CONFIG_FREERTOS_HZ=1000
CONFIG_CONSOLE_UART_RX_BUF_SIZE=1024 CONFIG_CONSOLE_UART_RX_BUF_SIZE=1024
CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_ESP_WIFI_CSI_ENABLED=n CONFIG_ESP_WIFI_CSI_ENABLED=n
# Use System Time (Wall Clock) for Logs instead of Boot Time
# Shared Base Defaults
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
CONFIG_CONSOLE_UART_RX_BUF_SIZE=1024
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_ESP_WIFI_CSI_ENABLED=n
CONFIG_LOG_TIMESTAMP_SOURCE_NONE=y