The Proper Way to Reconnect to WiFi?

antonyc
Posts: 20
Joined: Thu Dec 07, 2017 12:24 pm

The Proper Way to Reconnect to WiFi?

Postby antonyc » Thu Dec 07, 2017 1:02 pm

Hi everyone,

What is the proper way to reconnect to WiFi?

Let's say I've connected to my router, then the router is restarted. How should I handle this from within the ESP32?

Thanks, Antony

urs_eppenberger
Posts: 1
Joined: Sat Dec 16, 2017 4:57 pm

Re: The Proper Way to Reconnect to WiFi?

Postby urs_eppenberger » Sat Dec 16, 2017 5:02 pm

Hello Antony

I have unreliable WiFi and esp32 (and esp8266) based devices. They use MQTT to send their data to a server.
What I wanted to avoid is, that the device hangs or even crashes, when it looses the MQTT connection or even completely loose WiFi.
I wrote a code framework, where in the main loop there are three section.
In the first section, there is the code for the setup of the connections
In the second section, this is only executed, if WiFi and MQTT are up and running
In the third section, this is always executed, regardless of WiFi or MQTT status

Please have a look. Maybe you can use the concept for your own code.

Kind regards,

Urs.

Code: Select all

/* standalone device with MQTT function
 *  
 * The requirements are as follows:
 * - it should run its main tasks regardless of the availability of WiFi/MQTT
 * - it should recover lost WiFi/MQTT without interrupting the main tasks
 * - unless other examples on the net it does not start the WiFi/MQTT connection in the setup() section
 *   since the device should start with its main functions immediately and do the connection setup later
 * 
 * Other features
 * - OTA over WiFi using the Arduino IDE
 * - MQTT over TLS for encrypted data transfer
 * - configuration parameters for device name, SSID, WAP-PW, MQTT broker username and password are stored in Preferences
 * 
 *  Feedback to improve this code is welcome
 *  Urs Eppenberger
 */

#include <WiFiClientSecure.h>                // needed for the WiFi communication
#include <ESPmDNS.h>                         // for FOTA Suport
#include <WiFiUdp.h>                         //    ditto
#include <ArduinoOTA.h>                      //    ditto
#include <MQTTClient.h>                      // MQTT Client from Joël Gaehwiler https://github.com/256dpi/arduino-mqtt   keepalive manually to 15s

const char* Hostname = "sensor1";            // change according your setup : it is used in OTA and as MQTT identifier
String WiFi_SSID = "yourssid";               // change according your setup : SSID and password for the WiFi network
String WiFi_PW = "wifipw";                   //    "
const char* OTA_PW = "otapw";                // change according your setup : password for 'over the air sw update'
String mqtt_broker = "192.168.12.1";         // change according your setup : IP Adress or FQDN of your MQTT broker
String mqtt_user = "mqttuser";               // change according your setup : username and password for authenticated broker access
String mqtt_pw = "mqttpw";                   //    "
String input_topic = "input/sensor1";        // change according your setup : MQTT topic for messages from device to broker
unsigned long waitCount = 0;                 // counter
uint8_t conn_stat = 0;                       // Connection status for WiFi and MQTT:
                                             //
                                             // status |   WiFi   |    MQTT
                                             // -------+----------+------------
                                             //      0 |   down   |    down
                                             //      1 | starting |    down
                                             //      2 |    up    |    down
                                             //      3 |    up    |  starting
                                             //      4 |    up    | finalising
                                             //      5 |    up    |     up

unsigned long lastStatus = 0;                // counter in example code for conn_stat == 5
unsigned long lastTask = 0;                  // counter in example code for conn_stat <> 5

const char* Version = "{\"Version\":\"low_prio_wifi_v2\"}";
const char* Status = "{\"Message\":\"up and running\"}";

WiFiClientSecure TCP;                        // TCP client object, uses SSL/TLS
MQTTClient mqttClient(512);                  // MQTT client object with a buffer size of 512 (depends on your message size)


void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);                                            // config WiFi as client
  MDNS.begin(Hostname);                                           // start MDNS, needed for OTA
  ArduinoOTA.setHostname(Hostname);                               // initialize and start OTA
  ArduinoOTA.setPassword(OTA_PW);                                 //       set OTA password
  ArduinoOTA.onError([](ota_error_t error) {ESP.restart();});     //       restart in case of an error during OTA
  ArduinoOTA.begin();                                             //       at this point OTA is set up
}


void loop() {                                                     // with current code runs roughly 400 times per second
// start of non-blocking connection setup section
  if ((WiFi.status() != WL_CONNECTED) && (conn_stat != 1)) { conn_stat = 0; }
  if ((WiFi.status() == WL_CONNECTED) && !mqttClient.connected() && (conn_stat != 3))  { conn_stat = 2; }
  if ((WiFi.status() == WL_CONNECTED) && mqttClient.connected() && (conn_stat != 5)) { conn_stat = 4;}
  switch (conn_stat) {
    case 0:                                                       // MQTT and WiFi down: start WiFi
      Serial.println("MQTT and WiFi down: start WiFi");
      WiFi.begin(WiFi_SSID.c_str(), WiFi_PW.c_str());
      conn_stat = 1;
      break;
    case 1:                                                       // WiFi starting, do nothing here
      Serial.println("WiFi starting, wait : "+ String(waitCount));
      waitCount++;
      break;
    case 2:                                                       // WiFi up, MQTT down: start MQTT
      Serial.println("WiFi up, MQTT down: start MQTT");
      mqttClient.begin(mqtt_broker.c_str(), 8883, TCP);           //   config MQTT Server, use port 8883 for secure connection
      mqttClient.connect(Hostname, mqtt_user.c_str(), mqtt_pw.c_str());
      conn_stat = 3;
      waitCount = 0;
      break;
    case 3:                                                       // WiFi up, MQTT starting, do nothing here
      Serial.println("WiFi up, MQTT starting, wait : "+ String(waitCount));
      waitCount++;
      break;
    case 4:                                                       // WiFi up, MQTT up: finish MQTT configuration
      Serial.println("WiFi up, MQTT up: finish MQTT configuration");
      //mqttClient.subscribe(output_topic);
      mqttClient.publish(input_topic, Version);
      conn_stat = 5;                    
      break;
  }
// end of non-blocking connection setup section

// start section with tasks where WiFi/MQTT is required
  if (conn_stat == 5) {
    if (millis() - lastStatus > 10000) {                            // Start send status every 10 sec (just as an example)
      Serial.println(Status);
      mqttClient.publish(input_topic, Status);                      //      send status to broker
      mqttClient.loop();                                            //      give control to MQTT to send message to broker
      lastStatus = millis();                                        //      remember time of last sent status message
    }
    ArduinoOTA.handle();                                            // internal household function for OTA
    mqttClient.loop();                                              // internal household function for MQTT
  } 
// end of section for tasks where WiFi/MQTT are required

// start section for tasks which should run regardless of WiFi/MQTT
  if (millis() - lastTask > 1000) {                                 // Print message every second (just as an example)
    Serial.println("print this every second");
    lastTask = millis();
  }
  delay(100);
// end of section for tasks which should run regardless of WiFi/MQTT
}

abqmichaelj
Posts: 2
Joined: Sat Jun 08, 2019 3:46 pm

Re: The Proper Way to Reconnect to WiFi?

Postby abqmichaelj » Tue Jan 28, 2020 3:58 pm

This is a very sound algorithm. I successfully converted it to using PubSubClient and it works well there as well.

Okneib
Posts: 1
Joined: Mon Apr 20, 2020 9:43 pm

Re: The Proper Way to Reconnect to WiFi?

Postby Okneib » Mon Apr 20, 2020 9:47 pm

Would you mind sharing your converted code for pubsubclient? I'm new to this and using also pubsubclient.

Regards,
Oscar

LagomBra
Posts: 4
Joined: Fri Jun 12, 2020 7:17 pm

Re: The Proper Way to Reconnect to WiFi?

Postby LagomBra » Tue Jun 16, 2020 8:48 am

To reliably (re)connect WLAN and MQTT services after power or router outage, I use this state machine function void connectToWLANAndMQTT(). It works well on my end. I am using a Metro Mini with an external AirLift FeatherWing. Here is a complete example that publishes dummy data to a test feed.

Code: Select all

#include <SPI.h>

#include <WiFiNINA.h> // Adafruit's WiFiNINA fork, use version 1.4.0
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"

// Variables that remain constant
#define SPIWIFI SPI // SPI port
#define SPIWIFI_SS 10 // AirLift ESP32 chip select pin
#define ESP32_RESET 4 // AirLift ESP32 reset pin
#define SPIWIFI_ACK 3 // AirLift ESP32 ready pin
#define ESP32_GPIO0 -1 // AirLift ESP32 pin not used

#define WLAN_SSID "#"
#define WLAN_PASS "#"
//#define WLAN_SSID "Smartphone" // Alternative router
//#define WLAN_PASS "qvd75ogz97gouo7l" // Alternative router
#define AIO_SERVER "io.adafruit.com" // MQTT broker/server host
#define AIO_SERVERPORT 8883 // Secure port, 1883 insecure port
#define AIO_USERNAME "#"
#define AIO_KEY "#"

const int intervalWLAN = 11000; // WLAN (re-)connection interval 11s, depends on router
const int intervalMQTT = 3000; // MQTT (re-)connection interval 3s

enum : byte {
  WLAN_DOWN_MQTT_DOWN,
  WLAN_STARTING_MQTT_DOWN,
  WLAN_UP_MQTT_DOWN,
  WLAN_UP_MQTT_STARTING,
  WLAN_UP_MQTT_UP
} connectionState;

// Variables that can change
unsigned long timeNowWLAN = 0; // Timestamp that updates each loop() iteration
unsigned long timeNowMQTT = 0; // Timestamp that updates each loop() iteration

bool AIOconnected = false; // Flag to enable publish and subscribe

// Instances an object from the WiFiNINA library to connect and
// transfer data with SSL/TLS support
WiFiSSLClient client;

// Instances a client object from the MQTT_Client library with the
// WLAN client, MQTT server, port and login credentials
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);

// Instance publishing objects from the MQTT_Client library; a feed
// is an Adafruit IO specific MQTT topic
Adafruit_MQTT_Publish mytestfeed = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/Mytestfeed");

void setup()
{
  // Serial monitor printing is only needed for debugging
  Serial.begin(9600);
  while (!Serial); Serial.println();

  // Override default pins with the AirLift's breakout board pins
  WiFi.setPins(SPIWIFI_SS, SPIWIFI_ACK, ESP32_RESET, ESP32_GPIO0, &SPIWIFI);
}

void loop()
{
  // A call to this function (re)connects to the WLAN router and MQTT broker
  connectToWLANAndMQTT();

  if (AIOconnected)
  {
    // Dummy data for testing AIO feed and dashboard
    mytestfeed.publish(random(1024));
    Serial.println("Data published to AIO");

    // To avoid hitting AIO rate limit; later better publish with millis() timer
    // to not block the program
    delay(5000);
  }
}

void connectToWLANAndMQTT()
{

  static byte connectionState = WLAN_DOWN_MQTT_DOWN;

  switch (connectionState)
  {
    case WLAN_DOWN_MQTT_DOWN:
      if (WiFi.status() != WL_CONNECTED)
      {
        Serial.println("(Re)start WLAN connection");
        WiFi.setLEDs(0, 255, 0); // Red = no connection to anything
        WiFi.begin(WLAN_SSID, WLAN_PASS);
        timeNowWLAN = millis();
        connectionState = WLAN_STARTING_MQTT_DOWN;
      }
      break;

    case WLAN_STARTING_MQTT_DOWN:
      if (millis() - timeNowWLAN >= intervalWLAN)
      {
        Serial.println("Wait for WLAN connection");
        if (WiFi.status() == WL_CONNECTED)
        {
          connectionState = WLAN_UP_MQTT_DOWN;
        }
        else
        {
          Serial.println("Retry WLAN connection");
          WiFi.disconnect();
          connectionState = WLAN_DOWN_MQTT_DOWN;
        }
      }
      break;

    case WLAN_UP_MQTT_DOWN:
      if ((WiFi.status() == WL_CONNECTED) && !mqtt.connected())
      {
        Serial.println("WLAN connected. Start MQTT connection");
        WiFi.setLEDs(160, 255, 0); // Yellow = connection to router but not MQTT/AIO (= Internet)
        printWLANStatus();
        timeNowMQTT = millis();
        connectionState = WLAN_UP_MQTT_STARTING;
      }
      break;

    case WLAN_UP_MQTT_STARTING:
      if (millis() - timeNowMQTT >= intervalMQTT)
      {
        Serial.println("WLAN connected. Wait for MQTT connection");
        if (mqtt.connect() == 0)
        {
          connectionState = WLAN_UP_MQTT_UP;
        }
        else
        {
          Serial.println("Retry MQTT connection");
          connectionState = WLAN_UP_MQTT_DOWN;
        }
      }
      break;

    case 4:
      Serial.println("WLAN and MQTT connected");
      WiFi.setLEDs(255, 0, 0); // Green = connection to router and MQTT/AIO (= Internet)
      AIOconnected = true;
      break;
  }
}

void printWLANStatus()
{
  // Print the ESP32's MAC address
  byte mac[6];
  WiFi.macAddress(mac);
  Serial.print("MAC address: ");

  // A call to this function fetches the MAC address
  printMacAddress(mac);

  // Print the SSID of the WLAN network connected to
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // Print the ESP32's IP address assigned by the router
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // Print the router's subnet mask, usually 255.255.255.0
  Serial.print("Subnet mask: ");
  Serial.println((IPAddress)WiFi.subnetMask());

  // Print the rounter's IP address
  Serial.print("Gateway IP: ");
  Serial.println((IPAddress)WiFi.gatewayIP());

  // Print the WLAN router's signal strength received
  long rssi = WiFi.RSSI();
  Serial.print("Signal strength (RSSI): ");
  Serial.print(rssi);
  Serial.println(" dBm");
}

void printMacAddress(byte mac[]) {
  for (int i = 5; i >= 0; i--) {
    if (mac[i] < 16) {
      Serial.print("0");
    }
    Serial.print(mac[i], HEX);
    if (i > 0) {
      Serial.print(":");
    }
  }
  Serial.println();
}

idahowalker
Posts: 148
Joined: Wed Aug 01, 2018 12:06 pm

Re: The Proper Way to Reconnect to WiFi?

Postby idahowalker » Tue Jun 16, 2020 2:16 pm

I use the MQTT keep alive to detect a lack of network connection with MQTT.

Code: Select all

#include <WiFi.h>
#include <PubSubClient.h>
#include "certs.h"
#include "sdkconfig.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/event_groups.h"
#include "esp_sleep.h"
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1351.h>
#include "esp32-hal-psram.h"
#include "esp_himem.h"
//
//
hw_timer_t * timer = NULL;
EventGroupHandle_t eg;
#define evtDoDisplay ( 1 << 1 )
#define evtCollectHistory ( 1 << 2 )
#define evtParseMQTT ( 1 << 5 )
#define evtDoTheBME280Thing ( 1 << 8 )
#define evtDoTheHumidityThing ( 1 << 9 )
#define evtDoTheSunLampThing ( 1 << 10 )
#define evtGroupBits ( evtCollectHistory | evtDoTheBME280Thing | evtDoTheHumidityThing | evtDoTheSunLampThing )
//
Adafruit_SSD1351 tft = Adafruit_SSD1351( 128, 128, GPIO_NUM_27, GPIO_NUM_12, GPIO_NUM_13, GPIO_NUM_14, GPIO_NUM_26 );
////
Adafruit_BME280 bme( GPIO_NUM_5 ); // hardware SPI
////
WiFiClient wifiClient;
PubSubClient MQTTclient( mqtt_server, mqtt_port, wifiClient );
////
// memory pointers for variables stored in himem, WROVER PSRAM.
/*
   ePtr = envirtonemtal pointers to float values
   [0] = inside temperature
   [1] = inside humidity
   {2} = inside pressure
   [3] = outside temperature
   [4] = outside humidity
   [5] = outside pressure
   [6] = outside AQ index
*/
float *ePtr;
float *oPressureHistoryPtr;
float *oIAQ_HistoryPtr;
float *oHumidityHistoryPtr;
float *oTemperaturePtr;
////
char *strPayloadPtr;
char *str_eTopicPtr;
////
int *ColorPalettePtr;
/*
   int data pointer, used to store globals
   IntDataPtr[0] int DataCells = 50
   IntDataPtr[1] int arrayCellPtr = 0;
   IntDataPtr[2] int humidity set point
   IntDataPtr[3] humidity control enable
   IntDataPtr[4] = sunlamp on
   IntDataPtr[5] = sun lamp enable
*/
int *IntDataPtr;
////
// RTC_DATA_ATTR int bootCount = 0; // assign a memory location in RTC FAST RAM, an experiment remnant
////
SemaphoreHandle_t sema_HistoryCompleted;
SemaphoreHandle_t sema_MQTT_Parser;;
SemaphoreHandle_t sema_TheHumidityThing;
SemaphoreHandle_t sema_DoTheSunLampTHing;
////
volatile int iDoTheBME280Thing = 0;
////
void IRAM_ATTR onTimer()
{
  BaseType_t xHigherPriorityTaskWoken;
  iDoTheBME280Thing++;
  if ( iDoTheBME280Thing == 60000 )
  {
    xEventGroupSetBitsFromISR( eg, evtGroupBits, &xHigherPriorityTaskWoken );
    iDoTheBME280Thing = 0;
  }
}
////
void setup()
{
  pinMode( GPIO_NUM_0, OUTPUT );
  digitalWrite( GPIO_NUM_0, HIGH );
  pinMode( GPIO_NUM_2, OUTPUT );
  digitalWrite( GPIO_NUM_2, LOW );
  pinMode( GPIO_NUM_15, OUTPUT );
  digitalWrite( GPIO_NUM_15, LOW );
  /* Use 4th timer of 4.
    1 tick 1/(80MHZ/80) = 1us set divider 80 and count up.
    Attach onTimer function to timer
    Set alarm to call timer ISR, every 1000uS and repeat / reset ISR (true) after each alarm
    Start an timer alarm
  */
  timer = timerBegin( 3, 80, true );
  timerAttachInterrupt( timer, &onTimer, true );
  timerAlarmWrite(timer, 1000, true);
  timerAlarmEnable(timer);
  // messageTemp.reserve( 300 );
  // memory allocations for items to be stored in PSRAM
  log_i("before Free PSRAM: %d", ESP.getFreePsram());
  IntDataPtr = (int*)ps_calloc( 15, sizeof(int) );
  IntDataPtr[0] = 50; //const int DataCells = 50;
  oPressureHistoryPtr = (float*)ps_calloc( IntDataPtr[0], sizeof(float) );// oPressureHistoryPtr = (float*)ps_calloc( DataCells, sizeof(float) );
  oIAQ_HistoryPtr = (float*)ps_calloc( IntDataPtr[0], sizeof(float) );//oIAQ_HistoryPtr = (float*)ps_calloc( DataCells, sizeof(float) );
  oHumidityHistoryPtr = (float*)ps_calloc( IntDataPtr[0], sizeof(float) );
  oTemperaturePtr = (float*)ps_calloc( IntDataPtr[0], sizeof(float) );
  ePtr =  (float*)ps_calloc( 15, sizeof(float) );
  strPayloadPtr = (char *)ps_calloc(300, sizeof(char) );
  str_eTopicPtr = (char *)ps_calloc(300, sizeof(char) );
  ColorPalettePtr  = (int*)ps_calloc( 10, sizeof(int) );
  log_i("Total heap: %d", ESP.getHeapSize());
  log_i("Free heap: %d", ESP.getFreeHeap());
  log_i("Total PSRAM: %d", ESP.getPsramSize());
  log_i("Free PSRAM: %d", ESP.getFreePsram());
  // popuate color palette for display use
  ColorPalettePtr[0] = 0x0000; //BLACK
  ColorPalettePtr[1] = 0x001F; //BLUE
  ColorPalettePtr[2] = 0xF800; //RED
  ColorPalettePtr[3] = 0x07E0; //GREEN
  ColorPalettePtr[4] = 0x07FF; //CYAN
  ColorPalettePtr[5] = 0xF81F; //MAGENTA
  ColorPalettePtr[6] = 0xFFE0; //YELLOW
  ColorPalettePtr[7] = 0xFFFF; //WHITE
  ColorPalettePtr[8] = 0xFFFFD8; //LIGHTYELLOW
  ColorPalettePtr[9] = 0xFF8040; //BROWN
  //
  ePtr[2] = 50; // set a default humidity level
  ePtr[3] = 0; // set humidity control to off
  //
  eg = xEventGroupCreate();
  SPI.begin();
  bme.begin();
  while ( !bme.begin() )
  {
    log_i( "Waiting on response from BME280");
    vTaskDelay( 1000 );
  }
  tft.begin();
  tft.fillScreen(0x0000);
  //
  connectToWiFi();
  connectToMQTT();
  //
  sema_MQTT_Parser = xSemaphoreCreateBinary();
  sema_HistoryCompleted = xSemaphoreCreateBinary();
  sema_TheHumidityThing = xSemaphoreCreateBinary();
  sema_DoTheSunLampTHing = xSemaphoreCreateBinary();
  xSemaphoreGive( sema_DoTheSunLampTHing );
  xSemaphoreGive( sema_HistoryCompleted );
  xSemaphoreGive( sema_TheHumidityThing );
  ////
  xTaskCreatePinnedToCore( fparseMQTT, "fparseMQTT", 7000, NULL, 5, NULL, 1 ); // assign all to core 1, WiFi in use.
  xTaskCreatePinnedToCore( fCollectHistory, "fCollectHistory", 10000, NULL, 4, NULL, 1 );
  xTaskCreatePinnedToCore( MQTTkeepalive, "MQTTkeepalive", 10000, NULL, 3, NULL, 1 );
  xTaskCreatePinnedToCore( fUpdateDisplay, "fUpdateDisplay", 50000, NULL, 5, NULL, 1 );
  xTaskCreatePinnedToCore( DoTheBME280Thing, "DoTheBME280Thing", 7000, NULL, 3, NULL, 1 );
  xTaskCreatePinnedToCore( fDoTheHumidityThing, "fDoTheHumidityThing", 3000, NULL, 3, NULL, 1 );
  xTaskCreatePinnedToCore( fDoTheSunLampThing, "fDoTheSunLampThing", 3000, NULL, 3, NULL, 1 );
  ////
} //setup()
////
void fDoTheSunLampThing( void * parameter )
{
  // IntDataPtr[4] = sunlamp on manual mode, automatic mode off for manual mode to work
  // IntDataPtr[5] = sun lamp enable automatic mode
  //digitalWrite( GPIO_NUM_15, LOW );
  for (;;)
  {
    xEventGroupWaitBits (eg, evtDoTheSunLampThing, pdTRUE, pdTRUE, portMAX_DELAY );
    vTaskDelay( 2 );
    xSemaphoreTake( sema_DoTheSunLampTHing, portMAX_DELAY );
    if ( (IntDataPtr[4] == 1) && (IntDataPtr[5] == 0) )
    {
      digitalWrite( GPIO_NUM_15, HIGH );
    } else {
      digitalWrite( GPIO_NUM_15, LOW );
    }
    xSemaphoreGive( sema_DoTheSunLampTHing );
    log_i( "fDoTheSunLampThing high watermark %d",  uxTaskGetStackHighWaterMark( NULL ) );
  }
  vTaskDelete( NULL );
} //void fCollectHistory( void * parameter )
////
// send a signal out to relay to energize or de-energize humidifier based on set point
void fDoTheHumidityThing( void * parameter )
{
  for (;;)
  {
    xEventGroupWaitBits (eg, evtDoTheHumidityThing, pdTRUE, pdTRUE, portMAX_DELAY );
    vTaskDelay( 1 );
    xSemaphoreTake( sema_TheHumidityThing, portMAX_DELAY );
    if ( IntDataPtr[3] == 1 )
    {
      if ( IntDataPtr[2] + 1 < (int)ePtr[2] )
      {
        digitalWrite( GPIO_NUM_2, LOW );
      }
      if ( (IntDataPtr[2] - 1) > (int)ePtr[2] )
      {
        // energize humidifier
        digitalWrite( GPIO_NUM_2, HIGH );
      }
    } else {
      //de energize humidifier
      digitalWrite( GPIO_NUM_2, LOW );
    }
    xSemaphoreGive( sema_TheHumidityThing );
    log_i( "fDoTheHumidityThing high watermark %d",  uxTaskGetStackHighWaterMark( NULL ) );
  }
  vTaskDelete( NULL );
} //void fCollectHistory( void * parameter )
////
/*
   Collect history information
   task triggered by hardware timer once a minute
   stores history into PSRAM
   Using a semaphore to protect the PSRAM from multiple tasks access the same PSRM locations
*/
void fCollectHistory( void * parameter )
{
  int StorageTriggerCount = 59;
  for (;;)
  {
    xEventGroupWaitBits (eg, evtCollectHistory, pdTRUE, pdTRUE, portMAX_DELAY );
    //log_i( "history triggered" );
    StorageTriggerCount++; //triggered by the timer isr once a minute count 60 minutes
    if ( StorageTriggerCount == 60 )
    {
      xSemaphoreTake( sema_HistoryCompleted, portMAX_DELAY );
      // log_i( " store the history" );
      oPressureHistoryPtr[IntDataPtr[1]] = ePtr[5];
      oIAQ_HistoryPtr[IntDataPtr[1]] = ePtr[6];
      oHumidityHistoryPtr[IntDataPtr[1]] = ePtr[4];
      oTemperaturePtr[IntDataPtr[1]] = ePtr[3];
      IntDataPtr[1]++;
      xSemaphoreGive( sema_HistoryCompleted );
      //log_i( "pointer %d stored %f", IntDataPtr[1] - 1, oPressureHistoryPtr[IntDataPtr[1] - 1] );
      if ( IntDataPtr[1] == IntDataPtr[0] )
      {
        IntDataPtr[1] = 0;
      }
      StorageTriggerCount = 0;
    }
    //log_i( ">>>>>>>>>>>>ARRAY CELL POINTER %d storagetriggercount %d", IntDataPtr[1] - 1, StorageTriggerCount );
    // log_i( " high watermark %d",  uxTaskGetStackHighWaterMark( NULL ) );
  }
  vTaskDelete( NULL );
} //void fCollectHistory( void * parameter )
////
// Using a semaphore to protect the PSRAM from multiple tasks access the same PSRM locations
////
void fUpdateDisplay( void * parameter )
{
  // Color definitions
  // http://www.barth-dev.de/online/rgb565-color-picker/
  /*
    ColorPalettePtr[0] = 0x0000; //BLACK
    ColorPalettePtr[1] = 0x001F; //BLUE
    ColorPalettePtr[2] = 0xF800; //RED
    ColorPalettePtr[3] = 0x07E0; //GREEN
    ColorPalettePtr[4] = 0x07FF; //CYAN
    ColorPalettePtr[5] = 0xF81F; //MAGENTA
    ColorPalettePtr[6] = 0xFFE0; //YELLOW
    ColorPalettePtr[7] = 0xFFFF; //WHITE
    ColorPalettePtr[8] = 0xFFFFD8; //LIGHTYELLOW
    ColorPalettePtr[9] = 0xFF8040; //BROWN
  */
  for (;;)
  {
    xEventGroupWaitBits (eg, evtDoDisplay, pdTRUE, pdTRUE, portMAX_DELAY ); //
    tft.fillScreen( ColorPalettePtr[0] );
    tft.setTextColor( ColorPalettePtr[7] );
    tft.setCursor( 0, 0 );
    tft.print( "Inside"  );
    tft.setTextColor( ColorPalettePtr[3] );
    tft.setCursor( 0, 15 );
    tft.print( "Temp: " + String(ePtr[0]) + "C " + String((ePtr[0] * 9 / 5) + 32) + "F"  );
    tft.setCursor( 0, 25 );
    tft.print( "Humidity " + String(ePtr[2]) + "%" );
    //
    tft.setTextColor( ColorPalettePtr[7] );
    tft.setCursor( 0,  40 );
    tft.print( "Outside" );
    tft.setTextColor( ColorPalettePtr[6] );
    tft.setCursor( 0, 55 );
    tft.print( "Temperature: " + String(ePtr[3]) + "F" );
    tft.setTextColor( ColorPalettePtr[2] );
    tft.setCursor( 0, 65 );
    tft.print( "Humidity " + String(ePtr[4]) + "%" );
    tft.setCursor( 0, 75 );
    tft.setTextColor( ColorPalettePtr[1] );
    tft.print( "Pres. " + String(ePtr[5]) + "mmHg" );
    tft.setCursor( 0, 86 );
    //set the color of the value to be displayed
    if ( ePtr[6] < 51.0f )
    {
      tft.setTextColor( ColorPalettePtr[3] );
    }
    if ( (ePtr[6] >= 50.0f) && (ePtr[6] <= 100.0f) )
    {
      tft.setTextColor( ColorPalettePtr[6] );
    }
    if ( (ePtr[6] >= 100.0f) && (ePtr[6] <= 150.0f) )
    {
      tft.setTextColor( ColorPalettePtr[9] );
    }
    if ( (ePtr[6] >= 150.0f) && (ePtr[6] <= 200.0f) )
    {
      tft.setTextColor( ColorPalettePtr[2] );
    }
    if ( (ePtr[6] >= 200.00f) && (ePtr[6] <= 300.0f) )
    {
      tft.setTextColor( ColorPalettePtr[5] );
    }
    if ( (ePtr[6] > 300.0f) )
    {
      tft.setTextColor( ColorPalettePtr[7] );
    }
    tft.print( "AQ Index " + String(ePtr[6]) );
    tft.setTextColor( ColorPalettePtr[1] ); //set graph line color
    int rowRef = 110;
    int hRef = int( oPressureHistoryPtr[0] );
    int nextPoint = 2;
    int nextCol = 0;
    xSemaphoreTake( sema_HistoryCompleted, portMAX_DELAY );
    for (int i = 0; i < IntDataPtr[0]; i++)
    {
      int hDisplacement = hRef - int( oPressureHistoryPtr[i] ); // cell height displacement from base line
      tft.setCursor( nextCol , (rowRef + hDisplacement) );
      tft.print( "_" );
      nextCol += nextPoint;
    }
    tft.setCursor( (IntDataPtr[1] * nextPoint), (rowRef + 3) );
    tft.print( "I" );
    xSemaphoreGive( sema_HistoryCompleted );
    //     log_i( "fUpdateDisplay MEMORY WATERMARK %d", uxTaskGetStackHighWaterMark(NULL) );
    // xEventGroupSetBits( eg, evtConnect ); // trigger task
  }
  vTaskDelete( NULL );
} // void fUpdateDisplay( void * parameter )
////
// Do not try to send MQTT if MQTT Broker is not online
////
void DoTheBME280Thing( void *pvParameters )
{
  //log_i( "start bme680" );
  for (;;)
  {
    xEventGroupWaitBits (eg, evtDoTheBME280Thing, pdTRUE, pdTRUE, portMAX_DELAY ); //
    ePtr[0] = bme.readTemperature();
    ePtr[1] = bme.readPressure() / 133.3223684f; // mmHg
    ePtr[2] = bme.readHumidity();
    if ( MQTTclient.connected() )
    {
      MQTTclient.publish( topicInsideTemp, String(ePtr[0]).c_str() );
      vTaskDelay( 2 ); // gives the Raspberry Pi 4 time to receive the message and process
      MQTTclient.publish( topicInsideHumidity, String(ePtr[1]).c_str() );
      vTaskDelay( 2 ); // no delay and RPi is still processing previous message
      MQTTclient.publish( topicInsidePressure, String(ePtr[2]).c_str() );
      //log_i( "Signal strength %d", WiFi.RSSI() );
      //log_i( " high watermark %d",  uxTaskGetStackHighWaterMark( NULL ) );
    }
  }
  vTaskDelete ( NULL );
}
////
/*
    Impostant to not set vtaskDelay to less then 10. Errors begin to develop with the MQTT and network connection.
*/
void MQTTkeepalive( void *pvParameters )
{
  for (;;)
  {
    if ( wifiClient.connected() )
    {
      MQTTclient.loop();
      vTaskDelay( 17 );
    } else {
      log_i( "MQTT keep alive found MQTTclient disconnected" );
      connectToWiFi();
      connectToMQTT();
    }
  }
  vTaskDelete ( NULL );
}
/////
//void onConnectionEstablished()
//{
//}
////
void connectToMQTT()
{
  if ( !(MQTTclient.connect( clientID, mqtt_username, mqtt_password)) )
  {
    log_i("Connection to MQTT Broker failed...");
  }
  // log_i("MQTT Connected");
  MQTTclient.setCallback( mqttCallback );
  MQTTclient.subscribe( mqtt_topic ); //seems to need to want some topic to subscribe to but does not care in the callback
}
//
void connectToWiFi()
{
  if ( WiFi.status() != WL_CONNECTED )
  {
    WiFi.begin( SSID, PASSWORD );
    vTaskDelay( 2000 );
    while ( WiFi.status() != WL_CONNECTED )
    {
      vTaskDelay( 2000 );
      log_i(" waiting on wifi connection" );
    }
    WiFi.onEvent( WiFiEvent );
  }
}
////
void fparseMQTT( void *pvParameters )
{
  xSemaphoreGive ( sema_MQTT_Parser );
  for (;;)
  {
    xEventGroupWaitBits (eg, evtParseMQTT, pdTRUE, pdTRUE, portMAX_DELAY ); //
    xSemaphoreTake( sema_MQTT_Parser, portMAX_DELAY );
    if ( (String)str_eTopicPtr == topicOutsideTemperature )
    {
      ePtr[3] = String(strPayloadPtr).toFloat();
    }
    if ( (String)str_eTopicPtr == topicOutsideHumidity )
    {
      ePtr[4] = String(strPayloadPtr).toFloat();
    }
    if ( (String)str_eTopicPtr == topicAQIndex )
    {
      ePtr[6] = String(strPayloadPtr).toFloat();
    }
    if ( String(str_eTopicPtr) == topicStateLED )
    {
      if ( int(strPayloadPtr) )
      {
        digitalWrite( GPIO_NUM_0, LOW );
      } else {
        digitalWrite( GPIO_NUM_0, HIGH );
      }
    }
    if ( String(str_eTopicPtr) == topicOutsidePressure )
    {
      ePtr[5] = String(strPayloadPtr).toFloat();
      xEventGroupSetBits( eg, evtDoDisplay ); // trigger tasks
    }
    if ( (String)str_eTopicPtr == topic_hum_enable )
    {
      xSemaphoreTake( sema_TheHumidityThing, portMAX_DELAY );
      IntDataPtr[3] = String(strPayloadPtr).toInt();
      xSemaphoreGive( sema_TheHumidityThing );
    }
    if ( (String)str_eTopicPtr == topic_hum_setpoint )
    {
      xSemaphoreTake( sema_TheHumidityThing, portMAX_DELAY );
      IntDataPtr[2] = String(strPayloadPtr).toInt();
      xSemaphoreGive( sema_TheHumidityThing );
    }
    if ( (String)str_eTopicPtr == topic_SunLampOn )
    {
      xSemaphoreTake( sema_DoTheSunLampTHing, portMAX_DELAY );
      IntDataPtr[4] = String(strPayloadPtr).toInt();
      xSemaphoreGive( sema_DoTheSunLampTHing );
    }
    if ( (String)str_eTopicPtr == topic_SunLampEnable )
    {
      xSemaphoreTake( sema_DoTheSunLampTHing, portMAX_DELAY );
      IntDataPtr[5] = String(strPayloadPtr).toInt();
      xSemaphoreGive( sema_DoTheSunLampTHing );
    }
    //
    strPayloadPtr[0] = '\0';;
    strPayloadPtr[1] = '\0';;
    str_eTopicPtr[0] = '\0';
    xSemaphoreGive( sema_MQTT_Parser );
  }
} // void fparseMQTT( void *pvParameters )
////
// Important to get as much code out of the callback for realible operations.
////
void mqttCallback(char* topic, byte* payload, unsigned int length)
{
  xSemaphoreTake( sema_MQTT_Parser, portMAX_DELAY);
  str_eTopicPtr = topic;
  strPayloadPtr = (char *)payload;
  xSemaphoreGive ( sema_MQTT_Parser );
  xEventGroupSetBits( eg, evtParseMQTT ); // trigger tasks
} // void mqttCallback(char* topic, byte* payload, unsigned int length)
////
// great troubleshooting tool when uncommented
////
void WiFiEvent(WiFiEvent_t event)
{
  // log_i( "[WiFi-event] event: %d\n", event );
  //  switch (event) {
  //    case SYSTEM_EVENT_WIFI_READY:
  //      log_i("WiFi interface ready");
  //      break;
  //    case SYSTEM_EVENT_SCAN_DONE:
  //      log_i("Completed scan for access points");
  //      break;
  //    case SYSTEM_EVENT_STA_START:
  //      log_i("WiFi client started");
  //      break;
  //    case SYSTEM_EVENT_STA_STOP:
  //      log_i("WiFi clients stopped");
  //      break;
  //    case SYSTEM_EVENT_STA_CONNECTED:
  //      log_i("Connected to access point");
  //      break;
  //    case SYSTEM_EVENT_STA_DISCONNECTED:
  //      log_i("Disconnected from WiFi access point");
  //      break;
  //    case SYSTEM_EVENT_STA_AUTHMODE_CHANGE:
  //      log_i("Authentication mode of access point has changed");
  //      break;
  //    case SYSTEM_EVENT_STA_GOT_IP:
  //      log_i ("Obtained IP address: %s",  WiFi.localIP() );
  //      break;
  //    case SYSTEM_EVENT_STA_LOST_IP:
  //      log_i("Lost IP address and IP address is reset to 0");
  //      //      vTaskDelay( 5000 );
  //      //      ESP.restart();
  //      break;
  //    case SYSTEM_EVENT_STA_WPS_ER_SUCCESS:
  //      log_i("WiFi Protected Setup (WPS): succeeded in enrollee mode");
  //      break;
  //    case SYSTEM_EVENT_STA_WPS_ER_FAILED:
  //      log_i("WiFi Protected Setup (WPS): failed in enrollee mode");
  //      //      ESP.restart();
  //      break;
  //    case SYSTEM_EVENT_STA_WPS_ER_TIMEOUT:
  //      log_i("WiFi Protected Setup (WPS): timeout in enrollee mode");
  //      break;
  //    case SYSTEM_EVENT_STA_WPS_ER_PIN:
  //      log_i("WiFi Protected Setup (WPS): pin code in enrollee mode");
  //      break;
  //    case SYSTEM_EVENT_AP_START:
  //      log_i("WiFi access point started");
  //      break;
  //    case SYSTEM_EVENT_AP_STOP:
  //      log_i("WiFi access point  stopped");
  //      //      WiFi.mode(WIFI_OFF);
  //      //      esp_sleep_enable_timer_wakeup( 1000000 * 2 ); // 1 second times how many seconds wanted
  //      //      esp_deep_sleep_start();
  //      break;
  //    case SYSTEM_EVENT_AP_STACONNECTED:
  //      log_i("Client connected");
  //      break;
  //    case SYSTEM_EVENT_AP_STADISCONNECTED:
  //      log_i("Client disconnected");
  //      //      WiFi.mode(WIFI_OFF);
  //      //      esp_sleep_enable_timer_wakeup( 1000000 * 2 ); // 1 second times how many seconds wanted
  //      //      esp_deep_sleep_start();
  //      break;
  //    case SYSTEM_EVENT_AP_STAIPASSIGNED:
  //      log_i("Assigned IP address to client");
  //      break;
  //    case SYSTEM_EVENT_AP_PROBEREQRECVED:
  //      log_i("Received probe request");
  //      break;
  //    case SYSTEM_EVENT_GOT_IP6:
  //      log_i("IPv6 is preferred");
  //      break;
  //    case SYSTEM_EVENT_ETH_START:
  //      log_i("Ethernet started");
  //      break;
  //    case SYSTEM_EVENT_ETH_STOP:
  //      log_i("Ethernet stopped");
  //      break;
  //    case SYSTEM_EVENT_ETH_CONNECTED:
  //      log_i("Ethernet connected");
  //      break;
  //    case SYSTEM_EVENT_ETH_DISCONNECTED:
  //      log_i("Ethernet disconnected");
  //      break;
  //    case SYSTEM_EVENT_ETH_GOT_IP:
  //      log_i("Obtained IP address");
  //      break;
  //    case 128:
  //      log_i("err 128");
  //        WiFi.mode(WIFI_OFF);
  //        esp_sleep_enable_timer_wakeup( 1000000 * 2 ); // 1 second times how many seconds wanted
  //        esp_deep_sleep_start();
  //      break;
  //    case 104:
  //      log_i("err 104");
  //      WiFi.mode(WIFI_OFF);
  //      esp_sleep_enable_timer_wakeup( 1000000 * 2 ); // 1 second times how many seconds wanted
  //      esp_deep_sleep_start();
  //      break;
  //    default: break;
  //  }
}
////
void loop() { }
Detecting the status of the MQTT client (wifiClient.connected()) was the trick that worked for me.
The connectToWiFi(); called, if client is disconnected, will not try a WiFi reconnect if the WiFi is still connected by checking the status of connection with if ( WiFi.status() != WL_CONNECTED ). I found that if the issue is not a WiFi connection, just blindly trying to reconnect to the WiFi causes issues.

Most of the disconnect errors I get is error 128, which only needs a MQTT reconnect. I found running the MQTT keep alive loop faster then 10mS, also, produces errors. I found a good range for MQTT keep alive is between 10 and 20 mS.

Who is online

Users browsing this forum: No registered users and 9 guests