From bfb217c20318f47998476c51a75e648b2b53bcc0 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sun, 21 Jan 2024 00:30:15 +0100 Subject: [PATCH 01/10] Implement multiple WiFi - similar to #3705 - solves #2845, #2974, #852, #1228 --- wled00/cfg.cpp | 102 ++++++++++++++++++++++------------ wled00/const.h | 4 ++ wled00/data/settings_wifi.htm | 87 ++++++++++++++++++----------- wled00/e131.cpp | 2 +- wled00/fcn_declare.h | 15 +++++ wled00/improv.cpp | 10 ++-- wled00/network.cpp | 4 +- wled00/set.cpp | 56 +++++++++++-------- wled00/wled.cpp | 17 +++--- wled00/wled.h | 22 ++++---- wled00/wled_eeprom.cpp | 10 ++-- wled00/xml.cpp | 41 +++++++++----- 12 files changed, 233 insertions(+), 137 deletions(-) diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 1234a1c5f..79fab8106 100755 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -40,21 +40,39 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { linked_remote[12] = '\0'; #endif - JsonObject nw_ins_0 = nw["ins"][0]; - getStringFromJson(clientSSID, nw_ins_0[F("ssid")], 33); - //int nw_ins_0_pskl = nw_ins_0[F("pskl")]; - //The WiFi PSK is normally not contained in the regular file for security reasons. - //If it is present however, we will use it - getStringFromJson(clientPass, nw_ins_0["psk"], 65); + size_t n = 0; + JsonArray nw_ins = nw["ins"]; + if (!nw_ins.isNull()) { + // as password are stored separately in wsec.json when reading configuration vector resize happens there, but for dynamic config we need to resize if necessary + if (nw_ins.size() > 1 && nw_ins.size() > multiWiFi.size()) multiWiFi.resize(nw_ins.size()); // resize constructs objects while resizing + for (JsonObject wifi : nw_ins) { + JsonArray ip = wifi["ip"]; + JsonArray gw = wifi["gw"]; + JsonArray sn = wifi["sn"]; + char ssid[33] = ""; + char pass[65] = ""; + IPAddress nIP = (uint32_t)0U, nGW = (uint32_t)0U, nSN = (uint32_t)0x00FFFFFF; // little endian + getStringFromJson(ssid, wifi[F("ssid")], 33); + getStringFromJson(pass, wifi["psk"], 65); // password is not normally present but if it is, use it + for (size_t i = 0; i < 4; i++) { + CJSON(nIP[i], ip[i]); + CJSON(nGW[i], gw[i]); + CJSON(nSN[i], sn[i]); + } + if (strlen(ssid) > 0) strlcpy(multiWiFi[n].clientSSID, ssid, 33); // this will keep old SSID intact if not present in JSON + if (strlen(pass) > 0) strlcpy(multiWiFi[n].clientPass, pass, 65); // this will keep old password intact if not present in JSON + multiWiFi[n].staticIP = nIP; + multiWiFi[n].staticGW = nGW; + multiWiFi[n].staticSN = nSN; + if (++n >= WLED_MAX_WIFI_COUNT) break; + } + } - JsonArray nw_ins_0_ip = nw_ins_0["ip"]; - JsonArray nw_ins_0_gw = nw_ins_0["gw"]; - JsonArray nw_ins_0_sn = nw_ins_0["sn"]; - - for (byte i = 0; i < 4; i++) { - CJSON(staticIP[i], nw_ins_0_ip[i]); - CJSON(staticGateway[i], nw_ins_0_gw[i]); - CJSON(staticSubnet[i], nw_ins_0_sn[i]); + JsonArray dns = nw[F("dns")]; + if (!dns.isNull()) { + for (size_t i = 0; i < 4; i++) { + CJSON(dnsAddress[i], dns[i]); + } } JsonObject ap = doc["ap"]; @@ -212,7 +230,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { JsonObject btn_obj = hw["btn"]; bool pull = btn_obj[F("pull")] | (!disablePullUp); // if true, pullup is enabled disablePullUp = !pull; - JsonArray hw_btn_ins = btn_obj[F("ins")]; + JsonArray hw_btn_ins = btn_obj["ins"]; if (!hw_btn_ins.isNull()) { for (uint8_t b = 0; b < WLED_MAX_BUTTONS; b++) { // deallocate existing button pins pinManager.deallocatePin(btnPin[b], PinOwner::Button); // does nothing if trying to deallocate a pin with PinOwner != Button @@ -433,7 +451,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { if (e131Port == DDP_DEFAULT_PORT) e131Port = E131_DEFAULT_PORT; // prevent double DDP port allocation CJSON(e131Multicast, if_live[F("mc")]); - JsonObject if_live_dmx = if_live[F("dmx")]; + JsonObject if_live_dmx = if_live["dmx"]; CJSON(e131Universe, if_live_dmx[F("uni")]); CJSON(e131SkipOutOfSequence, if_live_dmx[F("seqskip")]); CJSON(DMXAddress, if_live_dmx[F("addr")]); @@ -665,19 +683,23 @@ void serializeConfig() { #endif JsonArray nw_ins = nw.createNestedArray("ins"); + for (size_t n = 0; n < multiWiFi.size(); n++) { + JsonObject wifi = nw_ins.createNestedObject(); + wifi[F("ssid")] = multiWiFi[n].clientSSID; + wifi[F("pskl")] = strlen(multiWiFi[n].clientPass); + JsonArray wifi_ip = wifi.createNestedArray("ip"); + JsonArray wifi_gw = wifi.createNestedArray("gw"); + JsonArray wifi_sn = wifi.createNestedArray("sn"); + for (size_t i = 0; i < 4; i++) { + wifi_ip.add(multiWiFi[n].staticIP[i]); + wifi_gw.add(multiWiFi[n].staticGW[i]); + wifi_sn.add(multiWiFi[n].staticSN[i]); + } + } - JsonObject nw_ins_0 = nw_ins.createNestedObject(); - nw_ins_0[F("ssid")] = clientSSID; - nw_ins_0[F("pskl")] = strlen(clientPass); - - JsonArray nw_ins_0_ip = nw_ins_0.createNestedArray("ip"); - JsonArray nw_ins_0_gw = nw_ins_0.createNestedArray("gw"); - JsonArray nw_ins_0_sn = nw_ins_0.createNestedArray("sn"); - - for (byte i = 0; i < 4; i++) { - nw_ins_0_ip.add(staticIP[i]); - nw_ins_0_gw.add(staticGateway[i]); - nw_ins_0_sn.add(staticSubnet[i]); + JsonArray dns = nw.createNestedArray(F("dns")); + for (size_t i = 0; i < 4; i++) { + dns.add(dnsAddress[i]); } JsonObject ap = root.createNestedObject("ap"); @@ -693,7 +715,7 @@ void serializeConfig() { ap_ip.add(2); ap_ip.add(1); - JsonObject wifi = root.createNestedObject("wifi"); + JsonObject wifi = root.createNestedObject(F("wifi")); wifi[F("sleep")] = !noWifiSleep; wifi[F("phy")] = force802_3g; @@ -721,7 +743,7 @@ void serializeConfig() { } #endif - JsonObject hw = root.createNestedObject("hw"); + JsonObject hw = root.createNestedObject(F("hw")); JsonObject hw_led = hw.createNestedObject("led"); hw_led[F("total")] = strip.getLengthTotal(); //provided for compatibility on downgrade and per-output ABL @@ -1048,8 +1070,17 @@ bool deserializeConfigSec() { JsonObject root = pDoc->as(); - JsonObject nw_ins_0 = root["nw"]["ins"][0]; - getStringFromJson(clientPass, nw_ins_0["psk"], 65); + size_t n = 0; + JsonArray nw_ins = root["nw"]["ins"]; + if (!nw_ins.isNull()) { + if (nw_ins.size() > 1 && nw_ins.size() > multiWiFi.size()) multiWiFi.resize(nw_ins.size()); // resize constructs objects while resizing + for (JsonObject wifi : nw_ins) { + char pw[65] = ""; + getStringFromJson(pw, wifi["psk"], 65); + strlcpy(multiWiFi[n].clientPass, pw, 65); + if (++n >= WLED_MAX_WIFI_COUNT) break; + } + } JsonObject ap = root["ap"]; getStringFromJson(apPass, ap["psk"] , 65); @@ -1088,9 +1119,10 @@ void serializeConfigSec() { JsonObject nw = root.createNestedObject("nw"); JsonArray nw_ins = nw.createNestedArray("ins"); - - JsonObject nw_ins_0 = nw_ins.createNestedObject(); - nw_ins_0["psk"] = clientPass; + for (size_t i = 0; i < multiWiFi.size(); i++) { + JsonObject wifi = nw_ins.createNestedObject(); + wifi[F("psk")] = multiWiFi[i].clientPass; + } JsonObject ap = root.createNestedObject("ap"); ap["psk"] = apPass; diff --git a/wled00/const.h b/wled00/const.h index 4ef81b907..165d93220 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -15,6 +15,10 @@ #define DEFAULT_MDNS_NAME "x" //increase if you need more +#ifndef WLED_MAX_WIFI_COUNT + #define WLED_MAX_WIFI_COUNT 3 +#endif + #ifndef WLED_MAX_USERMODS #ifdef ESP8266 #define WLED_MAX_USERMODS 4 diff --git a/wled00/data/settings_wifi.htm b/wled00/data/settings_wifi.htm index 24e6000cc..1f18dbec4 100644 --- a/wled00/data/settings_wifi.htm +++ b/wled00/data/settings_wifi.htm @@ -8,7 +8,7 @@ var d = document; var loc = false, locip, locproto = "http:"; var scanLoops = 0, preScanSSID = ""; - + var maxNetworks = 3; function gId(e) { return d.getElementById(e); } function cE(e) { return d.createElement(e); } function toggle(el){gId(el).classList.toggle("hide"); gId('No'+el).classList.toggle("hide");} @@ -52,13 +52,13 @@ } scanLoops = 0; - let cs = gId("CS"); - if (cs) { + let cs = d.querySelectorAll("#wifi_entries input[type=text]#CS0"); + for (let input of (cs||[])) { let select = cE("select"); - select.setAttribute("id", "CS"); - select.setAttribute("name", "CS"); - select.setAttribute("onchange", "T()"); - preScanSSID = cs.value; + select.id = input.id; + select.name = input.name; + select.setAttribute("onchange", "T(this)"); + preScanSSID = input.value; for (let i = 0; i < select.children.length; i++) { select.removeChild(select.children[i]); @@ -70,7 +70,7 @@ option.setAttribute("value", networks[i].ssid); option.textContent = `${networks[i].ssid} (${networks[i].rssi} dBm)`; - if (networks[i].ssid === cs.value) { + if (networks[i].ssid === input.value) { option.setAttribute("selected", "selected"); } @@ -79,10 +79,10 @@ const option = cE("option"); option.setAttribute("value", "!Cs"); - option.textContent = `Other network...`; + option.textContent = "Other network..."; select.appendChild(option); - cs.replaceWith(select); + input.replaceWith(select); } button.disabled = false; @@ -90,17 +90,48 @@ }); } // replace WiFi select with custom SSID input field again - function T() { - let cs = gId("CS"); + function T(cs) { if (!cs || cs.value != "!Cs") return; let input = cE("input"); input.type = "text"; - input.id = "CS"; - input.name ="CS"; + input.id = cs.id; + input.name = cs.name; input.setAttribute("maxlength",32); input.value = preScanSSID; cs.replaceWith(input); } + function resetWiFi(maxN = undefined) { + if (maxN) maxNetworks = maxN; + let entries = gId("wifi_entries").children + for (let i = entries.length; i > 0; i--) entries[i-1].remove(); + btnWiFi(0); + } + function btnWiFi(i) { + gId("wifi_add").style.display = (i1) ? "inline":"none"; + } + function addWiFi(ssid="",pass="",ip=0,gw=0,sn=0x00ffffff) { // little endian + var i = gId("wifi_entries").childNodes.length; + if (i >= maxNetworks) return; + var b = `

+Network name (SSID${i==0?", empty to not connect":""}):
1?"required":""}>
+Network password:

+Static IP (leave at 0.0.0.0 for DHCP)${i==0?"
Also used by Ethernet":""}:
+...
+Static gateway:
+...
+Static subnet mask:
+...
`; + gId("wifi_entries").insertAdjacentHTML("beforeend", b); + btnWiFi(i+1); + } + function remWiFi() { + const entries = gId("wifi_entries").children; + const i = entries.length; + if (i < 2) return; + entries[i-1].remove(); + btnWiFi(i-1); + } // https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript function loadJS(FILE_URL, async = true) { let scE = cE("script"); @@ -158,24 +189,16 @@

WiFi setup

Connect to existing network


- Network name (SSID, empty to not connect):
-
- Network password:

- Static IP (leave at 0.0.0.0 for DHCP):
- . - . - . -
- Static gateway:
- . - . - . -
- Static subnet mask:
- . - . - . -
+
+ Wireless networks +
+
+ +
+
+ DNS server address:
+ ...
+
mDNS address (leave empty for no mDNS):
http:// .local
Client IP: Not connected
diff --git a/wled00/e131.cpp b/wled00/e131.cpp index 68c7ca5a5..c9fe350be 100644 --- a/wled00/e131.cpp +++ b/wled00/e131.cpp @@ -490,7 +490,7 @@ void prepareArtnetPollReply(ArtPollReply *reply) { // Node is DHCP capable // Node supports 15 bit Port-Address (Art-Net 3 or 4) // Node is able to switch between ArtNet and sACN - reply->reply_status_2 = (staticIP[0] == 0) ? 0x1F : 0x1D; + reply->reply_status_2 = (multiWiFi[0].staticIP[0] == 0) ? 0x1F : 0x1D; // RDM is disabled // Output style is continuous diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index e256ceb5f..bb24725d9 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -48,6 +48,21 @@ bool getJsonValue(const JsonVariant& element, DestType& destination, const Defau return true; } +typedef struct WiFiConfig { + char clientSSID[33]; + char clientPass[65]; + IPAddress staticIP; + IPAddress staticGW; + IPAddress staticSN; + WiFiConfig(const char *ssid="", const char *pass="", uint32_t ip=0, uint32_t gw=0, uint32_t subnet=0x00FFFFFF) // little endian + : staticIP(ip) + , staticGW(gw) + , staticSN(subnet) + { + strncpy(clientSSID, ssid, 32); clientSSID[32] = 0; + strncpy(clientPass, pass, 64); clientPass[64] = 0; + } +} wifi_config; //colors.cpp // similar to NeoPixelBus NeoGammaTableMethod but allows dynamic changes (superseded by NPB::NeoGammaDynamicTableMethod) diff --git a/wled00/improv.cpp b/wled00/improv.cpp index 695d07ff7..f7867117f 100644 --- a/wled00/improv.cpp +++ b/wled00/improv.cpp @@ -259,14 +259,14 @@ void parseWiFiCommand(char* rpcData) { uint8_t ssidLen = rpcData[1]; if (ssidLen > len -1 || ssidLen > 32) return; - memset(clientSSID, 0, 32); - memcpy(clientSSID, rpcData+2, ssidLen); + memset(multiWiFi[0].clientSSID, 0, 32); + memcpy(multiWiFi[0].clientSSID, rpcData+2, ssidLen); - memset(clientPass, 0, 64); + memset(multiWiFi[0].clientPass, 0, 64); if (len > ssidLen +1) { uint8_t passLen = rpcData[2+ssidLen]; - memset(clientPass, 0, 64); - memcpy(clientPass, rpcData+3+ssidLen, passLen); + memset(multiWiFi[0].clientPass, 0, 64); + memcpy(multiWiFi[0].clientPass, rpcData+3+ssidLen, passLen); } sendImprovStateResponse(0x03); //provisioning diff --git a/wled00/network.cpp b/wled00/network.cpp index 1b02d0c5d..4d1ec6cff 100644 --- a/wled00/network.cpp +++ b/wled00/network.cpp @@ -163,8 +163,8 @@ void WiFiEvent(WiFiEvent_t event) if (!apActive) { WiFi.disconnect(true); } - if (staticIP != (uint32_t)0x00000000 && staticGateway != (uint32_t)0x00000000) { - ETH.config(staticIP, staticGateway, staticSubnet, IPAddress(8, 8, 8, 8)); + if (multiWiFi[0].staticIP != (uint32_t)0x00000000 && multiWiFi[0].staticGW != (uint32_t)0x00000000) { + ETH.config(multiWiFi[0].staticIP, multiWiFi[0].staticGW, multiWiFi[0].staticSN, dnsAddress); } else { ETH.config(INADDR_NONE, INADDR_NONE, INADDR_NONE); } diff --git a/wled00/set.cpp b/wled00/set.cpp index e83911783..6c20289f6 100755 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -19,15 +19,40 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) //WIFI SETTINGS if (subPage == SUBPAGE_WIFI) { - char oldSSID[sizeof(clientSSID)]; + char oldSSID[33]; + strlcpy(oldSSID, multiWiFi[0].clientSSID, 33); + for (size_t n = 0; n < WLED_MAX_WIFI_COUNT; n++) { + char cs[4] = "CS"; cs[2] = 48+n; cs[3] = 0; //client SSID + char pw[4] = "PW"; pw[2] = 48+n; pw[3] = 0; //client password + char ip[5] = "IP"; ip[2] = 48+n; ip[4] = 0; //IP address + char gw[5] = "GW"; gw[2] = 48+n; gw[4] = 0; //GW address + char sn[5] = "SN"; sn[2] = 48+n; sn[4] = 0; //subnet mask + if (request->hasArg(cs)) { + if (n >= multiWiFi.size()) multiWiFi.push_back(WiFiConfig()); + char oldSSID[33]; strcpy(oldSSID, multiWiFi[n].clientSSID); + char oldPass[65]; strcpy(oldPass, multiWiFi[n].clientPass); + if (!strncmp(request->arg(cs).c_str(), oldSSID, 32)) { + strlcpy(multiWiFi[n].clientSSID, request->arg(cs).c_str(), 33); + forceReconnect = true; + } + if (!isAsterisksOnly(request->arg(pw).c_str(), 65)) { + strlcpy(multiWiFi[n].clientPass, request->arg(pw).c_str(), 65); + forceReconnect = true; + } + for (size_t i = 0; i < 4; i++) { + ip[3] = 48+i; + gw[3] = 48+i; + sn[3] = 48+i; + multiWiFi[n].staticIP[i] = request->arg(ip).toInt(); + multiWiFi[n].staticGW[i] = request->arg(gw).toInt(); + multiWiFi[n].staticSN[i] = request->arg(sn).toInt(); + } + } + } - strcpy(oldSSID, clientSSID); - strlcpy(clientSSID,request->arg(F("CS")).c_str(), 33); - if (!strcmp(oldSSID, clientSSID)) forceReconnect = true; - - if (!isAsterisksOnly(request->arg(F("CP")).c_str(), 65)) { - strlcpy(clientPass, request->arg(F("CP")).c_str(), 65); - forceReconnect = true; + if (request->hasArg(F("D0"))) { + dnsAddress = IPAddress(request->arg(F("D0")).toInt(),request->arg(F("D1")).toInt(),request->arg(F("D2")).toInt(),request->arg(F("D3")).toInt()); + DEBUG_PRINTLN(dnsAddress); } strlcpy(cmDNS, request->arg(F("CM")).c_str(), 33); @@ -61,21 +86,6 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) ethernetType = request->arg(F("ETH")).toInt(); WLED::instance().initEthernet(); #endif - - char k[3]; k[2] = 0; - for (int i = 0; i<4; i++) - { - k[1] = i+48;//ascii 0,1,2,3 - - k[0] = 'I'; //static IP - staticIP[i] = request->arg(k).toInt(); - - k[0] = 'G'; //gateway - staticGateway[i] = request->arg(k).toInt(); - - k[0] = 'S'; //subnet - staticSubnet[i] = request->arg(k).toInt(); - } } //LED SETTINGS diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 967192efe..f386707c6 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -424,6 +424,7 @@ void WLED::setup() escapedMac.toLowerCase(); WLED_SET_AP_SSID(); // otherwise it is empty on first boot until config is saved + multiWiFi.push_back(WiFiConfig(CLIENT_SSID,CLIENT_PASS)); // initialise vector with default WiFi DEBUG_PRINTLN(F("Reading config")); deserializeConfigFromFS(); @@ -445,7 +446,7 @@ void WLED::setup() usermods.setup(); DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap()); - if (strcmp(clientSSID, DEFAULT_CLIENT_SSID) == 0) + if (strcmp(multiWiFi[0].clientSSID, DEFAULT_CLIENT_SSID) == 0) showWelcomePage = true; WiFi.persistent(false); #ifdef WLED_USE_ETHERNET @@ -719,8 +720,8 @@ void WLED::initConnection() WiFi.setPhyMode(force802_3g ? WIFI_PHY_MODE_11G : WIFI_PHY_MODE_11N); #endif - if (staticIP[0] != 0 && staticGateway[0] != 0) { - WiFi.config(staticIP, staticGateway, staticSubnet, IPAddress(1, 1, 1, 1)); + if (multiWiFi[selectedWiFi].staticIP != 0U && multiWiFi[selectedWiFi].staticGW != 0U) { + WiFi.config(multiWiFi[selectedWiFi].staticIP, multiWiFi[selectedWiFi].staticGW, multiWiFi[selectedWiFi].staticSN, dnsAddress); } else { WiFi.config(IPAddress((uint32_t)0), IPAddress((uint32_t)0), IPAddress((uint32_t)0)); } @@ -745,13 +746,13 @@ void WLED::initConnection() showWelcomePage = false; DEBUG_PRINT(F("Connecting to ")); - DEBUG_PRINT(clientSSID); + DEBUG_PRINT(multiWiFi[selectedWiFi].clientSSID); DEBUG_PRINTLN("..."); // convert the "serverDescription" into a valid DNS hostname (alphanumeric) char hostname[25]; prepareHostname(hostname); - WiFi.begin(clientSSID, clientPass); + WiFi.begin(multiWiFi[selectedWiFi].clientSSID, multiWiFi[selectedWiFi].clientPass); #ifdef ARDUINO_ARCH_ESP32 #if defined(LOLIN_WIFI_FIX) && (defined(ARDUINO_ARCH_ESP32C3) || defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32S3)) WiFi.setTxPower(WIFI_POWER_8_5dBm); @@ -851,8 +852,9 @@ void WLED::handleConnection() if (now < 2000 && (!WLED_WIFI_CONFIGURED || apBehavior == AP_BEHAVIOR_ALWAYS)) return; - if (lastReconnectAttempt == 0) { - DEBUG_PRINTLN(F("lastReconnectAttempt == 0")); + if (lastReconnectAttempt == 0 || forceReconnect) { + DEBUG_PRINTLN(F("Initial connect or forced reconnect.")); + if (forceReconnect) selectedWiFi = 0; initConnection(); return; } @@ -915,6 +917,7 @@ void WLED::handleConnection() if (now - lastReconnectAttempt > ((stac) ? 300000 : 18000) && WLED_WIFI_CONFIGURED) { if (improvActive == 2) improvActive = 3; DEBUG_PRINTLN(F("Last reconnect too old.")); + if (++selectedWiFi >= multiWiFi.size()) selectedWiFi = 0; initConnection(); } if (!apActive && now - lastReconnectAttempt > 12000 && (!wasConnected || apBehavior == AP_BEHAVIOR_NO_CONN)) { diff --git a/wled00/wled.h b/wled00/wled.h index 79488e8e8..174cb771b 100755 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -304,22 +304,21 @@ WLED_GLOBAL int8_t irPin _INIT(IRPIN); WLED_GLOBAL char ntpServerName[33] _INIT("0.wled.pool.ntp.org"); // NTP server to use // WiFi CONFIG (all these can be changed via web UI, no need to set them here) -WLED_GLOBAL char clientSSID[33] _INIT(CLIENT_SSID); -WLED_GLOBAL char clientPass[65] _INIT(CLIENT_PASS); -WLED_GLOBAL char cmDNS[33] _INIT(MDNS_NAME); // mDNS address (*.local, replaced by wledXXXXXX if default is used) -WLED_GLOBAL char apSSID[33] _INIT(""); // AP off by default (unless setup) -WLED_GLOBAL byte apChannel _INIT(1); // 2.4GHz WiFi AP channel (1-13) -WLED_GLOBAL byte apHide _INIT(0); // hidden AP SSID -WLED_GLOBAL byte apBehavior _INIT(AP_BEHAVIOR_BOOT_NO_CONN); // access point opens when no connection after boot by default -WLED_GLOBAL IPAddress staticIP _INIT_N((( 0, 0, 0, 0))); // static IP of ESP -WLED_GLOBAL IPAddress staticGateway _INIT_N((( 0, 0, 0, 0))); // gateway (router) IP -WLED_GLOBAL IPAddress staticSubnet _INIT_N(((255, 255, 255, 0))); // most common subnet in home networks +WLED_GLOBAL uint8_t selectedWiFi _INIT(0); +WLED_GLOBAL std::vector multiWiFi; +WLED_GLOBAL IPAddress dnsAddress _INIT_N((( 8, 8, 8, 8))); // Google's DNS +WLED_GLOBAL char cmDNS[33] _INIT(MDNS_NAME); // mDNS address (*.local, replaced by wledXXXXXX if default is used) +WLED_GLOBAL char apSSID[33] _INIT(""); // AP off by default (unless setup) +WLED_GLOBAL byte apChannel _INIT(1); // 2.4GHz WiFi AP channel (1-13) +WLED_GLOBAL byte apHide _INIT(0); // hidden AP SSID +WLED_GLOBAL byte apBehavior _INIT(AP_BEHAVIOR_BOOT_NO_CONN); // access point opens when no connection after boot by default #ifdef ARDUINO_ARCH_ESP32 -WLED_GLOBAL bool noWifiSleep _INIT(true); // disabling modem sleep modes will increase heat output and power usage, but may help with connection issues +WLED_GLOBAL bool noWifiSleep _INIT(true); // disabling modem sleep modes will increase heat output and power usage, but may help with connection issues #else WLED_GLOBAL bool noWifiSleep _INIT(false); #endif WLED_GLOBAL bool force802_3g _INIT(false); +#define WLED_WIFI_CONFIGURED (strlen(multiWiFi[0].clientSSID) >= 1 && strcmp(multiWiFi[0].clientSSID, DEFAULT_CLIENT_SSID) != 0) #ifdef WLED_USE_ETHERNET #ifdef WLED_ETH_DEFAULT // default ethernet board type if specified @@ -833,7 +832,6 @@ WLED_GLOBAL volatile uint8_t jsonBufferLock _INIT(0); #else #define WLED_CONNECTED (WiFi.status() == WL_CONNECTED) #endif -#define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID, DEFAULT_CLIENT_SSID) != 0) #ifndef WLED_AP_SSID_UNIQUE #define WLED_SET_AP_SSID() do { \ diff --git a/wled00/wled_eeprom.cpp b/wled00/wled_eeprom.cpp index 9ea838797..89e5d65c4 100755 --- a/wled00/wled_eeprom.cpp +++ b/wled00/wled_eeprom.cpp @@ -74,11 +74,11 @@ void loadSettingsFromEEPROM() int lastEEPROMversion = EEPROM.read(377); //last EEPROM version before update - readStringFromEEPROM( 0, clientSSID, 32); - readStringFromEEPROM( 32, clientPass, 64); - readStringFromEEPROM( 96, cmDNS, 32); - readStringFromEEPROM(128, apSSID, 32); - readStringFromEEPROM(160, apPass, 64); + readStringFromEEPROM( 0, multiWiFi[0].clientSSID, 32); + readStringFromEEPROM( 32, multiWiFi[0].clientPass, 64); + readStringFromEEPROM( 96, cmDNS, 32); + readStringFromEEPROM(128, apSSID, 32); + readStringFromEEPROM(160, apPass, 64); nightlightDelayMinsDefault = EEPROM.read(224); nightlightDelayMins = nightlightDelayMinsDefault; diff --git a/wled00/xml.cpp b/wled00/xml.cpp index de183c5a6..2354688ee 100755 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -248,23 +248,34 @@ void getSettingsJS(byte subPage, char* dest) if (subPage == SUBPAGE_WIFI) { - sappends('s',SET_F("CS"),clientSSID); - - byte l = strlen(clientPass); - char fpass[l+1]; //fill password field with *** - fpass[l] = 0; - memset(fpass,'*',l); - sappends('s',SET_F("CP"),fpass); - - char k[3]; k[2] = 0; //IP addresses - for (int i = 0; i<4; i++) - { - k[1] = 48+i; //ascii 0,1,2,3 - k[0] = 'I'; sappend('v',k,staticIP[i]); - k[0] = 'G'; sappend('v',k,staticGateway[i]); - k[0] = 'S'; sappend('v',k,staticSubnet[i]); + char nS[10]; + size_t l; + oappend(SET_F("resetWiFi(")); + oappend(itoa(WLED_MAX_WIFI_COUNT,nS,10)); + oappend(SET_F(");")); + for (size_t n = 0; n < multiWiFi.size(); n++) { + l = strlen(multiWiFi[n].clientPass); + char fpass[l+1]; //fill password field with *** + fpass[l] = 0; + memset(fpass,'*',l); + oappend(SET_F("addWiFi(\"")); + oappend(multiWiFi[n].clientSSID); + oappend(SET_F("\",\"")); + oappend(fpass); + oappend(SET_F("\",0x")); + oappend(itoa(multiWiFi[n].staticIP,nS,16)); + oappend(SET_F(",0x")); + oappend(itoa(multiWiFi[n].staticGW,nS,16)); + oappend(SET_F(",0x")); + oappend(itoa(multiWiFi[n].staticSN,nS,16)); + oappend(SET_F(");")); } + sappend('v',SET_F("D0"),dnsAddress[0]); + sappend('v',SET_F("D1"),dnsAddress[1]); + sappend('v',SET_F("D2"),dnsAddress[2]); + sappend('v',SET_F("D3"),dnsAddress[3]); + sappends('s',SET_F("CM"),cmDNS); sappend('i',SET_F("AB"),apBehavior); sappends('s',SET_F("AS"),apSSID); From 1bebf3d3d6f76a7afb2f7258425842f3af72bfef Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Tue, 23 Jan 2024 20:44:43 +0100 Subject: [PATCH 02/10] Optimise wifi scan - prioritize strongest signal - prune removed networks - fill present networks --- wled00/data/settings_wifi.htm | 7 +++-- wled00/data/style.css | 1 + wled00/set.cpp | 18 ++++++++---- wled00/wled.cpp | 52 ++++++++++++++++++++++++++++++++--- wled00/wled.h | 1 + 5 files changed, 67 insertions(+), 12 deletions(-) diff --git a/wled00/data/settings_wifi.htm b/wled00/data/settings_wifi.htm index 1f18dbec4..e07ef3401 100644 --- a/wled00/data/settings_wifi.htm +++ b/wled00/data/settings_wifi.htm @@ -52,8 +52,9 @@ } scanLoops = 0; - let cs = d.querySelectorAll("#wifi_entries input[type=text]#CS0"); + let cs = d.querySelectorAll("#wifi_entries input[type=text]"); for (let input of (cs||[])) { + let found = false; let select = cE("select"); select.id = input.id; select.name = input.name; @@ -72,6 +73,7 @@ if (networks[i].ssid === input.value) { option.setAttribute("selected", "selected"); + found = true; } select.appendChild(option); @@ -82,7 +84,8 @@ option.textContent = "Other network..."; select.appendChild(option); - input.replaceWith(select); + if (input.value === "" || found) input.replaceWith(select); + else select.remove(); } button.disabled = false; diff --git a/wled00/data/style.css b/wled00/data/style.css index 52b26a825..b6cb0f9e6 100644 --- a/wled00/data/style.css +++ b/wled00/data/style.css @@ -81,6 +81,7 @@ input:disabled { } input[type="text"], input[type="number"], +input[type="password"], select { font-size: medium; margin: 2px; diff --git a/wled00/set.cpp b/wled00/set.cpp index 6c20289f6..3d0e06815 100755 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -19,8 +19,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) //WIFI SETTINGS if (subPage == SUBPAGE_WIFI) { - char oldSSID[33]; - strlcpy(oldSSID, multiWiFi[0].clientSSID, 33); + unsigned cnt = 0; for (size_t n = 0; n < WLED_MAX_WIFI_COUNT; n++) { char cs[4] = "CS"; cs[2] = 48+n; cs[3] = 0; //client SSID char pw[4] = "PW"; pw[2] = 48+n; pw[3] = 0; //client password @@ -28,10 +27,11 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) char gw[5] = "GW"; gw[2] = 48+n; gw[4] = 0; //GW address char sn[5] = "SN"; sn[2] = 48+n; sn[4] = 0; //subnet mask if (request->hasArg(cs)) { - if (n >= multiWiFi.size()) multiWiFi.push_back(WiFiConfig()); + if (n >= multiWiFi.size()) multiWiFi.push_back(WiFiConfig()); // expand vector by one char oldSSID[33]; strcpy(oldSSID, multiWiFi[n].clientSSID); char oldPass[65]; strcpy(oldPass, multiWiFi[n].clientPass); - if (!strncmp(request->arg(cs).c_str(), oldSSID, 32)) { + + if (strlen(oldSSID) == 0 || !strncmp(request->arg(cs).c_str(), oldSSID, 32)) { strlcpy(multiWiFi[n].clientSSID, request->arg(cs).c_str(), 33); forceReconnect = true; } @@ -47,18 +47,24 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) multiWiFi[n].staticGW[i] = request->arg(gw).toInt(); multiWiFi[n].staticSN[i] = request->arg(sn).toInt(); } + cnt++; } } + // remove unused + if (cnt < multiWiFi.size()) { + cnt = multiWiFi.size() - cnt; + while (cnt--) multiWiFi.pop_back(); + multiWiFi.shrink_to_fit(); // release memory + } if (request->hasArg(F("D0"))) { dnsAddress = IPAddress(request->arg(F("D0")).toInt(),request->arg(F("D1")).toInt(),request->arg(F("D2")).toInt(),request->arg(F("D3")).toInt()); - DEBUG_PRINTLN(dnsAddress); } strlcpy(cmDNS, request->arg(F("CM")).c_str(), 33); apBehavior = request->arg(F("AB")).toInt(); - strcpy(oldSSID, apSSID); + char oldSSID[33]; strcpy(oldSSID, apSSID); strlcpy(apSSID, request->arg(F("AS")).c_str(), 33); if (!strcmp(oldSSID, apSSID) && apActive) forceReconnect = true; apHide = request->hasArg(F("AH")); diff --git a/wled00/wled.cpp b/wled00/wled.cpp index f386707c6..5a0054107 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -453,6 +453,8 @@ void WLED::setup() WiFi.onEvent(WiFiEvent); #endif + findWiFi(true); // start scanning for available WiFi-s + #ifdef WLED_ENABLE_ADALIGHT //Serial RX (Adalight, Improv, Serial JSON) only possible if GPIO3 unused //Serial TX (Debug, Improv, Serial JSON) only possible if GPIO1 unused @@ -698,7 +700,48 @@ bool WLED::initEthernet() #else return false; // Ethernet not enabled for build #endif +} +// performs asynchronous scan for available networks (which may take couple of seconds to finish) +// returns true if only one wifi is configured or scan completed +bool WLED::findWiFi(bool doScan) { + if (multiWiFi.size() <= 1) { + DEBUG_PRINTLN(F("Defaulf WiFi used.")); + selectedWiFi = 0; + return true; + } + + if (doScan) WiFi.scanDelete(); // restart scan + + int status = WiFi.scanComplete(); + + if (status == WIFI_SCAN_FAILED) { + DEBUG_PRINTLN(F("WiFi scan started.")); + WiFi.scanNetworks(true); // start scanning in asynchronous mode + return false; + } + if (status > 0) { // status contains number of found networks + DEBUG_PRINT(F("WiFi scan completed: ")); DEBUG_PRINTLN(status); + int rssi = -9999; + for (int o = 0; o < status; o++) { + DEBUG_PRINT(F(" WiFi available: ")); DEBUG_PRINT(WiFi.SSID(o)); + DEBUG_PRINT(F(" RSSI: ")); DEBUG_PRINT(WiFi.RSSI(o)); DEBUG_PRINTLN(F("dB")); + for (unsigned n = 0; n < multiWiFi.size(); n++) + if (!strcmp(WiFi.SSID(o).c_str(), multiWiFi[n].clientSSID)) { + // find the WiFi with the strongest signal (but keep priority of entry if signal difference is not big) + if ((n < selectedWiFi && WiFi.RSSI(o) > rssi-10) || WiFi.RSSI(o) > rssi) { + rssi = WiFi.RSSI(o); + selectedWiFi = n; + } + break; + } + } + DEBUG_PRINT(F("Selected: ")); DEBUG_PRINT(multiWiFi[selectedWiFi].clientSSID); + DEBUG_PRINT(F(" RSSI: ")); DEBUG_PRINT(rssi); DEBUG_PRINTLN(F("dB")); + return true; + } + //DEBUG_PRINT(F("WiFi scan running.")); + return false; // scan is still running or there was an error } void WLED::initConnection() @@ -715,7 +758,7 @@ void WLED::initConnection() } #endif - WiFi.disconnect(true); // close old connections + if (findWiFi()) WiFi.disconnect(true); // close old connections (only if scan completed and found networks) #ifdef ESP8266 WiFi.setPhyMode(force802_3g ? WIFI_PHY_MODE_11G : WIFI_PHY_MODE_11N); #endif @@ -752,7 +795,7 @@ void WLED::initConnection() // convert the "serverDescription" into a valid DNS hostname (alphanumeric) char hostname[25]; prepareHostname(hostname); - WiFi.begin(multiWiFi[selectedWiFi].clientSSID, multiWiFi[selectedWiFi].clientPass); + WiFi.begin(multiWiFi[selectedWiFi].clientSSID, multiWiFi[selectedWiFi].clientPass); // no harm if called multiple times #ifdef ARDUINO_ARCH_ESP32 #if defined(LOLIN_WIFI_FIX) && (defined(ARDUINO_ARCH_ESP32C3) || defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32S3)) WiFi.setTxPower(WIFI_POWER_8_5dBm); @@ -907,6 +950,7 @@ void WLED::handleConnection() if (interfacesInited) { DEBUG_PRINTLN(F("Disconnected!")); initConnection(); + findWiFi(true); // reinit scan interfacesInited = false; } //send improv failed 6 seconds after second init attempt (24 sec. after provisioning) @@ -917,15 +961,15 @@ void WLED::handleConnection() if (now - lastReconnectAttempt > ((stac) ? 300000 : 18000) && WLED_WIFI_CONFIGURED) { if (improvActive == 2) improvActive = 3; DEBUG_PRINTLN(F("Last reconnect too old.")); - if (++selectedWiFi >= multiWiFi.size()) selectedWiFi = 0; initConnection(); + findWiFi(true); // reinit scan } if (!apActive && now - lastReconnectAttempt > 12000 && (!wasConnected || apBehavior == AP_BEHAVIOR_NO_CONN)) { DEBUG_PRINTLN(F("Not connected AP.")); initAP(); } } else if (!interfacesInited) { //newly connected - DEBUG_PRINTLN(""); + DEBUG_PRINTLN(); DEBUG_PRINT(F("Connected! IP address: ")); DEBUG_PRINTLN(Network.localIP()); if (improvActive) { diff --git a/wled00/wled.h b/wled00/wled.h index 174cb771b..a513de398 100755 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -881,6 +881,7 @@ public: void initAP(bool resetAP = false); void initConnection(); void initInterfaces(); + bool findWiFi(bool doScan = false); #if defined(STATUSLED) void handleStatusLED(); #endif From 3e2aebcd10dab3def58cdcfb1b5e42c7712d5b3c Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Wed, 24 Jan 2024 15:43:59 +0100 Subject: [PATCH 03/10] Remove regression Init wifi for scan Always save WiFi name --- wled00/set.cpp | 4 ++-- wled00/wled.cpp | 24 +++++++++++------------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/wled00/set.cpp b/wled00/set.cpp index 3d0e06815..1ca27fad5 100755 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -31,8 +31,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) char oldSSID[33]; strcpy(oldSSID, multiWiFi[n].clientSSID); char oldPass[65]; strcpy(oldPass, multiWiFi[n].clientPass); - if (strlen(oldSSID) == 0 || !strncmp(request->arg(cs).c_str(), oldSSID, 32)) { - strlcpy(multiWiFi[n].clientSSID, request->arg(cs).c_str(), 33); + strlcpy(multiWiFi[n].clientSSID, request->arg(cs).c_str(), 33); + if (strlen(oldSSID) == 0 || !strncmp(multiWiFi[n].clientSSID, oldSSID, 32)) { forceReconnect = true; } if (!isAsterisksOnly(request->arg(pw).c_str(), 65)) { diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 5a0054107..3605fcfab 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -453,7 +453,8 @@ void WLED::setup() WiFi.onEvent(WiFiEvent); #endif - findWiFi(true); // start scanning for available WiFi-s + WiFi.mode(WIFI_STA); // enable scanning + findWiFi(true); // start scanning for available WiFi-s #ifdef WLED_ENABLE_ADALIGHT //Serial RX (Adalight, Improv, Serial JSON) only possible if GPIO3 unused @@ -720,7 +721,7 @@ bool WLED::findWiFi(bool doScan) { WiFi.scanNetworks(true); // start scanning in asynchronous mode return false; } - if (status > 0) { // status contains number of found networks + if (status >= 0) { // status contains number of found networks DEBUG_PRINT(F("WiFi scan completed: ")); DEBUG_PRINTLN(status); int rssi = -9999; for (int o = 0; o < status; o++) { @@ -758,7 +759,7 @@ void WLED::initConnection() } #endif - if (findWiFi()) WiFi.disconnect(true); // close old connections (only if scan completed and found networks) + if (findWiFi()) WiFi.disconnect(); // close old connections (only if scan completed and found networks) #ifdef ESP8266 WiFi.setPhyMode(force802_3g ? WIFI_PHY_MODE_11G : WIFI_PHY_MODE_11N); #endif @@ -796,6 +797,7 @@ void WLED::initConnection() char hostname[25]; prepareHostname(hostname); WiFi.begin(multiWiFi[selectedWiFi].clientSSID, multiWiFi[selectedWiFi].clientPass); // no harm if called multiple times + #ifdef ARDUINO_ARCH_ESP32 #if defined(LOLIN_WIFI_FIX) && (defined(ARDUINO_ARCH_ESP32C3) || defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32S3)) WiFi.setTxPower(WIFI_POWER_8_5dBm); @@ -892,13 +894,16 @@ void WLED::handleConnection() static unsigned long heapTime = 0; unsigned long now = millis(); - if (now < 2000 && (!WLED_WIFI_CONFIGURED || apBehavior == AP_BEHAVIOR_ALWAYS)) + if ((now < 500 && WLED_WIFI_CONFIGURED) || (now < 2000 && (!WLED_WIFI_CONFIGURED || apBehavior == AP_BEHAVIOR_ALWAYS))) return; if (lastReconnectAttempt == 0 || forceReconnect) { DEBUG_PRINTLN(F("Initial connect or forced reconnect.")); - if (forceReconnect) selectedWiFi = 0; + findWiFi(); initConnection(); + interfacesInited = false; + forceReconnect = false; + wasConnected = false; return; } @@ -938,14 +943,7 @@ void WLED::handleConnection() } } } - if (forceReconnect) { - DEBUG_PRINTLN(F("Forcing reconnect.")); - initConnection(); - interfacesInited = false; - forceReconnect = false; - wasConnected = false; - return; - } + if (!Network.isConnected()) { if (interfacesInited) { DEBUG_PRINTLN(F("Disconnected!")); From 5952edc55068fdedacca528829d33c15a4d2da4e Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Wed, 24 Jan 2024 19:52:41 +0100 Subject: [PATCH 04/10] Some fixes --- wled00/wled.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 3605fcfab..4775070ae 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -714,7 +714,7 @@ bool WLED::findWiFi(bool doScan) { if (doScan) WiFi.scanDelete(); // restart scan - int status = WiFi.scanComplete(); + int status = WiFi.scanComplete(); // complete scan may take as much as several seconds (usually <3s with not very crowded air) if (status == WIFI_SCAN_FAILED) { DEBUG_PRINTLN(F("WiFi scan started.")); @@ -747,6 +747,8 @@ bool WLED::findWiFi(bool doScan) { void WLED::initConnection() { + DEBUG_PRINTLN(F("initConnection() called.")); + #ifdef WLED_ENABLE_WEBSOCKETS ws.onEvent(wsEvent); #endif @@ -759,11 +761,13 @@ void WLED::initConnection() } #endif - if (findWiFi()) WiFi.disconnect(); // close old connections (only if scan completed and found networks) + WiFi.disconnect(true); // close old connections #ifdef ESP8266 WiFi.setPhyMode(force802_3g ? WIFI_PHY_MODE_11G : WIFI_PHY_MODE_11N); #endif + findWiFi(); // update selectedWiFi, initConnection() is called when scan is finished + if (multiWiFi[selectedWiFi].staticIP != 0U && multiWiFi[selectedWiFi].staticGW != 0U) { WiFi.config(multiWiFi[selectedWiFi].staticIP, multiWiFi[selectedWiFi].staticGW, multiWiFi[selectedWiFi].staticSN, dnsAddress); } else { @@ -893,13 +897,13 @@ void WLED::handleConnection() static uint32_t lastHeap = UINT32_MAX; static unsigned long heapTime = 0; unsigned long now = millis(); + const bool wifiConfigured = WLED_WIFI_CONFIGURED; - if ((now < 500 && WLED_WIFI_CONFIGURED) || (now < 2000 && (!WLED_WIFI_CONFIGURED || apBehavior == AP_BEHAVIOR_ALWAYS))) + if ((wifiConfigured && WiFi.scanComplete() < 0) || (now < 2000 && (!wifiConfigured || apBehavior == AP_BEHAVIOR_ALWAYS))) return; if (lastReconnectAttempt == 0 || forceReconnect) { DEBUG_PRINTLN(F("Initial connect or forced reconnect.")); - findWiFi(); initConnection(); interfacesInited = false; forceReconnect = false; @@ -935,7 +939,7 @@ void WLED::handleConnection() stacO = stac; DEBUG_PRINT(F("Connected AP clients: ")); DEBUG_PRINTLN(stac); - if (!WLED_CONNECTED && WLED_WIFI_CONFIGURED) { // trying to connect, but not connected + if (!WLED_CONNECTED && wifiConfigured) { // trying to connect, but not connected if (stac) WiFi.disconnect(); // disable search so that AP can work else @@ -956,7 +960,7 @@ void WLED::handleConnection() sendImprovStateResponse(0x03, true); improvActive = 2; } - if (now - lastReconnectAttempt > ((stac) ? 300000 : 18000) && WLED_WIFI_CONFIGURED) { + if (now - lastReconnectAttempt > ((stac) ? 300000 : 18000) && wifiConfigured) { if (improvActive == 2) improvActive = 3; DEBUG_PRINTLN(F("Last reconnect too old.")); initConnection(); From dde647c570bbd65ffdc5f54c0cb6c05a41e73e34 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Wed, 24 Jan 2024 20:53:17 +0100 Subject: [PATCH 05/10] Required fix --- wled00/data/settings_wifi.htm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/data/settings_wifi.htm b/wled00/data/settings_wifi.htm index e07ef3401..8290bb748 100644 --- a/wled00/data/settings_wifi.htm +++ b/wled00/data/settings_wifi.htm @@ -117,7 +117,7 @@ var i = gId("wifi_entries").childNodes.length; if (i >= maxNetworks) return; var b = `

-Network name (SSID${i==0?", empty to not connect":""}):
1?"required":""}>
+Network name (SSID${i==0?", empty to not connect":""}):
0?"required":""}>
Network password:

Static IP (leave at 0.0.0.0 for DHCP)${i==0?"
Also used by Ethernet":""}:
...
From 4408dffa87acab79bd72fddf030e3d1dcb6cef0f Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Thu, 25 Jan 2024 19:42:23 +0100 Subject: [PATCH 06/10] Better invalid password handling Changed function return value --- wled00/wled.cpp | 27 ++++++++++++++------------- wled00/wled.h | 2 +- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 4775070ae..d1e581a9f 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -143,6 +143,7 @@ void WLED::loop() refreshNodeList(); if (nodeBroadcastEnabled) sendSysInfoUDP(); yield(); + if (!Network.isConnected() && WiFi.scanComplete() > 0) selectedWiFi = findWiFi(); } // 15min PIN time-out @@ -704,8 +705,8 @@ bool WLED::initEthernet() } // performs asynchronous scan for available networks (which may take couple of seconds to finish) -// returns true if only one wifi is configured or scan completed -bool WLED::findWiFi(bool doScan) { +// returns configured WiFi ID with the strongest signal (or default if no configured networks available) +int8_t WLED::findWiFi(bool doScan) { if (multiWiFi.size() <= 1) { DEBUG_PRINTLN(F("Defaulf WiFi used.")); selectedWiFi = 0; @@ -719,30 +720,29 @@ bool WLED::findWiFi(bool doScan) { if (status == WIFI_SCAN_FAILED) { DEBUG_PRINTLN(F("WiFi scan started.")); WiFi.scanNetworks(true); // start scanning in asynchronous mode - return false; - } - if (status >= 0) { // status contains number of found networks + } else if (status >= 0) { // status contains number of found networks DEBUG_PRINT(F("WiFi scan completed: ")); DEBUG_PRINTLN(status); int rssi = -9999; + int selected = selectedWiFi; for (int o = 0; o < status; o++) { DEBUG_PRINT(F(" WiFi available: ")); DEBUG_PRINT(WiFi.SSID(o)); DEBUG_PRINT(F(" RSSI: ")); DEBUG_PRINT(WiFi.RSSI(o)); DEBUG_PRINTLN(F("dB")); for (unsigned n = 0; n < multiWiFi.size(); n++) if (!strcmp(WiFi.SSID(o).c_str(), multiWiFi[n].clientSSID)) { // find the WiFi with the strongest signal (but keep priority of entry if signal difference is not big) - if ((n < selectedWiFi && WiFi.RSSI(o) > rssi-10) || WiFi.RSSI(o) > rssi) { + if ((n < selected && WiFi.RSSI(o) > rssi-10) || WiFi.RSSI(o) > rssi) { rssi = WiFi.RSSI(o); - selectedWiFi = n; + selected = n; } break; } } - DEBUG_PRINT(F("Selected: ")); DEBUG_PRINT(multiWiFi[selectedWiFi].clientSSID); + DEBUG_PRINT(F("Selected: ")); DEBUG_PRINT(multiWiFi[selected].clientSSID); DEBUG_PRINT(F(" RSSI: ")); DEBUG_PRINT(rssi); DEBUG_PRINTLN(F("dB")); - return true; + return selected; } //DEBUG_PRINT(F("WiFi scan running.")); - return false; // scan is still running or there was an error + return status; // scan is still running or there was an error } void WLED::initConnection() @@ -766,8 +766,6 @@ void WLED::initConnection() WiFi.setPhyMode(force802_3g ? WIFI_PHY_MODE_11G : WIFI_PHY_MODE_11N); #endif - findWiFi(); // update selectedWiFi, initConnection() is called when scan is finished - if (multiWiFi[selectedWiFi].staticIP != 0U && multiWiFi[selectedWiFi].staticGW != 0U) { WiFi.config(multiWiFi[selectedWiFi].staticIP, multiWiFi[selectedWiFi].staticGW, multiWiFi[selectedWiFi].staticSN, dnsAddress); } else { @@ -899,11 +897,14 @@ void WLED::handleConnection() unsigned long now = millis(); const bool wifiConfigured = WLED_WIFI_CONFIGURED; + // ignore connection handling if WiFi is configured and scan still running + // or within first 2s if WiFi is not configured or AP is always active if ((wifiConfigured && WiFi.scanComplete() < 0) || (now < 2000 && (!wifiConfigured || apBehavior == AP_BEHAVIOR_ALWAYS))) return; if (lastReconnectAttempt == 0 || forceReconnect) { DEBUG_PRINTLN(F("Initial connect or forced reconnect.")); + selectedWiFi = findWiFi(); // find strongest WiFi initConnection(); interfacesInited = false; forceReconnect = false; @@ -963,8 +964,8 @@ void WLED::handleConnection() if (now - lastReconnectAttempt > ((stac) ? 300000 : 18000) && wifiConfigured) { if (improvActive == 2) improvActive = 3; DEBUG_PRINTLN(F("Last reconnect too old.")); + if (++selectedWiFi >= multiWiFi.size()) selectedWiFi = 0; // we couldn't connect, try with another network from the list initConnection(); - findWiFi(true); // reinit scan } if (!apActive && now - lastReconnectAttempt > 12000 && (!wasConnected || apBehavior == AP_BEHAVIOR_NO_CONN)) { DEBUG_PRINTLN(F("Not connected AP.")); diff --git a/wled00/wled.h b/wled00/wled.h index a513de398..c71cfcc52 100755 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -881,7 +881,7 @@ public: void initAP(bool resetAP = false); void initConnection(); void initInterfaces(); - bool findWiFi(bool doScan = false); + int8_t findWiFi(bool doScan = false); #if defined(STATUSLED) void handleStatusLED(); #endif From fbe26e13ae5b0b0a03ee83510a40e02682191b26 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Thu, 25 Jan 2024 19:46:47 +0100 Subject: [PATCH 07/10] Remove erroneous wifi selection --- wled00/wled.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/wled00/wled.cpp b/wled00/wled.cpp index d1e581a9f..1df0c7383 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -143,7 +143,6 @@ void WLED::loop() refreshNodeList(); if (nodeBroadcastEnabled) sendSysInfoUDP(); yield(); - if (!Network.isConnected() && WiFi.scanComplete() > 0) selectedWiFi = findWiFi(); } // 15min PIN time-out From 8817d4127583b71842e23944bff040e2fa0436d3 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Thu, 25 Jan 2024 19:57:04 +0100 Subject: [PATCH 08/10] Wrong return value --- wled00/wled.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 1df0c7383..f61af45e1 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -708,8 +708,7 @@ bool WLED::initEthernet() int8_t WLED::findWiFi(bool doScan) { if (multiWiFi.size() <= 1) { DEBUG_PRINTLN(F("Defaulf WiFi used.")); - selectedWiFi = 0; - return true; + return 0; } if (doScan) WiFi.scanDelete(); // restart scan From 3eb412b7509c405b8d127de51670b43b5f1759e9 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Fri, 26 Jan 2024 18:38:56 +0100 Subject: [PATCH 09/10] Add rescan & selection on disconnect --- wled00/wled.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/wled00/wled.cpp b/wled00/wled.cpp index f61af45e1..4a686f72c 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -889,6 +889,7 @@ void WLED::initInterfaces() void WLED::handleConnection() { + static bool scanDone = true; static byte stacO = 0; static uint32_t lastHeap = UINT32_MAX; static unsigned long heapTime = 0; @@ -949,10 +950,17 @@ void WLED::handleConnection() if (!Network.isConnected()) { if (interfacesInited) { + if (scanDone) { + DEBUG_PRINTLN(F("WiFi scan initiated on disconnect.")); + findWiFi(true); // reinit scan + scanDone = false; + return; // try to connect in next iteration + } DEBUG_PRINTLN(F("Disconnected!")); + selectedWiFi = findWiFi(); initConnection(); - findWiFi(true); // reinit scan interfacesInited = false; + scanDone = true; } //send improv failed 6 seconds after second init attempt (24 sec. after provisioning) if (improvActive > 2 && now - lastReconnectAttempt > 6000) { From df750c2a71efcd32b9f15a124b8fa6b260dceea4 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sat, 27 Jan 2024 08:39:54 +0100 Subject: [PATCH 10/10] Fix for single wifi --- wled00/wled.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 4a686f72c..4ab4d0686 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -898,7 +898,7 @@ void WLED::handleConnection() // ignore connection handling if WiFi is configured and scan still running // or within first 2s if WiFi is not configured or AP is always active - if ((wifiConfigured && WiFi.scanComplete() < 0) || (now < 2000 && (!wifiConfigured || apBehavior == AP_BEHAVIOR_ALWAYS))) + if ((wifiConfigured && multiWiFi.size() > 1 && WiFi.scanComplete() < 0) || (now < 2000 && (!wifiConfigured || apBehavior == AP_BEHAVIOR_ALWAYS))) return; if (lastReconnectAttempt == 0 || forceReconnect) { @@ -950,7 +950,7 @@ void WLED::handleConnection() if (!Network.isConnected()) { if (interfacesInited) { - if (scanDone) { + if (scanDone && multiWiFi.size() > 1) { DEBUG_PRINTLN(F("WiFi scan initiated on disconnect.")); findWiFi(true); // reinit scan scanDone = false;