Skip to content

Commit

Permalink
Merge pull request #601 from adafruit/wifi_multi_esp32
Browse files Browse the repository at this point in the history
Wifi multi network support for esp32/esp8266/PicoW
  • Loading branch information
tyeth authored Jul 23, 2024
2 parents 648071e + 4e099ab commit e8f0f75
Show file tree
Hide file tree
Showing 8 changed files with 281 additions and 50 deletions.
2 changes: 1 addition & 1 deletion src/Wippersnapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2479,7 +2479,7 @@ void Wippersnapper::haltError(String error, ws_led_status_t ledStatusColor) {
#else
// Calls to delay() and yield() feed the ESP8266's
// hardware and software watchdog timers, delayMicroseconds does not.
delayMicroseconds(1000);
delayMicroseconds(1000000);
#endif
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/Wippersnapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ typedef enum {
FSM_NET_ESTABLISH_MQTT,
} fsm_net_t;

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

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

// TODO: Does this need to be within this class?
int32_t totalDigitalPins; /*!< Total number of digital-input capable pins */
Expand Down
43 changes: 39 additions & 4 deletions src/network_interfaces/Wippersnapper_ESP32.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "Adafruit_MQTT_Client.h"
#include "Arduino.h"
#include "WiFi.h"
#include "WiFiMulti.h"
#include <WiFiClientSecure.h>
extern Wippersnapper WS;

Expand Down Expand Up @@ -109,8 +110,17 @@ class Wippersnapper_ESP32 : public Wippersnapper {

// Was the network within secrets.json found?
for (int i = 0; i < n; ++i) {
if (strcmp(_ssid, WiFi.SSID(i).c_str()) == 0)
if (strcmp(_ssid, WiFi.SSID(i).c_str()) == 0) {
return true;
}
if (WS._isWiFiMulti) {
// multi network mode
for (int j = 0; j < WS_MAX_ALT_WIFI_NETWORKS; j++) {
if (strcmp(WS._multiNetworks[j].ssid, WiFi.SSID(i).c_str()) == 0) {
return true;
}
}
}
}

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

const char *_aio_root_ca_staging =
"-----BEGIN CERTIFICATE-----\n"
Expand Down Expand Up @@ -262,11 +273,35 @@ class Wippersnapper_ESP32 : public Wippersnapper {
if (strlen(_ssid) == 0) {
_status = WS_SSID_INVALID;
} else {
WiFi.setAutoReconnect(false);
_disconnect();
delay(100);
WiFi.begin(_ssid, _pass);
_status = WS_NET_DISCONNECTED;
delay(5000);
if (WS._isWiFiMulti) {
// multi network mode
_wifiMulti.APlistClean();
_wifiMulti.setAllowOpenAP(false);
// add default network
_wifiMulti.addAP(_ssid, _pass);
// add array of alternative networks
for (int i = 0; i < WS_MAX_ALT_WIFI_NETWORKS; i++) {
if (strlen(WS._multiNetworks[i].ssid) > 0) {
_wifiMulti.addAP(WS._multiNetworks[i].ssid,
WS._multiNetworks[i].pass);
}
}
if (_wifiMulti.run(20000) == WL_CONNECTED) {
_status = WS_NET_CONNECTED;
} else {
_status = WS_NET_DISCONNECTED;
}
} else {
// single network mode
WiFi.begin(_ssid, _pass);
_status = WS_NET_DISCONNECTED;
WS.feedWDT();
delay(5000);
}
WS.feedWDT();
}
}

Expand Down
83 changes: 68 additions & 15 deletions src/network_interfaces/Wippersnapper_ESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
#include "ESP8266WiFi.h"
#include "ESP8266WiFiMulti.h"
#include "Wippersnapper.h"

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

/**************************************************************************/
Expand Down Expand Up @@ -132,8 +135,16 @@ class Wippersnapper_ESP8266 : public Wippersnapper {

// Was the network within secrets.json found?
for (int i = 0; i < n; ++i) {
if (strcmp(_ssid, WiFi.SSID(i).c_str()) == 0)
if (strcmp(_ssid, WiFi.SSID(i).c_str()) == 0) {
return true;
}
if (WS._isWiFiMulti) {
// multi network mode
for (int j = 0; j < WS_MAX_ALT_WIFI_NETWORKS; j++) {
if (strcmp(WS._multiNetworks[j].ssid, WiFi.SSID(i).c_str()) == 0)
return true;
}
}
}

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

/**************************************************************************/
/*!
Expand All @@ -222,21 +234,62 @@ class Wippersnapper_ESP8266 : public Wippersnapper {
if (WiFi.status() == WL_CONNECTED)
return;

// Attempt connection
_disconnect();
delay(100);
// ESP8266 MUST be in STA mode to avoid device acting as client/server
WiFi.mode(WIFI_STA);
WiFi.begin(_ssid, _pass);
_status = WS_NET_DISCONNECTED;
delay(100);
if (strlen(_ssid) == 0) {
_status = WS_SSID_INVALID;
} else {
WiFi.setAutoReconnect(false);
// Attempt connection
_disconnect();
delay(100);
// ESP8266 MUST be in STA mode to avoid device acting as client/server
WiFi.mode(WIFI_STA);
WiFi.begin(_ssid, _pass);
_status = WS_NET_DISCONNECTED;
delay(100);

if (strlen(WS._multiNetworks[0].ssid) > 0) {
// multi network mode
for (int i = 0; i < WS_MAX_ALT_WIFI_NETWORKS; i++) {
if (strlen(WS._multiNetworks[i].ssid) > 0 &&
(_wifiMulti.existsAP(WS._multiNetworks[i].ssid) == false)) {
// doesn't exist, add it
_wifiMulti.addAP(WS._multiNetworks[i].ssid,
WS._multiNetworks[i].pass);
}
}
// add default network
if (_wifiMulti.existsAP(_ssid) == false) {
_wifiMulti.addAP(_ssid, _pass);
}
long startRetry = millis();
WS_DEBUG_PRINTLN("CONNECTING");
while (_wifiMulti.run(5000) != WL_CONNECTED &&
millis() - startRetry < 10000) {
// ESP8266 WDT requires yield() during a busy-loop so it doesn't bite
yield();
}
if (WiFi.status() == WL_CONNECTED) {
_status = WS_NET_CONNECTED;
} else {
_status = WS_NET_DISCONNECTED;
}
} else {
// single network mode

// wait for a connection to be established
long startRetry = millis();
WS_DEBUG_PRINTLN("CONNECTING");
while (WiFi.status() != WL_CONNECTED && millis() - startRetry < 10000) {
// ESP8266 WDT requires yield() during a busy-loop so it doesn't bite
yield();
// wait for a connection to be established
long startRetry = millis();
WS_DEBUG_PRINTLN("CONNECTING");
while (WiFi.status() != WL_CONNECTED && millis() - startRetry < 10000) {
// ESP8266 WDT requires yield() during a busy-loop so it doesn't bite
yield();
}
if (WiFi.status() == WL_CONNECTED) {
_status = WS_NET_CONNECTED;
} else {
_status = WS_NET_DISCONNECTED;
}
}
WS.feedWDT();
}
}

Expand Down
54 changes: 42 additions & 12 deletions src/network_interfaces/ws_networking_pico.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,17 @@ class ws_networking_pico : public Wippersnapper {

// Was the network within secrets.json found?
for (int i = 0; i < n; ++i) {
if (strcmp(_ssid, WiFi.SSID(i)) == 0)
if (strcmp(_ssid, WiFi.SSID(i)) == 0) {
return true;
}
if (WS._isWiFiMulti) {
// multi network mode
for (int j = 0; j < WS_MAX_ALT_WIFI_NETWORKS; j++) {
if (strcmp(WS._multiNetworks[j].ssid, WiFi.SSID(i)) == 0) {
return true;
}
}
}
}

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

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

WiFi.mode(WIFI_STA);
WS.feedWDT();
WiFi.setTimeout(20000);
WS.feedWDT();

if (strlen(_ssid) == 0) {
_status = WS_SSID_INVALID;
} else {
_disconnect();
delay(5000);
WS.feedWDT();
WiFi.mode(WIFI_STA);
WS.feedWDT();
WiFi.setTimeout(20000);
WS.feedWDT();
WiFi.begin(_ssid, _pass);
// Wait setTimeout duration for a connection and check if connected every
// 5 seconds
for (int i = 0; i < 4; i++) {
WS.feedWDT();
delay(5000);
if (WS._isWiFiMulti) {
// multi network mode
_wifiMulti.clearAPList();
// add default network
_wifiMulti.addAP(_ssid, _pass);
// add array of alternative networks
for (int i = 0; i < WS_MAX_ALT_WIFI_NETWORKS; i++) {
_wifiMulti.addAP(WS._multiNetworks[i].ssid,
WS._multiNetworks[i].pass);
}
WS.feedWDT();
if (WiFi.status() == WL_CONNECTED) {
if (_wifiMulti.run(10000) == WL_CONNECTED) {
WS.feedWDT();
_status = WS_NET_CONNECTED;
return;
}
WS.feedWDT();
} else {
WiFi.begin(_ssid, _pass);
// Wait setTimeout duration for a connection and check if connected
// every 5 seconds
for (int i = 0; i < 4; i++) {
WS.feedWDT();
delay(5000);
WS.feedWDT();
if (WiFi.status() == WL_CONNECTED) {
_status = WS_NET_CONNECTED;
return;
}
}
}
_status = WS_NET_DISCONNECTED;
}
Expand Down
3 changes: 3 additions & 0 deletions src/provisioning/ConfigJson.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
#include "Config.h"
#include <ArduinoJson.h>

// Converters for network configuration
void convertToJson(const networkConfig &src, JsonVariant dst);
void convertFromJson(JsonVariantConst src, networkConfig &dst);
// Converters for secrets configuration
void convertToJson(const secretsConfig &src, JsonVariant dst);
void convertFromJson(JsonVariantConst src, secretsConfig &dst);
Expand Down
43 changes: 43 additions & 0 deletions src/provisioning/littlefs/WipperSnapper_LittleFS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,49 @@ void WipperSnapper_LittleFS::parseSecrets() {
fsHalt(String("ERROR: deserializeJson() failed with code ") +
error.c_str());
}
if (doc.containsKey("network_type_wifi")) {
// set default network config
convertFromJson(doc["network_type_wifi"], WS._config.network);

if (!doc["network_type_wifi"].containsKey("alternative_networks")) {
// do nothing extra, we already have the only network
WS_DEBUG_PRINTLN("Found single wifi network in secrets.json");

} else if (doc["network_type_wifi"]["alternative_networks"]
.is<JsonArray>()) {

WS_DEBUG_PRINTLN("Found multiple wifi networks in secrets.json");
// Parse network credentials from array in secrets
JsonArray altnetworks = doc["network_type_wifi"]["alternative_networks"];
int8_t altNetworkCount = (int8_t)altnetworks.size();
WS_DEBUG_PRINT("Network count: ");
WS_DEBUG_PRINTLN(altNetworkCount);
if (altNetworkCount == 0) {
fsHalt("ERROR: No alternative network entries found under "
"network_type_wifi.alternative_networks in secrets.json!");
}
// check if over 3, warn user and take first three
for (int i = 0; i < altNetworkCount; i++) {
if (i >= 3) {
WS_DEBUG_PRINT("WARNING: More than 3 networks in secrets.json, "
"only the first 3 will be used. Not using ");
WS_DEBUG_PRINTLN(altnetworks[i]["network_ssid"].as<const char *>());
break;
}
convertFromJson(altnetworks[i], WS._multiNetworks[i]);
WS_DEBUG_PRINT("Added SSID: ");
WS_DEBUG_PRINTLN(WS._multiNetworks[i].ssid);
WS_DEBUG_PRINT("PASS: ");
WS_DEBUG_PRINTLN(WS._multiNetworks[i].pass);
}
WS._isWiFiMulti = true;
} else {
fsHalt("ERROR: Unrecognised value type for "
"network_type_wifi.alternative_networks in secrets.json!");
}
} else {
fsHalt("ERROR: Could not find network_type_wifi in secrets.json!");
}

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

0 comments on commit e8f0f75

Please sign in to comment.