Compare commits
No commits in common. "099c28f9c70a725e125c102e4cd41141e5424360" and "6c214e8e927f389a642a139def09374f8b97611e" have entirely different histories.
099c28f9c7
...
6c214e8e92
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
@ -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", ¤t) == ESP_OK) {
|
if (nvs_get_u8(h, "mon_ch", ¤t) == 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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 ---
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue