Add native Wi-Fi management and persistence
This commit is contained in:
@@ -0,0 +1,670 @@
|
||||
/* 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 "driver/uart.h"
|
||||
#include "esp_console.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_netif_ip_addr.h"
|
||||
#include "esp_wifi_types.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "wifi_config.h"
|
||||
#include "wifi_manager.h"
|
||||
|
||||
#define WIFI_CONSOLE_UART UART_NUM_0
|
||||
#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\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");
|
||||
}
|
||||
|
||||
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];
|
||||
size_t length = 0U;
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
|
||||
/*
|
||||
* esp_console may execute on CR while the terminal's trailing LF is still
|
||||
* arriving. Let that line ending settle, then discard only pre-prompt RX so
|
||||
* it cannot be mistaken for an immediately submitted empty secret.
|
||||
*/
|
||||
vTaskDelay(1U);
|
||||
esp_err_t flush_error = uart_flush_input(WIFI_CONSOLE_UART);
|
||||
if (flush_error != ESP_OK) {
|
||||
wifi_config_secure_wipe(buffer, sizeof(buffer));
|
||||
printf("Could not prepare secret input: %s\n", esp_err_to_name(flush_error));
|
||||
return flush_error;
|
||||
}
|
||||
|
||||
printf("Enter 8..63 printable ASCII characters (input hidden, Ctrl-C cancels): ");
|
||||
fflush(stdout);
|
||||
|
||||
for (;;) {
|
||||
uint8_t byte = 0U;
|
||||
int received = uart_read_bytes(WIFI_CONSOLE_UART, &byte, 1U, portMAX_DELAY);
|
||||
if (received != 1) {
|
||||
wifi_config_secure_wipe(buffer, sizeof(buffer));
|
||||
printf("\nSecret input failed.\n");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (byte == 0x03U) {
|
||||
wifi_config_secure_wipe(buffer, sizeof(buffer));
|
||||
printf("\nCancelled.\n");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (byte == '\r' || byte == '\n') {
|
||||
break;
|
||||
}
|
||||
if (byte == 0x08U || byte == 0x7fU) {
|
||||
if (length > 0U) {
|
||||
buffer[--length] = 0U;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (byte < 0x20U || byte > 0x7eU || length >= sizeof(buffer)) {
|
||||
putchar('\a');
|
||||
fflush(stdout);
|
||||
continue;
|
||||
}
|
||||
buffer[length++] = byte;
|
||||
}
|
||||
putchar('\n');
|
||||
|
||||
if (length < WIFI_CONFIG_PSK_MIN_LEN || length > WIFI_CONFIG_PSK_MAX_LEN) {
|
||||
wifi_config_secure_wipe(buffer, sizeof(buffer));
|
||||
printf("Secret length must be 8..63 characters.\n");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
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 ESP_OK;
|
||||
}
|
||||
|
||||
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 {
|
||||
error = wifi_manager_reconnect();
|
||||
}
|
||||
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 == 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)) {
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user