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.