From 8b50d99b38fb9e1710f43236941a71828cc178e7 Mon Sep 17 00:00:00 2001
From: tyeth <tyethgundry@googlemail.com>
Date: Wed, 29 May 2024 16:06:15 +0100
Subject: [PATCH 1/9] Add reset before connect for AIRLIFT/WIFININA

---
 .../Wippersnapper_AIRLIFT.h                   | 582 +++++++++---------
 .../Wippersnapper_WIFININA.h                  | 564 ++++++++---------
 2 files changed, 588 insertions(+), 558 deletions(-)

diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h
index ce39590b9..dbd8bc2f2 100644
--- a/src/network_interfaces/Wippersnapper_AIRLIFT.h
+++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h
@@ -1,285 +1,299 @@
-/*!
- * @file Wippersnapper_AIRLIFT.h
- *
- * This is a driver for using the Adafruit AirLift
- * ESP32 Co-Processor's network interface with Wippersnapper.
- *
- * The ESP32 AirLift uses SPI to communicate. Three lines (CS, ACK, RST) are
- * required to communicate with the ESP32 AirLift.
- *
- * Adafruit invests time and resources providing this open source code,
- * please support Adafruit and open-source hardware by purchasing
- * products from Adafruit!
- *
- * Copyright (c) Brent Rubell 2020-2021 for Adafruit Industries.
- *
- * MIT license, all text here must be included in any redistribution.
- *
- */
-
-#ifndef WIPPERSNAPPER_AIRLIFT_H
-#define WIPPERSNAPPER_AIRLIFT_H
-
-#include "Adafruit_MQTT.h"
-#include "Adafruit_MQTT_Client.h"
-#include "Arduino.h"
-#include "SPI.h"
-#include "WiFiNINA.h"
-#include "Wippersnapper.h"
-
-#define NINAFWVER                                                              \
-  "1.6.0" /*!< min. nina-fw version compatible with this library. */
-
-#define SPIWIFI SPI /*!< Instance of SPI interface used by an AirLift. */
-
-extern Wippersnapper WS;
-/****************************************************************************/
-/*!
-    @brief  Class for using the AirLift Co-Processor network iface.
-*/
-/****************************************************************************/
-class Wippersnapper_AIRLIFT : public Wippersnapper {
-
-public:
-  /**************************************************************************/
-  /*!
-  @brief  Initializes the Adafruit IO class for AirLift devices.
-  */
-  /**************************************************************************/
-  Wippersnapper_AIRLIFT() : Wippersnapper() {
-    _ssPin = 10;
-    _ackPin = 7;
-    _rstPin = 5;
-    _gpio0Pin = -1;
-    _wifi = &SPIWIFI;
-    _ssid = 0;
-    _pass = 0;
-    _mqtt_client = new WiFiSSLClient;
-
-    // setup ESP32 co-processor pins during init.
-    WiFi.setPins(_ssPin, _ackPin, _rstPin, _gpio0Pin, _wifi);
-  }
-
-  /**************************************************************************/
-  /*!
-  @brief  Destructor for the Adafruit IO AirLift class.
-  */
-  /**************************************************************************/
-  ~Wippersnapper_AIRLIFT() {
-    if (_mqtt)
-      delete _mqtt;
-  }
-
-  /**********************************************************/
-  /*!
-  @brief  Sets the WiFi client's ssid and password.
-  @param  ssid
-            Wireless network's SSID.
-  @param  ssidPassword
-            Wireless network's password.
-  */
-  /**********************************************************/
-  void set_ssid_pass(const char *ssid, const char *ssidPassword) {
-    _ssid = ssid;
-    _pass = ssidPassword;
-  }
-
-  /**********************************************************/
-  /*!
-  @brief  Sets the WiFi client's ssid and password from the
-            secrets.json provisioning file.
-  */
-  /**********************************************************/
-  void set_ssid_pass() {
-    _ssid = WS._config.network.ssid;
-    _pass = WS._config.network.pass;
-  }
-
-  /***********************************************************/
-  /*!
-  @brief   Performs a scan of local WiFi networks.
-  @returns True if `_network_ssid` is found, False otherwise.
-  */
-  /***********************************************************/
-  bool check_valid_ssid() {
-    // Disconnect WiFi from an AP if it was previously connected
-    WiFi.disconnect();
-    delay(100);
-
-    // Perform a network scan
-    int n = WiFi.scanNetworks();
-    if (n == 0) {
-      WS_DEBUG_PRINTLN("ERROR: No WiFi networks found!");
-      return false;
-    }
-
-    // Was the network within secrets.json found?
-    for (int i = 0; i < n; ++i) {
-      if (strcmp(_ssid, WiFi.SSID(i)) == 0)
-        return true;
-    }
-
-    // User-set network not found, print scan results to serial console
-    WS_DEBUG_PRINTLN("ERROR: Your requested WiFi network was not found!");
-    WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks: ");
-    for (int i = 0; i < n; ++i) {
-      WS_DEBUG_PRINT(WiFi.SSID(i));
-      WS_DEBUG_PRINT(" ");
-      WS_DEBUG_PRINT(WiFi.RSSI(i));
-      WS_DEBUG_PRINTLN("dB");
-    }
-
-    return false;
-  }
-
-  /********************************************************/
-  /*!
-  @brief  Sets the WiFi client.
-  @param  wifi
-          Instance of SPIClass.
-  */
-  /********************************************************/
-  void set_wifi(SPIClass *wifi) {
-    _wifi = wifi;
-    _mqtt_client = new WiFiSSLClient;
-  }
-
-  /********************************************************/
-  /*!
-  @brief  Configures ESP32 "AirLift" pins.
-  @param  ssPin
-            ESP32 S.S. pin.
-  @param  ackPin
-            ESP32 ACK pin.
-  @param  rstPin
-            ESP32 RST pin.
-  @param  gpio0Pin
-            ESP32 GPIO0 pin.
-  */
-  /********************************************************/
-  void set_airlift_pins(int ssPin, int ackPin, int rstPin, int gpio0Pin) {
-    _ssPin = ssPin;
-    _ackPin = ackPin;
-    _rstPin = rstPin;
-    _gpio0Pin = gpio0Pin;
-  }
-
-  /********************************************************/
-  /*!
-  @brief    Checks the version of an ESP32 module running
-            nina-fw.
-  @returns  True if matches min. required to run
-            WipperSnapper, False otherwise.
-  */
-  /********************************************************/
-  bool firmwareCheck() {
-    _fv = WiFi.firmwareVersion();
-    if (_fv < NINAFWVER)
-      return false;
-    return true;
-  }
-
-  /********************************************************/
-  /*!
-  @brief  Gets the ESP32's unique client identifier.
-  @note   For the ESP32, the UID is the MAC address.
-  */
-  /********************************************************/
-  void getMacAddr() {
-    uint8_t mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-    WiFi.macAddress(mac);
-    memcpy(WS._macAddr, mac, sizeof(mac));
-  }
-
-  /********************************************************/
-  /*!
-  @brief  Initializes the MQTT client.
-  @param  clientID
-          MQTT client identifier
-  */
-  /********************************************************/
-  void setupMQTTClient(const char *clientID) {
-    WS._mqtt = new Adafruit_MQTT_Client(
-        _mqtt_client, WS._config.aio_url, WS._config.io_port, clientID,
-        WS._config.aio_user, WS._config.aio_key);
-  }
-
-  /********************************************************/
-  /*!
-  @brief  Returns the network status of an ESP32 module.
-  @return ws_status_t
-  */
-  /********************************************************/
-  ws_status_t networkStatus() {
-    switch (WiFi.status()) {
-    case WL_CONNECTED:
-      return WS_NET_CONNECTED;
-    case WL_CONNECT_FAILED:
-      return WS_NET_CONNECT_FAILED;
-    case WL_IDLE_STATUS:
-      return WS_IDLE;
-    default:
-      return WS_NET_DISCONNECTED;
-    }
-  }
-
-  /*******************************************************************/
-  /*!
-  @brief  Returns the type of network connection used by Wippersnapper
-  @return AIRLIFT
-  */
-  /*******************************************************************/
-  const char *connectionType() { return "AIRLIFT"; }
-
-protected:
-  const char *_ssid;           /*!< Network SSID. */
-  const char *_pass;           /*!< Network password. */
-  String _fv;                  /*!< nina-fw firmware version. */
-  int _ssPin = -1;             /*!< SPI S.S. pin. */
-  int _ackPin = -1;            /*!< SPI ACK pin. */
-  int _rstPin = -1;            /*!< SPI RST pin. */
-  int _gpio0Pin = -1;          /*!< SPI GPIO0 pin, unused. */
-  WiFiSSLClient *_mqtt_client; /*!< Instance of a secure WiFi client. */
-  SPIClass *_wifi; /*!< Instance of the SPI bus used by the AirLift. */
-
-  /**************************************************************************/
-  /*!
-  @brief  Establishes a connection with the wireless network.
-  */
-  /**************************************************************************/
-  void _connect() {
-    if (strlen(_ssid) == 0) {
-      _status = WS_SSID_INVALID;
-    } else {
-
-      // validate co-processor is physically connected connection
-      if (WiFi.status() == WL_NO_MODULE) {
-        WS_DEBUG_PRINT("No ESP32 module detected!");
-        return;
-      }
-
-      // validate co-processor's firmware version
-      if (!firmwareCheck())
-        WS_DEBUG_PRINTLN("Please upgrade the firmware on the ESP module to the "
-                         "latest version.");
-
-      // disconnect from possible previous connection
-      _disconnect();
-
-      WiFi.begin(_ssid, _pass);
-      _status = WS_NET_DISCONNECTED;
-    }
-  }
-
-  /**************************************************************************/
-  /*!
-      @brief  Disconnects from the wireless network.
-  */
-  /**************************************************************************/
-  void _disconnect() {
-    WiFi.disconnect();
-    delay(500);
-  }
-};
-
+/*!
+ * @file Wippersnapper_AIRLIFT.h
+ *
+ * This is a driver for using the Adafruit AirLift
+ * ESP32 Co-Processor's network interface with Wippersnapper.
+ *
+ * The ESP32 AirLift uses SPI to communicate. Three lines (CS, ACK, RST) are
+ * required to communicate with the ESP32 AirLift.
+ *
+ * Adafruit invests time and resources providing this open source code,
+ * please support Adafruit and open-source hardware by purchasing
+ * products from Adafruit!
+ *
+ * Copyright (c) Brent Rubell 2020-2021 for Adafruit Industries.
+ *
+ * MIT license, all text here must be included in any redistribution.
+ *
+ */
+
+#ifndef WIPPERSNAPPER_AIRLIFT_H
+#define WIPPERSNAPPER_AIRLIFT_H
+
+#include "Adafruit_MQTT.h"
+#include "Adafruit_MQTT_Client.h"
+#include "Arduino.h"
+#include "SPI.h"
+#include "WiFiNINA.h"
+#include "Wippersnapper.h"
+
+#define NINAFWVER                                                              \
+  "1.6.0" /*!< min. nina-fw version compatible with this library. */
+
+#define SPIWIFI SPI /*!< Instance of SPI interface used by an AirLift. */
+
+extern Wippersnapper WS;
+/****************************************************************************/
+/*!
+    @brief  Class for using the AirLift Co-Processor network iface.
+*/
+/****************************************************************************/
+class Wippersnapper_AIRLIFT : public Wippersnapper {
+
+public:
+  /**************************************************************************/
+  /*!
+  @brief  Initializes the Adafruit IO class for AirLift devices.
+  */
+  /**************************************************************************/
+  Wippersnapper_AIRLIFT() : Wippersnapper() {
+    _ssPin = 10;
+    _ackPin = 7;
+    _rstPin = 5;
+    _gpio0Pin = -1;
+    _wifi = &SPIWIFI;
+    _ssid = 0;
+    _pass = 0;
+    _mqtt_client = new WiFiSSLClient;
+
+    // setup ESP32 co-processor pins during init.
+    WiFi.setPins(_ssPin, _ackPin, _rstPin, _gpio0Pin, _wifi);
+  }
+
+  /**************************************************************************/
+  /*!
+  @brief  Destructor for the Adafruit IO AirLift class.
+  */
+  /**************************************************************************/
+  ~Wippersnapper_AIRLIFT() {
+    if (_mqtt)
+      delete _mqtt;
+  }
+
+  /**********************************************************/
+  /*!
+  @brief  Sets the WiFi client's ssid and password.
+  @param  ssid
+            Wireless network's SSID.
+  @param  ssidPassword
+            Wireless network's password.
+  */
+  /**********************************************************/
+  void set_ssid_pass(const char *ssid, const char *ssidPassword) {
+    _ssid = ssid;
+    _pass = ssidPassword;
+  }
+
+  /**********************************************************/
+  /*!
+  @brief  Sets the WiFi client's ssid and password from the
+            secrets.json provisioning file.
+  */
+  /**********************************************************/
+  void set_ssid_pass() {
+    _ssid = WS._config.network.ssid;
+    _pass = WS._config.network.pass;
+  }
+
+  /***********************************************************/
+  /*!
+  @brief   Performs a scan of local WiFi networks.
+  @returns True if `_network_ssid` is found, False otherwise.
+  */
+  /***********************************************************/
+  bool check_valid_ssid() {
+    // Disconnect WiFi from an AP if it was previously connected
+    WiFi.disconnect();
+    delay(100);
+
+    // Perform a network scan
+    int n = WiFi.scanNetworks();
+    if (n == 0) {
+      WS_DEBUG_PRINTLN("ERROR: No WiFi networks found!");
+      return false;
+    }
+
+    // Was the network within secrets.json found?
+    for (int i = 0; i < n; ++i) {
+      if (strcmp(_ssid, WiFi.SSID(i)) == 0)
+        return true;
+    }
+
+    // User-set network not found, print scan results to serial console
+    WS_DEBUG_PRINTLN("ERROR: Your requested WiFi network was not found!");
+    WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks: ");
+    for (int i = 0; i < n; ++i) {
+      WS_DEBUG_PRINT(WiFi.SSID(i));
+      WS_DEBUG_PRINT(" ");
+      WS_DEBUG_PRINT(WiFi.RSSI(i));
+      WS_DEBUG_PRINTLN("dB");
+    }
+
+    return false;
+  }
+
+  /********************************************************/
+  /*!
+  @brief  Sets the WiFi client.
+  @param  wifi
+          Instance of SPIClass.
+  */
+  /********************************************************/
+  void set_wifi(SPIClass *wifi) {
+    _wifi = wifi;
+    _mqtt_client = new WiFiSSLClient;
+  }
+
+  /********************************************************/
+  /*!
+  @brief  Configures ESP32 "AirLift" pins.
+  @param  ssPin
+            ESP32 S.S. pin.
+  @param  ackPin
+            ESP32 ACK pin.
+  @param  rstPin
+            ESP32 RST pin.
+  @param  gpio0Pin
+            ESP32 GPIO0 pin.
+  */
+  /********************************************************/
+  void set_airlift_pins(int ssPin, int ackPin, int rstPin, int gpio0Pin) {
+    _ssPin = ssPin;
+    _ackPin = ackPin;
+    _rstPin = rstPin;
+    _gpio0Pin = gpio0Pin;
+  }
+
+  /********************************************************/
+  /*!
+  @brief    Checks the version of an ESP32 module running
+            nina-fw.
+  @returns  True if matches min. required to run
+            WipperSnapper, False otherwise.
+  */
+  /********************************************************/
+  bool firmwareCheck() {
+    _fv = WiFi.firmwareVersion();
+    if (_fv < NINAFWVER)
+      return false;
+    return true;
+  }
+
+  /********************************************************/
+  /*!
+  @brief  Gets the ESP32's unique client identifier.
+  @note   For the ESP32, the UID is the MAC address.
+  */
+  /********************************************************/
+  void getMacAddr() {
+    uint8_t mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    WiFi.macAddress(mac);
+    memcpy(WS._macAddr, mac, sizeof(mac));
+  }
+
+  /********************************************************/
+  /*!
+  @brief  Initializes the MQTT client.
+  @param  clientID
+          MQTT client identifier
+  */
+  /********************************************************/
+  void setupMQTTClient(const char *clientID) {
+    WS._mqtt = new Adafruit_MQTT_Client(
+        _mqtt_client, WS._config.aio_url, WS._config.io_port, clientID,
+        WS._config.aio_user, WS._config.aio_key);
+  }
+
+  /********************************************************/
+  /*!
+  @brief  Returns the network status of an ESP32 module.
+  @return ws_status_t
+  */
+  /********************************************************/
+  ws_status_t networkStatus() {
+    switch (WiFi.status()) {
+    case WL_CONNECTED:
+      return WS_NET_CONNECTED;
+    case WL_CONNECT_FAILED:
+      return WS_NET_CONNECT_FAILED;
+    case WL_IDLE_STATUS:
+      return WS_IDLE;
+    default:
+      return WS_NET_DISCONNECTED;
+    }
+  }
+
+  /*******************************************************************/
+  /*!
+  @brief  Returns the type of network connection used by Wippersnapper
+  @return AIRLIFT
+  */
+  /*******************************************************************/
+  const char *connectionType() { return "AIRLIFT"; }
+
+protected:
+  const char *_ssid;           /*!< Network SSID. */
+  const char *_pass;           /*!< Network password. */
+  String _fv;                  /*!< nina-fw firmware version. */
+  int _ssPin = -1;             /*!< SPI S.S. pin. */
+  int _ackPin = -1;            /*!< SPI ACK pin. */
+  int _rstPin = -1;            /*!< SPI RST pin. */
+  int _gpio0Pin = -1;          /*!< SPI GPIO0 pin, unused. */
+  WiFiSSLClient *_mqtt_client; /*!< Instance of a secure WiFi client. */
+  SPIClass *_wifi; /*!< Instance of the SPI bus used by the AirLift. */
+
+  /**************************************************************************/
+  /*!
+  @brief  Establishes a connection with the wireless network.
+  */
+  /**************************************************************************/
+  void _connect() {
+    if (strlen(_ssid) == 0) {
+      _status = WS_SSID_INVALID;
+    } else {
+
+      // validate co-processor is physically connected connection
+      if (WiFi.status() == WL_NO_MODULE) {
+        WS_DEBUG_PRINT("No ESP32 module detected!");
+        return;
+      }
+
+      // validate co-processor's firmware version
+      if (!firmwareCheck())
+        WS_DEBUG_PRINTLN("Please upgrade the firmware on the ESP module to the "
+                         "latest version.");
+
+      // disconnect from possible previous connection
+      _disconnect();
+
+      // reset the esp32 if possible
+      if (_rstPin != -1) {
+        WS_DEBUG("Resetting ESP32...");
+        pinMode(_rstPin, OUTPUT);
+        digitalWrite(_rstPin, LOW);
+        delay(10);
+        digitalWrite(_rstPin, HIGH);
+        delay(10);
+      }
+      // wait for the ESP32 to boot
+      delay(1000);
+
+      WS_DEBUG_PRINT("Connecting to ");
+      WS_DEBUG_PRINTLN(_ssid);
+      WiFi.begin(_ssid, _pass);
+      _status = WS_NET_DISCONNECTED;
+    }
+  }
+
+  /**************************************************************************/
+  /*!
+      @brief  Disconnects from the wireless network.
+  */
+  /**************************************************************************/
+  void _disconnect() {
+    WiFi.disconnect();
+    delay(500);
+  }
+};
+
 #endif // WIPPERSNAPPER_AIRLIFT_H
\ No newline at end of file
diff --git a/src/network_interfaces/Wippersnapper_WIFININA.h b/src/network_interfaces/Wippersnapper_WIFININA.h
index aa56c3b40..9b618143f 100644
--- a/src/network_interfaces/Wippersnapper_WIFININA.h
+++ b/src/network_interfaces/Wippersnapper_WIFININA.h
@@ -1,275 +1,291 @@
-/*!
- * @file Wippersnapper_WIFININA.h
- *
- * Network interface for the ublox wifi module on the
- * Arduino MKR WiFi 1010, Arduino Nano 33 IoT and Arduino UNO WiFi Rev.2.
- *
- * Adafruit invests time and resources providing this open source code,
- * please support Adafruit and open-source hardware by purchasing
- * products from Adafruit!
- *
- * Copyright (c) Brent Rubell 2021 for Adafruit Industries.
- *
- * MIT license, all text here must be included in any redistribution.
- *
- */
-
-#ifndef WIPPERSNAPPER_WIFININA_H
-#define WIPPERSNAPPER_WIFININA_H
-#include <Adafruit_MQTT.h>
-#include <Adafruit_MQTT_Client.h>
-#include <Arduino.h>
-#include <SPI.h>
-#include <WiFiNINA.h>
-
-#include "Wippersnapper.h"
-
-#define SPIWIFI                                                                \
-  SPI /*!< Instance of SPI interface used by an external uBlox module. */
-
-extern Wippersnapper WS;
-/****************************************************************************/
-/*!
-    @brief  Class for using the AirLift Co-Processor network iface.
-*/
-/****************************************************************************/
-class Wippersnapper_WIFININA : public Wippersnapper {
-
-public:
-  /**************************************************************************/
-  /*!
-  @brief  Initializes the Adafruit IO class for ublox devices.
-  @param  aioUsername
-          Adafruit IO username
-  @param  aioKey
-          Adafruit IO key
-  @param  netSSID
-          Wireless Network SSID
-  @param  netPass
-          Wireless Network password
-  */
-  /**************************************************************************/
-  Wippersnapper_WIFININA(const char *aioUsername, const char *aioKey,
-                         const char *netSSID, const char *netPass)
-      : Wippersnapper() {
-    _ssid = netSSID;
-    _pass = netPass;
-    _username = aioUsername;
-    _key = aioKey;
-
-    _wifi = &SPIWIFI;
-    _mqtt_client = new WiFiSSLClient;
-  }
-
-  /**************************************************************************/
-  /*!
-  @brief  Destructor for the Adafruit IO ublox class.
-  */
-  /**************************************************************************/
-  ~Wippersnapper_WIFININA() {
-    if (_mqtt)
-      delete _mqtt;
-  }
-
-  /****************************************************************************/
-  /*!
-      @brief    Configures the device's Adafruit IO credentials. This method
-                should be used only if filesystem-backed provisioning is
-                not avaliable.
-  */
-  /****************************************************************************/
-  void set_user_key() {
-    strlcpy(WS._config.aio_user, _username, sizeof(WS._config.aio_user));
-    strlcpy(WS._config.aio_key, _key, sizeof(WS._config.aio_key));
-  }
-
-  /**********************************************************/
-  /*!
-  @brief  Sets the WiFi client's ssid and password.
-  @param  ssid
-            Wireless network's SSID.
-  @param  ssidPassword
-            Wireless network's password.
-  */
-  /**********************************************************/
-  void set_ssid_pass(const char *ssid, const char *ssidPassword) {
-    strlcpy(WS._config.network.ssid, ssid, sizeof(WS._config.network.ssid));
-    strlcpy(WS._config.network.pass, ssidPassword,
-            sizeof(WS._config.network.pass));
-  }
-
-  /**********************************************************/
-  /*!
-  @brief  Sets the WiFi client's ssid and password from the
-          header file's credentials.
-  */
-  /**********************************************************/
-  void set_ssid_pass() {
-    strlcpy(WS._config.network.ssid, _ssid, sizeof(WS._config.network.ssid));
-    strlcpy(WS._config.network.pass, _pass, sizeof(WS._config.network.pass));
-  }
-
-  /***********************************************************/
-  /*!
-  @brief   Performs a scan of local WiFi networks.
-  @returns True if `_network_ssid` is found, False otherwise.
-  */
-  /***********************************************************/
-  bool check_valid_ssid() {
-    // Set WiFi to station mode and disconnect from an AP if it was previously
-    // connected
-    WiFi.disconnect();
-    delay(100);
-
-    // Perform a network scan
-    int n = WiFi.scanNetworks();
-    if (n == 0) {
-      WS_DEBUG_PRINTLN("ERROR: No WiFi networks found!");
-      return false;
-    }
-
-    // Was the network within secrets.json found?
-    for (int i = 0; i < n; ++i) {
-      if (strcmp(_ssid, WiFi.SSID(i)) == 0)
-        return true;
-    }
-
-    // User-set network not found, print scan results to serial console
-    WS_DEBUG_PRINTLN("ERROR: Your requested WiFi network was not found!");
-    WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks: ");
-    for (int i = 0; i < n; ++i) {
-      WS_DEBUG_PRINT(WiFi.SSID(i));
-      WS_DEBUG_PRINT(" ");
-      WS_DEBUG_PRINT(WiFi.RSSI(i));
-      WS_DEBUG_PRINTLN("dB");
-    }
-
-    return false;
-  }
-
-  /********************************************************/
-  /*!
-  @brief  Sets the WiFi client.
-  @param  wifi
-          Instance of SPIClass.
-  */
-  /********************************************************/
-  void set_wifi(SPIClass *wifi) {
-    _wifi = wifi;
-    _mqtt_client = new WiFiSSLClient;
-  }
-
-  /***********************************************************/
-  /*!
-  @brief   Checks the nina-fw version on the module.
-  @return  True if firmware on the ublox module matches
-           the latest version of the library, False otherwise.
-  */
-  /***********************************************************/
-  bool firmwareCheck() {
-    String fv = WiFi.firmwareVersion();
-    if (fv < WIFI_FIRMWARE_LATEST_VERSION)
-      return false;
-    return true;
-  }
-
-  /********************************************************/
-  /*!
-  @brief  Gets the ESP32's unique client identifier.
-  @note   For the ESP32, the UID is the MAC address.
-  */
-  /********************************************************/
-  void getMacAddr() {
-    uint8_t mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-    WiFi.macAddress(mac);
-    memcpy(WS._macAddr, mac, sizeof(mac));
-  }
-
-  /********************************************************/
-  /*!
-  @brief  Initializes the MQTT client.
-  @param  clientID
-          MQTT client identifier
-  */
-  /********************************************************/
-  void setupMQTTClient(const char *clientID) {
-    WS._mqtt = new Adafruit_MQTT_Client(
-        _mqtt_client, WS._config.aio_url, WS._config.io_port, clientID,
-        WS._config.aio_user, WS._config.aio_key);
-  }
-
-  /********************************************************/
-  /*!
-  @brief  Returns the network status of an ESP32 module.
-  @return ws_status_t
-  */
-  /********************************************************/
-  ws_status_t networkStatus() {
-    switch (WiFi.status()) {
-    case WL_CONNECTED:
-      return WS_NET_CONNECTED;
-    case WL_CONNECT_FAILED:
-      return WS_NET_CONNECT_FAILED;
-    case WL_IDLE_STATUS:
-      return WS_IDLE;
-    default:
-      return WS_NET_DISCONNECTED;
-    }
-  }
-
-  /*******************************************************************/
-  /*!
-  @brief  Returns the type of network connection used by Wippersnapper
-  @return AIRLIFT
-  */
-  /*******************************************************************/
-  const char *connectionType() { return "AIRLIFT"; }
-
-protected:
-  const char *_ssid;     /*!< Network SSID. */
-  const char *_pass;     /*!< Network password. */
-  const char *_username; /*!< Adafruit IO username. */
-  const char *_key;      /*!< Adafruit IO key. */
-
-  WiFiSSLClient *_mqtt_client; /*!< Instance of a secure WiFi client. */
-  SPIClass *_wifi; /*!< Instance of the SPI bus used by the ublox. */
-
-  /**************************************************************************/
-  /*!
-  @brief  Establishes a connection with the wireless network.
-  */
-  /**************************************************************************/
-  void _connect() {
-
-    // check if co-processor connected first
-    if (WiFi.status() == WL_NO_MODULE)
-      errorWriteHang("No WiFi Module Detected!");
-
-    // validate the nina-fw version
-    if (!firmwareCheck())
-      errorWriteHang("Please upgrade the firmware on the ESP module to the "
-                     "latest version.");
-
-    if (strlen(_ssid) == 0) {
-      _status = WS_SSID_INVALID;
-    } else {
-      // disconnect from possible previous connection
-      _disconnect();
-
-      WiFi.begin(_ssid, _pass);
-      _status = WS_NET_DISCONNECTED;
-    }
-  }
-
-  /**************************************************************************/
-  /*!
-      @brief  Disconnects from the wireless network.
-  */
-  /**************************************************************************/
-  void _disconnect() {
-    WiFi.disconnect();
-    delay(500);
-  }
-};
-
+/*!
+ * @file Wippersnapper_WIFININA.h
+ *
+ * Network interface for the ublox wifi module on the
+ * Arduino MKR WiFi 1010, Arduino Nano 33 IoT and Arduino UNO WiFi Rev.2.
+ *
+ * Adafruit invests time and resources providing this open source code,
+ * please support Adafruit and open-source hardware by purchasing
+ * products from Adafruit!
+ *
+ * Copyright (c) Brent Rubell 2021 for Adafruit Industries.
+ *
+ * MIT license, all text here must be included in any redistribution.
+ *
+ */
+
+#ifndef WIPPERSNAPPER_WIFININA_H
+#define WIPPERSNAPPER_WIFININA_H
+#include <Adafruit_MQTT.h>
+#include <Adafruit_MQTT_Client.h>
+#include <Arduino.h>
+#include <SPI.h>
+#include <WiFiNINA.h>
+
+#include "Wippersnapper.h"
+
+#define SPIWIFI                                                                \
+  SPI /*!< Instance of SPI interface used by an external uBlox module. */
+
+extern Wippersnapper WS;
+/****************************************************************************/
+/*!
+    @brief  Class for using the AirLift Co-Processor network iface.
+*/
+/****************************************************************************/
+class Wippersnapper_WIFININA : public Wippersnapper {
+
+public:
+  /**************************************************************************/
+  /*!
+  @brief  Initializes the Adafruit IO class for ublox devices.
+  @param  aioUsername
+          Adafruit IO username
+  @param  aioKey
+          Adafruit IO key
+  @param  netSSID
+          Wireless Network SSID
+  @param  netPass
+          Wireless Network password
+  */
+  /**************************************************************************/
+  Wippersnapper_WIFININA(const char *aioUsername, const char *aioKey,
+                         const char *netSSID, const char *netPass)
+      : Wippersnapper() {
+    _ssid = netSSID;
+    _pass = netPass;
+    _username = aioUsername;
+    _key = aioKey;
+
+    _wifi = &SPIWIFI;
+    _mqtt_client = new WiFiSSLClient;
+  }
+
+  /**************************************************************************/
+  /*!
+  @brief  Destructor for the Adafruit IO ublox class.
+  */
+  /**************************************************************************/
+  ~Wippersnapper_WIFININA() {
+    if (_mqtt)
+      delete _mqtt;
+  }
+
+  /****************************************************************************/
+  /*!
+      @brief    Configures the device's Adafruit IO credentials. This method
+                should be used only if filesystem-backed provisioning is
+                not avaliable.
+  */
+  /****************************************************************************/
+  void set_user_key() {
+    strlcpy(WS._config.aio_user, _username, sizeof(WS._config.aio_user));
+    strlcpy(WS._config.aio_key, _key, sizeof(WS._config.aio_key));
+  }
+
+  /**********************************************************/
+  /*!
+  @brief  Sets the WiFi client's ssid and password.
+  @param  ssid
+            Wireless network's SSID.
+  @param  ssidPassword
+            Wireless network's password.
+  */
+  /**********************************************************/
+  void set_ssid_pass(const char *ssid, const char *ssidPassword) {
+    strlcpy(WS._config.network.ssid, ssid, sizeof(WS._config.network.ssid));
+    strlcpy(WS._config.network.pass, ssidPassword,
+            sizeof(WS._config.network.pass));
+  }
+
+  /**********************************************************/
+  /*!
+  @brief  Sets the WiFi client's ssid and password from the
+          header file's credentials.
+  */
+  /**********************************************************/
+  void set_ssid_pass() {
+    strlcpy(WS._config.network.ssid, _ssid, sizeof(WS._config.network.ssid));
+    strlcpy(WS._config.network.pass, _pass, sizeof(WS._config.network.pass));
+  }
+
+  /***********************************************************/
+  /*!
+  @brief   Performs a scan of local WiFi networks.
+  @returns True if `_network_ssid` is found, False otherwise.
+  */
+  /***********************************************************/
+  bool check_valid_ssid() {
+    // Set WiFi to station mode and disconnect from an AP if it was previously
+    // connected
+    WiFi.disconnect();
+    delay(100);
+
+    // Perform a network scan
+    int n = WiFi.scanNetworks();
+    if (n == 0) {
+      WS_DEBUG_PRINTLN("ERROR: No WiFi networks found!");
+      return false;
+    }
+
+    // Was the network within secrets.json found?
+    for (int i = 0; i < n; ++i) {
+      if (strcmp(_ssid, WiFi.SSID(i)) == 0)
+        return true;
+    }
+
+    // User-set network not found, print scan results to serial console
+    WS_DEBUG_PRINTLN("ERROR: Your requested WiFi network was not found!");
+    WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks: ");
+    for (int i = 0; i < n; ++i) {
+      WS_DEBUG_PRINT(WiFi.SSID(i));
+      WS_DEBUG_PRINT(" ");
+      WS_DEBUG_PRINT(WiFi.RSSI(i));
+      WS_DEBUG_PRINTLN("dB");
+    }
+
+    return false;
+  }
+
+  /********************************************************/
+  /*!
+  @brief  Sets the WiFi client.
+  @param  wifi
+          Instance of SPIClass.
+  */
+  /********************************************************/
+  void set_wifi(SPIClass *wifi) {
+    _wifi = wifi;
+    _mqtt_client = new WiFiSSLClient;
+  }
+
+  /***********************************************************/
+  /*!
+  @brief   Checks the nina-fw version on the module.
+  @return  True if firmware on the ublox module matches
+           the latest version of the library, False otherwise.
+  */
+  /***********************************************************/
+  bool firmwareCheck() {
+    String fv = WiFi.firmwareVersion();
+    if (fv < WIFI_FIRMWARE_LATEST_VERSION)
+      return false;
+    return true;
+  }
+
+  /********************************************************/
+  /*!
+  @brief  Gets the ESP32's unique client identifier.
+  @note   For the ESP32, the UID is the MAC address.
+  */
+  /********************************************************/
+  void getMacAddr() {
+    uint8_t mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    WiFi.macAddress(mac);
+    memcpy(WS._macAddr, mac, sizeof(mac));
+  }
+
+  /********************************************************/
+  /*!
+  @brief  Initializes the MQTT client.
+  @param  clientID
+          MQTT client identifier
+  */
+  /********************************************************/
+  void setupMQTTClient(const char *clientID) {
+    WS._mqtt = new Adafruit_MQTT_Client(
+        _mqtt_client, WS._config.aio_url, WS._config.io_port, clientID,
+        WS._config.aio_user, WS._config.aio_key);
+  }
+
+  /********************************************************/
+  /*!
+  @brief  Returns the network status of an ESP32 module.
+  @return ws_status_t
+  */
+  /********************************************************/
+  ws_status_t networkStatus() {
+    switch (WiFi.status()) {
+    case WL_CONNECTED:
+      return WS_NET_CONNECTED;
+    case WL_CONNECT_FAILED:
+      return WS_NET_CONNECT_FAILED;
+    case WL_IDLE_STATUS:
+      return WS_IDLE;
+    default:
+      return WS_NET_DISCONNECTED;
+    }
+  }
+
+  /*******************************************************************/
+  /*!
+  @brief  Returns the type of network connection used by Wippersnapper
+  @return AIRLIFT
+  */
+  /*******************************************************************/
+  const char *connectionType() { return "AIRLIFT"; }
+
+protected:
+  const char *_ssid;     /*!< Network SSID. */
+  const char *_pass;     /*!< Network password. */
+  const char *_username; /*!< Adafruit IO username. */
+  const char *_key;      /*!< Adafruit IO key. */
+
+  WiFiSSLClient *_mqtt_client; /*!< Instance of a secure WiFi client. */
+  SPIClass *_wifi; /*!< Instance of the SPI bus used by the ublox. */
+
+  /**************************************************************************/
+  /*!
+  @brief  Establishes a connection with the wireless network.
+  */
+  /**************************************************************************/
+  void _connect() {
+
+    // check if co-processor connected first
+    if (WiFi.status() == WL_NO_MODULE)
+      errorWriteHang("No WiFi Module Detected!");
+
+    // validate the nina-fw version
+    if (!firmwareCheck())
+      errorWriteHang("Please upgrade the firmware on the ESP module to the "
+                     "latest version.");
+
+    if (strlen(_ssid) == 0) {
+      _status = WS_SSID_INVALID;
+    } else {
+      // disconnect from possible previous connection
+      _disconnect();
+
+#if defined(ESP32_RESETN)
+      // reset the esp32 if possible, better if we didn't do this first time
+      if (ESP32_RESETN != -1) {
+        WS_DEBUG("Resetting ESP32...");
+        pinMode(ESP32_RESETN, OUTPUT);
+        digitalWrite(ESP32_RESETN, LOW);
+        delay(10);
+        digitalWrite(ESP32_RESETN, HIGH);
+        delay(10);
+      }
+      // wait for the ESP32 to boot
+      delay(1000);
+#endif
+
+      WS_DEBUG_PRINT("Connecting to ");
+      WS_DEBUG_PRINTLN(_ssid);
+      WiFi.begin(_ssid, _pass);
+      _status = WS_NET_DISCONNECTED;
+    }
+  }
+
+  /**************************************************************************/
+  /*!
+      @brief  Disconnects from the wireless network.
+  */
+  /**************************************************************************/
+  void _disconnect() {
+    WiFi.disconnect();
+    delay(500);
+  }
+};
+
 #endif // WIPPERSNAPPER_WIFININA_H
\ No newline at end of file

From d624e80780742f1d182e124c1dfa8ad6e9744bff Mon Sep 17 00:00:00 2001
From: tyeth <tyethgundry@googlemail.com>
Date: Wed, 29 May 2024 16:23:04 +0100
Subject: [PATCH 2/9] Correct WIFININA reset pin name inline with Arduino
 Nano_33_iot/MkrWifi1010

---
 .../Wippersnapper_WIFININA.h                  | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/src/network_interfaces/Wippersnapper_WIFININA.h b/src/network_interfaces/Wippersnapper_WIFININA.h
index 9b618143f..87de9087f 100644
--- a/src/network_interfaces/Wippersnapper_WIFININA.h
+++ b/src/network_interfaces/Wippersnapper_WIFININA.h
@@ -256,14 +256,23 @@ class Wippersnapper_WIFININA : public Wippersnapper {
       // disconnect from possible previous connection
       _disconnect();
 
-#if defined(ESP32_RESETN)
+// no clear recommendation, all three defined for both boards, future-proofing
+#if defined(NINA_RESETN)
+#define NINA_RESET_PIN NINA_RESETN
+#elif defined(SPIWIFI_RESET)
+#define NINA_RESET_PIN SPIWIFI_RESET
+#elif defined(ESP32_RESETN)
+#define NINA_RESET_PIN ESP32_RESETN
+#endif
+
+#if defined(NINA_RESET_PIN)
       // reset the esp32 if possible, better if we didn't do this first time
-      if (ESP32_RESETN != -1) {
+      if (NINA_RESET_PIN != -1) {
         WS_DEBUG("Resetting ESP32...");
-        pinMode(ESP32_RESETN, OUTPUT);
-        digitalWrite(ESP32_RESETN, LOW);
+        pinMode(NINA_RESET_PIN, OUTPUT);
+        digitalWrite(NINA_RESET_PIN, LOW);
         delay(10);
-        digitalWrite(ESP32_RESETN, HIGH);
+        digitalWrite(NINA_RESET_PIN, HIGH);
         delay(10);
       }
       // wait for the ESP32 to boot

From 3883b66feb819cde258c19424bdfc5f757d78d87 Mon Sep 17 00:00:00 2001
From: Tyeth Gundry <tyethgundry@googlemail.com>
Date: Thu, 30 May 2024 18:01:59 +0100
Subject: [PATCH 3/9] WIP: use SS/GPIO0 pins for reset too

---
 .../Wippersnapper_AIRLIFT.h                   | 651 ++++++++++--------
 .../Wippersnapper_WIFININA.h                  |  13 +-
 src/network_interfaces/ws_networking_pico.h   |   3 +
 3 files changed, 366 insertions(+), 301 deletions(-)

diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h
index dbd8bc2f2..d9a4fc422 100644
--- a/src/network_interfaces/Wippersnapper_AIRLIFT.h
+++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h
@@ -1,299 +1,354 @@
-/*!
- * @file Wippersnapper_AIRLIFT.h
- *
- * This is a driver for using the Adafruit AirLift
- * ESP32 Co-Processor's network interface with Wippersnapper.
- *
- * The ESP32 AirLift uses SPI to communicate. Three lines (CS, ACK, RST) are
- * required to communicate with the ESP32 AirLift.
- *
- * Adafruit invests time and resources providing this open source code,
- * please support Adafruit and open-source hardware by purchasing
- * products from Adafruit!
- *
- * Copyright (c) Brent Rubell 2020-2021 for Adafruit Industries.
- *
- * MIT license, all text here must be included in any redistribution.
- *
- */
-
-#ifndef WIPPERSNAPPER_AIRLIFT_H
-#define WIPPERSNAPPER_AIRLIFT_H
-
-#include "Adafruit_MQTT.h"
-#include "Adafruit_MQTT_Client.h"
-#include "Arduino.h"
-#include "SPI.h"
-#include "WiFiNINA.h"
-#include "Wippersnapper.h"
-
-#define NINAFWVER                                                              \
-  "1.6.0" /*!< min. nina-fw version compatible with this library. */
-
-#define SPIWIFI SPI /*!< Instance of SPI interface used by an AirLift. */
-
-extern Wippersnapper WS;
-/****************************************************************************/
-/*!
-    @brief  Class for using the AirLift Co-Processor network iface.
-*/
-/****************************************************************************/
-class Wippersnapper_AIRLIFT : public Wippersnapper {
-
-public:
-  /**************************************************************************/
-  /*!
-  @brief  Initializes the Adafruit IO class for AirLift devices.
-  */
-  /**************************************************************************/
-  Wippersnapper_AIRLIFT() : Wippersnapper() {
-    _ssPin = 10;
-    _ackPin = 7;
-    _rstPin = 5;
-    _gpio0Pin = -1;
-    _wifi = &SPIWIFI;
-    _ssid = 0;
-    _pass = 0;
-    _mqtt_client = new WiFiSSLClient;
-
-    // setup ESP32 co-processor pins during init.
-    WiFi.setPins(_ssPin, _ackPin, _rstPin, _gpio0Pin, _wifi);
-  }
-
-  /**************************************************************************/
-  /*!
-  @brief  Destructor for the Adafruit IO AirLift class.
-  */
-  /**************************************************************************/
-  ~Wippersnapper_AIRLIFT() {
-    if (_mqtt)
-      delete _mqtt;
-  }
-
-  /**********************************************************/
-  /*!
-  @brief  Sets the WiFi client's ssid and password.
-  @param  ssid
-            Wireless network's SSID.
-  @param  ssidPassword
-            Wireless network's password.
-  */
-  /**********************************************************/
-  void set_ssid_pass(const char *ssid, const char *ssidPassword) {
-    _ssid = ssid;
-    _pass = ssidPassword;
-  }
-
-  /**********************************************************/
-  /*!
-  @brief  Sets the WiFi client's ssid and password from the
-            secrets.json provisioning file.
-  */
-  /**********************************************************/
-  void set_ssid_pass() {
-    _ssid = WS._config.network.ssid;
-    _pass = WS._config.network.pass;
-  }
-
-  /***********************************************************/
-  /*!
-  @brief   Performs a scan of local WiFi networks.
-  @returns True if `_network_ssid` is found, False otherwise.
-  */
-  /***********************************************************/
-  bool check_valid_ssid() {
-    // Disconnect WiFi from an AP if it was previously connected
-    WiFi.disconnect();
-    delay(100);
-
-    // Perform a network scan
-    int n = WiFi.scanNetworks();
-    if (n == 0) {
-      WS_DEBUG_PRINTLN("ERROR: No WiFi networks found!");
-      return false;
-    }
-
-    // Was the network within secrets.json found?
-    for (int i = 0; i < n; ++i) {
-      if (strcmp(_ssid, WiFi.SSID(i)) == 0)
-        return true;
-    }
-
-    // User-set network not found, print scan results to serial console
-    WS_DEBUG_PRINTLN("ERROR: Your requested WiFi network was not found!");
-    WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks: ");
-    for (int i = 0; i < n; ++i) {
-      WS_DEBUG_PRINT(WiFi.SSID(i));
-      WS_DEBUG_PRINT(" ");
-      WS_DEBUG_PRINT(WiFi.RSSI(i));
-      WS_DEBUG_PRINTLN("dB");
-    }
-
-    return false;
-  }
-
-  /********************************************************/
-  /*!
-  @brief  Sets the WiFi client.
-  @param  wifi
-          Instance of SPIClass.
-  */
-  /********************************************************/
-  void set_wifi(SPIClass *wifi) {
-    _wifi = wifi;
-    _mqtt_client = new WiFiSSLClient;
-  }
-
-  /********************************************************/
-  /*!
-  @brief  Configures ESP32 "AirLift" pins.
-  @param  ssPin
-            ESP32 S.S. pin.
-  @param  ackPin
-            ESP32 ACK pin.
-  @param  rstPin
-            ESP32 RST pin.
-  @param  gpio0Pin
-            ESP32 GPIO0 pin.
-  */
-  /********************************************************/
-  void set_airlift_pins(int ssPin, int ackPin, int rstPin, int gpio0Pin) {
-    _ssPin = ssPin;
-    _ackPin = ackPin;
-    _rstPin = rstPin;
-    _gpio0Pin = gpio0Pin;
-  }
-
-  /********************************************************/
-  /*!
-  @brief    Checks the version of an ESP32 module running
-            nina-fw.
-  @returns  True if matches min. required to run
-            WipperSnapper, False otherwise.
-  */
-  /********************************************************/
-  bool firmwareCheck() {
-    _fv = WiFi.firmwareVersion();
-    if (_fv < NINAFWVER)
-      return false;
-    return true;
-  }
-
-  /********************************************************/
-  /*!
-  @brief  Gets the ESP32's unique client identifier.
-  @note   For the ESP32, the UID is the MAC address.
-  */
-  /********************************************************/
-  void getMacAddr() {
-    uint8_t mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-    WiFi.macAddress(mac);
-    memcpy(WS._macAddr, mac, sizeof(mac));
-  }
-
-  /********************************************************/
-  /*!
-  @brief  Initializes the MQTT client.
-  @param  clientID
-          MQTT client identifier
-  */
-  /********************************************************/
-  void setupMQTTClient(const char *clientID) {
-    WS._mqtt = new Adafruit_MQTT_Client(
-        _mqtt_client, WS._config.aio_url, WS._config.io_port, clientID,
-        WS._config.aio_user, WS._config.aio_key);
-  }
-
-  /********************************************************/
-  /*!
-  @brief  Returns the network status of an ESP32 module.
-  @return ws_status_t
-  */
-  /********************************************************/
-  ws_status_t networkStatus() {
-    switch (WiFi.status()) {
-    case WL_CONNECTED:
-      return WS_NET_CONNECTED;
-    case WL_CONNECT_FAILED:
-      return WS_NET_CONNECT_FAILED;
-    case WL_IDLE_STATUS:
-      return WS_IDLE;
-    default:
-      return WS_NET_DISCONNECTED;
-    }
-  }
-
-  /*******************************************************************/
-  /*!
-  @brief  Returns the type of network connection used by Wippersnapper
-  @return AIRLIFT
-  */
-  /*******************************************************************/
-  const char *connectionType() { return "AIRLIFT"; }
-
-protected:
-  const char *_ssid;           /*!< Network SSID. */
-  const char *_pass;           /*!< Network password. */
-  String _fv;                  /*!< nina-fw firmware version. */
-  int _ssPin = -1;             /*!< SPI S.S. pin. */
-  int _ackPin = -1;            /*!< SPI ACK pin. */
-  int _rstPin = -1;            /*!< SPI RST pin. */
-  int _gpio0Pin = -1;          /*!< SPI GPIO0 pin, unused. */
-  WiFiSSLClient *_mqtt_client; /*!< Instance of a secure WiFi client. */
-  SPIClass *_wifi; /*!< Instance of the SPI bus used by the AirLift. */
-
-  /**************************************************************************/
-  /*!
-  @brief  Establishes a connection with the wireless network.
-  */
-  /**************************************************************************/
-  void _connect() {
-    if (strlen(_ssid) == 0) {
-      _status = WS_SSID_INVALID;
-    } else {
-
-      // validate co-processor is physically connected connection
-      if (WiFi.status() == WL_NO_MODULE) {
-        WS_DEBUG_PRINT("No ESP32 module detected!");
-        return;
-      }
-
-      // validate co-processor's firmware version
-      if (!firmwareCheck())
-        WS_DEBUG_PRINTLN("Please upgrade the firmware on the ESP module to the "
-                         "latest version.");
-
-      // disconnect from possible previous connection
-      _disconnect();
-
-      // reset the esp32 if possible
-      if (_rstPin != -1) {
-        WS_DEBUG("Resetting ESP32...");
-        pinMode(_rstPin, OUTPUT);
-        digitalWrite(_rstPin, LOW);
-        delay(10);
-        digitalWrite(_rstPin, HIGH);
-        delay(10);
-      }
-      // wait for the ESP32 to boot
-      delay(1000);
-
-      WS_DEBUG_PRINT("Connecting to ");
-      WS_DEBUG_PRINTLN(_ssid);
-      WiFi.begin(_ssid, _pass);
-      _status = WS_NET_DISCONNECTED;
-    }
-  }
-
-  /**************************************************************************/
-  /*!
-      @brief  Disconnects from the wireless network.
-  */
-  /**************************************************************************/
-  void _disconnect() {
-    WiFi.disconnect();
-    delay(500);
-  }
-};
-
+/*!
+ * @file Wippersnapper_AIRLIFT.h
+ *
+ * This is a driver for using the Adafruit AirLift
+ * ESP32 Co-Processor's network interface with Wippersnapper.
+ *
+ * The ESP32 AirLift uses SPI to communicate. Three lines (CS, ACK, RST) are
+ * required to communicate with the ESP32 AirLift.
+ *
+ * Adafruit invests time and resources providing this open source code,
+ * please support Adafruit and open-source hardware by purchasing
+ * products from Adafruit!
+ *
+ * Copyright (c) Brent Rubell 2020-2021 for Adafruit Industries.
+ *
+ * MIT license, all text here must be included in any redistribution.
+ *
+ */
+
+#ifndef WIPPERSNAPPER_AIRLIFT_H
+#define WIPPERSNAPPER_AIRLIFT_H
+
+#include "Adafruit_MQTT.h"
+#include "Adafruit_MQTT_Client.h"
+#include "Arduino.h"
+#include "SPI.h"
+#include "WiFiNINA.h"
+#include "Wippersnapper.h"
+
+#define NINAFWVER                                                              \
+  "1.7.7" /*!< min. nina-fw version compatible with this library. */
+
+#define SPIWIFI SPI /*!< Instance of SPI interface used by an AirLift. */
+
+extern Wippersnapper WS;
+/****************************************************************************/
+/*!
+    @brief  Class for using the AirLift Co-Processor network iface.
+*/
+/****************************************************************************/
+class Wippersnapper_AIRLIFT : public Wippersnapper {
+
+public:
+  /**************************************************************************/
+  /*!
+  @brief  Initializes the Adafruit IO class for AirLift devices.
+  */
+  /**************************************************************************/
+  Wippersnapper_AIRLIFT() : Wippersnapper() {
+    _ssPin = SPIWIFI_SS; // 10;
+    _ackPin = SPIWIFI_ACK; //7;
+    _rstPin = SPIWIFI_RESET; // 5; // should be 7 on PyPortals
+    #ifdef ESP32_GPIO0
+    _gpio0Pin = ESP32_GPIO0;
+    #else
+    _gpio0Pin = -1;
+    #endif
+    _wifi = &SPIWIFI;
+    _ssid = 0;
+    _pass = 0;
+    _mqtt_client = new WiFiSSLClient;
+
+    // setup ESP32 co-processor pins during init.
+    WiFi.setPins(_ssPin, _ackPin, _rstPin, _gpio0Pin, _wifi);
+    delay(1000);
+  }
+
+  /**************************************************************************/
+  /*!
+  @brief  Destructor for the Adafruit IO AirLift class.
+  */
+  /**************************************************************************/
+  ~Wippersnapper_AIRLIFT() {
+    if (_mqtt)
+      delete _mqtt;
+  }
+
+  /**********************************************************/
+  /*!
+  @brief  Sets the WiFi client's ssid and password.
+  @param  ssid
+            Wireless network's SSID.
+  @param  ssidPassword
+            Wireless network's password.
+  */
+  /**********************************************************/
+  void set_ssid_pass(const char *ssid, const char *ssidPassword) {
+    _ssid = ssid;
+    _pass = ssidPassword;
+  }
+
+  /**********************************************************/
+  /*!
+  @brief  Sets the WiFi client's ssid and password from the
+            secrets.json provisioning file.
+  */
+  /**********************************************************/
+  void set_ssid_pass() {
+    _ssid = WS._config.network.ssid;
+    _pass = WS._config.network.pass;
+  }
+
+  /***********************************************************/
+  /*!
+  @brief   Performs a scan of local WiFi networks.
+  @returns True if `_network_ssid` is found, False otherwise.
+  */
+  /***********************************************************/
+  bool check_valid_ssid() {
+    // Disconnect WiFi from an AP if it was previously connected
+    WiFi.disconnect();
+    delay(100);
+
+    // Perform a network scan
+    int n = WiFi.scanNetworks();
+    if (n == 0) {
+      WS_DEBUG_PRINTLN("ERROR: No WiFi networks found!");
+      return false;
+    }
+
+    // Was the network within secrets.json found?
+    for (int i = 0; i < n; ++i) {
+      if (strcmp(_ssid, WiFi.SSID(i)) == 0)
+        return true;
+    }
+
+    // User-set network not found, print scan results to serial console
+    WS_DEBUG_PRINTLN("ERROR: Your requested WiFi network was not found!");
+    WS_DEBUG_PRINTLN("WipperSnapper found these WiFi networks: ");
+    for (int i = 0; i < n; ++i) {
+      WS_DEBUG_PRINT(WiFi.SSID(i));
+      WS_DEBUG_PRINT(" ");
+      WS_DEBUG_PRINT(WiFi.RSSI(i));
+      WS_DEBUG_PRINTLN("dB");
+    }
+
+    return false;
+  }
+
+  /********************************************************/
+  /*!
+  @brief  Sets the WiFi client.
+  @param  wifi
+          Instance of SPIClass.
+  */
+  /********************************************************/
+  void set_wifi(SPIClass *wifi) {
+    _wifi = wifi;
+    _mqtt_client = new WiFiSSLClient;
+  }
+
+  /********************************************************/
+  /*!
+  @brief  Configures ESP32 "AirLift" pins.
+  @param  ssPin
+            ESP32 S.S. pin.
+  @param  ackPin
+            ESP32 ACK pin.
+  @param  rstPin
+            ESP32 RST pin.
+  @param  gpio0Pin
+            ESP32 GPIO0 pin.
+  */
+  /********************************************************/
+  void set_airlift_pins(int ssPin, int ackPin, int rstPin, int gpio0Pin) {
+    _ssPin = ssPin;
+    _ackPin = ackPin;
+    _rstPin = rstPin;
+    _gpio0Pin = gpio0Pin;
+  }
+
+  /********************************************************/
+  /*!
+  @brief    Checks the version of an ESP32 module running
+            nina-fw.
+  @returns  True if matches min. required to run
+            WipperSnapper, False otherwise.
+  */
+  /********************************************************/
+  bool firmwareCheck() {
+    _fv = WiFi.firmwareVersion();
+    return compareVersions(_fv, NINAFWVER);
+  }
+
+  /********************************************************/
+  /*!
+  @brief  Compares two version strings.
+  @param  currentVersion
+          Current version string.
+  @param  requiredVersion
+          Required version string.
+  @returns True if the current version is greater than or
+          equal to the required version, False otherwise.
+  */
+  /********************************************************/
+  bool compareVersions(const String& currentVersion, const String& requiredVersion) {
+      int curMajor, curMinor, curPatch;
+      int reqMajor, reqMinor, reqPatch;
+      int per_major, per_minor, per_patch;
+      
+      sscanf(currentVersion.c_str(), "%d.%d.%d", &curMajor, &curMinor, &curPatch);
+      sscanf(requiredVersion.c_str(), "%d.%d.%d", &reqMajor, &reqMinor, &reqPatch);
+
+      if (curMajor != reqMajor) return curMajor > reqMajor;
+      if (curMinor != reqMinor) return curMinor > reqMinor;
+      return curPatch >= reqPatch;
+  }
+
+  /********************************************************/
+  /*!
+  @brief  Gets the ESP32's unique client identifier.
+  @note   For the ESP32, the UID is the MAC address.
+  */
+  /********************************************************/
+  void getMacAddr() {
+    uint8_t mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    WiFi.macAddress(mac);
+    memcpy(WS._macAddr, mac, sizeof(mac));
+  }
+
+  /********************************************************/
+  /*!
+  @brief  Initializes the MQTT client.
+  @param  clientID
+          MQTT client identifier
+  */
+  /********************************************************/
+  void setupMQTTClient(const char *clientID) {
+    WS._mqtt = new Adafruit_MQTT_Client(
+        _mqtt_client, WS._config.aio_url, WS._config.io_port, clientID,
+        WS._config.aio_user, WS._config.aio_key);
+  }
+
+  /********************************************************/
+  /*!
+  @brief  Returns the network status of an ESP32 module.
+  @return ws_status_t
+  */
+  /********************************************************/
+  ws_status_t networkStatus() {
+    switch (WiFi.status()) {
+    case WL_CONNECTED:
+      return WS_NET_CONNECTED;
+    case WL_CONNECT_FAILED:
+      return WS_NET_CONNECT_FAILED;
+    case WL_IDLE_STATUS:
+      return WS_IDLE;
+    default:
+      return WS_NET_DISCONNECTED;
+    }
+  }
+
+  /*******************************************************************/
+  /*!
+  @brief  Returns the type of network connection used by Wippersnapper
+  @return AIRLIFT
+  */
+  /*******************************************************************/
+  const char *connectionType() { return "AIRLIFT"; }
+
+protected:
+  const char *_ssid;           /*!< Network SSID. */
+  const char *_pass;           /*!< Network password. */
+  String _fv;                  /*!< nina-fw firmware version. */
+  int _ssPin = -1;             /*!< SPI S.S. pin. */
+  int _ackPin = -1;            /*!< SPI ACK pin. */
+  int _rstPin = -1;            /*!< SPI RST pin. */
+  int _gpio0Pin = -1;          /*!< SPI GPIO0 pin, unused. */
+  WiFiSSLClient *_mqtt_client; /*!< Instance of a secure WiFi client. */
+  SPIClass *_wifi; /*!< Instance of the SPI bus used by the AirLift. */
+
+  /**************************************************************************/
+  /*!
+  @brief  Establishes a connection with the wireless network.
+  */
+  /**************************************************************************/
+  void _connect() {
+    if (strlen(_ssid) == 0) {
+      _status = WS_SSID_INVALID;  // possibly unneccesary  as already checking elsewhere
+    } else {
+      // disconnect from possible previous connection
+      _disconnect();
+      feedWDT();
+      WS_DEBUG_PRINT("Reset Pin: ");
+      WS_DEBUG_PRINTLN(_rstPin);
+      // reset the esp32 if possible
+      if (_rstPin != -1) {
+        WS_DEBUG_PRINTLN("Resetting ESP32...");
+        WS_PRINTER.flush();
+        // Chip select for esp32
+        pinMode(_ssPin, OUTPUT);
+        digitalWrite(_ssPin, HIGH);
+        // Do we need to set SS low again?
+        if (_gpio0Pin != -1) {
+          pinMode(_gpio0Pin, OUTPUT);
+          digitalWrite(_gpio0Pin, LOW);
+        }
+        pinMode(_rstPin, OUTPUT);
+        digitalWrite(_rstPin, LOW);
+        delay(50);
+        digitalWrite(_rstPin, HIGH);
+        delay(10);
+        if (_gpio0Pin != -1) {
+          pinMode(_gpio0Pin, INPUT);
+        }
+        // wait for the ESP32 to boot
+        delay(1000);
+      }
+      feedWDT();
+      // WS_DEBUG_PRINT("ESP32 booted, version: ");
+      // WS_PRINTER.flush();
+      // WS_DEBUG_PRINTLN(WiFi.firmwareVersion());
+      // WS_PRINTER.flush();
+      // feedWDT();
+
+      // // validate co-processor is physically connected connection
+      // if (WiFi.status() == WL_NO_MODULE) {
+      //   WS_DEBUG_PRINT("No ESP32 module detected!");
+      //   WS_DEBUG_PRINT("Current Module Status:");
+      //   WS_DEBUG_PRINTLN(WiFi.status());
+      //   return;
+      // }
+
+      // validate co-processor's firmware version
+      if (!firmwareCheck())
+        WS_DEBUG_PRINTLN("Please upgrade the firmware on the ESP module to the "
+                         "latest version.");
+
+      WS_DEBUG_PRINT("Connecting to ");
+      WS_DEBUG_PRINTLN(_ssid);
+      WS_PRINTER.flush();
+      feedWDT();
+      WiFi.begin(_ssid, _pass);
+      _status = WS_NET_DISCONNECTED;
+      feedWDT();
+      delay(5000);
+      feedWDT();
+      delay(5000);
+      feedWDT();
+    }
+  }
+
+  /**************************************************************************/
+  /*!
+      @brief  Disconnects from the wireless network.
+  */
+  /**************************************************************************/
+  void _disconnect() {
+    WiFi.disconnect();
+    delay(500);
+  }
+};
+
 #endif // WIPPERSNAPPER_AIRLIFT_H
\ No newline at end of file
diff --git a/src/network_interfaces/Wippersnapper_WIFININA.h b/src/network_interfaces/Wippersnapper_WIFININA.h
index 87de9087f..d7cd2eec8 100644
--- a/src/network_interfaces/Wippersnapper_WIFININA.h
+++ b/src/network_interfaces/Wippersnapper_WIFININA.h
@@ -268,21 +268,28 @@ class Wippersnapper_WIFININA : public Wippersnapper {
 #if defined(NINA_RESET_PIN)
       // reset the esp32 if possible, better if we didn't do this first time
       if (NINA_RESET_PIN != -1) {
-        WS_DEBUG("Resetting ESP32...");
+        WS_DEBUG_PRINTLN("Resetting ESP32...");
+        WS_PRINTER.flush();
         pinMode(NINA_RESET_PIN, OUTPUT);
         digitalWrite(NINA_RESET_PIN, LOW);
-        delay(10);
+        delay(50);
         digitalWrite(NINA_RESET_PIN, HIGH);
         delay(10);
       }
       // wait for the ESP32 to boot
-      delay(1000);
+      delay(2000);
+      feedWDT();
 #endif
 
       WS_DEBUG_PRINT("Connecting to ");
       WS_DEBUG_PRINTLN(_ssid);
       WiFi.begin(_ssid, _pass);
       _status = WS_NET_DISCONNECTED;
+      feedWDT();
+      delay(5000);
+      feedWDT();
+      delay(5000);
+      feedWDT();
     }
   }
 
diff --git a/src/network_interfaces/ws_networking_pico.h b/src/network_interfaces/ws_networking_pico.h
index c2a8d556c..c9733368e 100644
--- a/src/network_interfaces/ws_networking_pico.h
+++ b/src/network_interfaces/ws_networking_pico.h
@@ -282,6 +282,9 @@ class ws_networking_pico : public Wippersnapper {
         }
       }
       _status = WS_NET_DISCONNECTED;
+      WS.feedWDT();
+      delay(5000);
+      WS.feedWDT();
     }
   }
 

From 4f672fccb41a80d85efa37390f6e35c4bf1c15c4 Mon Sep 17 00:00:00 2001
From: tyeth <tyethgundry@googlemail.com>
Date: Thu, 30 May 2024 18:16:21 +0100
Subject: [PATCH 4/9] WIP: end wifi object before restart

---
 src/network_interfaces/Wippersnapper_AIRLIFT.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h
index d9a4fc422..458a40750 100644
--- a/src/network_interfaces/Wippersnapper_AIRLIFT.h
+++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h
@@ -280,6 +280,7 @@ class Wippersnapper_AIRLIFT : public Wippersnapper {
     } else {
       // disconnect from possible previous connection
       _disconnect();
+      WiFi.end();
       feedWDT();
       WS_DEBUG_PRINT("Reset Pin: ");
       WS_DEBUG_PRINTLN(_rstPin);
@@ -289,8 +290,7 @@ class Wippersnapper_AIRLIFT : public Wippersnapper {
         WS_PRINTER.flush();
         // Chip select for esp32
         pinMode(_ssPin, OUTPUT);
-        digitalWrite(_ssPin, HIGH);
-        // Do we need to set SS low again?
+        digitalWrite(_ssPin, HIGH); // Do we need to set SS low again?
         if (_gpio0Pin != -1) {
           pinMode(_gpio0Pin, OUTPUT);
           digitalWrite(_gpio0Pin, LOW);
@@ -304,7 +304,7 @@ class Wippersnapper_AIRLIFT : public Wippersnapper {
           pinMode(_gpio0Pin, INPUT);
         }
         // wait for the ESP32 to boot
-        delay(1000);
+        delay(2000);
       }
       feedWDT();
       // WS_DEBUG_PRINT("ESP32 booted, version: ");

From 1cb35d3cabe803e4fbeb67a8e947357d3cbc3343 Mon Sep 17 00:00:00 2001
From: tyeth <tyethgundry@googlemail.com>
Date: Thu, 30 May 2024 18:50:14 +0100
Subject: [PATCH 5/9] WIP: restart SPI after wifi end/deinit

---
 src/network_interfaces/Wippersnapper_AIRLIFT.h | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h
index 458a40750..8a8a2727f 100644
--- a/src/network_interfaces/Wippersnapper_AIRLIFT.h
+++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h
@@ -280,7 +280,10 @@ class Wippersnapper_AIRLIFT : public Wippersnapper {
     } else {
       // disconnect from possible previous connection
       _disconnect();
+      delay(100);
       WiFi.end();
+      delay(100);
+      _wifi->begin();
       feedWDT();
       WS_DEBUG_PRINT("Reset Pin: ");
       WS_DEBUG_PRINTLN(_rstPin);
@@ -307,11 +310,11 @@ class Wippersnapper_AIRLIFT : public Wippersnapper {
         delay(2000);
       }
       feedWDT();
-      // WS_DEBUG_PRINT("ESP32 booted, version: ");
-      // WS_PRINTER.flush();
-      // WS_DEBUG_PRINTLN(WiFi.firmwareVersion());
-      // WS_PRINTER.flush();
-      // feedWDT();
+      WS_DEBUG_PRINT("ESP32 booted, version: ");
+      WS_PRINTER.flush();
+      WS_DEBUG_PRINTLN(WiFi.firmwareVersion());
+      WS_PRINTER.flush();
+      feedWDT();
 
       // // validate co-processor is physically connected connection
       // if (WiFi.status() == WL_NO_MODULE) {

From 2d83f9c9aef84dab179c6e46f59d097e5d4ee312 Mon Sep 17 00:00:00 2001
From: tyeth <tyethgundry@googlemail.com>
Date: Thu, 30 May 2024 19:29:57 +0100
Subject: [PATCH 6/9] WIP: end and begin SPIWIFI

---
 src/network_interfaces/Wippersnapper_AIRLIFT.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h
index 8a8a2727f..45b3e2705 100644
--- a/src/network_interfaces/Wippersnapper_AIRLIFT.h
+++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h
@@ -282,6 +282,7 @@ class Wippersnapper_AIRLIFT : public Wippersnapper {
       _disconnect();
       delay(100);
       WiFi.end();
+      _wifi->end();
       delay(100);
       _wifi->begin();
       feedWDT();

From b042c258077fc2ba0ae80d4a7e69e8d6a2fce8c0 Mon Sep 17 00:00:00 2001
From: tyeth <tyethgundry@googlemail.com>
Date: Thu, 30 May 2024 20:51:54 +0100
Subject: [PATCH 7/9] clang format

---
 .../Wippersnapper_AIRLIFT.h                   | 39 +++++++++++--------
 1 file changed, 22 insertions(+), 17 deletions(-)

diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h
index 45b3e2705..5e95eb0a8 100644
--- a/src/network_interfaces/Wippersnapper_AIRLIFT.h
+++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h
@@ -47,14 +47,14 @@ class Wippersnapper_AIRLIFT : public Wippersnapper {
   */
   /**************************************************************************/
   Wippersnapper_AIRLIFT() : Wippersnapper() {
-    _ssPin = SPIWIFI_SS; // 10;
-    _ackPin = SPIWIFI_ACK; //7;
+    _ssPin = SPIWIFI_SS;     // 10;
+    _ackPin = SPIWIFI_ACK;   // 7;
     _rstPin = SPIWIFI_RESET; // 5; // should be 7 on PyPortals
-    #ifdef ESP32_GPIO0
+#ifdef ESP32_GPIO0
     _gpio0Pin = ESP32_GPIO0;
-    #else
+#else
     _gpio0Pin = -1;
-    #endif
+#endif
     _wifi = &SPIWIFI;
     _ssid = 0;
     _pass = 0;
@@ -193,17 +193,21 @@ class Wippersnapper_AIRLIFT : public Wippersnapper {
           equal to the required version, False otherwise.
   */
   /********************************************************/
-  bool compareVersions(const String& currentVersion, const String& requiredVersion) {
-      int curMajor, curMinor, curPatch;
-      int reqMajor, reqMinor, reqPatch;
-      int per_major, per_minor, per_patch;
-      
-      sscanf(currentVersion.c_str(), "%d.%d.%d", &curMajor, &curMinor, &curPatch);
-      sscanf(requiredVersion.c_str(), "%d.%d.%d", &reqMajor, &reqMinor, &reqPatch);
-
-      if (curMajor != reqMajor) return curMajor > reqMajor;
-      if (curMinor != reqMinor) return curMinor > reqMinor;
-      return curPatch >= reqPatch;
+  bool compareVersions(const String &currentVersion,
+                       const String &requiredVersion) {
+    int curMajor, curMinor, curPatch;
+    int reqMajor, reqMinor, reqPatch;
+    int per_major, per_minor, per_patch;
+
+    sscanf(currentVersion.c_str(), "%d.%d.%d", &curMajor, &curMinor, &curPatch);
+    sscanf(requiredVersion.c_str(), "%d.%d.%d", &reqMajor, &reqMinor,
+           &reqPatch);
+
+    if (curMajor != reqMajor)
+      return curMajor > reqMajor;
+    if (curMinor != reqMinor)
+      return curMinor > reqMinor;
+    return curPatch >= reqPatch;
   }
 
   /********************************************************/
@@ -276,7 +280,8 @@ class Wippersnapper_AIRLIFT : public Wippersnapper {
   /**************************************************************************/
   void _connect() {
     if (strlen(_ssid) == 0) {
-      _status = WS_SSID_INVALID;  // possibly unneccesary  as already checking elsewhere
+      _status = WS_SSID_INVALID; // possibly unneccesary  as already checking
+                                 // elsewhere
     } else {
       // disconnect from possible previous connection
       _disconnect();

From a6f50ce7db733389c0104f37dca78a67ac734f9e Mon Sep 17 00:00:00 2001
From: tyeth <tyethgundry@googlemail.com>
Date: Thu, 30 May 2024 23:55:35 +0100
Subject: [PATCH 8/9] Fix: runNetFSM if ping fails

---
 src/Wippersnapper.cpp | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/Wippersnapper.cpp b/src/Wippersnapper.cpp
index d702b9158..fa67eb152 100644
--- a/src/Wippersnapper.cpp
+++ b/src/Wippersnapper.cpp
@@ -2527,9 +2527,15 @@ void Wippersnapper::pingBroker() {
   // ping within keepalive-10% to keep connection open
   if (millis() > (_prv_ping + (WS_KEEPALIVE_INTERVAL_MS -
                                (WS_KEEPALIVE_INTERVAL_MS * 0.10)))) {
-    WS_DEBUG_PRINTLN("PING!");
+    WS_DEBUG_PRINT("PING!");
     // TODO: Add back, is crashing currently
-    WS._mqtt->ping();
+    if (WS._mqtt->ping()) {
+      WS_DEBUG_PRINTLN("SUCCESS!");
+    } else {
+      WS_DEBUG_PRINTLN("FAILURE! Running network FSM...");
+      WS._mqtt->disconnect();
+      runNetFSM();
+    }
     _prv_ping = millis();
   }
   // blink status LED every STATUS_LED_KAT_BLINK_TIME millis

From d2cb47d52f391bb18c926adbd9e86bd5d435076a Mon Sep 17 00:00:00 2001
From: tyeth <tyethgundry@googlemail.com>
Date: Fri, 14 Jun 2024 09:19:08 +0100
Subject: [PATCH 9/9] Relabel PING to MQTT PING

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

diff --git a/src/Wippersnapper.cpp b/src/Wippersnapper.cpp
index fa67eb152..45331083f 100644
--- a/src/Wippersnapper.cpp
+++ b/src/Wippersnapper.cpp
@@ -2527,7 +2527,7 @@ void Wippersnapper::pingBroker() {
   // ping within keepalive-10% to keep connection open
   if (millis() > (_prv_ping + (WS_KEEPALIVE_INTERVAL_MS -
                                (WS_KEEPALIVE_INTERVAL_MS * 0.10)))) {
-    WS_DEBUG_PRINT("PING!");
+    WS_DEBUG_PRINT("MQTT PING: ");
     // TODO: Add back, is crashing currently
     if (WS._mqtt->ping()) {
       WS_DEBUG_PRINTLN("SUCCESS!");