Skip to content

Wifi multi network support for esp32/esp8266/PicoW #601

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
84 changes: 69 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,9 @@ class Wippersnapper_ESP8266 : public Wippersnapper {
_ssid = 0;
_pass = 0;
_wifi_client = new WiFiClient;
WiFi.persistent(false);
WiFi.mode(WIFI_STA);
// _wifiMulti
}

/**************************************************************************/
Expand Down Expand Up @@ -132,8 +136,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 +223,7 @@ class Wippersnapper_ESP8266 : public Wippersnapper {
const char *_ssid = NULL;
const char *_pass = NULL;
WiFiClient *_wifi_client;
ESP8266WiFiMulti _wifiMulti;

/**************************************************************************/
/*!
Expand All @@ -222,21 +235,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
Loading