Skip to content

Commit cceebb5

Browse files
authored
Improves WiFiMulti (#9139)
* feat(wifi): Improves WiFiMulti * fix(wifi): Fixes Initialization of Security Mode * feat(wifi): simplifies the example by using HTTPClient * fix(WiFi): fixes a type in the commentaries
1 parent 39043b8 commit cceebb5

File tree

4 files changed

+211
-28
lines changed

4 files changed

+211
-28
lines changed

Diff for: libraries/WiFi/examples/WiFiMultiAdvanced/.skip.esp32h2

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* This sketch tries to connect to the best AP available
3+
* and tests for captive portals on open networks
4+
*
5+
*/
6+
7+
#include <WiFi.h>
8+
#include <WiFiMulti.h>
9+
#include <HTTPClient.h>
10+
11+
WiFiMulti wifiMulti;
12+
13+
// callback used to check Internet connectivity
14+
bool testConnection(){
15+
HTTPClient http;
16+
http.begin("http://www.espressif.com");
17+
int httpCode = http.GET();
18+
// we expect to get a 301 because it will ask to use HTTPS instead of HTTP
19+
if (httpCode == HTTP_CODE_MOVED_PERMANENTLY) return true;
20+
return false;
21+
}
22+
23+
void setup()
24+
{
25+
Serial.begin(115200);
26+
delay(10);
27+
28+
wifiMulti.addAP("ssid_from_AP_1", "your_password_for_AP_1");
29+
wifiMulti.addAP("ssid_from_AP_2", "your_password_for_AP_2");
30+
wifiMulti.addAP("ssid_from_AP_3", "your_password_for_AP_3");
31+
32+
// These options can help when you need ANY kind of wifi connection to get a config file, report errors, etc.
33+
wifiMulti.setStrictMode(false); // Default is true. Library will disconnect and forget currently connected AP if it's not in the AP list.
34+
wifiMulti.setAllowOpenAP(true); // Default is false. True adds open APs to the AP list.
35+
wifiMulti.setConnectionTestCallbackFunc(testConnection); // Attempts to connect to a remote webserver in case of captive portals.
36+
37+
Serial.println("Connecting Wifi...");
38+
if(wifiMulti.run() == WL_CONNECTED) {
39+
Serial.println("");
40+
Serial.println("WiFi connected");
41+
Serial.println("IP address: ");
42+
Serial.println(WiFi.localIP());
43+
}
44+
}
45+
46+
void loop()
47+
{
48+
static bool isConnected = false;
49+
uint8_t WiFiStatus = wifiMulti.run();
50+
51+
if (WiFiStatus == WL_CONNECTED) {
52+
if (!isConnected) {
53+
Serial.println("");
54+
Serial.println("WiFi connected");
55+
Serial.println("IP address: ");
56+
Serial.println(WiFi.localIP());
57+
}
58+
isConnected = true;
59+
} else {
60+
Serial.println("WiFi not connected!");
61+
isConnected = false;
62+
delay(5000);
63+
}
64+
}

Diff for: libraries/WiFi/src/WiFiMulti.cpp

+119-26
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,9 @@ WiFiMulti::WiFiMulti()
3333
ipv6_support = false;
3434
}
3535

36-
WiFiMulti::~WiFiMulti()
36+
void WiFiMulti::APlistClean(void)
3737
{
38-
for(uint32_t i = 0; i < APlist.size(); i++) {
39-
WifiAPlist_t entry = APlist[i];
38+
for(auto entry : APlist) {
4039
if(entry.ssid) {
4140
free(entry.ssid);
4241
}
@@ -47,17 +46,22 @@ WiFiMulti::~WiFiMulti()
4746
APlist.clear();
4847
}
4948

49+
WiFiMulti::~WiFiMulti()
50+
{
51+
APlistClean();
52+
}
53+
5054
bool WiFiMulti::addAP(const char* ssid, const char *passphrase)
5155
{
5256
WifiAPlist_t newAP;
5357

54-
if(!ssid || *ssid == 0x00 || strlen(ssid) > 31) {
58+
if(!ssid || *ssid == '\0' || strlen(ssid) > 31) {
5559
// fail SSID too long or missing!
5660
log_e("[WIFI][APlistAdd] no ssid or ssid too long");
5761
return false;
5862
}
5963

60-
if(passphrase && strlen(passphrase) > 64) {
64+
if(passphrase && strlen(passphrase) > 63) {
6165
// fail passphrase too long!
6266
log_e("[WIFI][APlistAdd] passphrase too long");
6367
return false;
@@ -70,7 +74,7 @@ bool WiFiMulti::addAP(const char* ssid, const char *passphrase)
7074
return false;
7175
}
7276

73-
if(passphrase && *passphrase != 0x00) {
77+
if(passphrase && *passphrase != '\0') {
7478
newAP.passphrase = strdup(passphrase);
7579
if(!newAP.passphrase) {
7680
log_e("[WIFI][APlistAdd] fail newAP.passphrase == 0");
@@ -80,7 +84,7 @@ bool WiFiMulti::addAP(const char* ssid, const char *passphrase)
8084
} else {
8185
newAP.passphrase = NULL;
8286
}
83-
87+
newAP.hasFailed = false;
8488
APlist.push_back(newAP);
8589
log_i("[WIFI][APlistAdd] add SSID: %s", newAP.ssid);
8690
return true;
@@ -91,9 +95,20 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
9195
int8_t scanResult;
9296
uint8_t status = WiFi.status();
9397
if(status == WL_CONNECTED) {
94-
for(uint32_t x = 0; x < APlist.size(); x++) {
95-
if(WiFi.SSID()==APlist[x].ssid) {
98+
if (!_bWFMInit && _connectionTestCBFunc != NULL){
99+
if (_connectionTestCBFunc() == true) {
100+
_bWFMInit = true;
101+
return status;
102+
}
103+
} else {
104+
if (!_bStrict) {
96105
return status;
106+
} else {
107+
for(auto ap : APlist) {
108+
if(WiFi.SSID() == ap.ssid) {
109+
return status;
110+
}
111+
}
97112
}
98113
}
99114
WiFi.disconnect(false,false);
@@ -102,22 +117,27 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
102117
}
103118

104119
scanResult = WiFi.scanNetworks();
105-
if(scanResult == WIFI_SCAN_RUNNING) {
120+
if (scanResult == WIFI_SCAN_RUNNING) {
106121
// scan is running
107122
return WL_NO_SSID_AVAIL;
108-
} else if(scanResult >= 0) {
123+
} else if (scanResult >= 0) {
109124
// scan done analyze
110-
WifiAPlist_t bestNetwork { NULL, NULL };
125+
int32_t bestIndex = 0;
126+
WifiAPlist_t bestNetwork { NULL, NULL, false };
111127
int bestNetworkDb = INT_MIN;
128+
int bestNetworkSec = WIFI_AUTH_MAX;
112129
uint8_t bestBSSID[6];
113130
int32_t bestChannel = 0;
114131

115132
log_i("[WIFI] scan done");
116133

117-
if(scanResult == 0) {
134+
if (scanResult == 0) {
118135
log_e("[WIFI] no networks found");
119136
} else {
120137
log_i("[WIFI] %d networks found", scanResult);
138+
139+
int8_t failCount = 0;
140+
int8_t foundCount = 0;
121141
for(int8_t i = 0; i < scanResult; ++i) {
122142

123143
String ssid_scan;
@@ -127,22 +147,47 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
127147
int32_t chan_scan;
128148

129149
WiFi.getNetworkInfo(i, ssid_scan, sec_scan, rssi_scan, BSSID_scan, chan_scan);
150+
// add any Open WiFi AP to the list, if allowed with setAllowOpenAP(true)
151+
if (_bAllowOpenAP && sec_scan == WIFI_AUTH_OPEN){
152+
bool found = false;
153+
for(auto check : APlist) {
154+
if (ssid_scan == check.ssid){
155+
found = true;
156+
break;
157+
}
158+
}
159+
// If we didn't find it, add this Open WiFi AP to the list
160+
if (!found){
161+
log_i("[WIFI][APlistAdd] adding Open WiFi SSID: %s", ssid_scan.c_str());
162+
addAP(ssid_scan.c_str());
163+
}
164+
}
130165

131166
bool known = false;
132-
for(uint32_t x = APlist.size() ; x > 0; x--) {
133-
WifiAPlist_t entry = APlist[x-1];
167+
for(uint32_t x = 0; x < APlist.size(); x++) {
168+
WifiAPlist_t entry = APlist[x];
134169

135170
if(ssid_scan == entry.ssid) { // SSID match
136-
known = true;
137-
if(rssi_scan > bestNetworkDb) { // best network
138-
if(sec_scan == WIFI_AUTH_OPEN || entry.passphrase) { // check for passphrase if not open wlan
139-
bestNetworkDb = rssi_scan;
140-
bestChannel = chan_scan;
141-
memcpy((void*) &bestNetwork, (void*) &entry, sizeof(bestNetwork));
142-
memcpy((void*) &bestBSSID, (void*) BSSID_scan, sizeof(bestBSSID));
171+
log_v("known ssid: %s, has failed: %s", entry.ssid, entry.hasFailed ? "yes" : "no");
172+
foundCount++;
173+
if (!entry.hasFailed){
174+
known = true;
175+
log_v("rssi_scan: %d, bestNetworkDb: %d", rssi_scan, bestNetworkDb);
176+
if(rssi_scan > bestNetworkDb) { // best network
177+
if(_bAllowOpenAP || (sec_scan == WIFI_AUTH_OPEN || entry.passphrase)) { // check for passphrase if not open wlan
178+
log_v("best network is now: %s", ssid_scan);
179+
bestIndex = x;
180+
bestNetworkSec = sec_scan;
181+
bestNetworkDb = rssi_scan;
182+
bestChannel = chan_scan;
183+
memcpy((void*) &bestNetwork, (void*) &entry, sizeof(bestNetwork));
184+
memcpy((void*) &bestBSSID, (void*) BSSID_scan, sizeof(bestBSSID));
185+
}
143186
}
187+
break;
188+
} else {
189+
failCount++;
144190
}
145-
break;
146191
}
147192
}
148193

@@ -152,8 +197,12 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
152197
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) ? ' ' : '*');
153198
}
154199
}
200+
log_v("foundCount = %d, failCount = %d", foundCount, failCount);
201+
// if all the APs in the list have failed, reset the failure flags
202+
if (foundCount == failCount) {
203+
resetFails(); // keeps trying the APs in the list
204+
}
155205
}
156-
157206
// clean up ram
158207
WiFi.scanDelete();
159208

@@ -163,12 +212,15 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
163212
if (ipv6_support == true) {
164213
WiFi.enableIPv6();
165214
}
166-
WiFi.begin(bestNetwork.ssid, bestNetwork.passphrase, bestChannel, bestBSSID);
215+
WiFi.disconnect();
216+
delay(10);
217+
WiFi.begin(bestNetwork.ssid, (_bAllowOpenAP && bestNetworkSec == WIFI_AUTH_OPEN) ? NULL : bestNetwork.passphrase, bestChannel, bestBSSID);
167218
status = WiFi.status();
219+
_bWFMInit = true;
168220

169221
auto startTime = millis();
170222
// wait for connection, fail, or timeout
171-
while(status != WL_CONNECTED && status != WL_NO_SSID_AVAIL && status != WL_CONNECT_FAILED && (millis() - startTime) <= connectTimeout) {
223+
while(status != WL_CONNECTED && (millis() - startTime) <= connectTimeout) { // && status != WL_NO_SSID_AVAIL && status != WL_CONNECT_FAILED
172224
delay(10);
173225
status = WiFi.status();
174226
}
@@ -180,15 +232,32 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
180232
log_d("[WIFI] IP: %s", WiFi.localIP().toString().c_str());
181233
log_d("[WIFI] MAC: %s", WiFi.BSSIDstr().c_str());
182234
log_d("[WIFI] Channel: %d", WiFi.channel());
235+
236+
if (_connectionTestCBFunc != NULL) {
237+
// We connected to an AP but if it's a captive portal we're not going anywhere. Test it.
238+
if (_connectionTestCBFunc()) {
239+
resetFails();
240+
} else {
241+
markAsFailed(bestIndex);
242+
WiFi.disconnect();
243+
delay(10);
244+
status = WiFi.status();
245+
}
246+
} else {
247+
resetFails();
248+
}
183249
break;
184250
case WL_NO_SSID_AVAIL:
185251
log_e("[WIFI] Connecting Failed AP not found.");
252+
markAsFailed(bestIndex);
186253
break;
187254
case WL_CONNECT_FAILED:
188255
log_e("[WIFI] Connecting Failed.");
256+
markAsFailed(bestIndex);
189257
break;
190258
default:
191259
log_e("[WIFI] Connecting Failed (%d).", status);
260+
markAsFailed(bestIndex);
192261
break;
193262
}
194263
} else {
@@ -210,3 +279,27 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout)
210279
void WiFiMulti::enableIPv6(bool state) {
211280
ipv6_support = state;
212281
}
282+
283+
void WiFiMulti::markAsFailed(int32_t i) {
284+
APlist[i].hasFailed = true;
285+
log_d("[WIFI] Marked SSID %s as failed", APlist[i].ssid);
286+
}
287+
288+
void WiFiMulti::resetFails(){
289+
for(uint32_t i = 0; i < APlist.size(); i++) {
290+
APlist[i].hasFailed = false;
291+
}
292+
log_d("[WIFI] Resetting failure flags");
293+
}
294+
295+
void WiFiMulti::setStrictMode(bool bStrict) {
296+
_bStrict = bStrict;
297+
}
298+
299+
void WiFiMulti::setAllowOpenAP(bool bAllowOpenAP) {
300+
_bAllowOpenAP = bAllowOpenAP;
301+
}
302+
303+
void WiFiMulti::setConnectionTestCallbackFunc(ConnectionTestCB_t cbFunc) {
304+
_connectionTestCBFunc = cbFunc;
305+
}

Diff for: libraries/WiFi/src/WiFiMulti.h

+28-2
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,48 @@
3232
typedef struct {
3333
char * ssid;
3434
char * passphrase;
35+
bool hasFailed;
3536
} WifiAPlist_t;
3637

38+
typedef std::function<bool(void)> ConnectionTestCB_t;
39+
3740
class WiFiMulti
3841
{
3942
public:
4043
WiFiMulti();
4144
~WiFiMulti();
4245

4346
bool addAP(const char* ssid, const char *passphrase = NULL);
44-
45-
void enableIPv6(bool state);
4647
uint8_t run(uint32_t connectTimeout=5000);
48+
void enableIPv6(bool state);
49+
50+
// Force (default: true) to only keep connected or to connect to an AP from the provided WiFiMulti list.
51+
// When bStrict is false, it will keep the last/current connected AP even if not in the WiFiMulti List.
52+
void setStrictMode(bool bStrict = true);
53+
54+
// allows (true) to connect to ANY open AP, even if not in the user list
55+
// default false (do not connect to an open AP that has not been explicitaly added by the user to list)
56+
void setAllowOpenAP(bool bAllowOpenAP = false);
57+
58+
// clears the current list of Multi APs and frees the memory
59+
void APlistClean(void);
60+
61+
// allow the user to define a callback function that will validate the connection to the Internet.
62+
// if the callback returns true, the connection is considered valid and the AP will added to the validated AP list.
63+
// set the callback to NULL to disable the feature and validate any SSID that is in the list.
64+
void setConnectionTestCallbackFunc(ConnectionTestCB_t cbFunc);
4765

4866
private:
4967
std::vector<WifiAPlist_t> APlist;
5068
bool ipv6_support;
69+
70+
bool _bStrict = true;
71+
bool _bAllowOpenAP = false;
72+
ConnectionTestCB_t _connectionTestCBFunc = NULL;
73+
bool _bWFMInit = false;
74+
75+
void markAsFailed(int32_t i);
76+
void resetFails();
5177
};
5278

5379
#endif /* WIFICLIENTMULTI_H_ */

0 commit comments

Comments
 (0)