Compare commits

..

No commits in common. "099c28f9c70a725e125c102e4cd41141e5424360" and "6c214e8e927f389a642a139def09374f8b97611e" have entirely different histories.

13 changed files with 740 additions and 1486 deletions

View File

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

View File

@ -30,25 +30,712 @@
* 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.
*/ */
/*
* app_console.c
*
* Copyright (c) 2025 Umber Networks & Robert McMahon
* All rights reserved.
*/
#include "app_console.h" #include "app_console.h"
#include "esp_console.h" #include "esp_console.h"
#include "esp_log.h" #include "esp_log.h"
#include "argtable3/argtable3.h"
#include "wifi_cfg.h"
#include "iperf.h"
#include "gps_sync.h"
#include "wifi_controller.h"
#include "esp_wifi.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "ping/ping_sock.h"
#include <netdb.h>
#include <string.h>
#include <arpa/inet.h>
#include <inttypes.h>
#include <time.h>
// --- Registration --- // --- Helper: Prompt Update ---
void app_console_register_commands(void) { // Updates the "esp32>" vs "esp32*>" prompt based on dirty state
register_system_cmd(); static void end_cmd(void) {
register_nvs_cmd(); app_console_update_prompt();
register_wifi_cmd(); }
register_iperf_cmd();
register_gps_cmd(); // ============================================================================
register_ping_cmd(); // COMMAND: nvs (Storage Management)
register_monitor_cmd(); // ============================================================================
register_ip_cmd(); static struct {
struct arg_lit *dump;
struct arg_lit *clear_all;
struct arg_lit *help;
struct arg_end *end;
} nvs_args;
static void print_nvs_key_str(nvs_handle_t h, const char *key, const char *label) {
char buf[64] = {0};
size_t len = sizeof(buf);
if (nvs_get_str(h, key, buf, &len) == ESP_OK) {
printf(" %-12s : %s\n", label, buf);
} else {
printf(" %-12s : <empty>\n", label);
}
}
static void print_nvs_key_u32(nvs_handle_t h, const char *key, const char *label) {
uint32_t val = 0;
if (nvs_get_u32(h, key, &val) == ESP_OK) {
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) {
uint8_t val = 0;
if (nvs_get_u8(h, key, &val) == ESP_OK) {
printf(" %-12s : %u\n", label, val);
} 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;
}
if (nvs_args.help->count > 0) {
printf("Usage: nvs [--dump] [--clear-all]\n");
return 0;
}
// --- CLEAR ALL ---
if (nvs_args.clear_all->count > 0) {
printf("Erasing ALL settings from NVS...\n");
wifi_cfg_clear_credentials();
wifi_cfg_clear_monitor_channel();
iperf_param_clear();
printf("Done. Please reboot.\n");
end_cmd();
return 0;
}
// --- DUMP ---
printf("\n--- [WiFi Config (netcfg)] ---\n");
nvs_handle_t h;
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.\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");
nvs_close(h);
} else {
printf("Failed to open 'storage' namespace.\n");
}
printf("\n");
end_cmd();
return 0;
}
static 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 = {
.command = "nvs",
.help = "Storage Management",
.func = &cmd_nvs,
.argtable = &nvs_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
// ============================================================================
// COMMAND: gps (Configure & Status)
// ============================================================================
static struct {
struct arg_lit *enable;
struct arg_lit *disable;
struct arg_lit *status;
struct arg_lit *help;
struct arg_end *end;
} gps_args;
static int cmd_gps(int argc, char **argv) {
int nerrors = arg_parse(argc, argv, (void **)&gps_args);
if (nerrors > 0) {
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;
}
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;
}
// --- HANDLE SETTERS ---
bool changed = false;
if (gps_args.enable->count > 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);
// --- DISPLAY STATUS ---
// 1. NVS State
uint8_t val = 1;
if (nvs_get_u8(h, "gps_enabled", &val) != ESP_OK) val = 1;
printf("GPS NVS State: %s\n", val ? "ENABLED" : "DISABLED");
nvs_close(h);
// 2. Runtime Status
if (val) {
gps_timestamp_t ts = gps_get_timestamp();
int64_t pps_age = gps_get_pps_age_ms();
printf("Status: %s\n", ts.synced ? "SYNCED" : "SEARCHING");
// Print Raw NMEA ---
char nmea_buf[128];
gps_get_last_nmea(nmea_buf, sizeof(nmea_buf));
// Remove trailing \r\n for cleaner printing
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 > 1 && (nmea_buf[len-2] == '\r' || nmea_buf[len-2] == '\n')) nmea_buf[len-2] = 0;
printf("Last NMEA: [%s]\n", nmea_buf);
// ---------------------------
if (pps_age < 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) {
time_t now_sec = ts.gps_us / 1000000;
struct tm tm_info;
gmtime_r(&now_sec, &tm_info);
char time_buf[64];
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");
}
}
end_cmd();
return 0;
}
static void register_gps_cmd(void) {
gps_args.enable = arg_lit0(NULL, "enable", "Enable GPS");
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);
const esp_console_cmd_t cmd = {
.command = "gps",
.help = "Configure GPS",
.func = &cmd_gps,
.argtable = &gps_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
// ============================================================================
// COMMAND: iperf
// ============================================================================
static struct {
struct arg_lit *start, *stop, *status, *save, *reload;
struct arg_lit *clear_nvs;
struct arg_str *ip;
struct arg_int *port, *pps, *len, *burst;
struct arg_lit *help;
struct arg_end *end;
} iperf_args;
static int cmd_iperf(int argc, char **argv) {
int nerrors = arg_parse(argc, argv, (void **)&iperf_args);
if (nerrors > 0) {
arg_print_errors(stderr, iperf_args.end, argv[0]);
return 1;
}
if (iperf_args.help->count > 0) {
printf("Usage: iperf [options]\n");
printf(" --start Start traffic\n");
printf(" --stop Stop traffic\n");
printf(" --save Save config to 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;
}
if (iperf_args.clear_nvs->count > 0) {
iperf_param_clear();
printf("iPerf Configuration cleared (Reset to defaults).\n");
end_cmd();
return 0;
}
if (iperf_args.reload->count > 0) {
iperf_param_init();
printf("Configuration reloaded from NVS.\n");
}
bool config_changed = false;
iperf_cfg_t cfg;
iperf_param_get(&cfg);
if (iperf_args.ip->count > 0) { cfg.dip = inet_addr(iperf_args.ip->sval[0]); config_changed = true; }
if (iperf_args.port->count > 0) { cfg.dport = (uint16_t)iperf_args.port->ival[0]; config_changed = true; }
if (iperf_args.len->count > 0) { cfg.send_len = (uint32_t)iperf_args.len->ival[0]; config_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 (iperf_args.pps->ival[0] > 0) {
cfg.target_pps = (uint32_t)iperf_args.pps->ival[0];
config_changed = true;
}
}
if (config_changed) {
iperf_param_set(&cfg);
printf("RAM configuration updated.\n");
}
if (iperf_args.save->count > 0) {
bool changed = false;
if (iperf_param_save(&changed) == ESP_OK) {
printf(changed ? "Configuration saved to NVS.\n" : "No changes to save (NVS matches RAM).\n");
} else {
printf("Error saving to NVS.\n");
}
}
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();
end_cmd();
return 0;
}
static 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 = { .command = "iperf", .help = "Traffic Gen", .func = &cmd_iperf, .argtable = &iperf_args };
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
// ============================================================================
// COMMAND: monitor
// ============================================================================
static struct {
struct arg_lit *start, *stop, *status, *save, *reload;
struct arg_lit *clear_nvs;
struct arg_int *channel;
struct arg_lit *help;
struct arg_end *end;
} mon_args;
static int cmd_monitor(int argc, char **argv) {
int nerrors = arg_parse(argc, argv, (void **)&mon_args);
if (nerrors > 0) {
arg_print_errors(stderr, mon_args.end, argv[0]);
return 1;
}
if (mon_args.help->count > 0) {
printf("Usage: monitor [--start|--stop] [-c <ch>] [--save|--reload]\n");
return 0;
}
if (mon_args.clear_nvs->count > 0) {
wifi_ctl_param_clear();
printf("Monitor config cleared (Defaulting to Ch 6).\n");
end_cmd();
return 0;
}
if (mon_args.reload->count > 0) {
wifi_ctl_param_reload();
printf("Config reloaded from NVS.\n");
}
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");
}
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");
}
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());
}
end_cmd();
return 0;
}
static void register_monitor_cmd(void) {
mon_args.start = arg_lit0(NULL, "start", "Start");
mon_args.stop = arg_lit0(NULL, "stop", "Stop");
mon_args.status = arg_lit0(NULL, "status", "Status");
mon_args.save = arg_lit0(NULL, "save", "Save");
mon_args.reload = arg_lit0(NULL, "reload", "Reload");
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 = { .command = "monitor", .help = "Monitor Mode", .func = &cmd_monitor, .argtable = &mon_args };
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
// ============================================================================
// COMMAND: scan
// ============================================================================
static struct {
struct arg_lit *help;
struct arg_end *end;
} scan_args;
static int cmd_scan(int argc, char **argv) {
wifi_scan_config_t scan_config = {0};
scan_config.show_hidden = true;
printf("Starting Wi-Fi Scan...\n");
esp_err_t err = esp_wifi_scan_start(&scan_config, true);
// --- SMART RETRY LOGIC ---
if (err == ESP_ERR_WIFI_STATE) {
printf("WARN: WiFi is busy connecting. Forcing disconnect to allow scan...\n");
esp_wifi_disconnect(); // Stop the connection attempt
vTaskDelay(pdMS_TO_TICKS(100)); // Give the driver a moment to reset state
// Retry scan
err = esp_wifi_scan_start(&scan_config, true);
}
// -------------------------
if (err != ESP_OK) {
printf("Error starting scan: %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 APs 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("Failed to allocate memory for scan results.\n");
return 1;
}
ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_count, ap_info));
printf("Found %u unique BSSIDs\n", ap_count);
printf("------------------------------------------------------------------------------------------------\n");
printf("SSID | BSSID | RSSI | Channel | Auth Mode\n");
printf("------------------------------------------------------------------------------------------------\n");
for (int i = 0; i < ap_count; i++) {
char *authmode;
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-PSK"; break;
case WIFI_AUTH_WPA2_PSK: authmode = "WPA2-PSK"; break;
case WIFI_AUTH_WPA_WPA2_PSK: authmode = "WPA/WPA2"; break;
case WIFI_AUTH_WPA2_ENTERPRISE: authmode = "WPA2-ENT"; break;
case WIFI_AUTH_WPA3_PSK: authmode = "WPA3-PSK"; break;
case WIFI_AUTH_WPA2_WPA3_PSK: authmode = "WPA2/WPA3"; break;
default: authmode = "UNKNOWN"; break;
}
printf("%-32.32s | %02x:%02x:%02x:%02x:%02x:%02x | %4d | %7d | %s\n",
ap_info[i].ssid,
ap_info[i].bssid[0], ap_info[i].bssid[1], ap_info[i].bssid[2],
ap_info[i].bssid[3], ap_info[i].bssid[4], ap_info[i].bssid[5],
ap_info[i].rssi,
ap_info[i].primary,
authmode);
}
printf("------------------------------------------------------------------------------------------------\n");
free(ap_info);
return 0;
}
static void register_scan_cmd(void) {
scan_args.help = arg_lit0("h", "help", "Help");
scan_args.end = arg_end(1);
const esp_console_cmd_t cmd = { .command = "scan", .help = "WiFi Scan", .func = &cmd_scan, .argtable = &scan_args };
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
// ============================================================================
// COMMAND: wifi_config
// ============================================================================
static struct {
struct arg_str *ssid;
struct arg_str *pass;
struct arg_str *ip;
struct arg_lit *dhcp;
struct arg_lit *clear_nvs;
struct arg_lit *help;
struct arg_end *end;
} wifi_args;
static int cmd_wifi_config(int argc, char **argv) {
int nerrors = arg_parse(argc, argv, (void **)&wifi_args);
if (nerrors > 0) {
arg_print_errors(stderr, wifi_args.end, argv[0]);
return 1;
}
if (wifi_args.help->count > 0) {
printf("Usage: wifi_config -s <ssid> [-p <pass>] [--clear-nvs]\n");
return 0;
}
if (wifi_args.clear_nvs->count > 0) {
wifi_cfg_clear_credentials();
printf("WiFi Credentials CLEARED from NVS.\n");
return 0;
}
if (wifi_args.ssid->count == 0) {
printf("Error: SSID is required (-s)\n");
return 1;
}
const char* ssid = wifi_args.ssid->sval[0];
const char* pass = (wifi_args.pass->count > 0) ? wifi_args.pass->sval[0] : "";
const char* ip = (wifi_args.ip->count > 0) ? wifi_args.ip->sval[0] : NULL;
bool dhcp = (wifi_args.dhcp->count > 0);
printf("Saving WiFi Config: SSID='%s' DHCP=%d\n", ssid, dhcp);
wifi_cfg_set_credentials(ssid, pass);
if (ip) {
char mask[] = "255.255.255.0";
char gw[32];
strlcpy(gw, ip, sizeof(gw));
char *last_dot = strrchr(gw, '.');
if (last_dot) strcpy(last_dot, ".1");
wifi_cfg_set_static_ip(ip, mask, gw);
wifi_cfg_set_dhcp(false);
} else {
wifi_cfg_set_dhcp(dhcp);
}
printf("Config saved. Rebooting to apply...\n");
esp_restart();
return 0;
}
static void register_wifi_cmd(void) {
wifi_args.ssid = arg_str0("s", "ssid", "<ssid>", "SSID");
wifi_args.pass = arg_str0("p", "password", "<pass>", "Pass");
wifi_args.ip = arg_str0("i", "ip", "<ip>", "Static IP");
wifi_args.dhcp = arg_lit0("d", "dhcp", "Enable DHCP");
wifi_args.clear_nvs = arg_lit0(NULL, "clear-nvs", "Clear NVS");
wifi_args.help = arg_lit0("h", "help", "Help");
wifi_args.end = arg_end(20);
const esp_console_cmd_t cmd = { .command = "wifi_config", .help = "Configure WiFi", .func = &cmd_wifi_config, .argtable = &wifi_args };
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
static void cmd_ping_on_ping_success(esp_ping_handle_t hdl, void *args) {
uint8_t ttl;
uint16_t seqno;
uint32_t elapsed_time, recv_len;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
printf("%" PRIu32 " bytes from %s: icmp_seq=%u ttl=%u time=%" PRIu32 " ms\n",
recv_len, inet_ntoa(target_addr.u_addr.ip4), seqno, ttl, elapsed_time);
}
static void cmd_ping_on_ping_timeout(esp_ping_handle_t hdl, void *args) {
uint16_t seqno;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
printf("From %s: icmp_seq=%u timeout\n", inet_ntoa(target_addr.u_addr.ip4), seqno);
}
static void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args) {
uint32_t transmitted;
uint32_t received;
uint32_t total_time_ms;
esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted));
esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received));
esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms));
printf("\n--- ping statistics ---\n");
printf("%" PRIu32 " packets transmitted, %" PRIu32 " received, %" PRIu32 "%% packet loss, time %" PRIu32 "ms\n",
transmitted, received, (transmitted - received) * 100 / transmitted, total_time_ms);
esp_ping_delete_session(hdl);
}
static struct {
struct arg_str *host;
struct arg_int *count;
struct arg_int *interval;
struct arg_end *end;
} ping_args;
static int cmd_ping(int argc, char **argv) {
int nerrors = arg_parse(argc, argv, (void **)&ping_args);
if (nerrors != 0) {
arg_print_errors(stderr, ping_args.end, argv[0]);
return 1;
}
esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG();
// Parse Args
if (ping_args.count->count > 0) {
config.count = ping_args.count->ival[0];
}
if (ping_args.interval->count > 0) {
config.interval_ms = ping_args.interval->ival[0] * 1000;
}
// Parse Target IP
ip_addr_t target_addr;
struct addrinfo hint;
struct addrinfo *res = NULL;
memset(&hint, 0, sizeof(hint));
memset(&target_addr, 0, sizeof(target_addr));
// Check if simple IP string
if (inet_aton(ping_args.host->sval[0], &target_addr.u_addr.ip4)) {
target_addr.type = IPADDR_TYPE_V4;
} else {
// Resolve Hostname
printf("Resolving %s...\n", ping_args.host->sval[0]);
if (getaddrinfo(ping_args.host->sval[0], NULL, &hint, &res) != 0) {
printf("ping: unknown host %s\n", ping_args.host->sval[0]);
return 1;
}
struct in_addr addr4 = ((struct sockaddr_in *)(res->ai_addr))->sin_addr;
inet_addr_to_ip4addr(ip_2_ip4(&target_addr), &addr4);
target_addr.type = IPADDR_TYPE_V4;
freeaddrinfo(res);
}
config.target_addr = target_addr;
config.task_stack_size = 4096; // Ensure enough stack
esp_ping_callbacks_t cbs = {
.on_ping_success = cmd_ping_on_ping_success,
.on_ping_timeout = cmd_ping_on_ping_timeout,
.on_ping_end = cmd_ping_on_ping_end,
.cb_args = NULL
};
esp_ping_handle_t ping;
esp_ping_new_session(&config, &cbs, &ping);
esp_ping_start(ping);
return 0;
}
static void register_ping(void) {
ping_args.host = arg_str1(NULL, NULL, "<host>", "Host address or name");
ping_args.count = arg_int0("c", "count", "<n>", "Stop after <n> replies");
ping_args.interval = arg_int0("i", "interval", "<seconds>", "Wait interval");
ping_args.end = arg_end(1);
const esp_console_cmd_t cmd = {
.command = "ping",
.help = "Send ICMP ECHO_REQUEST to network hosts",
.hint = NULL,
.func = &cmd_ping,
.argtable = &ping_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
void app_console_register_commands(void) {
register_iperf_cmd();
register_monitor_cmd();
register_scan_cmd();
register_wifi_cmd();
register_nvs_cmd();
register_gps_cmd();
register_ping();
} }

View File

@ -30,28 +30,20 @@
* 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
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
// This matches the call in main.c /**
* @brief Register application-specific console commands
*/
void app_console_register_commands(void); void app_console_register_commands(void);
// Implemented in main.c - updates prompt based on NVS dirty state
// Helper for prompt updates
void app_console_update_prompt(void); void app_console_update_prompt(void);
// Sub-module registers
void register_system_cmd(void);
void register_wifi_cmd(void);
void register_iperf_cmd(void);
void register_nvs_cmd(void);
void register_gps_cmd(void);
void register_ping_cmd(void);
void register_monitor_cmd(void);
void register_ip_cmd(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -1,154 +0,0 @@
/*
* cmd_gps.c
*
* Copyright (c) 2025 Umber Networks & Robert McMahon
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <inttypes.h>
#include "esp_log.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "gps_sync.h"
#include "app_console.h"
// ============================================================================
// COMMAND: gps (Configure & Status)
// ============================================================================
static struct {
struct arg_lit *enable;
struct arg_lit *disable;
struct arg_lit *status;
struct arg_lit *help;
struct arg_end *end;
} gps_args;
static int cmd_gps(int argc, char **argv) {
int nerrors = arg_parse(argc, argv, (void **)&gps_args);
if (nerrors > 0) {
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;
}
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;
}
// --- HANDLE SETTERS ---
bool changed = false;
if (gps_args.enable->count > 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);
// --- 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();
int64_t pps_age = gps_get_pps_age_ms();
printf("Status: %s\n", ts.synced ? "SYNCED" : "SEARCHING");
// Print Raw NMEA
char nmea_buf[128];
gps_get_last_nmea(nmea_buf, sizeof(nmea_buf));
// Cleanup CR/LF for printing
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 > 1 && (nmea_buf[len-2] == '\r' || nmea_buf[len-2] == '\n')) nmea_buf[len-2] = 0;
printf("Last NMEA: [%s]\n", nmea_buf);
if (pps_age < 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) {
time_t now_sec = ts.gps_us / 1000000;
struct tm tm_info;
gmtime_r(&now_sec, &tm_info);
char time_buf[64];
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();
return 0;
}
void register_gps_cmd(void) {
gps_args.enable = arg_lit0(NULL, "enable", "Enable GPS");
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);
const esp_console_cmd_t cmd = {
.command = "gps",
.help = "Configure GPS",
.func = &cmd_gps,
.argtable = &gps_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}

View File

@ -1,106 +0,0 @@
/*
* cmd_ip.c
*
* Copyright (c) 2025 Umber Networks & Robert McMahon
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "esp_netif.h"
#include "esp_mac.h"
#include "lwip/inet.h"
#include "app_console.h"
static struct {
struct arg_lit *addr;
struct arg_lit *help;
struct arg_end *end;
} ip_args;
static void print_iface_details(const char* key) {
esp_netif_t *netif = esp_netif_get_handle_from_ifkey(key);
if (!netif) return;
const char *desc = esp_netif_get_desc(netif);
bool is_up = esp_netif_is_netif_up(netif);
uint8_t mac[6] = {0};
esp_netif_get_mac(netif, mac);
esp_netif_ip_info_t ip_info;
esp_netif_get_ip_info(netif, &ip_info);
printf("%s (%s): <%s>\n", key, desc ? desc : "auth", is_up ? "UP" : "DOWN");
printf(" link/ether %02x:%02x:%02x:%02x:%02x:%02x\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
if (is_up && ip_info.ip.addr != 0) {
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) {
int nerrors = arg_parse(argc, argv, (void **)&ip_args);
if (nerrors > 0) {
arg_print_errors(stderr, ip_args.end, argv[0]);
return 1;
}
if (ip_args.help->count > 0) {
printf("Usage: ip [addr]\n");
return 0;
}
// Explicitly check standard interfaces
print_iface_details("WIFI_STA_DEF");
print_iface_details("WIFI_AP_DEF");
print_iface_details("ETH_DEF");
return 0;
}
void register_ip_cmd(void) {
ip_args.addr = arg_lit0("a", "addr", "Show addresses");
ip_args.help = arg_lit0("h", "help", "Help");
ip_args.end = arg_end(1);
const esp_console_cmd_t cmd = {
.command = "ip",
.help = "Show network interfaces",
.func = &cmd_ip,
.argtable = &ip_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}

View File

@ -1,151 +0,0 @@
/*
* cmd_iperf.c
*
* Copyright (c) 2025 Umber Networks & Robert McMahon
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "arpa/inet.h"
#include "iperf.h"
#include "app_console.h"
// ============================================================================
// COMMAND: iperf
// ============================================================================
static struct {
struct arg_lit *start, *stop, *status, *save, *reload;
struct arg_lit *clear_nvs;
struct arg_str *ip;
struct arg_int *port, *pps, *len, *burst;
struct arg_lit *help;
struct arg_end *end;
} iperf_args;
static int cmd_iperf(int argc, char **argv) {
int nerrors = arg_parse(argc, argv, (void **)&iperf_args);
if (nerrors > 0) {
arg_print_errors(stderr, iperf_args.end, argv[0]);
return 1;
}
if (iperf_args.help->count > 0) {
printf("Usage: iperf [options]\n");
printf(" --start Start traffic\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;
}
// --- 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_param_get(&cfg); // Get current staging
if (iperf_args.ip->count > 0) { cfg.dip = inet_addr(iperf_args.ip->sval[0]); config_changed = true; }
if (iperf_args.port->count > 0) { cfg.dport = (uint16_t)iperf_args.port->ival[0]; config_changed = true; }
if (iperf_args.len->count > 0) { cfg.send_len = (uint32_t)iperf_args.len->ival[0]; config_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 (iperf_args.pps->ival[0] > 0) {
cfg.target_pps = (uint32_t)iperf_args.pps->ival[0];
config_changed = true;
}
}
if (config_changed) {
iperf_param_set(&cfg);
printf("RAM configuration updated.\n");
}
if (iperf_args.save->count > 0) {
bool changed = false;
if (iperf_param_save(&changed) == ESP_OK) {
printf(changed ? "Configuration saved to NVS.\n" : "No changes to save (NVS matches RAM).\n");
} else {
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;
}
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 = {
.command = "iperf",
.help = "Traffic Gen",
.func = &cmd_iperf,
.argtable = &iperf_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}

View File

@ -1,134 +0,0 @@
/*
* cmd_monitor.c
*
* Copyright (c) 2025 Umber Networks & Robert McMahon
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "esp_log.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "wifi_controller.h"
#include "iperf.h"
#include "app_console.h"
// ============================================================================
// COMMAND: monitor
// ============================================================================
static struct {
struct arg_lit *start, *stop, *status, *save, *reload;
struct arg_lit *clear_nvs;
struct arg_int *channel;
struct arg_lit *help;
struct arg_end *end;
} mon_args;
static int cmd_monitor(int argc, char **argv) {
int nerrors = arg_parse(argc, argv, (void **)&mon_args);
if (nerrors > 0) {
arg_print_errors(stderr, mon_args.end, argv[0]);
return 1;
}
if (mon_args.help->count > 0) {
printf("Usage: monitor [--start|--stop] [-c <ch>] [--save|--reload]\n");
return 0;
}
// --- Actions: NVS Management ---
if (mon_args.clear_nvs->count > 0) {
wifi_ctl_param_clear();
printf("Monitor config cleared (Defaulting to Ch 6).\n");
app_console_update_prompt();
return 0;
}
if (mon_args.reload->count > 0) {
wifi_ctl_param_reload();
printf("Config reloaded from NVS.\n");
}
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) {
mon_args.start = arg_lit0(NULL, "start", "Start");
mon_args.stop = arg_lit0(NULL, "stop", "Stop");
mon_args.status = arg_lit0(NULL, "status", "Status");
mon_args.save = arg_lit0(NULL, "save", "Save");
mon_args.reload = arg_lit0(NULL, "reload", "Reload");
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 = {
.command = "monitor",
.help = "Monitor Mode",
.func = &cmd_monitor,
.argtable = &mon_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}

View File

@ -1,159 +0,0 @@
/*
* cmd_nvs.c
*
* Copyright (c) 2025 Umber Networks & Robert McMahon
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "esp_log.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "wifi_cfg.h"
#include "iperf.h"
#include "app_console.h"
// ============================================================================
// COMMAND: nvs (Storage Management)
// ============================================================================
static struct {
struct arg_lit *dump;
struct arg_lit *clear_all;
struct arg_lit *help;
struct arg_end *end;
} nvs_args;
// --- Helper Functions for Dumping ---
static void print_nvs_key_str(nvs_handle_t h, const char *key, const char *label) {
char buf[64] = {0};
size_t len = sizeof(buf);
if (nvs_get_str(h, key, buf, &len) == ESP_OK) {
printf(" %-12s : %s\n", label, buf);
} else {
printf(" %-12s : <empty>\n", label);
}
}
static void print_nvs_key_u32(nvs_handle_t h, const char *key, const char *label) {
uint32_t val = 0;
if (nvs_get_u32(h, key, &val) == ESP_OK) {
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) {
uint8_t val = 0;
if (nvs_get_u8(h, key, &val) == ESP_OK) {
printf(" %-12s : %u\n", label, val);
} 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;
}
if (nvs_args.help->count > 0) {
printf("Usage: nvs [--dump] [--clear-all]\n");
return 0;
}
// --- CLEAR ALL ---
if (nvs_args.clear_all->count > 0) {
printf("Erasing ALL settings from NVS...\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 ---
// Note: NVS iterators are complex in ESP-IDF; explicit key listing is often simpler for known keys.
printf("\n--- [WiFi Config (netcfg)] ---\n");
nvs_handle_t h;
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");
app_console_update_prompt();
return 0;
}
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 = {
.command = "nvs",
.help = "Storage Management",
.func = &cmd_nvs,
.argtable = &nvs_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}

View File

@ -1,173 +0,0 @@
/*
* cmd_ping.c
*
* Copyright (c) 2025 Umber Networks & Robert McMahon
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <inttypes.h>
#include "esp_log.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "ping/ping_sock.h"
#include "lwip/inet.h"
#include "lwip/netdb.h"
#include "app_console.h"
// ============================================================================
// COMMAND: ping (ICMP Echo)
// ============================================================================
static struct {
struct arg_str *host;
struct arg_int *count;
struct arg_int *interval;
struct arg_end *end;
} ping_args;
static void cmd_ping_on_ping_success(esp_ping_handle_t hdl, void *args) {
uint8_t ttl;
uint16_t seqno;
uint32_t elapsed_time, recv_len;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
printf("%" PRIu32 " bytes from %s: icmp_seq=%u ttl=%u time=%" PRIu32 " ms\n",
recv_len, inet_ntoa(target_addr.u_addr.ip4), seqno, ttl, elapsed_time);
}
static void cmd_ping_on_ping_timeout(esp_ping_handle_t hdl, void *args) {
uint16_t seqno;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
printf("From %s: icmp_seq=%u timeout\n", inet_ntoa(target_addr.u_addr.ip4), seqno);
}
static void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args) {
uint32_t transmitted;
uint32_t received;
uint32_t total_time_ms;
esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted));
esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received));
esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms));
printf("\n--- ping statistics ---\n");
printf("%" PRIu32 " packets transmitted, %" PRIu32 " received, %" PRIu32 "%% packet loss, time %" PRIu32 "ms\n",
transmitted, received, (transmitted - received) * 100 / transmitted, total_time_ms);
esp_ping_delete_session(hdl);
}
static int cmd_ping(int argc, char **argv) {
int nerrors = arg_parse(argc, argv, (void **)&ping_args);
if (nerrors != 0) {
arg_print_errors(stderr, ping_args.end, argv[0]);
return 1;
}
esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG();
// Parse Args
if (ping_args.count->count > 0) {
config.count = ping_args.count->ival[0];
}
if (ping_args.interval->count > 0) {
config.interval_ms = ping_args.interval->ival[0] * 1000;
}
// Parse Target IP or Hostname
ip_addr_t target_addr;
struct addrinfo hint;
struct addrinfo *res = NULL;
memset(&hint, 0, sizeof(hint));
memset(&target_addr, 0, sizeof(target_addr));
// Check if simple IP string
if (inet_aton(ping_args.host->sval[0], &target_addr.u_addr.ip4)) {
target_addr.type = IPADDR_TYPE_V4;
} else {
// Resolve Hostname
printf("Resolving %s...\n", ping_args.host->sval[0]);
// Set hint to prefer IPv4 if desired, or leave 0 for ANY
hint.ai_family = AF_INET;
if (getaddrinfo(ping_args.host->sval[0], NULL, &hint, &res) != 0) {
printf("ping: unknown host %s\n", ping_args.host->sval[0]);
return 1;
}
// Convert struct sockaddr_in to ip_addr_t
struct sockaddr_in *sa = (struct sockaddr_in *)res->ai_addr;
inet_addr_to_ip4addr(ip_2_ip4(&target_addr), &sa->sin_addr);
target_addr.type = IPADDR_TYPE_V4;
freeaddrinfo(res);
}
config.target_addr = target_addr;
config.task_stack_size = 4096; // Ensure enough stack for callbacks
esp_ping_callbacks_t cbs = {
.on_ping_success = cmd_ping_on_ping_success,
.on_ping_timeout = cmd_ping_on_ping_timeout,
.on_ping_end = cmd_ping_on_ping_end,
.cb_args = NULL
};
esp_ping_handle_t ping;
esp_ping_new_session(&config, &cbs, &ping);
esp_ping_start(ping);
return 0;
}
void register_ping_cmd(void) {
ping_args.host = arg_str1(NULL, NULL, "<host>", "Host address or name");
ping_args.count = arg_int0("c", "count", "<n>", "Stop after <n> replies");
ping_args.interval = arg_int0("i", "interval", "<seconds>", "Wait interval");
ping_args.end = arg_end(1);
const esp_console_cmd_t cmd = {
.command = "ping",
.help = "Send ICMP ECHO_REQUEST to network hosts",
.hint = NULL,
.func = &cmd_ping,
.argtable = &ping_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}

View File

@ -1,164 +0,0 @@
/*
* cmd_system.c
*
* Copyright (c) 2025 Umber Networks & Robert McMahon
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "esp_log.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "esp_system.h"
#include "esp_chip_info.h"
#include "esp_mac.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "app_console.h"
static const char *TAG = "CMD_SYS";
// ============================================================================
// COMMAND: system (Reboot, Info, Heap)
// ============================================================================
static struct {
struct arg_lit *reboot;
struct arg_lit *info;
struct arg_lit *heap;
struct arg_lit *help;
struct arg_end *end;
} sys_args;
static const char* get_chip_model_string(esp_chip_model_t model) {
switch (model) {
case CHIP_ESP32: return "ESP32";
case CHIP_ESP32S2: return "ESP32-S2";
case CHIP_ESP32S3: return "ESP32-S3";
case CHIP_ESP32C3: return "ESP32-C3";
case CHIP_ESP32C2: return "ESP32-C2";
case CHIP_ESP32C6: return "ESP32-C6";
case CHIP_ESP32H2: return "ESP32-H2";
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0))
case CHIP_ESP32C5: return "ESP32-C5"; // Requires recent IDF
case CHIP_ESP32P4: return "ESP32-P4";
#endif
default: return "Unknown";
}
}
static int cmd_system(int argc, char **argv) {
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) {
printf("Usage: system [--reboot|--info|--heap]\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();
}
return 0;
}
void register_system_cmd(void) {
sys_args.reboot = arg_lit0(NULL, "reboot", "Reboot device");
sys_args.info = arg_lit0(NULL, "info", "Chip Info");
sys_args.heap = arg_lit0(NULL, "heap", "Memory Info");
sys_args.help = arg_lit0("h", "help", "Help");
sys_args.end = arg_end(1);
const esp_console_cmd_t cmd = {
.command = "system",
.help = "System Management (Reboot, Info, Heap)",
.func = &cmd_system,
.argtable = &sys_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}

View File

@ -1,356 +0,0 @@
/*
* cmd_wifi.c
*
* Copyright (c) 2025 Umber Networks & Robert McMahon
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "esp_wifi.h"
#include "esp_netif.h"
#include "wifi_cfg.h"
#include "wifi_controller.h"
#include "app_console.h"
// --- Forward Declarations ---
static int wifi_do_scan(int argc, char **argv);
static int wifi_do_connect(int argc, char **argv);
static int wifi_do_disconnect(int argc, char **argv);
static int wifi_do_link(int argc, char **argv);
static int wifi_do_power(int argc, char **argv);
static int wifi_do_mode(int argc, char **argv);
// ============================================================================
// COMMAND: wifi (Dispatcher)
// ============================================================================
static void print_wifi_usage(void) {
printf("Usage: wifi <subcommand> [args]\n");
printf("Subcommands:\n");
printf(" scan Scan for networks\n");
printf(" connect Connect to an AP (alias: join)\n");
printf(" disconnect Disconnect from AP\n");
printf(" status Show connection status (alias: link)\n");
printf(" power Set power save (on/off)\n");
printf(" mode Set mode (sta/monitor)\n");
printf("\nType 'wifi <subcommand> --help' for details.\n");
}
static int cmd_wifi(int argc, char **argv) {
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) {
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");
esp_err_t err = esp_wifi_scan_start(&scan_config, true);
if (err == ESP_ERR_WIFI_STATE) {
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
// SSID: 32 chars | BSSID: 17 chars | RSSI: 4 chars | Ch: 3 chars | Auth
printf("%-32s | %-17s | %4s | %3s | %s\n", "SSID", "BSSID", "RSSI", "Ch", "Auth");
printf("--------------------------------------------------------------------------------------\n");
for (int i = 0; i < ap_count; i++) {
char *authmode = "UNK";
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",
ap_info[i].ssid,
ap_info[i].bssid[0], ap_info[i].bssid[1], ap_info[i].bssid[2],
ap_info[i].bssid[3], ap_info[i].bssid[4], ap_info[i].bssid[5],
ap_info[i].rssi, ap_info[i].primary, authmode);
}
printf("--------------------------------------------------------------------------------------\n");
free(ap_info);
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;
static int wifi_do_connect(int argc, char **argv) {
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);
if (nerrors > 0) {
arg_print_errors(stderr, connect_args.end, argv[0]);
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 *pass = (connect_args.password->count > 0) ? connect_args.password->sval[0] : "";
const char *bssid = (connect_args.bssid->count > 0) ? connect_args.bssid->sval[0] : "";
printf("Connecting to '%s' (BSSID: %s)...\n", ssid, bssid[0] ? bssid : "Any");
wifi_cfg_set_credentials(ssid, pass, bssid);
wifi_cfg_set_dhcp(true);
printf("Credentials saved. Rebooting to apply...\n");
esp_restart();
return 0;
}
// ----------------------------------------------------------------------------
// Sub-command: wifi disconnect
// ----------------------------------------------------------------------------
static int wifi_do_disconnect(int argc, char **argv) {
printf("Disconnecting...\n");
esp_wifi_disconnect();
return 0;
}
// ----------------------------------------------------------------------------
// Registration
// ----------------------------------------------------------------------------
void register_wifi_cmd(void) {
const esp_console_cmd_t cmd = {
.command = "wifi",
.help = "Wi-Fi Tool: scan, connect, status, power, mode",
.hint = "<subcommand> [args]",
.func = &cmd_wifi,
.argtable = NULL
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}

View File

@ -30,12 +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.
*/ */
/*
* wifi_cfg.c
*
* Copyright (c) 2025 Umber Networks & Robert McMahon
* All rights reserved.
*/
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@ -50,13 +44,15 @@
#include "esp_event.h" #include "esp_event.h"
#include "wifi_cfg.h" #include "wifi_cfg.h"
// Removed unused TAG
static esp_netif_t *sta_netif = NULL; static esp_netif_t *sta_netif = NULL;
// --- Helper: NVS Write --- // --- Helper: NVS Write ---
static void nvs_write_str(const char *key, const char *val) { static void nvs_write_str(const char *key, const char *val) {
nvs_handle_t h; nvs_handle_t h;
if (nvs_open("netcfg", NVS_READWRITE, &h) == ESP_OK) { if (nvs_open("netcfg", NVS_READWRITE, &h) == ESP_OK) {
if (val && strlen(val) > 0) nvs_set_str(h, key, val); if (val) nvs_set_str(h, key, val);
else nvs_erase_key(h, key); else nvs_erase_key(h, key);
nvs_commit(h); nvs_commit(h);
nvs_close(h); nvs_close(h);
@ -72,25 +68,11 @@ static void nvs_write_u8(const char *key, uint8_t val) {
} }
} }
// --- Helper: MAC Parser ---
static bool parse_bssid(const char *str, uint8_t *out_bssid) {
if (!str || strlen(str) != 17) return false;
unsigned int bytes[6];
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) {
for (int i = 0; i < 6; i++) out_bssid[i] = (uint8_t)bytes[i];
return true;
}
return false;
}
// --- Public Setters --- // --- Public Setters ---
void wifi_cfg_set_credentials(const char* ssid, const char* pass, const char* bssid) { void wifi_cfg_set_credentials(const char* ssid, const char* pass) {
nvs_write_str("ssid", ssid); nvs_write_str("ssid", ssid);
nvs_write_str("pass", pass); 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) { void wifi_cfg_set_static_ip(const char* ip, const char* mask, const char* gw) {
@ -109,7 +91,6 @@ void wifi_cfg_clear_credentials(void) {
if (nvs_open("netcfg", NVS_READWRITE, &h) == ESP_OK) { if (nvs_open("netcfg", NVS_READWRITE, &h) == ESP_OK) {
nvs_erase_key(h, "ssid"); nvs_erase_key(h, "ssid");
nvs_erase_key(h, "pass"); nvs_erase_key(h, "pass");
nvs_erase_key(h, "bssid");
nvs_erase_key(h, "ip"); nvs_erase_key(h, "ip");
nvs_erase_key(h, "mask"); nvs_erase_key(h, "mask");
nvs_erase_key(h, "gw"); nvs_erase_key(h, "gw");
@ -134,9 +115,9 @@ void wifi_cfg_init(void) {
nvs_flash_init(); nvs_flash_init();
} }
static bool load_cfg(char* ssid, size_t ssz, char* pass, size_t psz, char* bssid, size_t bsz, static bool load_cfg(char* ssid, size_t ssz, char* pass, size_t psz,
char* ip, size_t isz, char* mask, size_t msz, char* gw, size_t gsz, char* 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, char* band, size_t bsz, char* bw, size_t bwsz, char* powersave, size_t pssz,
char* mode, size_t modesz, uint8_t* mon_ch, bool* dhcp){ char* mode, size_t modesz, uint8_t* mon_ch, bool* dhcp){
nvs_handle_t h; nvs_handle_t h;
if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) return false; if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) return false;
@ -148,13 +129,12 @@ static bool load_cfg(char* ssid, size_t ssz, char* pass, size_t psz, char* bssid
// Load Optionals // Load Optionals
len = psz; if (nvs_get_str(h, "pass", pass, &len) != ESP_OK) pass[0]=0; 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 = 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 = 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; len = gsz; if (nvs_get_str(h, "gw", gw, &len) != ESP_OK) gw[0]=0;
// Defaults // Defaults
len = bsz_band; if (nvs_get_str(h, "band", band, &len) != ESP_OK) strcpy(band, "2.4G"); len = bsz; if (nvs_get_str(h, "band", band, &len) != ESP_OK) strcpy(band, "2.4G");
len = bwsz; if (nvs_get_str(h, "bw", bw, &len) != ESP_OK) strcpy(bw, "HT20"); len = 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 = 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"); len = modesz; if (nvs_get_str(h, "mode", mode, &len) != ESP_OK) strcpy(mode, "STA");
@ -181,14 +161,12 @@ static void apply_ip_static(const char* ip, const char* mask, const char* gw){
} }
bool wifi_cfg_apply_from_nvs(void) { 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 ssid[64]={0}, pass[64]={0}, ip[32]={0}, mask[32]={0}, gw[32]={0};
char band[16]={0}, bw[16]={0}, powersave[16]={0}, mode[16]={0}; char band[16]={0}, bw[16]={0}, powersave[16]={0}, mode[16]={0};
uint8_t mon_ch = 36; bool dhcp = true; uint8_t mon_ch = 36; bool dhcp = true;
if (!load_cfg(ssid,sizeof(ssid), pass,sizeof(pass), bssid_str,sizeof(bssid_str), if (!load_cfg(ssid,sizeof(ssid), pass,sizeof(pass), ip,sizeof(ip), mask,sizeof(mask), gw,sizeof(gw),
ip,sizeof(ip), mask,sizeof(mask), gw,sizeof(gw), band,sizeof(band), bw,sizeof(bw), powersave,sizeof(powersave), mode,sizeof(mode), &mon_ch, &dhcp)){
band,sizeof(band), bw,sizeof(bw), powersave,sizeof(powersave),
mode,sizeof(mode), &mon_ch, &dhcp)){
return false; return false;
} }
@ -204,16 +182,6 @@ bool wifi_cfg_apply_from_nvs(void) {
wcfg.sta.sae_pwe_h2e = WPA3_SAE_PWE_BOTH; wcfg.sta.sae_pwe_h2e = WPA3_SAE_PWE_BOTH;
wcfg.sta.scan_method = WIFI_ALL_CHANNEL_SCAN; 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_mode(WIFI_MODE_STA);
esp_wifi_set_config(WIFI_IF_STA, &wcfg); esp_wifi_set_config(WIFI_IF_STA, &wcfg);
@ -226,7 +194,7 @@ bool wifi_cfg_apply_from_nvs(void) {
} }
wifi_ps_type_t wifi_cfg_get_power_save_mode(void) { wifi_ps_type_t wifi_cfg_get_power_save_mode(void) {
return WIFI_PS_NONE; // Default implementation return WIFI_PS_NONE;
} }
bool wifi_cfg_get_bandwidth(char *buf, size_t buf_size) { bool wifi_cfg_get_bandwidth(char *buf, size_t buf_size) {
@ -244,28 +212,37 @@ bool wifi_cfg_get_mode(char *mode, uint8_t *mon_ch) {
return true; return true;
} }
// --- State Checkers ---
bool wifi_cfg_monitor_channel_is_unsaved(uint8_t ram_value) { bool wifi_cfg_monitor_channel_is_unsaved(uint8_t ram_value) {
nvs_handle_t h; nvs_handle_t h;
if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) return true; if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) return true; // Assume dirty if error
uint8_t nvs_val = 0; uint8_t nvs_val = 0;
esp_err_t err = nvs_get_u8(h, "mon_ch", &nvs_val); esp_err_t err = nvs_get_u8(h, "mon_ch", &nvs_val);
nvs_close(h); nvs_close(h);
if (err != ESP_OK) return true;
if (err != ESP_OK) return true; // Key missing = dirty (using default)
return (nvs_val != ram_value); return (nvs_val != ram_value);
} }
// --- Setters ---
bool wifi_cfg_set_monitor_channel(uint8_t channel) { bool wifi_cfg_set_monitor_channel(uint8_t channel) {
nvs_handle_t h; nvs_handle_t h;
if (nvs_open("netcfg", NVS_READWRITE, &h) != ESP_OK) return false; if (nvs_open("netcfg", NVS_READWRITE, &h) != ESP_OK) return false;
uint8_t current = 0; uint8_t current = 0;
// Check if write is necessary
if (nvs_get_u8(h, "mon_ch", &current) == ESP_OK) { if (nvs_get_u8(h, "mon_ch", &current) == ESP_OK) {
if (current == channel) { if (current == channel) {
nvs_close(h); nvs_close(h);
return false; return false; // No change needed
} }
} }
nvs_set_u8(h, "mon_ch", channel); nvs_set_u8(h, "mon_ch", channel);
nvs_commit(h); nvs_commit(h);
nvs_close(h); nvs_close(h);
return true; return true; // Write occurred
} }

View File

@ -30,6 +30,8 @@
* 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 #ifndef WIFI_CFG_H
#define WIFI_CFG_H #define WIFI_CFG_H
@ -49,15 +51,16 @@ 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_bandwidth(char *buf, size_t buf_size);
bool wifi_cfg_get_mode(char *mode, uint8_t *mon_ch); bool wifi_cfg_get_mode(char *mode, uint8_t *mon_ch);
// --- State Checkers --- // --- State Checkers (Dirty Flag) ---
// Returns true if RAM value differs from NVS
bool wifi_cfg_monitor_channel_is_unsaved(uint8_t ram_value); bool wifi_cfg_monitor_channel_is_unsaved(uint8_t ram_value);
// --- Setters --- // --- Setters ---
// UPDATED: Now accepts optional bssid (pass NULL if not used) void wifi_cfg_set_credentials(const char* ssid, const char* pass);
void wifi_cfg_set_credentials(const char* ssid, const char* pass, const char* bssid);
void wifi_cfg_set_static_ip(const char* ip, const char* mask, const char* gw); void wifi_cfg_set_static_ip(const char* ip, const char* mask, const char* gw);
void wifi_cfg_set_dhcp(bool enable); void wifi_cfg_set_dhcp(bool enable);
// Returns true if NVS was actually updated, false if values were identical
bool wifi_cfg_set_monitor_channel(uint8_t channel); bool wifi_cfg_set_monitor_channel(uint8_t channel);
// --- Clearing --- // --- Clearing ---