From 099c28f9c70a725e125c102e4cd41141e5424360 Mon Sep 17 00:00:00 2001 From: Robert McMahon Date: Sun, 21 Dec 2025 18:54:18 -0800 Subject: [PATCH] wifi scan --- components/app_console/cmd_wifi.c | 200 +++++++++++++++++++++--------- components/wifi_cfg/wifi_cfg.c | 71 +++++++---- components/wifi_cfg/wifi_cfg.h | 11 +- 3 files changed, 194 insertions(+), 88 deletions(-) diff --git a/components/app_console/cmd_wifi.c b/components/app_console/cmd_wifi.c index 6c07228..2badf91 100644 --- a/components/app_console/cmd_wifi.c +++ b/components/app_console/cmd_wifi.c @@ -30,7 +30,6 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ - #include #include #include "esp_log.h" @@ -42,39 +41,52 @@ #include "wifi_controller.h" #include "app_console.h" -// --- Forward Declarations for Sub-Command Handlers --- +// --- Forward Declarations --- static int wifi_do_scan(int argc, char **argv); static int wifi_do_connect(int argc, char **argv); static int wifi_do_disconnect(int argc, char **argv); static int wifi_do_link(int argc, char **argv); +static int wifi_do_power(int argc, char **argv); +static int wifi_do_mode(int argc, char **argv); // ============================================================================ // COMMAND: wifi (Dispatcher) // ============================================================================ +static void print_wifi_usage(void) { + printf("Usage: wifi [args]\n"); + printf("Subcommands:\n"); + printf(" scan Scan for networks\n"); + printf(" connect Connect to an AP (alias: join)\n"); + printf(" disconnect Disconnect from AP\n"); + printf(" status Show connection status (alias: link)\n"); + printf(" power Set power save (on/off)\n"); + printf(" mode Set mode (sta/monitor)\n"); + printf("\nType 'wifi --help' for details.\n"); +} + static int cmd_wifi(int argc, char **argv) { - if (argc < 2) { - printf("Usage: wifi [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"); + if (argc < 2 || strcmp(argv[1], "help") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) { + print_wifi_usage(); 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], "join") == 0) return wifi_do_connect(argc - 1, &argv[1]); if (strcmp(argv[1], "disconnect") == 0) return wifi_do_disconnect(argc - 1, &argv[1]); + if (strcmp(argv[1], "status") == 0) return wifi_do_link(argc - 1, &argv[1]); if (strcmp(argv[1], "link") == 0) return wifi_do_link(argc - 1, &argv[1]); + if (strcmp(argv[1], "power") == 0) return wifi_do_power(argc - 1, &argv[1]); + if (strcmp(argv[1], "mode") == 0) return wifi_do_mode(argc - 1, &argv[1]); - printf("Unknown subcommand '%s'. See 'wifi --help'.\n", argv[1]); + printf("Unknown subcommand '%s'.\n", argv[1]); + print_wifi_usage(); return 1; } // ---------------------------------------------------------------------------- -// Sub-command: wifi link +// Sub-command: wifi status / link // ---------------------------------------------------------------------------- static struct { struct arg_lit *help; @@ -82,11 +94,13 @@ static struct { } link_args; static int wifi_do_link(int argc, char **argv) { - // Parse args mainly to support --help + link_args.help = arg_lit0("h", "help", "Help"); + link_args.end = arg_end(1); + if (arg_parse(argc, argv, (void **)&link_args) == 0) { if (link_args.help->count > 0) { - printf("Usage: wifi link\nDescription: Show connection status (RSSI, BSSID, SSID, IP).\n"); - return 0; + printf("Usage: wifi status\nDescription: Show connection status.\n"); + goto exit; } } @@ -96,19 +110,101 @@ static int wifi_do_link(int argc, char **argv) { 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("\tfreq: %d (Primary)\n", ap_info.primary); printf("\tsignal: %d dBm\n", ap_info.rssi); esp_netif_t *netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); esp_netif_ip_info_t ip; if (netif && esp_netif_get_ip_info(netif, &ip) == ESP_OK) { printf("\tIP: " IPSTR "\n", IP2STR(&ip.ip)); - printf("\tMask: " IPSTR "\n", IP2STR(&ip.netmask)); - printf("\tGW: " IPSTR "\n", IP2STR(&ip.gw)); } } else { printf("Not connected.\n"); } + + wifi_ps_type_t ps_type; + if (esp_wifi_get_ps(&ps_type) == ESP_OK) { + printf("\tPower Save: %s\n", (ps_type == WIFI_PS_NONE) ? "off" : "on"); + } + +exit: + return 0; +} + +// ---------------------------------------------------------------------------- +// Sub-command: wifi power +// ---------------------------------------------------------------------------- +static struct { + struct arg_lit *on; + struct arg_lit *off; + struct arg_lit *help; + struct arg_end *end; +} power_args; + +static int wifi_do_power(int argc, char **argv) { + power_args.on = arg_lit0(NULL, "on", "Enable Power Save"); + power_args.off = arg_lit0(NULL, "off", "Disable Power Save"); + power_args.help = arg_lit0("h", "help", "Help"); + power_args.end = arg_end(1); + + int nerrors = arg_parse(argc, argv, (void **)&power_args); + if (nerrors > 0) { + arg_print_errors(stderr, power_args.end, argv[0]); + return 1; + } + if (power_args.help->count > 0) { + printf("Usage: wifi power [on|off]\n"); + return 0; + } + if (power_args.on->count > 0) { + esp_wifi_set_ps(WIFI_PS_MIN_MODEM); + printf("Power save enabled (WIFI_PS_MIN_MODEM).\n"); + } else if (power_args.off->count > 0) { + esp_wifi_set_ps(WIFI_PS_NONE); + printf("Power save disabled (WIFI_PS_NONE).\n"); + } else { + wifi_ps_type_t ps_type; + esp_wifi_get_ps(&ps_type); + printf("Current Power Save Mode: %s\n", (ps_type == WIFI_PS_NONE) ? "OFF" : "ON"); + } + return 0; +} + +// ---------------------------------------------------------------------------- +// Sub-command: wifi mode +// ---------------------------------------------------------------------------- +static struct { + struct arg_lit *sta; + struct arg_lit *monitor; + struct arg_lit *help; + struct arg_end *end; +} mode_args; + +static int wifi_do_mode(int argc, char **argv) { + mode_args.sta = arg_lit0(NULL, "sta", "Station Mode"); + mode_args.monitor = arg_lit0(NULL, "monitor", "Monitor Mode"); + mode_args.help = arg_lit0("h", "help", "Help"); + mode_args.end = arg_end(1); + + int nerrors = arg_parse(argc, argv, (void **)&mode_args); + if (nerrors > 0) { + arg_print_errors(stderr, mode_args.end, argv[0]); + return 1; + } + if (mode_args.help->count > 0) { + printf("Usage: wifi mode [sta|monitor]\n"); + return 0; + } + if (mode_args.sta->count > 0) { + wifi_ctl_switch_to_sta(WIFI_BW_HT20); + printf("Switched to STATION mode.\n"); + } else if (mode_args.monitor->count > 0) { + wifi_ctl_switch_to_monitor(0, WIFI_BW_HT20); + printf("Switched to MONITOR mode.\n"); + } else { + wifi_ctl_mode_t mode = wifi_ctl_get_mode(); + printf("Current Mode: %s\n", (mode == WIFI_CTL_MODE_MONITOR) ? "MONITOR" : "STATION"); + } return 0; } @@ -121,58 +217,52 @@ static struct { } scan_args; static int wifi_do_scan(int argc, char **argv) { + scan_args.help = arg_lit0("h", "help", "Help"); + scan_args.end = arg_end(1); + if (arg_parse(argc, argv, (void **)&scan_args) == 0) { if (scan_args.help->count > 0) { printf("Usage: wifi scan\n"); return 0; } } - - // 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"); + printf("Out of memory.\n"); return 1; } - ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_count, ap_info)); - printf("Found %u unique BSSIDs\n", ap_count); - printf("BSS | SSID | RSSI | Ch | Auth\n"); - printf("-------------------------------------------------------------------------------------\n"); + + // FORMATTING: Using exact width specifiers for Header and Data to ensure alignment + // SSID: 32 chars | BSSID: 17 chars | RSSI: 4 chars | Ch: 3 chars | Auth + printf("%-32s | %-17s | %4s | %3s | %s\n", "SSID", "BSSID", "RSSI", "Ch", "Auth"); + printf("--------------------------------------------------------------------------------------\n"); + for (int i = 0; i < ap_count; i++) { char *authmode = "UNK"; switch (ap_info[i].authmode) { @@ -182,35 +272,38 @@ static int wifi_do_scan(int argc, char **argv) { case WIFI_AUTH_WPA2_PSK: authmode = "WPA2"; break; case WIFI_AUTH_WPA_WPA2_PSK: authmode = "WPA/2"; break; case WIFI_AUTH_WPA3_PSK: authmode = "WPA3"; break; + case WIFI_AUTH_WPA2_WPA3_PSK: authmode = "W2/W3"; break; default: break; } - printf("%02x:%02x:%02x:%02x:%02x:%02x | %-32.32s | %4d | %2d | %s\n", + printf("%-32.32s | %02x:%02x:%02x:%02x:%02x:%02x | %4d | %3d | %s\n", + ap_info[i].ssid, ap_info[i].bssid[0], ap_info[i].bssid[1], ap_info[i].bssid[2], ap_info[i].bssid[3], ap_info[i].bssid[4], ap_info[i].bssid[5], - ap_info[i].ssid, ap_info[i].rssi, ap_info[i].primary, authmode); + ap_info[i].rssi, ap_info[i].primary, authmode); } - printf("-------------------------------------------------------------------------------------\n"); + printf("--------------------------------------------------------------------------------------\n"); free(ap_info); return 0; } // ---------------------------------------------------------------------------- -// Sub-command: wifi connect +// Sub-command: wifi connect / join // ---------------------------------------------------------------------------- static struct { struct arg_str *ssid; struct arg_str *password; + struct arg_str *bssid; struct arg_lit *help; struct arg_end *end; } connect_args; static int wifi_do_connect(int argc, char **argv) { - // Re-init pointers to be safe with static structs - connect_args.ssid = arg_str1(NULL, NULL, "", "SSID"); + connect_args.ssid = arg_str1(NULL, NULL, "", "SSID"); connect_args.password = arg_str0(NULL, NULL, "", "Password"); - connect_args.help = arg_lit0("h", "help", "Help"); - connect_args.end = arg_end(2); + connect_args.bssid = arg_str0("b", "bssid", "", "Lock BSSID"); + connect_args.help = arg_lit0("h", "help", "Help"); + connect_args.end = arg_end(2); int nerrors = arg_parse(argc, argv, (void **)&connect_args); if (nerrors > 0) { @@ -219,22 +312,19 @@ static int wifi_do_connect(int argc, char **argv) { } if (connect_args.help->count > 0) { - printf("Usage: wifi connect [password]\n"); + printf("Usage: wifi connect [password] [-b ]\n"); return 0; } const char *ssid = connect_args.ssid->sval[0]; const char *pass = (connect_args.password->count > 0) ? connect_args.password->sval[0] : ""; + const char *bssid = (connect_args.bssid->count > 0) ? connect_args.bssid->sval[0] : ""; - printf("Connecting to '%s'...\n", ssid); + printf("Connecting to '%s' (BSSID: %s)...\n", ssid, bssid[0] ? bssid : "Any"); - // Save to NVS - wifi_cfg_set_credentials(ssid, pass); - wifi_cfg_set_dhcp(true); // Default to DHCP for simple connect + wifi_cfg_set_credentials(ssid, pass, bssid); + wifi_cfg_set_dhcp(true); - // 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(); @@ -247,11 +337,6 @@ static int wifi_do_connect(int argc, char **argv) { 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; } @@ -262,9 +347,10 @@ static int wifi_do_disconnect(int argc, char **argv) { void register_wifi_cmd(void) { const esp_console_cmd_t cmd = { .command = "wifi", - .help = "Wi-Fi Configuration Tool (iw style)", + .help = "Wi-Fi Tool: scan, connect, status, power, mode", + .hint = " [args]", .func = &cmd_wifi, - .argtable = NULL // Handled internally by sub-dispatchers + .argtable = NULL }; ESP_ERROR_CHECK(esp_console_cmd_register(&cmd)); } diff --git a/components/wifi_cfg/wifi_cfg.c b/components/wifi_cfg/wifi_cfg.c index 180b956..56076dd 100644 --- a/components/wifi_cfg/wifi_cfg.c +++ b/components/wifi_cfg/wifi_cfg.c @@ -30,6 +30,12 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ +/* + * wifi_cfg.c + * + * Copyright (c) 2025 Umber Networks & Robert McMahon + * All rights reserved. + */ #include #include @@ -44,15 +50,13 @@ #include "esp_event.h" #include "wifi_cfg.h" -// Removed unused TAG - static esp_netif_t *sta_netif = NULL; // --- Helper: NVS Write --- static void nvs_write_str(const char *key, const char *val) { nvs_handle_t h; if (nvs_open("netcfg", NVS_READWRITE, &h) == ESP_OK) { - if (val) nvs_set_str(h, key, val); + if (val && strlen(val) > 0) nvs_set_str(h, key, val); else nvs_erase_key(h, key); nvs_commit(h); nvs_close(h); @@ -68,11 +72,25 @@ static void nvs_write_u8(const char *key, uint8_t val) { } } +// --- Helper: MAC Parser --- +static bool parse_bssid(const char *str, uint8_t *out_bssid) { + if (!str || strlen(str) != 17) return false; + unsigned int bytes[6]; + int count = sscanf(str, "%02x:%02x:%02x:%02x:%02x:%02x", + &bytes[0], &bytes[1], &bytes[2], &bytes[3], &bytes[4], &bytes[5]); + if (count == 6) { + for (int i = 0; i < 6; i++) out_bssid[i] = (uint8_t)bytes[i]; + return true; + } + return false; +} + // --- Public Setters --- -void wifi_cfg_set_credentials(const char* ssid, const char* pass) { +void wifi_cfg_set_credentials(const char* ssid, const char* pass, const char* bssid) { nvs_write_str("ssid", ssid); nvs_write_str("pass", pass); + nvs_write_str("bssid", bssid); // Save BSSID to NVS } void wifi_cfg_set_static_ip(const char* ip, const char* mask, const char* gw) { @@ -91,6 +109,7 @@ void wifi_cfg_clear_credentials(void) { if (nvs_open("netcfg", NVS_READWRITE, &h) == ESP_OK) { nvs_erase_key(h, "ssid"); nvs_erase_key(h, "pass"); + nvs_erase_key(h, "bssid"); nvs_erase_key(h, "ip"); nvs_erase_key(h, "mask"); nvs_erase_key(h, "gw"); @@ -115,9 +134,9 @@ void wifi_cfg_init(void) { nvs_flash_init(); } -static bool load_cfg(char* ssid, size_t ssz, char* pass, size_t psz, +static bool load_cfg(char* ssid, size_t ssz, char* pass, size_t psz, char* bssid, size_t bsz, char* ip, size_t isz, char* mask, size_t msz, char* gw, size_t gsz, - char* band, size_t bsz, char* bw, size_t bwsz, char* powersave, size_t pssz, + char* band, size_t bsz_band, char* bw, size_t bwsz, char* powersave, size_t pssz, char* mode, size_t modesz, uint8_t* mon_ch, bool* dhcp){ nvs_handle_t h; if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) return false; @@ -129,12 +148,13 @@ static bool load_cfg(char* ssid, size_t ssz, char* pass, size_t psz, // Load Optionals len = psz; if (nvs_get_str(h, "pass", pass, &len) != ESP_OK) pass[0]=0; + len = bsz; if (nvs_get_str(h, "bssid", bssid, &len) != ESP_OK) bssid[0]=0; len = isz; if (nvs_get_str(h, "ip", ip, &len) != ESP_OK) ip[0]=0; len = msz; if (nvs_get_str(h, "mask", mask, &len) != ESP_OK) mask[0]=0; len = gsz; if (nvs_get_str(h, "gw", gw, &len) != ESP_OK) gw[0]=0; // Defaults - len = bsz; if (nvs_get_str(h, "band", band, &len) != ESP_OK) strcpy(band, "2.4G"); + len = bsz_band; if (nvs_get_str(h, "band", band, &len) != ESP_OK) strcpy(band, "2.4G"); len = bwsz; if (nvs_get_str(h, "bw", bw, &len) != ESP_OK) strcpy(bw, "HT20"); len = pssz; if (nvs_get_str(h, "powersave", powersave, &len) != ESP_OK) strcpy(powersave, "NONE"); len = modesz; if (nvs_get_str(h, "mode", mode, &len) != ESP_OK) strcpy(mode, "STA"); @@ -161,12 +181,14 @@ static void apply_ip_static(const char* ip, const char* mask, const char* gw){ } bool wifi_cfg_apply_from_nvs(void) { - char ssid[64]={0}, pass[64]={0}, ip[32]={0}, mask[32]={0}, gw[32]={0}; + char ssid[64]={0}, pass[64]={0}, bssid_str[20]={0}, ip[32]={0}, mask[32]={0}, gw[32]={0}; char band[16]={0}, bw[16]={0}, powersave[16]={0}, mode[16]={0}; uint8_t mon_ch = 36; bool dhcp = true; - if (!load_cfg(ssid,sizeof(ssid), pass,sizeof(pass), ip,sizeof(ip), mask,sizeof(mask), gw,sizeof(gw), - band,sizeof(band), bw,sizeof(bw), powersave,sizeof(powersave), mode,sizeof(mode), &mon_ch, &dhcp)){ + if (!load_cfg(ssid,sizeof(ssid), pass,sizeof(pass), bssid_str,sizeof(bssid_str), + ip,sizeof(ip), mask,sizeof(mask), gw,sizeof(gw), + band,sizeof(band), bw,sizeof(bw), powersave,sizeof(powersave), + mode,sizeof(mode), &mon_ch, &dhcp)){ return false; } @@ -182,6 +204,16 @@ bool wifi_cfg_apply_from_nvs(void) { wcfg.sta.sae_pwe_h2e = WPA3_SAE_PWE_BOTH; wcfg.sta.scan_method = WIFI_ALL_CHANNEL_SCAN; + // Apply BSSID Lock if present + if (bssid_str[0] != 0) { + if (parse_bssid(bssid_str, wcfg.sta.bssid)) { + wcfg.sta.bssid_set = true; + ESP_LOGI("WIFI_CFG", "Locking to BSSID: %s", bssid_str); + } else { + ESP_LOGW("WIFI_CFG", "Invalid BSSID format in NVS: %s", bssid_str); + } + } + esp_wifi_set_mode(WIFI_MODE_STA); esp_wifi_set_config(WIFI_IF_STA, &wcfg); @@ -194,7 +226,7 @@ bool wifi_cfg_apply_from_nvs(void) { } wifi_ps_type_t wifi_cfg_get_power_save_mode(void) { - return WIFI_PS_NONE; + return WIFI_PS_NONE; // Default implementation } bool wifi_cfg_get_bandwidth(char *buf, size_t buf_size) { @@ -212,37 +244,28 @@ bool wifi_cfg_get_mode(char *mode, uint8_t *mon_ch) { return true; } -// --- State Checkers --- - bool wifi_cfg_monitor_channel_is_unsaved(uint8_t ram_value) { nvs_handle_t h; - if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) return true; // Assume dirty if error - + if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) return true; uint8_t nvs_val = 0; esp_err_t err = nvs_get_u8(h, "mon_ch", &nvs_val); nvs_close(h); - - if (err != ESP_OK) return true; // Key missing = dirty (using default) + if (err != ESP_OK) return true; return (nvs_val != ram_value); } -// --- Setters --- - bool wifi_cfg_set_monitor_channel(uint8_t channel) { nvs_handle_t h; if (nvs_open("netcfg", NVS_READWRITE, &h) != ESP_OK) return false; - uint8_t current = 0; - // Check if write is necessary if (nvs_get_u8(h, "mon_ch", ¤t) == ESP_OK) { if (current == channel) { nvs_close(h); - return false; // No change needed + return false; } } - nvs_set_u8(h, "mon_ch", channel); nvs_commit(h); nvs_close(h); - return true; // Write occurred + return true; } diff --git a/components/wifi_cfg/wifi_cfg.h b/components/wifi_cfg/wifi_cfg.h index 4905343..7e7b70f 100644 --- a/components/wifi_cfg/wifi_cfg.h +++ b/components/wifi_cfg/wifi_cfg.h @@ -30,8 +30,6 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ - - #ifndef WIFI_CFG_H #define WIFI_CFG_H @@ -51,16 +49,15 @@ wifi_ps_type_t wifi_cfg_get_power_save_mode(void); bool wifi_cfg_get_bandwidth(char *buf, size_t buf_size); bool wifi_cfg_get_mode(char *mode, uint8_t *mon_ch); -// --- State Checkers (Dirty Flag) --- -// Returns true if RAM value differs from NVS +// --- State Checkers --- bool wifi_cfg_monitor_channel_is_unsaved(uint8_t ram_value); // --- Setters --- -void wifi_cfg_set_credentials(const char* ssid, const char* pass); +// UPDATED: Now accepts optional bssid (pass NULL if not used) +void wifi_cfg_set_credentials(const char* ssid, const char* pass, const char* bssid); + void wifi_cfg_set_static_ip(const char* ip, const char* mask, const char* gw); void wifi_cfg_set_dhcp(bool enable); - -// Returns true if NVS was actually updated, false if values were identical bool wifi_cfg_set_monitor_channel(uint8_t channel); // --- Clearing ---