Add per-session history, tab completion, interactive prompts, and bounded input handling. Support deferred lifecycle and host-key actions after output drains, and document the expanded administration workflow.
631 lines
20 KiB
C
631 lines
20 KiB
C
/* SPDX-License-Identifier: GPL-3.0-only */
|
|
/* Physical-admin UART0 console for Wi-Fi configuration and diagnostics. */
|
|
|
|
#include "wifi_console.h"
|
|
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "console_input.h"
|
|
#include "esp_console.h"
|
|
#include "esp_err.h"
|
|
#include "esp_netif_ip_addr.h"
|
|
#include "esp_wifi_types.h"
|
|
|
|
#include "network_console.h"
|
|
#include "wifi_config.h"
|
|
#include "wifi_manager.h"
|
|
|
|
#define WIFI_CONSOLE_SECRET_CAPACITY WIFI_CONFIG_PSK_MAX_LEN
|
|
|
|
static void print_usage(void)
|
|
{
|
|
printf("Usage:\n");
|
|
printf(" wifi status|profiles|counters|clear-counters\n");
|
|
printf(" wifi start|stop|reconnect|next-profile\n");
|
|
printf(" wifi profile set <slot> <priority> <mixed|wpa3> <ssid>\n");
|
|
printf(" wifi profile secret <slot>\n");
|
|
printf(" wifi profile enable|disable|delete <slot>\n");
|
|
printf(" wifi ap policy <off|fallback|always>\n");
|
|
printf(" wifi ap ssid <ssid>\n");
|
|
printf(" wifi ap channel <1..11>\n");
|
|
printf(" wifi ap secret|show-secret\n");
|
|
printf(" wifi save|load|defaults|reset\n");
|
|
printf(" wifi ping <host> [count]\n");
|
|
printf(" wifi nslookup <host>\n");
|
|
printf(" wifi traceroute <host> [max-hops]\n");
|
|
}
|
|
|
|
static bool parse_u32(const char *text, uint32_t maximum, uint32_t *value)
|
|
{
|
|
if (text == NULL || *text == '\0') {
|
|
return false;
|
|
}
|
|
for (const char *character = text; *character != '\0'; ++character) {
|
|
if (*character < '0' || *character > '9') {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
char *end = NULL;
|
|
errno = 0;
|
|
unsigned long parsed = strtoul(text, &end, 10);
|
|
if (errno != 0 || end == text || *end != '\0' || parsed > maximum) {
|
|
return false;
|
|
}
|
|
*value = (uint32_t)parsed;
|
|
return true;
|
|
}
|
|
|
|
static bool parse_slot(const char *text, uint8_t *slot)
|
|
{
|
|
uint32_t parsed;
|
|
if (!parse_u32(text, WIFI_CONFIG_STA_PROFILE_COUNT - 1U, &parsed)) {
|
|
return false;
|
|
}
|
|
*slot = (uint8_t)parsed;
|
|
return true;
|
|
}
|
|
|
|
static void print_bytes_escaped(const uint8_t *data, size_t length)
|
|
{
|
|
putchar('"');
|
|
for (size_t index = 0; index < length; ++index) {
|
|
uint8_t byte = data[index];
|
|
if (byte == '\\' || byte == '"') {
|
|
printf("\\%c", (char)byte);
|
|
} else if (byte >= 0x20U && byte <= 0x7eU) {
|
|
putchar((char)byte);
|
|
} else {
|
|
printf("\\x%02x", (unsigned int)byte);
|
|
}
|
|
}
|
|
putchar('"');
|
|
}
|
|
|
|
static const char *auth_mode_name(wifi_auth_mode_t auth)
|
|
{
|
|
switch (auth) {
|
|
case WIFI_AUTH_OPEN:
|
|
return "open";
|
|
case WIFI_AUTH_WEP:
|
|
return "wep";
|
|
case WIFI_AUTH_WPA_PSK:
|
|
return "wpa";
|
|
case WIFI_AUTH_WPA2_PSK:
|
|
return "wpa2";
|
|
case WIFI_AUTH_WPA_WPA2_PSK:
|
|
return "wpa/wpa2";
|
|
case WIFI_AUTH_WPA2_ENTERPRISE:
|
|
return "wpa2-enterprise";
|
|
case WIFI_AUTH_WPA3_PSK:
|
|
return "wpa3";
|
|
case WIFI_AUTH_WPA2_WPA3_PSK:
|
|
return "wpa2/wpa3";
|
|
case WIFI_AUTH_WPA3_ENT_192:
|
|
return "wpa3-enterprise-192";
|
|
default:
|
|
return "other";
|
|
}
|
|
}
|
|
|
|
static void print_ipv4(uint32_t address)
|
|
{
|
|
esp_ip4_addr_t ip = {.addr = address};
|
|
printf(IPSTR, IP2STR(&ip));
|
|
}
|
|
|
|
static int show_status(void)
|
|
{
|
|
wifi_manager_snapshot_t snapshot;
|
|
wifi_app_config_t config;
|
|
esp_err_t error = wifi_manager_get_snapshot(&snapshot);
|
|
if (error == ESP_OK) {
|
|
error = wifi_manager_get_working_config(&config);
|
|
}
|
|
if (error != ESP_OK) {
|
|
printf("Wi-Fi manager unavailable: %s\n", esp_err_to_name(error));
|
|
return 1;
|
|
}
|
|
|
|
printf("Wi-Fi: initialized=%s enabled-at-boot=%s started=%s state=%s generation=%" PRIu32 "\n",
|
|
snapshot.initialized ? "yes" : "no",
|
|
config.enabled_at_boot ? "yes" : "no",
|
|
snapshot.started ? "yes" : "no",
|
|
wifi_manager_state_to_string(snapshot.state),
|
|
snapshot.config_generation);
|
|
|
|
if (snapshot.active_profile >= 0) {
|
|
printf("STA: profile=%d SSID=", snapshot.active_profile);
|
|
print_bytes_escaped((const uint8_t *)snapshot.sta_ssid, snapshot.sta_ssid_len);
|
|
printf(" channel=%u RSSI=%d auth=%s\n",
|
|
(unsigned int)snapshot.sta_channel,
|
|
(int)snapshot.sta_rssi,
|
|
auth_mode_name(snapshot.sta_auth));
|
|
} else {
|
|
printf("STA: no active profile\n");
|
|
}
|
|
|
|
if (snapshot.ip != 0U) {
|
|
printf("IPv4: address=");
|
|
print_ipv4(snapshot.ip);
|
|
printf(" netmask=");
|
|
print_ipv4(snapshot.netmask);
|
|
printf(" gateway=");
|
|
print_ipv4(snapshot.gateway);
|
|
putchar('\n');
|
|
}
|
|
|
|
printf("AP: policy=%s running=%s clients=%u channel=%u SSID=",
|
|
wifi_config_ap_policy_to_string(snapshot.ap_policy),
|
|
snapshot.ap_running ? "yes" : "no",
|
|
(unsigned int)snapshot.ap_client_count,
|
|
(unsigned int)snapshot.ap_channel);
|
|
print_bytes_escaped(config.ap_ssid, config.ap_ssid_len);
|
|
printf(" secret=set\n");
|
|
|
|
printf("Last: disconnect-reason=%u error=%s retry=%" PRIu32 "s\n",
|
|
(unsigned int)snapshot.last_disconnect_reason,
|
|
esp_err_to_name(snapshot.last_error),
|
|
snapshot.retry_seconds);
|
|
wifi_config_secure_wipe(&config, sizeof(config));
|
|
return 0;
|
|
}
|
|
|
|
static int show_profiles(void)
|
|
{
|
|
wifi_app_config_t config;
|
|
esp_err_t error = wifi_manager_get_working_config(&config);
|
|
if (error != ESP_OK) {
|
|
printf("Could not read Wi-Fi profiles: %s\n", esp_err_to_name(error));
|
|
return 1;
|
|
}
|
|
|
|
printf("slot enabled priority security secret SSID\n");
|
|
for (size_t slot = 0U; slot < WIFI_CONFIG_STA_PROFILE_COUNT; ++slot) {
|
|
const wifi_config_sta_profile_t *profile = &config.profiles[slot];
|
|
printf("%u %-7s %-8u %-8s %-6s ",
|
|
(unsigned int)slot,
|
|
profile->enabled ? "yes" : "no",
|
|
(unsigned int)profile->priority,
|
|
wifi_config_security_to_string(profile->security),
|
|
profile->psk_len > 0U ? "set" : "unset");
|
|
if (profile->ssid_len > 0U) {
|
|
print_bytes_escaped(profile->ssid, profile->ssid_len);
|
|
} else {
|
|
printf("<empty>");
|
|
}
|
|
putchar('\n');
|
|
}
|
|
|
|
wifi_config_secure_wipe(&config, sizeof(config));
|
|
return 0;
|
|
}
|
|
|
|
static int show_counters(void)
|
|
{
|
|
wifi_manager_snapshot_t snapshot;
|
|
esp_err_t error = wifi_manager_get_snapshot(&snapshot);
|
|
if (error != ESP_OK) {
|
|
printf("Could not read Wi-Fi counters: %s\n", esp_err_to_name(error));
|
|
return 1;
|
|
}
|
|
|
|
const wifi_manager_counters_t *counter = &snapshot.counters;
|
|
printf("Lifecycle: starts=%" PRIu64 " stops=%" PRIu64 " applies=%" PRIu64 "\n",
|
|
counter->starts, counter->stops, counter->applies);
|
|
printf("STA: attempts=%" PRIu64 " associations=%" PRIu64
|
|
" got-IP=%" PRIu64 " disconnects=%" PRIu64 "\n",
|
|
counter->connect_attempts, counter->associations,
|
|
counter->got_ip, counter->disconnects);
|
|
printf("Policy: exhausted-cycles=%" PRIu64 " retries=%" PRIu64 "\n",
|
|
counter->profile_cycles, counter->retries);
|
|
printf("AP: starts=%" PRIu64 " stops=%" PRIu64
|
|
" joins=%" PRIu64 " leaves=%" PRIu64 "\n",
|
|
counter->ap_starts, counter->ap_stops,
|
|
counter->client_joins, counter->client_leaves);
|
|
printf("Manager queue drops=%" PRIu64 "\n", counter->queue_drops);
|
|
return 0;
|
|
}
|
|
|
|
static esp_err_t apply_candidate(wifi_app_config_t *candidate)
|
|
{
|
|
esp_err_t error = wifi_manager_apply_working_config(candidate);
|
|
wifi_config_secure_wipe(candidate, sizeof(*candidate));
|
|
return error;
|
|
}
|
|
|
|
static esp_err_t read_secret_no_echo(uint8_t *secret, uint8_t *secret_len)
|
|
{
|
|
uint8_t buffer[WIFI_CONSOLE_SECRET_CAPACITY + 1U] = {0};
|
|
size_t length = 0U;
|
|
esp_err_t error = console_input_read_hidden(
|
|
"Enter 8..63 printable ASCII characters (input hidden, Ctrl-C cancels): ",
|
|
buffer, sizeof(buffer), WIFI_CONFIG_PSK_MIN_LEN,
|
|
WIFI_CONFIG_PSK_MAX_LEN, &length);
|
|
if (error == ESP_OK) {
|
|
memset(secret, 0, WIFI_CONFIG_PSK_MAX_LEN);
|
|
memcpy(secret, buffer, length);
|
|
*secret_len = (uint8_t)length;
|
|
}
|
|
wifi_config_secure_wipe(buffer, sizeof(buffer));
|
|
return error;
|
|
}
|
|
|
|
static int set_profile(char **argv)
|
|
{
|
|
uint8_t slot;
|
|
uint32_t priority;
|
|
wifi_config_security_t security;
|
|
size_t ssid_len = strlen(argv[6]);
|
|
if (!parse_slot(argv[3], &slot) ||
|
|
!parse_u32(argv[4], UINT8_MAX, &priority) ||
|
|
!wifi_config_parse_security(argv[5], &security) ||
|
|
ssid_len == 0U || ssid_len > WIFI_CONFIG_SSID_MAX_LEN) {
|
|
printf("Profile requires slot 0..%u, priority 0..255, valid security, and a 1..32-byte SSID.\n",
|
|
WIFI_CONFIG_STA_PROFILE_COUNT - 1U);
|
|
return 1;
|
|
}
|
|
|
|
wifi_app_config_t config;
|
|
esp_err_t error = wifi_manager_get_working_config(&config);
|
|
if (error != ESP_OK) {
|
|
printf("Could not read working configuration: %s\n", esp_err_to_name(error));
|
|
return 1;
|
|
}
|
|
|
|
wifi_config_sta_profile_t *profile = &config.profiles[slot];
|
|
memset(profile->ssid, 0, sizeof(profile->ssid));
|
|
memcpy(profile->ssid, argv[6], ssid_len);
|
|
profile->ssid_len = (uint8_t)ssid_len;
|
|
profile->priority = (uint8_t)priority;
|
|
profile->security = security;
|
|
error = apply_candidate(&config);
|
|
if (error != ESP_OK) {
|
|
printf("Could not apply profile: %s\n", esp_err_to_name(error));
|
|
return 1;
|
|
}
|
|
|
|
printf("Profile %u updated in RAM; its existing secret and enabled state were preserved.\n",
|
|
(unsigned int)slot);
|
|
return 0;
|
|
}
|
|
|
|
static int set_profile_secret(const char *slot_text)
|
|
{
|
|
uint8_t slot;
|
|
if (!parse_slot(slot_text, &slot)) {
|
|
printf("Profile slot must be 0..%u.\n", WIFI_CONFIG_STA_PROFILE_COUNT - 1U);
|
|
return 1;
|
|
}
|
|
|
|
wifi_app_config_t config;
|
|
esp_err_t error = wifi_manager_get_working_config(&config);
|
|
if (error != ESP_OK) {
|
|
printf("Could not read working configuration: %s\n", esp_err_to_name(error));
|
|
return 1;
|
|
}
|
|
if (config.profiles[slot].ssid_len == 0U) {
|
|
wifi_config_secure_wipe(&config, sizeof(config));
|
|
printf("Set the profile SSID before its secret.\n");
|
|
return 1;
|
|
}
|
|
|
|
error = read_secret_no_echo(config.profiles[slot].psk,
|
|
&config.profiles[slot].psk_len);
|
|
if (error == ESP_OK) {
|
|
error = apply_candidate(&config);
|
|
} else {
|
|
wifi_config_secure_wipe(&config, sizeof(config));
|
|
}
|
|
if (error != ESP_OK) {
|
|
if (error != ESP_ERR_INVALID_STATE) {
|
|
printf("Could not set profile secret: %s\n", esp_err_to_name(error));
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
printf("Profile %u secret updated in RAM; use 'wifi save' to persist it.\n",
|
|
(unsigned int)slot);
|
|
return 0;
|
|
}
|
|
|
|
static int change_profile_state(const char *operation, const char *slot_text)
|
|
{
|
|
uint8_t slot;
|
|
if (!parse_slot(slot_text, &slot)) {
|
|
printf("Profile slot must be 0..%u.\n", WIFI_CONFIG_STA_PROFILE_COUNT - 1U);
|
|
return 1;
|
|
}
|
|
|
|
wifi_app_config_t config;
|
|
esp_err_t error = wifi_manager_get_working_config(&config);
|
|
if (error != ESP_OK) {
|
|
printf("Could not read working configuration: %s\n", esp_err_to_name(error));
|
|
return 1;
|
|
}
|
|
|
|
wifi_config_sta_profile_t *profile = &config.profiles[slot];
|
|
if (strcmp(operation, "delete") == 0) {
|
|
wifi_config_secure_wipe(profile, sizeof(*profile));
|
|
} else {
|
|
profile->enabled = strcmp(operation, "enable") == 0 ? 1U : 0U;
|
|
}
|
|
|
|
error = apply_candidate(&config);
|
|
if (error != ESP_OK) {
|
|
printf("Could not %s profile %u: %s\n", operation,
|
|
(unsigned int)slot, esp_err_to_name(error));
|
|
return 1;
|
|
}
|
|
printf("Profile %u %sd in RAM.\n", (unsigned int)slot,
|
|
strcmp(operation, "delete") == 0 ? "delete" : operation);
|
|
return 0;
|
|
}
|
|
|
|
static int set_ap_parameter(const char *parameter, const char *value)
|
|
{
|
|
wifi_app_config_t config;
|
|
esp_err_t error = wifi_manager_get_working_config(&config);
|
|
if (error != ESP_OK) {
|
|
printf("Could not read working configuration: %s\n", esp_err_to_name(error));
|
|
return 1;
|
|
}
|
|
|
|
if (strcmp(parameter, "policy") == 0) {
|
|
if (!wifi_config_parse_ap_policy(value, &config.ap_policy)) {
|
|
wifi_config_secure_wipe(&config, sizeof(config));
|
|
printf("AP policy must be off, fallback, or always.\n");
|
|
return 1;
|
|
}
|
|
} else if (strcmp(parameter, "ssid") == 0) {
|
|
size_t length = strlen(value);
|
|
if (length == 0U || length > WIFI_CONFIG_SSID_MAX_LEN) {
|
|
wifi_config_secure_wipe(&config, sizeof(config));
|
|
printf("AP SSID must be 1..32 bytes.\n");
|
|
return 1;
|
|
}
|
|
memset(config.ap_ssid, 0, sizeof(config.ap_ssid));
|
|
memcpy(config.ap_ssid, value, length);
|
|
config.ap_ssid_len = (uint8_t)length;
|
|
} else {
|
|
uint32_t channel;
|
|
if (!parse_u32(value, WIFI_CONFIG_AP_CHANNEL_MAX, &channel) ||
|
|
channel < WIFI_CONFIG_AP_CHANNEL_MIN) {
|
|
wifi_config_secure_wipe(&config, sizeof(config));
|
|
printf("AP channel must be %u..%u.\n",
|
|
WIFI_CONFIG_AP_CHANNEL_MIN, WIFI_CONFIG_AP_CHANNEL_MAX);
|
|
return 1;
|
|
}
|
|
config.ap_channel = (uint8_t)channel;
|
|
}
|
|
|
|
error = apply_candidate(&config);
|
|
if (error != ESP_OK) {
|
|
printf("Could not apply AP configuration: %s\n", esp_err_to_name(error));
|
|
return 1;
|
|
}
|
|
printf("AP %s updated in RAM; use 'wifi save' to persist it.\n", parameter);
|
|
return 0;
|
|
}
|
|
|
|
static int set_ap_secret(void)
|
|
{
|
|
wifi_app_config_t config;
|
|
esp_err_t error = wifi_manager_get_working_config(&config);
|
|
if (error != ESP_OK) {
|
|
printf("Could not read working configuration: %s\n", esp_err_to_name(error));
|
|
return 1;
|
|
}
|
|
|
|
error = read_secret_no_echo(config.ap_psk, &config.ap_psk_len);
|
|
if (error == ESP_OK) {
|
|
error = apply_candidate(&config);
|
|
} else {
|
|
wifi_config_secure_wipe(&config, sizeof(config));
|
|
}
|
|
if (error != ESP_OK) {
|
|
if (error != ESP_ERR_INVALID_STATE) {
|
|
printf("Could not set AP secret: %s\n", esp_err_to_name(error));
|
|
}
|
|
return 1;
|
|
}
|
|
printf("AP secret updated in RAM; use 'wifi save' to persist it.\n");
|
|
return 0;
|
|
}
|
|
|
|
static int show_ap_secret(void)
|
|
{
|
|
wifi_app_config_t config;
|
|
esp_err_t error = wifi_manager_get_working_config(&config);
|
|
if (error != ESP_OK) {
|
|
printf("Could not read AP secret: %s\n", esp_err_to_name(error));
|
|
return 1;
|
|
}
|
|
|
|
printf("AP secret: ");
|
|
fwrite(config.ap_psk, 1U, config.ap_psk_len, stdout);
|
|
putchar('\n');
|
|
printf("This credential is stored as plaintext in NVS until NVS encryption is enabled.\n");
|
|
wifi_config_secure_wipe(&config, sizeof(config));
|
|
return 0;
|
|
}
|
|
|
|
static int save_config(void)
|
|
{
|
|
wifi_app_config_t config;
|
|
esp_err_t error = wifi_manager_get_working_config(&config);
|
|
if (error == ESP_OK) {
|
|
error = wifi_config_save(&config);
|
|
}
|
|
wifi_config_secure_wipe(&config, sizeof(config));
|
|
if (error != ESP_OK) {
|
|
printf("Could not save Wi-Fi configuration: %s\n", esp_err_to_name(error));
|
|
return 1;
|
|
}
|
|
printf("Wi-Fi configuration saved to NVS. Credentials are not yet encrypted at rest.\n");
|
|
return 0;
|
|
}
|
|
|
|
static int load_config(void)
|
|
{
|
|
wifi_app_config_t config;
|
|
wifi_config_load_source_t source;
|
|
esp_err_t error = wifi_config_load(&config, &source);
|
|
if (error == ESP_OK) {
|
|
error = wifi_manager_apply_working_config(&config);
|
|
}
|
|
wifi_config_secure_wipe(&config, sizeof(config));
|
|
if (error != ESP_OK) {
|
|
printf("Could not load Wi-Fi configuration: %s\n", esp_err_to_name(error));
|
|
return 1;
|
|
}
|
|
const char *description = source == WIFI_CONFIG_LOAD_STORED
|
|
? "stored"
|
|
: (source == WIFI_CONFIG_LOAD_GENERATED_MISSING
|
|
? "new default (no stored blob)"
|
|
: "new default (stored blob is incompatible)");
|
|
printf("Loaded %s Wi-Fi configuration into RAM.\n", description);
|
|
return 0;
|
|
}
|
|
|
|
static int apply_defaults(bool persist)
|
|
{
|
|
wifi_app_config_t previous;
|
|
wifi_app_config_t defaults;
|
|
esp_err_t error = wifi_manager_get_working_config(&previous);
|
|
if (error == ESP_OK) {
|
|
error = wifi_config_defaults(&defaults);
|
|
}
|
|
if (error == ESP_OK) {
|
|
error = wifi_manager_apply_working_config(&defaults);
|
|
}
|
|
if (error == ESP_OK && persist) {
|
|
error = wifi_config_reset_storage(&defaults);
|
|
if (error != ESP_OK) {
|
|
/* Restore RAM behavior if persistence failed. */
|
|
(void)wifi_manager_apply_working_config(&previous);
|
|
}
|
|
}
|
|
|
|
wifi_config_secure_wipe(&previous, sizeof(previous));
|
|
wifi_config_secure_wipe(&defaults, sizeof(defaults));
|
|
if (error != ESP_OK) {
|
|
printf("Could not apply Wi-Fi defaults: %s\n", esp_err_to_name(error));
|
|
return 1;
|
|
}
|
|
printf("Fresh defaults applied%s; use 'wifi ap show-secret' for the new AP credential.\n",
|
|
persist ? " and saved" : " in RAM");
|
|
return 0;
|
|
}
|
|
|
|
static int queue_lifecycle(const char *operation)
|
|
{
|
|
esp_err_t error;
|
|
if (strcmp(operation, "start") == 0) {
|
|
error = wifi_manager_start();
|
|
} else if (strcmp(operation, "stop") == 0) {
|
|
error = wifi_manager_stop();
|
|
} else if (strcmp(operation, "reconnect") == 0) {
|
|
error = wifi_manager_reconnect();
|
|
} else {
|
|
error = wifi_manager_next_profile();
|
|
}
|
|
if (error != ESP_OK) {
|
|
printf("Could not queue Wi-Fi %s: %s\n", operation, esp_err_to_name(error));
|
|
return 1;
|
|
}
|
|
printf("Wi-Fi %s queued.\n", operation);
|
|
return 0;
|
|
}
|
|
|
|
static int command_wifi(int argc, char **argv)
|
|
{
|
|
if (argc >= 2 && network_console_is_command(argv[1])) {
|
|
/* Shift `wifi` away so aliases and subcommands share one implementation. */
|
|
return network_console_execute(argc - 1, argv + 1);
|
|
}
|
|
if (argc == 1 || (argc == 2 && strcmp(argv[1], "status") == 0)) {
|
|
return show_status();
|
|
}
|
|
if (argc == 2 && strcmp(argv[1], "profiles") == 0) {
|
|
return show_profiles();
|
|
}
|
|
if (argc == 2 && strcmp(argv[1], "counters") == 0) {
|
|
return show_counters();
|
|
}
|
|
if (argc == 2 && strcmp(argv[1], "clear-counters") == 0) {
|
|
esp_err_t error = wifi_manager_clear_counters();
|
|
if (error != ESP_OK) {
|
|
printf("Could not clear Wi-Fi counters: %s\n", esp_err_to_name(error));
|
|
return 1;
|
|
}
|
|
printf("Wi-Fi counters cleared.\n");
|
|
return 0;
|
|
}
|
|
if (argc == 2 && (strcmp(argv[1], "start") == 0 ||
|
|
strcmp(argv[1], "stop") == 0 ||
|
|
strcmp(argv[1], "reconnect") == 0 ||
|
|
strcmp(argv[1], "next-profile") == 0)) {
|
|
return queue_lifecycle(argv[1]);
|
|
}
|
|
if (argc == 7 && strcmp(argv[1], "profile") == 0 &&
|
|
strcmp(argv[2], "set") == 0) {
|
|
return set_profile(argv);
|
|
}
|
|
if (argc == 4 && strcmp(argv[1], "profile") == 0 &&
|
|
strcmp(argv[2], "secret") == 0) {
|
|
return set_profile_secret(argv[3]);
|
|
}
|
|
if (argc == 4 && strcmp(argv[1], "profile") == 0 &&
|
|
(strcmp(argv[2], "enable") == 0 ||
|
|
strcmp(argv[2], "disable") == 0 ||
|
|
strcmp(argv[2], "delete") == 0)) {
|
|
return change_profile_state(argv[2], argv[3]);
|
|
}
|
|
if (argc == 4 && strcmp(argv[1], "ap") == 0 &&
|
|
(strcmp(argv[2], "policy") == 0 ||
|
|
strcmp(argv[2], "ssid") == 0 ||
|
|
strcmp(argv[2], "channel") == 0)) {
|
|
return set_ap_parameter(argv[2], argv[3]);
|
|
}
|
|
if (argc == 3 && strcmp(argv[1], "ap") == 0 &&
|
|
strcmp(argv[2], "secret") == 0) {
|
|
return set_ap_secret();
|
|
}
|
|
if (argc == 3 && strcmp(argv[1], "ap") == 0 &&
|
|
strcmp(argv[2], "show-secret") == 0) {
|
|
return show_ap_secret();
|
|
}
|
|
if (argc == 2 && strcmp(argv[1], "save") == 0) {
|
|
return save_config();
|
|
}
|
|
if (argc == 2 && strcmp(argv[1], "load") == 0) {
|
|
return load_config();
|
|
}
|
|
if (argc == 2 && strcmp(argv[1], "defaults") == 0) {
|
|
return apply_defaults(false);
|
|
}
|
|
if (argc == 2 && strcmp(argv[1], "reset") == 0) {
|
|
return apply_defaults(true);
|
|
}
|
|
|
|
print_usage();
|
|
return 1;
|
|
}
|
|
|
|
esp_err_t wifi_console_register_commands(void)
|
|
{
|
|
const esp_console_cmd_t command = {
|
|
.command = "wifi",
|
|
.help = "Configure multi-profile Wi-Fi and AP fallback; use 'wifi' for usage/status",
|
|
.hint = NULL,
|
|
.func = &command_wifi,
|
|
.argtable = NULL,
|
|
};
|
|
return esp_console_cmd_register(&command);
|
|
}
|