more on commands
This commit is contained in:
parent
6c214e8e92
commit
42905200ea
|
|
@ -1,3 +1,11 @@
|
|||
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 "."
|
||||
PRIV_REQUIRES console iperf wifi_cfg wifi_controller nvs_flash gps_sync lwip)
|
||||
PRIV_REQUIRES console iperf wifi_cfg wifi_controller nvs_flash gps_sync lwip driver)
|
||||
|
|
|
|||
|
|
@ -30,712 +30,25 @@
|
|||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/*
|
||||
* app_console.c
|
||||
*
|
||||
* Copyright (c) 2025 Umber Networks & Robert McMahon
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
#include "app_console.h"
|
||||
#include "esp_console.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>
|
||||
|
||||
// --- Helper: Prompt Update ---
|
||||
// Updates the "esp32>" vs "esp32*>" prompt based on dirty state
|
||||
static void end_cmd(void) {
|
||||
app_console_update_prompt();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 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;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
// --- Registration ---
|
||||
void app_console_register_commands(void) {
|
||||
register_iperf_cmd();
|
||||
register_monitor_cmd();
|
||||
register_scan_cmd();
|
||||
register_wifi_cmd();
|
||||
register_system_cmd();
|
||||
register_nvs_cmd();
|
||||
register_wifi_cmd();
|
||||
register_iperf_cmd();
|
||||
register_gps_cmd();
|
||||
register_ping();
|
||||
register_ping_cmd();
|
||||
register_monitor_cmd();
|
||||
register_ip_cmd();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,20 +30,28 @@
|
|||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Register application-specific console commands
|
||||
*/
|
||||
// This matches the call in main.c
|
||||
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);
|
||||
|
||||
// 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
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* 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));
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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));
|
||||
}
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* 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));
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* 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));
|
||||
}
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* 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));
|
||||
}
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* 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));
|
||||
}
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* 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));
|
||||
}
|
||||
|
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* 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 for Sub-Command Handlers ---
|
||||
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);
|
||||
|
||||
// ============================================================================
|
||||
// COMMAND: wifi (Dispatcher)
|
||||
// ============================================================================
|
||||
|
||||
static int cmd_wifi(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
printf("Usage: wifi <subcommand> [args]\n");
|
||||
printf("Subcommands:\n");
|
||||
printf(" link Show connection status\n");
|
||||
printf(" scan Scan for networks\n");
|
||||
printf(" connect Connect to an AP\n");
|
||||
printf(" disconnect Disconnect from AP\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Dispatch based on subcommand
|
||||
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], "disconnect") == 0) return wifi_do_disconnect(argc - 1, &argv[1]);
|
||||
if (strcmp(argv[1], "link") == 0) return wifi_do_link(argc - 1, &argv[1]);
|
||||
|
||||
printf("Unknown subcommand '%s'. See 'wifi --help'.\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Sub-command: wifi link
|
||||
// ----------------------------------------------------------------------------
|
||||
static struct {
|
||||
struct arg_lit *help;
|
||||
struct arg_end *end;
|
||||
} link_args;
|
||||
|
||||
static int wifi_do_link(int argc, char **argv) {
|
||||
// Parse args mainly to support --help
|
||||
if (arg_parse(argc, argv, (void **)&link_args) == 0) {
|
||||
if (link_args.help->count > 0) {
|
||||
printf("Usage: wifi link\nDescription: Show connection status (RSSI, BSSID, SSID, IP).\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
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\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));
|
||||
printf("\tMask: " IPSTR "\n", IP2STR(&ip.netmask));
|
||||
printf("\tGW: " IPSTR "\n", IP2STR(&ip.gw));
|
||||
}
|
||||
} else {
|
||||
printf("Not connected.\n");
|
||||
}
|
||||
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) {
|
||||
if (arg_parse(argc, argv, (void **)&scan_args) == 0) {
|
||||
if (scan_args.help->count > 0) {
|
||||
printf("Usage: wifi scan\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent scanning in Monitor Mode (Hardware limitation)
|
||||
if (wifi_ctl_get_mode() == WIFI_CTL_MODE_MONITOR) {
|
||||
printf("Error: Cannot scan while in Monitor Mode.\n");
|
||||
printf("Run 'monitor --stop' first, or 'wifi disconnect'.\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);
|
||||
|
||||
// Smart Retry: If busy connecting, force disconnect momentarily
|
||||
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 for scan list.\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("BSS | SSID | RSSI | Ch | Auth\n");
|
||||
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;
|
||||
default: break;
|
||||
}
|
||||
|
||||
printf("%02x:%02x:%02x:%02x:%02x:%02x | %-32.32s | %4d | %2d | %s\n",
|
||||
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].ssid, ap_info[i].rssi, ap_info[i].primary, authmode);
|
||||
}
|
||||
printf("-------------------------------------------------------------------------------------\n");
|
||||
free(ap_info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Sub-command: wifi connect
|
||||
// ----------------------------------------------------------------------------
|
||||
static struct {
|
||||
struct arg_str *ssid;
|
||||
struct arg_str *password;
|
||||
struct arg_lit *help;
|
||||
struct arg_end *end;
|
||||
} connect_args;
|
||||
|
||||
static int wifi_do_connect(int argc, char **argv) {
|
||||
// Re-init pointers to be safe with static structs
|
||||
connect_args.ssid = arg_str1(NULL, NULL, "<ssid>", "SSID");
|
||||
connect_args.password = arg_str0(NULL, NULL, "<pass>", "Password");
|
||||
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]\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *ssid = connect_args.ssid->sval[0];
|
||||
const char *pass = (connect_args.password->count > 0) ? connect_args.password->sval[0] : "";
|
||||
|
||||
printf("Connecting to '%s'...\n", ssid);
|
||||
|
||||
// Save to NVS
|
||||
wifi_cfg_set_credentials(ssid, pass);
|
||||
wifi_cfg_set_dhcp(true); // Default to DHCP for simple connect
|
||||
|
||||
// Trigger Reconnect
|
||||
// The most robust way to switch networks completely is to restart,
|
||||
// ensuring the WiFi Controller state machine resets cleanly from NVS.
|
||||
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();
|
||||
|
||||
// Optionally clear credentials if you want "disconnect" to mean "forget"
|
||||
// wifi_cfg_clear_credentials();
|
||||
// printf("Credentials forgotten.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Registration
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void register_wifi_cmd(void) {
|
||||
const esp_console_cmd_t cmd = {
|
||||
.command = "wifi",
|
||||
.help = "Wi-Fi Configuration Tool (iw style)",
|
||||
.func = &cmd_wifi,
|
||||
.argtable = NULL // Handled internally by sub-dispatchers
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
|
||||
}
|
||||
Loading…
Reference in New Issue