/* * wifi_cfg.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. */ /* * wifi_cfg.c * * Copyright (c) 2025 Umber Networks & Robert McMahon * All rights reserved. */ #include #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_log.h" #include "nvs_flash.h" #include "nvs.h" #include "esp_netif.h" #include "esp_wifi.h" #include "esp_event.h" #include "wifi_cfg.h" 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 && strlen(val) > 0) nvs_set_str(h, key, val); else nvs_erase_key(h, key); nvs_commit(h); nvs_close(h); } } static void nvs_write_u8(const char *key, uint8_t val) { nvs_handle_t h; if (nvs_open("netcfg", NVS_READWRITE, &h) == ESP_OK) { nvs_set_u8(h, key, val); nvs_commit(h); nvs_close(h); } } // --- 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, 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) { nvs_write_str("ip", ip); nvs_write_str("mask", mask); nvs_write_str("gw", gw); } void wifi_cfg_set_dhcp(bool enable) { nvs_write_u8("dhcp", enable ? 1 : 0); } // --- Clearing --- void wifi_cfg_clear_credentials(void) { nvs_handle_t h; 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"); nvs_erase_key(h, "dhcp"); nvs_commit(h); nvs_close(h); } } void wifi_cfg_clear_monitor_channel(void) { nvs_handle_t h; if (nvs_open("netcfg", NVS_READWRITE, &h) == ESP_OK) { nvs_erase_key(h, "mon_ch"); nvs_commit(h); nvs_close(h); } } // --- Init & Load --- void wifi_cfg_init(void) { nvs_flash_init(); } 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_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; size_t len; // Load SSID (Mandatory) len = ssz; if (nvs_get_str(h, "ssid", ssid, &len) != ESP_OK) { nvs_close(h); return false; } // 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_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"); uint8_t ch=36; nvs_get_u8(h, "mon_ch", &ch); *mon_ch = ch; uint8_t d=1; nvs_get_u8(h, "dhcp", &d); *dhcp = (d!=0); nvs_close(h); return true; } static void apply_ip_static(const char* ip, const char* mask, const char* gw){ if (!sta_netif) return; if (!ip || !ip[0]) return; esp_netif_ip_info_t info = {0}; esp_netif_dhcpc_stop(sta_netif); info.ip.addr = esp_ip4addr_aton(ip); info.netmask.addr = (mask && mask[0]) ? esp_ip4addr_aton(mask) : esp_ip4addr_aton("255.255.255.0"); info.gw.addr = (gw && gw[0]) ? esp_ip4addr_aton(gw) : 0; esp_netif_set_ip_info(sta_netif, &info); } bool wifi_cfg_apply_from_nvs(void) { char ssid[64]={0}, pass[64]={0}, bssid_str[20]={0}, ip[32]={0}, mask[32]={0}, gw[32]={0}; char 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), 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; } if (sta_netif == NULL) sta_netif = esp_netif_create_default_wifi_sta(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); esp_wifi_init(&cfg); wifi_config_t wcfg = {0}; strlcpy((char*)wcfg.sta.ssid, ssid, sizeof(wcfg.sta.ssid)); strlcpy((char*)wcfg.sta.password, pass, sizeof(wcfg.sta.password)); wcfg.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK; 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); if (!dhcp && ip[0]) apply_ip_static(ip, mask, gw); else esp_netif_dhcpc_start(sta_netif); esp_wifi_start(); esp_wifi_connect(); return true; } wifi_ps_type_t wifi_cfg_get_power_save_mode(void) { return WIFI_PS_NONE; // Default implementation } bool wifi_cfg_get_bandwidth(char *buf, size_t buf_size) { if (buf) strncpy(buf, "HT20", buf_size); return true; } bool wifi_cfg_get_mode(char *mode, uint8_t *mon_ch) { nvs_handle_t h; if (nvs_open("netcfg", NVS_READONLY, &h) != ESP_OK) return false; size_t len = 16; if (nvs_get_str(h, "mode", mode, &len) != ESP_OK) strcpy(mode, "STA"); nvs_get_u8(h, "mon_ch", mon_ch); nvs_close(h); return true; } 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; 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; return (nvs_val != ram_value); } 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; if (nvs_get_u8(h, "mon_ch", ¤t) == ESP_OK) { if (current == channel) { nvs_close(h); return false; } } nvs_set_u8(h, "mon_ch", channel); nvs_commit(h); nvs_close(h); return true; }