Skip to content

Commit e8f0f75

Browse files
authored
Merge pull request #601 from adafruit/wifi_multi_esp32
Wifi multi network support for esp32/esp8266/PicoW
2 parents 648071e + 4e099ab commit e8f0f75

File tree

8 files changed

+281
-50
lines changed

8 files changed

+281
-50
lines changed

src/Wippersnapper.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2479,7 +2479,7 @@ void Wippersnapper::haltError(String error, ws_led_status_t ledStatusColor) {
24792479
#else
24802480
// Calls to delay() and yield() feed the ESP8266's
24812481
// hardware and software watchdog timers, delayMicroseconds does not.
2482-
delayMicroseconds(1000);
2482+
delayMicroseconds(1000000);
24832483
#endif
24842484
}
24852485
}

src/Wippersnapper.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,8 @@ typedef enum {
162162
FSM_NET_ESTABLISH_MQTT,
163163
} fsm_net_t;
164164

165-
#define WS_WDT_TIMEOUT 60000 ///< WDT timeout
165+
#define WS_WDT_TIMEOUT 60000 ///< WDT timeout
166+
#define WS_MAX_ALT_WIFI_NETWORKS 3 ///< Maximum number of alternative networks
166167
/* MQTT Configuration */
167168
#define WS_KEEPALIVE_INTERVAL_MS \
168169
5000 ///< Session keepalive interval time, in milliseconds
@@ -316,6 +317,8 @@ class Wippersnapper {
316317
Adafruit_MQTT *_mqtt; /*!< Reference to Adafruit_MQTT, _mqtt. */
317318

318319
secretsConfig _config; /*!< Wippersnapper secrets.json as a struct. */
320+
networkConfig _multiNetworks[3]; /*!< Wippersnapper networks as structs. */
321+
bool _isWiFiMulti = false; /*!< True if multiple networks are defined. */
319322

320323
// TODO: Does this need to be within this class?
321324
int32_t totalDigitalPins; /*!< Total number of digital-input capable pins */

src/network_interfaces/Wippersnapper_ESP32.h

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "Adafruit_MQTT_Client.h"
2525
#include "Arduino.h"
2626
#include "WiFi.h"
27+
#include "WiFiMulti.h"
2728
#include <WiFiClientSecure.h>
2829
extern Wippersnapper WS;
2930

@@ -109,8 +110,17 @@ class Wippersnapper_ESP32 : public Wippersnapper {
109110

110111
// Was the network within secrets.json found?
111112
for (int i = 0; i < n; ++i) {
112-
if (strcmp(_ssid, WiFi.SSID(i).c_str()) == 0)
113+
if (strcmp(_ssid, WiFi.SSID(i).c_str()) == 0) {
113114
return true;
115+
}
116+
if (WS._isWiFiMulti) {
117+
// multi network mode
118+
for (int j = 0; j < WS_MAX_ALT_WIFI_NETWORKS; j++) {
119+
if (strcmp(WS._multiNetworks[j].ssid, WiFi.SSID(i).c_str()) == 0) {
120+
return true;
121+
}
122+
}
123+
}
114124
}
115125

116126
// User-set network not found, print scan results to serial console
@@ -191,6 +201,7 @@ class Wippersnapper_ESP32 : public Wippersnapper {
191201
const char *_ssid; ///< WiFi SSID
192202
const char *_pass; ///< WiFi password
193203
WiFiClientSecure *_mqtt_client; ///< Pointer to a WiFi client object (TLS/SSL)
204+
WiFiMulti _wifiMulti; ///< WiFiMulti object for multi-network mode
194205

195206
const char *_aio_root_ca_staging =
196207
"-----BEGIN CERTIFICATE-----\n"
@@ -262,11 +273,35 @@ class Wippersnapper_ESP32 : public Wippersnapper {
262273
if (strlen(_ssid) == 0) {
263274
_status = WS_SSID_INVALID;
264275
} else {
276+
WiFi.setAutoReconnect(false);
265277
_disconnect();
266278
delay(100);
267-
WiFi.begin(_ssid, _pass);
268-
_status = WS_NET_DISCONNECTED;
269-
delay(5000);
279+
if (WS._isWiFiMulti) {
280+
// multi network mode
281+
_wifiMulti.APlistClean();
282+
_wifiMulti.setAllowOpenAP(false);
283+
// add default network
284+
_wifiMulti.addAP(_ssid, _pass);
285+
// add array of alternative networks
286+
for (int i = 0; i < WS_MAX_ALT_WIFI_NETWORKS; i++) {
287+
if (strlen(WS._multiNetworks[i].ssid) > 0) {
288+
_wifiMulti.addAP(WS._multiNetworks[i].ssid,
289+
WS._multiNetworks[i].pass);
290+
}
291+
}
292+
if (_wifiMulti.run(20000) == WL_CONNECTED) {
293+
_status = WS_NET_CONNECTED;
294+
} else {
295+
_status = WS_NET_DISCONNECTED;
296+
}
297+
} else {
298+
// single network mode
299+
WiFi.begin(_ssid, _pass);
300+
_status = WS_NET_DISCONNECTED;
301+
WS.feedWDT();
302+
delay(5000);
303+
}
304+
WS.feedWDT();
270305
}
271306
}
272307

src/network_interfaces/Wippersnapper_ESP8266.h

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "Adafruit_MQTT.h"
2222
#include "Adafruit_MQTT_Client.h"
2323
#include "ESP8266WiFi.h"
24+
#include "ESP8266WiFiMulti.h"
2425
#include "Wippersnapper.h"
2526

2627
/* NOTE - Projects that require "Secure MQTT" (TLS/SSL) also require a new
@@ -64,6 +65,8 @@ class Wippersnapper_ESP8266 : public Wippersnapper {
6465
_ssid = 0;
6566
_pass = 0;
6667
_wifi_client = new WiFiClient;
68+
WiFi.persistent(false);
69+
WiFi.mode(WIFI_STA);
6770
}
6871

6972
/**************************************************************************/
@@ -132,8 +135,16 @@ class Wippersnapper_ESP8266 : public Wippersnapper {
132135

133136
// Was the network within secrets.json found?
134137
for (int i = 0; i < n; ++i) {
135-
if (strcmp(_ssid, WiFi.SSID(i).c_str()) == 0)
138+
if (strcmp(_ssid, WiFi.SSID(i).c_str()) == 0) {
136139
return true;
140+
}
141+
if (WS._isWiFiMulti) {
142+
// multi network mode
143+
for (int j = 0; j < WS_MAX_ALT_WIFI_NETWORKS; j++) {
144+
if (strcmp(WS._multiNetworks[j].ssid, WiFi.SSID(i).c_str()) == 0)
145+
return true;
146+
}
147+
}
137148
}
138149

139150
// User-set network not found, print scan results to serial console
@@ -211,6 +222,7 @@ class Wippersnapper_ESP8266 : public Wippersnapper {
211222
const char *_ssid = NULL;
212223
const char *_pass = NULL;
213224
WiFiClient *_wifi_client;
225+
ESP8266WiFiMulti _wifiMulti;
214226

215227
/**************************************************************************/
216228
/*!
@@ -222,21 +234,62 @@ class Wippersnapper_ESP8266 : public Wippersnapper {
222234
if (WiFi.status() == WL_CONNECTED)
223235
return;
224236

225-
// Attempt connection
226-
_disconnect();
227-
delay(100);
228-
// ESP8266 MUST be in STA mode to avoid device acting as client/server
229-
WiFi.mode(WIFI_STA);
230-
WiFi.begin(_ssid, _pass);
231-
_status = WS_NET_DISCONNECTED;
232-
delay(100);
237+
if (strlen(_ssid) == 0) {
238+
_status = WS_SSID_INVALID;
239+
} else {
240+
WiFi.setAutoReconnect(false);
241+
// Attempt connection
242+
_disconnect();
243+
delay(100);
244+
// ESP8266 MUST be in STA mode to avoid device acting as client/server
245+
WiFi.mode(WIFI_STA);
246+
WiFi.begin(_ssid, _pass);
247+
_status = WS_NET_DISCONNECTED;
248+
delay(100);
249+
250+
if (strlen(WS._multiNetworks[0].ssid) > 0) {
251+
// multi network mode
252+
for (int i = 0; i < WS_MAX_ALT_WIFI_NETWORKS; i++) {
253+
if (strlen(WS._multiNetworks[i].ssid) > 0 &&
254+
(_wifiMulti.existsAP(WS._multiNetworks[i].ssid) == false)) {
255+
// doesn't exist, add it
256+
_wifiMulti.addAP(WS._multiNetworks[i].ssid,
257+
WS._multiNetworks[i].pass);
258+
}
259+
}
260+
// add default network
261+
if (_wifiMulti.existsAP(_ssid) == false) {
262+
_wifiMulti.addAP(_ssid, _pass);
263+
}
264+
long startRetry = millis();
265+
WS_DEBUG_PRINTLN("CONNECTING");
266+
while (_wifiMulti.run(5000) != WL_CONNECTED &&
267+
millis() - startRetry < 10000) {
268+
// ESP8266 WDT requires yield() during a busy-loop so it doesn't bite
269+
yield();
270+
}
271+
if (WiFi.status() == WL_CONNECTED) {
272+
_status = WS_NET_CONNECTED;
273+
} else {
274+
_status = WS_NET_DISCONNECTED;
275+
}
276+
} else {
277+
// single network mode
233278

234-
// wait for a connection to be established
235-
long startRetry = millis();
236-
WS_DEBUG_PRINTLN("CONNECTING");
237-
while (WiFi.status() != WL_CONNECTED && millis() - startRetry < 10000) {
238-
// ESP8266 WDT requires yield() during a busy-loop so it doesn't bite
239-
yield();
279+
// wait for a connection to be established
280+
long startRetry = millis();
281+
WS_DEBUG_PRINTLN("CONNECTING");
282+
while (WiFi.status() != WL_CONNECTED && millis() - startRetry < 10000) {
283+
// ESP8266 WDT requires yield() during a busy-loop so it doesn't bite
284+
yield();
285+
}
286+
if (WiFi.status() == WL_CONNECTED) {
287+
_status = WS_NET_CONNECTED;
288+
} else {
289+
_status = WS_NET_DISCONNECTED;
290+
}
291+
}
292+
WS.feedWDT();
240293
}
241294
}
242295

src/network_interfaces/ws_networking_pico.h

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,17 @@ class ws_networking_pico : public Wippersnapper {
108108

109109
// Was the network within secrets.json found?
110110
for (int i = 0; i < n; ++i) {
111-
if (strcmp(_ssid, WiFi.SSID(i)) == 0)
111+
if (strcmp(_ssid, WiFi.SSID(i)) == 0) {
112112
return true;
113+
}
114+
if (WS._isWiFiMulti) {
115+
// multi network mode
116+
for (int j = 0; j < WS_MAX_ALT_WIFI_NETWORKS; j++) {
117+
if (strcmp(WS._multiNetworks[j].ssid, WiFi.SSID(i)) == 0) {
118+
return true;
119+
}
120+
}
121+
}
113122
}
114123

115124
// User-set network not found, print scan results to serial console
@@ -191,6 +200,7 @@ class ws_networking_pico : public Wippersnapper {
191200
const char *_ssid; ///< WiFi SSID
192201
const char *_pass; ///< WiFi password
193202
WiFiClientSecure *_mqtt_client; ///< Pointer to a secure MQTT client object
203+
WiFiMulti _wifiMulti; ///< WiFiMulti object for multi-network mode
194204

195205
const char *_aio_root_ca_staging =
196206
"-----BEGIN CERTIFICATE-----\n"
@@ -259,27 +269,47 @@ class ws_networking_pico : public Wippersnapper {
259269
if (WiFi.status() == WL_CONNECTED)
260270
return;
261271

272+
WiFi.mode(WIFI_STA);
273+
WS.feedWDT();
274+
WiFi.setTimeout(20000);
275+
WS.feedWDT();
276+
262277
if (strlen(_ssid) == 0) {
263278
_status = WS_SSID_INVALID;
264279
} else {
265280
_disconnect();
266281
delay(5000);
267282
WS.feedWDT();
268-
WiFi.mode(WIFI_STA);
269-
WS.feedWDT();
270-
WiFi.setTimeout(20000);
271-
WS.feedWDT();
272-
WiFi.begin(_ssid, _pass);
273-
// Wait setTimeout duration for a connection and check if connected every
274-
// 5 seconds
275-
for (int i = 0; i < 4; i++) {
276-
WS.feedWDT();
277-
delay(5000);
283+
if (WS._isWiFiMulti) {
284+
// multi network mode
285+
_wifiMulti.clearAPList();
286+
// add default network
287+
_wifiMulti.addAP(_ssid, _pass);
288+
// add array of alternative networks
289+
for (int i = 0; i < WS_MAX_ALT_WIFI_NETWORKS; i++) {
290+
_wifiMulti.addAP(WS._multiNetworks[i].ssid,
291+
WS._multiNetworks[i].pass);
292+
}
278293
WS.feedWDT();
279-
if (WiFi.status() == WL_CONNECTED) {
294+
if (_wifiMulti.run(10000) == WL_CONNECTED) {
295+
WS.feedWDT();
280296
_status = WS_NET_CONNECTED;
281297
return;
282298
}
299+
WS.feedWDT();
300+
} else {
301+
WiFi.begin(_ssid, _pass);
302+
// Wait setTimeout duration for a connection and check if connected
303+
// every 5 seconds
304+
for (int i = 0; i < 4; i++) {
305+
WS.feedWDT();
306+
delay(5000);
307+
WS.feedWDT();
308+
if (WiFi.status() == WL_CONNECTED) {
309+
_status = WS_NET_CONNECTED;
310+
return;
311+
}
312+
}
283313
}
284314
_status = WS_NET_DISCONNECTED;
285315
}

src/provisioning/ConfigJson.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
#include "Config.h"
2020
#include <ArduinoJson.h>
2121

22+
// Converters for network configuration
23+
void convertToJson(const networkConfig &src, JsonVariant dst);
24+
void convertFromJson(JsonVariantConst src, networkConfig &dst);
2225
// Converters for secrets configuration
2326
void convertToJson(const secretsConfig &src, JsonVariant dst);
2427
void convertFromJson(JsonVariantConst src, secretsConfig &dst);

src/provisioning/littlefs/WipperSnapper_LittleFS.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,49 @@ void WipperSnapper_LittleFS::parseSecrets() {
6767
fsHalt(String("ERROR: deserializeJson() failed with code ") +
6868
error.c_str());
6969
}
70+
if (doc.containsKey("network_type_wifi")) {
71+
// set default network config
72+
convertFromJson(doc["network_type_wifi"], WS._config.network);
73+
74+
if (!doc["network_type_wifi"].containsKey("alternative_networks")) {
75+
// do nothing extra, we already have the only network
76+
WS_DEBUG_PRINTLN("Found single wifi network in secrets.json");
77+
78+
} else if (doc["network_type_wifi"]["alternative_networks"]
79+
.is<JsonArray>()) {
80+
81+
WS_DEBUG_PRINTLN("Found multiple wifi networks in secrets.json");
82+
// Parse network credentials from array in secrets
83+
JsonArray altnetworks = doc["network_type_wifi"]["alternative_networks"];
84+
int8_t altNetworkCount = (int8_t)altnetworks.size();
85+
WS_DEBUG_PRINT("Network count: ");
86+
WS_DEBUG_PRINTLN(altNetworkCount);
87+
if (altNetworkCount == 0) {
88+
fsHalt("ERROR: No alternative network entries found under "
89+
"network_type_wifi.alternative_networks in secrets.json!");
90+
}
91+
// check if over 3, warn user and take first three
92+
for (int i = 0; i < altNetworkCount; i++) {
93+
if (i >= 3) {
94+
WS_DEBUG_PRINT("WARNING: More than 3 networks in secrets.json, "
95+
"only the first 3 will be used. Not using ");
96+
WS_DEBUG_PRINTLN(altnetworks[i]["network_ssid"].as<const char *>());
97+
break;
98+
}
99+
convertFromJson(altnetworks[i], WS._multiNetworks[i]);
100+
WS_DEBUG_PRINT("Added SSID: ");
101+
WS_DEBUG_PRINTLN(WS._multiNetworks[i].ssid);
102+
WS_DEBUG_PRINT("PASS: ");
103+
WS_DEBUG_PRINTLN(WS._multiNetworks[i].pass);
104+
}
105+
WS._isWiFiMulti = true;
106+
} else {
107+
fsHalt("ERROR: Unrecognised value type for "
108+
"network_type_wifi.alternative_networks in secrets.json!");
109+
}
110+
} else {
111+
fsHalt("ERROR: Could not find network_type_wifi in secrets.json!");
112+
}
70113

71114
// Extract a config struct from the JSON document
72115
WS._config = doc.as<secretsConfig>();

0 commit comments

Comments
 (0)