From f45134432e2eadf3d8e9a674ace7b7fab8578993 Mon Sep 17 00:00:00 2001
From: Rodrigo Garcia <rodrigo.garcia@espressif.com>
Date: Fri, 19 Jan 2024 00:58:31 -0300
Subject: [PATCH 1/4] feat(wifi): Improves WiFiMulti

---
 .../examples/WiFiMultiAdvanced/.skip.esp32h2  |   0
 .../WiFiMultiAdvanced/WiFiMultiAdvanced.ino   | 113 ++++++++++++++
 libraries/WiFi/src/WiFiMulti.cpp              | 145 ++++++++++++++----
 libraries/WiFi/src/WiFiMulti.h                |  30 +++-
 4 files changed, 260 insertions(+), 28 deletions(-)
 create mode 100644 libraries/WiFi/examples/WiFiMultiAdvanced/.skip.esp32h2
 create mode 100644 libraries/WiFi/examples/WiFiMultiAdvanced/WiFiMultiAdvanced.ino

diff --git a/libraries/WiFi/examples/WiFiMultiAdvanced/.skip.esp32h2 b/libraries/WiFi/examples/WiFiMultiAdvanced/.skip.esp32h2
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/libraries/WiFi/examples/WiFiMultiAdvanced/WiFiMultiAdvanced.ino b/libraries/WiFi/examples/WiFiMultiAdvanced/WiFiMultiAdvanced.ino
new file mode 100644
index 00000000000..096d040f45e
--- /dev/null
+++ b/libraries/WiFi/examples/WiFiMultiAdvanced/WiFiMultiAdvanced.ino
@@ -0,0 +1,113 @@
+/*
+ *  This sketch tries to connect to the best AP available
+ *  and tests for captive portals on open networks
+ *
+ */
+
+#include <WiFi.h>
+#include <WiFiMulti.h>
+
+WiFiMulti wifiMulti;
+
+// This is used to test the Internet connection of connected the AP
+// Use a non-302 status code to ensure we bypass captive portals.  Can be any text in the webpage. 
+String _testResp = "301 Moved"; // usually http:// is moves to https:// by a 301 code
+// You can also set this to a simple test page on your own server to ensure you can reach it, 
+// like "http://www.mysite.com/test.html"
+String _testURL = "http://www.espressif.com"; // Must include "http://" if testing a HTTP host
+const int _testPort = 80;  // HTTP port
+
+bool testConnection(){
+    //parse url
+    int8_t split = _testURL.indexOf('/',7);
+    String host = _testURL.substring(7, split);
+    String url = (split < 0) ? "/":_testURL.substring(split,_testURL.length());
+    log_i("Testing Connection to %s.  Test Respponse is \"%s\"",_testURL.c_str(), _testResp.c_str());
+    // Use WiFiClient class to create TCP connections
+    WiFiClient client;
+    if (!client.connect(host.c_str(), _testPort)) {
+        log_e("Connection failed");
+        return false;
+    } else {
+        log_i("Connected to test host");
+    }
+
+    // This will send the request to the server
+    client.print(String("GET ") + url + " HTTP/1.1\r\n" +
+                 "Host: " + host + "\r\n" +
+                 "Connection: close\r\n\r\n");
+    unsigned long timeout = millis();
+    while (client.available() == 0) {
+        if (millis() - timeout > 5000) {
+            log_e(">>>Client timeout!");
+            client.stop();
+            return false;
+        }
+    }
+
+    bool bSuccess = false;
+    timeout = millis();
+    while(client.available()) {
+        if (millis() - timeout < 5000) {
+           String line = client.readStringUntil('\r');
+           Serial.println("=============HTTP RESPONSE=============");
+           Serial.print(line);
+           Serial.println("\n=======================================");
+
+            bSuccess = client.find(_testResp.c_str());
+            if (bSuccess){
+                log_i("Success. Found test response");
+            } else {
+                log_e("Failed.  Can't find test response");
+            }
+            return bSuccess;
+        } else {
+            log_e("Test Response checking has timed out!");
+            break;
+        }
+    }
+    return false; // timeout
+}
+
+void setup()
+{
+    Serial.begin(115200);
+    delay(10);
+
+    wifiMulti.addAP("ssid_from_AP_1", "your_password_for_AP_1");
+    wifiMulti.addAP("ssid_from_AP_2", "your_password_for_AP_2");
+    wifiMulti.addAP("ssid_from_AP_3", "your_password_for_AP_3");
+
+    // These options can help when you need ANY kind of wifi connection to get a config file, report errors, etc.
+    wifiMulti.setStrictMode(false);  // Default is true.  Library will disconnect and forget currently connected AP if it's not in the AP list.
+    wifiMulti.setAllowOpenAP(true);   // Default is false.  True adds open APs to the AP list.
+    wifiMulti.setConnectionTestCallbackFunc(testConnection);   // Attempts to connect to a remote webserver in case of captive portals.
+
+    Serial.println("Connecting Wifi...");
+    if(wifiMulti.run() == WL_CONNECTED) {
+        Serial.println("");
+        Serial.println("WiFi connected");
+        Serial.println("IP address: ");
+        Serial.println(WiFi.localIP());
+    }
+}
+
+void loop()
+{
+    static bool isConnected = false; 
+    uint8_t WiFiStatus = wifiMulti.run();
+    
+    if (WiFiStatus == WL_CONNECTED) {
+        if (!isConnected) {
+            Serial.println("");
+            Serial.println("WiFi connected");
+            Serial.println("IP address: ");
+            Serial.println(WiFi.localIP());
+        }
+        isConnected = true;
+    } else {
+        Serial.println("WiFi not connected!");
+        isConnected = false; 
+        delay(5000);
+    }
+}
diff --git a/libraries/WiFi/src/WiFiMulti.cpp b/libraries/WiFi/src/WiFiMulti.cpp
index 22ec1ea46f9..a0d54f20de9 100644
--- a/libraries/WiFi/src/WiFiMulti.cpp
+++ b/libraries/WiFi/src/WiFiMulti.cpp
@@ -33,10 +33,9 @@ WiFiMulti::WiFiMulti()
     ipv6_support = false;
 }
 
-WiFiMulti::~WiFiMulti()
+void WiFiMulti::APlistClean(void)
 {
-    for(uint32_t i = 0; i < APlist.size(); i++) {
-        WifiAPlist_t entry = APlist[i];
+    for(auto entry : APlist) {
         if(entry.ssid) {
             free(entry.ssid);
         }
@@ -47,17 +46,22 @@ WiFiMulti::~WiFiMulti()
     APlist.clear();
 }
 
+WiFiMulti::~WiFiMulti()
+{
+    APlistClean();
+}
+
 bool WiFiMulti::addAP(const char* ssid, const char *passphrase)
 {
     WifiAPlist_t newAP;
 
-    if(!ssid || *ssid == 0x00 || strlen(ssid) > 31) {
+    if(!ssid || *ssid == '\0' || strlen(ssid) > 31) {
         // fail SSID too long or missing!
         log_e("[WIFI][APlistAdd] no ssid or ssid too long");
         return false;
     }
 
-    if(passphrase && strlen(passphrase) > 64) {
+    if(passphrase && strlen(passphrase) > 63) {
         // fail passphrase too long!
         log_e("[WIFI][APlistAdd] passphrase too long");
         return false;
@@ -70,7 +74,7 @@ bool WiFiMulti::addAP(const char* ssid, const char *passphrase)
         return false;
     }
 
-    if(passphrase && *passphrase != 0x00) {
+    if(passphrase && *passphrase != '\0') {
         newAP.passphrase = strdup(passphrase);
         if(!newAP.passphrase) {
             log_e("[WIFI][APlistAdd] fail newAP.passphrase == 0");
@@ -80,7 +84,7 @@ bool WiFiMulti::addAP(const char* ssid, const char *passphrase)
     } else {
         newAP.passphrase = NULL;
     }
-
+    newAP.hasFailed = false;
     APlist.push_back(newAP);
     log_i("[WIFI][APlistAdd] add SSID: %s", newAP.ssid);
     return true;
@@ -91,9 +95,20 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
     int8_t scanResult;
     uint8_t status = WiFi.status();
     if(status == WL_CONNECTED) {
-        for(uint32_t x = 0; x < APlist.size(); x++) {
-            if(WiFi.SSID()==APlist[x].ssid) {
+        if (!_bWFMInit && _connectionTestCBFunc != NULL){
+            if (_connectionTestCBFunc() == true) {
+                _bWFMInit = true;
+                return status;
+            }
+        } else {
+            if (!_bStrict) {
                 return status;
+            } else {
+                for(auto ap : APlist) {
+                    if(WiFi.SSID() == ap.ssid) {
+                        return status;
+                    }
+                }
             }
         }
         WiFi.disconnect(false,false);
@@ -102,22 +117,27 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
     }
 
     scanResult = WiFi.scanNetworks();
-    if(scanResult == WIFI_SCAN_RUNNING) {
+    if (scanResult == WIFI_SCAN_RUNNING) {
         // scan is running
         return WL_NO_SSID_AVAIL;
-    } else if(scanResult >= 0) {
+    } else if (scanResult >= 0) {
         // scan done analyze
-        WifiAPlist_t bestNetwork { NULL, NULL };
+        int32_t bestIndex = 0;
+        WifiAPlist_t bestNetwork { NULL, NULL, false };
         int bestNetworkDb = INT_MIN;
+        int bestNetworkSec;
         uint8_t bestBSSID[6];
         int32_t bestChannel = 0;
 
         log_i("[WIFI] scan done");
 
-        if(scanResult == 0) {
+        if (scanResult == 0) {
             log_e("[WIFI] no networks found");
         } else {
             log_i("[WIFI] %d networks found", scanResult);
+
+            int8_t failCount = 0;
+            int8_t foundCount = 0;
             for(int8_t i = 0; i < scanResult; ++i) {
 
                 String ssid_scan;
@@ -127,22 +147,47 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
                 int32_t chan_scan;
 
                 WiFi.getNetworkInfo(i, ssid_scan, sec_scan, rssi_scan, BSSID_scan, chan_scan);
+                // add any Open WiFi AP to the list, if allowed with setAllowOpenAP(true)
+                if (_bAllowOpenAP && sec_scan == WIFI_AUTH_OPEN){
+                    bool found = false;
+                    for(auto check : APlist) {
+                        if (ssid_scan == check.ssid){
+                            found = true;
+                            break;
+                        }
+                    }
+                    // If we didn't find it, add this Open WiFi AP to the list
+                    if (!found){
+                        log_i("[WIFI][APlistAdd] adding Open WiFi SSID: %s", ssid_scan.c_str());
+                        addAP(ssid_scan.c_str());
+                    }
+                }
 
                 bool known = false;
-                for(uint32_t x = APlist.size() ; x > 0; x--) {
-                    WifiAPlist_t entry = APlist[x-1];
+                for(uint32_t x = 0; x < APlist.size(); x++) {
+                    WifiAPlist_t entry = APlist[x];
 
                     if(ssid_scan == entry.ssid) { // SSID match
-                        known = true;
-                        if(rssi_scan > bestNetworkDb) { // best network
-                            if(sec_scan == WIFI_AUTH_OPEN || entry.passphrase) { // check for passphrase if not open wlan
-                                bestNetworkDb = rssi_scan;
-                                bestChannel = chan_scan;
-                                memcpy((void*) &bestNetwork, (void*) &entry, sizeof(bestNetwork));
-                                memcpy((void*) &bestBSSID, (void*) BSSID_scan, sizeof(bestBSSID));
+                        log_v("known ssid: %s, has failed: %s", entry.ssid, entry.hasFailed ? "yes" : "no");
+                        foundCount++;
+                        if (!entry.hasFailed){
+                            known = true;
+                            log_v("rssi_scan: %d, bestNetworkDb: %d", rssi_scan, bestNetworkDb);
+                            if(rssi_scan > bestNetworkDb) { // best network
+                                if(_bAllowOpenAP || (sec_scan == WIFI_AUTH_OPEN || entry.passphrase)) { // check for passphrase if not open wlan
+                                    log_v("best network is now: %s", ssid_scan);
+                                    bestIndex = x;
+                                    bestNetworkSec = sec_scan;
+                                    bestNetworkDb = rssi_scan;
+                                    bestChannel = chan_scan;
+                                    memcpy((void*) &bestNetwork, (void*) &entry, sizeof(bestNetwork));
+                                    memcpy((void*) &bestBSSID, (void*) BSSID_scan, sizeof(bestBSSID));
+                                }
                             }
+                            break;
+                        } else {
+                            failCount++;
                         }
-                        break;
                     }
                 }
 
@@ -152,8 +197,12 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
                     log_d("       %d: [%d][%02X:%02X:%02X:%02X:%02X:%02X] %s (%d) %c", i, chan_scan, BSSID_scan[0], BSSID_scan[1], BSSID_scan[2], BSSID_scan[3], BSSID_scan[4], BSSID_scan[5], ssid_scan.c_str(), rssi_scan, (sec_scan == WIFI_AUTH_OPEN) ? ' ' : '*');
                 }
             }
+            log_v("foundCount = %d, failCount = %d", foundCount, failCount);
+            // if all the APs in the list have failed, reset the failure flags
+            if (foundCount == failCount) {
+                resetFails();   // keeps trying the APs in the list
+            }
         }
-
         // clean up ram
         WiFi.scanDelete();
 
@@ -163,12 +212,15 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
             if (ipv6_support == true) {
                 WiFi.enableIPv6();
             }
-            WiFi.begin(bestNetwork.ssid, bestNetwork.passphrase, bestChannel, bestBSSID);
+            WiFi.disconnect();
+            delay(10);
+            WiFi.begin(bestNetwork.ssid, (_bAllowOpenAP && bestNetworkSec == WIFI_AUTH_OPEN) ? NULL : bestNetwork.passphrase, bestChannel, bestBSSID);
             status = WiFi.status();
+            _bWFMInit = true;
 
             auto startTime = millis();
             // wait for connection, fail, or timeout
-            while(status != WL_CONNECTED && status != WL_NO_SSID_AVAIL && status != WL_CONNECT_FAILED && (millis() - startTime) <= connectTimeout) {
+            while(status != WL_CONNECTED && (millis() - startTime) <= connectTimeout) {  // && status != WL_NO_SSID_AVAIL && status != WL_CONNECT_FAILED
                 delay(10);
                 status = WiFi.status();
             }
@@ -180,15 +232,32 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
                 log_d("[WIFI] IP: %s", WiFi.localIP().toString().c_str());
                 log_d("[WIFI] MAC: %s", WiFi.BSSIDstr().c_str());
                 log_d("[WIFI] Channel: %d", WiFi.channel());
+
+                if (_connectionTestCBFunc != NULL) {
+                    // We connected to an AP but if it's a captive portal we're not going anywhere.  Test it.
+                    if (_connectionTestCBFunc()) {
+                        resetFails();
+                    } else {
+                        markAsFailed(bestIndex);
+                        WiFi.disconnect();
+                        delay(10);
+                        status = WiFi.status();
+                    }
+                } else {
+                    resetFails();
+                }
                 break;
             case WL_NO_SSID_AVAIL:
                 log_e("[WIFI] Connecting Failed AP not found.");
+                markAsFailed(bestIndex);
                 break;
             case WL_CONNECT_FAILED:
                 log_e("[WIFI] Connecting Failed.");
+                markAsFailed(bestIndex);
                 break;
             default:
                 log_e("[WIFI] Connecting Failed (%d).", status);
+                markAsFailed(bestIndex);
                 break;
             }
         } else {
@@ -210,3 +279,27 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
 void WiFiMulti::enableIPv6(bool state) {
     ipv6_support = state;
 }
+
+void WiFiMulti::markAsFailed(int32_t i) {
+    APlist[i].hasFailed = true;
+    log_d("[WIFI] Marked SSID %s as failed", APlist[i].ssid);
+}
+
+void WiFiMulti::resetFails(){
+    for(uint32_t i = 0; i < APlist.size(); i++) {
+        APlist[i].hasFailed = false;
+    }
+    log_d("[WIFI] Resetting failure flags");
+}
+
+void WiFiMulti::setStrictMode(bool bStrict) {
+    _bStrict = bStrict;
+}
+
+void WiFiMulti::setAllowOpenAP(bool bAllowOpenAP) {
+    _bAllowOpenAP = bAllowOpenAP;
+}
+
+void WiFiMulti::setConnectionTestCallbackFunc(ConnectionTestCB_t cbFunc) {
+    _connectionTestCBFunc = cbFunc;
+}
diff --git a/libraries/WiFi/src/WiFiMulti.h b/libraries/WiFi/src/WiFiMulti.h
index d8551fab9dd..75a40c87679 100644
--- a/libraries/WiFi/src/WiFiMulti.h
+++ b/libraries/WiFi/src/WiFiMulti.h
@@ -32,8 +32,11 @@
 typedef struct {
     char * ssid;
     char * passphrase;
+    bool hasFailed;
 } WifiAPlist_t;
 
+typedef std::function<bool(void)> ConnectionTestCB_t;
+
 class WiFiMulti
 {
 public:
@@ -41,13 +44,36 @@ class WiFiMulti
     ~WiFiMulti();
 
     bool addAP(const char* ssid, const char *passphrase = NULL);
-
-    void enableIPv6(bool state);
     uint8_t run(uint32_t connectTimeout=5000);
+    void enableIPv6(bool state);
+
+    // Force (default: true) to only keep connected or to connect to an AP from the provided WiFiMulti list.
+    // When bStrict is false, it will keep the last/current connected AP even if not in the WiFiMulti List.
+    void setStrictMode(bool bStrict = true);
+    
+    // allows (true) to connect to ANY open AP, even if not in the user list
+    // default false (do not connect to an open AP that has not been explicitaly added by the user to list)
+    void setAllowOpenAP(bool bAllowOpenAP = false);
+
+    // clears the current list of Multi APs and frees the memory
+    void APlistClean(void);
+
+    // allow the user to define a callback function that will validate the connection to the Internet.
+    // if the callback returns true, the connection is considered valid and the AP will added to the validated AP list.
+    // set the callback to NULL to disable the feature and validate any SSID that is in the list.
+    void setConnectionTestCallbackFunc(ConnectionTestCB_t cbFunc);
 
 private:
     std::vector<WifiAPlist_t> APlist;
     bool ipv6_support;
+
+    bool _bStrict = true;
+    bool _bAllowOpenAP = false;
+    ConnectionTestCB_t _connectionTestCBFunc = NULL;
+    bool _bWFMInit = false;
+
+    void markAsFailed(int32_t i);
+    void resetFails();
 };
 
 #endif /* WIFICLIENTMULTI_H_ */

From 638ab50c32d513d7b9a70ec20c63be6445d7cbea Mon Sep 17 00:00:00 2001
From: Rodrigo Garcia <rodrigo.garcia@espressif.com>
Date: Fri, 19 Jan 2024 01:24:37 -0300
Subject: [PATCH 2/4] fix(wifi): Fixes Initialization of Security Mode

---
 libraries/WiFi/src/WiFiMulti.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/WiFi/src/WiFiMulti.cpp b/libraries/WiFi/src/WiFiMulti.cpp
index a0d54f20de9..b0de3a44484 100644
--- a/libraries/WiFi/src/WiFiMulti.cpp
+++ b/libraries/WiFi/src/WiFiMulti.cpp
@@ -125,7 +125,7 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
         int32_t bestIndex = 0;
         WifiAPlist_t bestNetwork { NULL, NULL, false };
         int bestNetworkDb = INT_MIN;
-        int bestNetworkSec;
+        int bestNetworkSec = WIFI_AUTH_MAX;
         uint8_t bestBSSID[6];
         int32_t bestChannel = 0;
 

From 787f27f1fca274561a1c3f56977d02837afd2259 Mon Sep 17 00:00:00 2001
From: Rodrigo Garcia <rodrigo.garcia@espressif.com>
Date: Tue, 23 Jan 2024 10:55:49 -0300
Subject: [PATCH 3/4] feat(wifi): simplifies the example by using HTTPClient

---
 .../WiFiMultiAdvanced/WiFiMultiAdvanced.ino   | 65 +++----------------
 1 file changed, 8 insertions(+), 57 deletions(-)

diff --git a/libraries/WiFi/examples/WiFiMultiAdvanced/WiFiMultiAdvanced.ino b/libraries/WiFi/examples/WiFiMultiAdvanced/WiFiMultiAdvanced.ino
index 096d040f45e..f6266b0c6e3 100644
--- a/libraries/WiFi/examples/WiFiMultiAdvanced/WiFiMultiAdvanced.ino
+++ b/libraries/WiFi/examples/WiFiMultiAdvanced/WiFiMultiAdvanced.ino
@@ -6,67 +6,18 @@
 
 #include <WiFi.h>
 #include <WiFiMulti.h>
+#include <HTTPClient.h>
 
 WiFiMulti wifiMulti;
 
-// This is used to test the Internet connection of connected the AP
-// Use a non-302 status code to ensure we bypass captive portals.  Can be any text in the webpage. 
-String _testResp = "301 Moved"; // usually http:// is moves to https:// by a 301 code
-// You can also set this to a simple test page on your own server to ensure you can reach it, 
-// like "http://www.mysite.com/test.html"
-String _testURL = "http://www.espressif.com"; // Must include "http://" if testing a HTTP host
-const int _testPort = 80;  // HTTP port
-
+// callback used to check Internet connectivity
 bool testConnection(){
-    //parse url
-    int8_t split = _testURL.indexOf('/',7);
-    String host = _testURL.substring(7, split);
-    String url = (split < 0) ? "/":_testURL.substring(split,_testURL.length());
-    log_i("Testing Connection to %s.  Test Respponse is \"%s\"",_testURL.c_str(), _testResp.c_str());
-    // Use WiFiClient class to create TCP connections
-    WiFiClient client;
-    if (!client.connect(host.c_str(), _testPort)) {
-        log_e("Connection failed");
-        return false;
-    } else {
-        log_i("Connected to test host");
-    }
-
-    // This will send the request to the server
-    client.print(String("GET ") + url + " HTTP/1.1\r\n" +
-                 "Host: " + host + "\r\n" +
-                 "Connection: close\r\n\r\n");
-    unsigned long timeout = millis();
-    while (client.available() == 0) {
-        if (millis() - timeout > 5000) {
-            log_e(">>>Client timeout!");
-            client.stop();
-            return false;
-        }
-    }
-
-    bool bSuccess = false;
-    timeout = millis();
-    while(client.available()) {
-        if (millis() - timeout < 5000) {
-           String line = client.readStringUntil('\r');
-           Serial.println("=============HTTP RESPONSE=============");
-           Serial.print(line);
-           Serial.println("\n=======================================");
-
-            bSuccess = client.find(_testResp.c_str());
-            if (bSuccess){
-                log_i("Success. Found test response");
-            } else {
-                log_e("Failed.  Can't find test response");
-            }
-            return bSuccess;
-        } else {
-            log_e("Test Response checking has timed out!");
-            break;
-        }
-    }
-    return false; // timeout
+  HTTPClient http;
+  http.begin("http://www.espressif.com");
+  int httpCode = http.GET();
+  // we expect to get a 301 because it will ask to use HTTPS intead of HTTP
+  if (httpCode == HTTP_CODE_MOVED_PERMANENTLY) return true;
+  return false;
 }
 
 void setup()

From 459dd9840b2ef3cddcad949cfedcfa1309eb58f4 Mon Sep 17 00:00:00 2001
From: Rodrigo Garcia <rodrigo.garcia@espressif.com>
Date: Tue, 23 Jan 2024 10:57:22 -0300
Subject: [PATCH 4/4] fix(WiFi): fixes a type in the commentaries

---
 libraries/WiFi/examples/WiFiMultiAdvanced/WiFiMultiAdvanced.ino | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/WiFi/examples/WiFiMultiAdvanced/WiFiMultiAdvanced.ino b/libraries/WiFi/examples/WiFiMultiAdvanced/WiFiMultiAdvanced.ino
index f6266b0c6e3..55a3e4d49a6 100644
--- a/libraries/WiFi/examples/WiFiMultiAdvanced/WiFiMultiAdvanced.ino
+++ b/libraries/WiFi/examples/WiFiMultiAdvanced/WiFiMultiAdvanced.ino
@@ -15,7 +15,7 @@ bool testConnection(){
   HTTPClient http;
   http.begin("http://www.espressif.com");
   int httpCode = http.GET();
-  // we expect to get a 301 because it will ask to use HTTPS intead of HTTP
+  // we expect to get a 301 because it will ask to use HTTPS instead of HTTP
   if (httpCode == HTTP_CODE_MOVED_PERMANENTLY) return true;
   return false;
 }