The Proper Way to Reconnect to WiFi?
The Proper Way to Reconnect to WiFi?
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
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
-
- Posts: 1
- Joined: Sat Dec 16, 2017 4:57 pm
Re: The Proper Way to Reconnect to WiFi?
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.
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
}
-
- Posts: 2
- Joined: Sat Jun 08, 2019 3:46 pm
Re: The Proper Way to Reconnect to WiFi?
This is a very sound algorithm. I successfully converted it to using PubSubClient and it works well there as well.
Re: The Proper Way to Reconnect to WiFi?
Would you mind sharing your converted code for pubsubclient? I'm new to this and using also pubsubclient.
Regards,
Oscar
Regards,
Oscar
Re: The Proper Way to Reconnect to WiFi?
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();
}
-
- Posts: 166
- Joined: Wed Aug 01, 2018 12:06 pm
Re: The Proper Way to Reconnect to WiFi?
I use the MQTT keep alive to detect a lack of network connection with MQTT.
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.
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() { }
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.
-
- Posts: 1
- Joined: Sat Jul 25, 2020 5:57 am
Re: The Proper Way to Reconnect to WiFi?
This is a response to Urs' contribution,
I have thankfully used Urs' idea as published on https://esp32.com/viewtopic.php?t=3851 and made some modifications as below.
I could not find a source for <WiFiUdp.h> and that may have been the reason for some errors, -not sure about that- but initially I could not get things running on a Raspberry3.
I use ESP32 for
Reason for using esp32 here: the lights do not disclose their status after sending a pulse.
To be able to receive the status I included <PubSubClient.h> in the sketch for ESP32.
I encountered several C++ variable-problems with the switch structure when compiling, so I changed it to <if{ } else if{}>. It is almost the same structure.
I encountered further errors when trying to do OTA as in Urs' example, so I changed my sketch to the web-version. I used the code of the example-file in Arduino-IDE, that worked out of the box. I just had to modify the html-dimensions, that was all.
My MQTT Broker is an intern instance on the forementioned Raspberry. Every information stays in the network, no reason to use a third-party broker. But btw I also can access the Raspberry over internet using true.ddns (a free ddns-service of True Thailand, it requires port-forwarding, works like a charm). DDNS is in use by a second raspberry which is securing a well with PIR & camera on our remote farmland. The code is almost the same (replace the broker address with the ddns.address and change the portnumber) so I did not add the pertinent files here.
To switch the lights on or off, I use the sum of status daylight and status optocoupler. In my case this sum should be 1. Either in the night 0 +1 or 1 +0 during the day. The ESP32 checks daylight in every cycle.
In conn_stat 5 there is a 30000 ms routine for publishing. As soon as the daylight changes the cycletime is temporarily set to 1 sec and is being reset to 30000 as soon as the reaction has been concluded by the change of the optocoupler-signal.
I found that sending two payloads in one message is better than two individual messages to the broker. Especially if both values are required for one and the same action this simplfies the python-code.
On that raspberry a python script reacts on status-changes. That is a developing story; attached please find the last python status
A third Raspberry is combined. This Raspberry (B) was set up years earlier to control the outdoor light in the garden and it works flawlessly, so it was more than logical to integrate it by publishing its status via MQTT.
The ESP's are powered with 3.3V miniature adapters. The outputs of ESP are not powerful enough to control solid-state relays (switching off the 5.5 kW water-heater and the laundry-machine), so for the bathroom the ESP output is connected to a relay.
I hope this is helpful to somebody. I am a tech-freak, was involved inrobotics before retirement, but I am not a professional, happy to receive comments.
Cheers
Mike
THE SKETCH
PYTHON
I have thankfully used Urs' idea as published on https://esp32.com/viewtopic.php?t=3851 and made some modifications as below.
I could not find a source for <WiFiUdp.h> and that may have been the reason for some errors, -not sure about that- but initially I could not get things running on a Raspberry3.
I use ESP32 for
- controlling water level in a 1000 L tank
- switching off some units when the house is battery/inverter-powered, and for
- auto-switching outdoor lights.
Reason for using esp32 here: the lights do not disclose their status after sending a pulse.
To be able to receive the status I included <PubSubClient.h> in the sketch for ESP32.
I encountered several C++ variable-problems with the switch structure when compiling, so I changed it to <if{ } else if{}>. It is almost the same structure.
I encountered further errors when trying to do OTA as in Urs' example, so I changed my sketch to the web-version. I used the code of the example-file in Arduino-IDE, that worked out of the box. I just had to modify the html-dimensions, that was all.
My MQTT Broker is an intern instance on the forementioned Raspberry. Every information stays in the network, no reason to use a third-party broker. But btw I also can access the Raspberry over internet using true.ddns (a free ddns-service of True Thailand, it requires port-forwarding, works like a charm). DDNS is in use by a second raspberry which is securing a well with PIR & camera on our remote farmland. The code is almost the same (replace the broker address with the ddns.address and change the portnumber) so I did not add the pertinent files here.
To switch the lights on or off, I use the sum of status daylight and status optocoupler. In my case this sum should be 1. Either in the night 0 +1 or 1 +0 during the day. The ESP32 checks daylight in every cycle.
In conn_stat 5 there is a 30000 ms routine for publishing. As soon as the daylight changes the cycletime is temporarily set to 1 sec and is being reset to 30000 as soon as the reaction has been concluded by the change of the optocoupler-signal.
I found that sending two payloads in one message is better than two individual messages to the broker. Especially if both values are required for one and the same action this simplfies the python-code.
On that raspberry a python script reacts on status-changes. That is a developing story; attached please find the last python status
A third Raspberry is combined. This Raspberry (B) was set up years earlier to control the outdoor light in the garden and it works flawlessly, so it was more than logical to integrate it by publishing its status via MQTT.
The ESP's are powered with 3.3V miniature adapters. The outputs of ESP are not powerful enough to control solid-state relays (switching off the 5.5 kW water-heater and the laundry-machine), so for the bathroom the ESP output is connected to a relay.
I hope this is helpful to somebody. I am a tech-freak, was involved inrobotics before retirement, but I am not a professional, happy to receive comments.
Cheers
Mike
THE SKETCH
Code: Select all
#include <WiFi.h> // Enables the ESP32 to connect to the local network (via WiFi)
#include <WiFiClient.h> //
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>
#include <PubSubClient.h>
/* standalone device with MQTT function
found on https://esp32.com/viewtopic.php?t=3851
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
* ******************************************************************************************************
*/
const char* host = "esp32-100";
const char* ssid = "********";
const char* password = "********";
int payload = 0;
unsigned long waitCount = 0; // counter
unsigned long WiFiCount = 0; // counter
uint8_t lastSensor = 2;
uint8_t lastOptoc = 2;
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
unsigned long emptyloopCount = 0;
unsigned long loopCount = 0;
unsigned long prevms = 0;
unsigned long interval = 0;
unsigned long cycle = 30000;
unsigned long currentMillis = 0;
unsigned long modific_time = 0;
String mesg_1 = "";
String mesg_2 = "";
String mesg = "";
int ledState = LOW;
int d = 0;
int e = 0;
int f = 0;
int o = 0;
int p = 0;
int q = 0;
const char* clientID = "client_livingroom"; // MQTT client ID
const char* daylight_topic = "home/livingroom/daylight";
const char* optocoupler_topic = "home/livingroom/optocop";
const char* mqtt_server = "192.168.1.110"; // IP of the MQTT broker
const int LED = 2; // the onboard blue LED
const char* Version = "{\"Version\":\"low_prio_wifi_v2\"}";
const char* Status = "{\"Message\":\"up and running\"}";
const char* mqtt_username = "mikebkk"; // MQTT username
const char* mqtt_password = "DdhGmsk1"; // MQTT password
const int sensorPin = 4; // the number of the sensor pin
const int optocPin = 22; // the number of the optocoupler pin
const int ledPin = 23;
boolean statuschange = 0;
WebServer server(80);
/* Style */
String style =
"<style>#file-input,input{width:100%;height:44px;border-radius:4px;margin:10px auto;font-size:15px}"
"input{background:#f1f1f1;border:0;padding:0 15px}body{background:#3498db;font-family:sans-serif;font-size:14px;color:#777}"
"#file-input{padding:0;border:1px solid #ddd;line-height:44px;text-align:left;display:block;cursor:pointer}"
"#bar,#prgbar{background-color:#f1f1f1;border-radius:10px}#bar{background-color:#3498db;width:0%;height:10px}"
"form{background:#fff;max-width:258px;margin:75px auto;padding:30px;border-radius:5px;text-align:center}"
".btn{background:#3498db;color:#fff;cursor:pointer}</style>";
//**************************************************************************** Login page
String loginIndex =
"<form name='loginForm'>"
"<table width='100%' bgcolor='A09F9F' align='center'>"
"<tr>"
"<td colspan=2>"
"<center><font size=4><b>ESP32 Login Page</b></font></center>"
"<br>"
"<center><font size=4><b>Living-room</b></font></center>"
"<br>"
"</td>"
"<br>"
"<br>"
"</tr>"
"<tr>"
"<td>Username:</td>"
"<td><input type='text' size=25 name='userid'><br></td>"
"</tr>"
"<br>"
"<br>"
"<tr>"
"<td>Password:</td>"
"<td><input type='Password' size=25 name='pwd'><br></td>"
"<br>"
"<br>"
"</tr>"
"<tr>"
"<td><input type='submit' onclick='check(this.form)' value='Login'></td>"
"</tr>"
"</table>"
"</form>"
"<script>"
"function check(form)"
"{"
"if(form.userid.value=='admin' && form.pwd.value=='DdhGmsk0')"
"{"
"window.open('/serverIndex')"
"}"
"else"
"{"
" alert('Error Password or Username')/*displays error message*/"
"}"
"}"
"</script>" + style;
//************************************************************************ Server Index Page
const char* serverIndex =
"<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>"
"<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>"
"<input type='file' name='update'>"
"<input type='submit' value='Update'>"
"</form>"
"<div id='prg'>progress: 0%</div>"
"<script>"
"$('form').submit(function(e){"
"e.preventDefault();"
"var form = $('#upload_form')[0];"
"var data = new FormData(form);"
" $.ajax({"
"url: '/update',"
"type: 'POST',"
"data: data,"
"contentType: false,"
"processData:false,"
"xhr: function() {"
"var xhr = new window.XMLHttpRequest();"
"xhr.upload.addEventListener('progress', function(evt) {"
"if (evt.lengthComputable) {"
"var per = evt.loaded / evt.total;"
"$('#prg').html('progress: ' + Math.round(per*100) + '%');"
"}"
"}, false);"
"return xhr;"
"},"
"success:function(d, s) {"
"console.log('success!')"
"},"
"error: function (a, b, c) {"
"}"
"});" // end ajax
"});" // end form
"</script>";
//**************************************************************** setup function
void setup(void) {
pinMode(LED, OUTPUT); // the onboard blue LED
pinMode(sensorPin, INPUT);
pinMode(optocPin, INPUT);
pinMode(ledPin, OUTPUT);
Serial.begin(115200);
WiFi.begin(ssid, password); // Connect to WiFi network
Serial.println("");
while (WiFi.status() != WL_CONNECTED) { // Wait for connection
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
if (!MDNS.begin(host)) { //http://esp32-100.local use mdns for host name resolution
Serial.println("Error setting up MDNS responder!");
while (1) {
delay(1000);
}
}
Serial.println("mDNS responder started"); //return index page which is stored in serverIndex
server.on("/", HTTP_GET, []() {
server.sendHeader("Connection", "close");
server.send(200, "text/html", loginIndex);
});
server.on("/serverIndex", HTTP_GET, []() {
server.sendHeader("Connection", "close");
server.send(200, "text/html", serverIndex);
});
server.on("/update", HTTP_POST, []() { // handling uploading firmware file
server.sendHeader("Connection", "close");
server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
ESP.restart();
}, []() {
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
Serial.printf("Update: %s\n", upload.filename.c_str());
if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size
Update.printError(Serial);
}
} else if (upload.status == UPLOAD_FILE_WRITE) {
/* flashing firmware to ESP*/
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
Update.printError(Serial);
}
} else if (upload.status == UPLOAD_FILE_END) {
if (Update.end(true)) { //true to set the size to the current progress
Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
} else {
Update.printError(Serial);
}
}
});
server.begin();
}
WiFiClient wifiClient;
PubSubClient client(mqtt_server, 1883, wifiClient); // 1883 is the listener port for the Broker
// keepAlive, timeout changed to 75 in PubSubClient.h
void connect_MQTT() { // Custom function to connect to the MQTT broker via WiFi:
Serial.print("Connecting to ");
Serial.println(ssid);
Serial.println("WiFi is connected"); // Debugging - Output the IP Address of the ESP32
Serial.print("IP address of ESP32: ");
Serial.println(WiFi.localIP());
Serial.print("going to connect clientID; "); Serial.print(clientID);
Serial.print(", user: "); Serial.print(mqtt_username);
Serial.print(". password: "); Serial.println(mqtt_password);
delay(100);
boolean rc = client.connect(clientID, mqtt_username, mqtt_password);
Serial.print("returned answer on connect: ");
Serial.print(String(rc));
if (rc == 1) {
Serial.println(" (True)");
int s = client.state();
Serial.print("Connected to MQTT Broker returned "); // client.connect returns a boolean value to let us know if the connection was successful.
Serial.println("client.state() = " + String(s));
} else {
Serial.println(" (False)");
Serial.println("Connection to MQTT Broker failed...");
}
}
void loop(void) { // with current code runs roughly 400 times per second
server.handleClient();
delay(1);
// start of non-blocking connection setup section
if ((WiFi.status() != WL_CONNECTED) && (conn_stat != 1)) {
conn_stat = 0;
Serial.print("1st criterium, "); Serial.print("connection status = "); Serial.println(String(conn_stat));
}
if ((WiFi.status() == WL_CONNECTED) && !client.connected() && (conn_stat != 3)) {
conn_stat = 2;
Serial.print("2nd criterium, "); Serial.print("connection status = "); Serial.println(String(conn_stat));
}
if ((WiFi.status() == WL_CONNECTED) && client.connected() && (conn_stat != 5)) {
conn_stat = 4;
Serial.print("3rd criterium, "); Serial.print("connection status = "); Serial.println(String(conn_stat));
}
if (conn_stat == 0) { // MQTT and WiFi down: start WiFi
Serial.println("MQTT and WiFi down: start WiFi");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password); // Connect to the WiFi
conn_stat = 1;
} else if (conn_stat == 1) { // WiFi starting, do nothing here
Serial.println("WiFi starting, wait : " + String(waitCount));
if (client.connected()) {
Serial.println("Connected is True: " + String(client.connected()));
} else {
Serial.println("Connected is False: " + String(client.connected()));
}
} else if (conn_stat == 2) { // WiFi up, MQTT down: start MQTT // WiFi up, MQTT down: start MQTT
Serial.println("WiFi up, MQTT down: start MQTT");
connect_MQTT();
conn_stat = 3;
waitCount = 0;
} else if (conn_stat == 3) { // WiFi up, MQTT starting, do nothing here
Serial.println("WiFi up, MQTT starting, wait : " + String(waitCount));
if (client.connected()) {
Serial.println("Connected is True: " + String(client.connected()));
} else {
Serial.println("Connected is False: " + String(client.connected()));
connect_MQTT();
}
waitCount++;
} else if (conn_stat == 4) { // WiFi up, MQTT up: finish MQTT configuration
Serial.println("WiFi up, MQTT up: finish MQTT configuration");
emptyloopCount = 0;
delay(500);
conn_stat = 5;
/*
************************* end of non-blocking connection setup section ***********************************
************************* start section with tasks where WiFi/MQTT is required **********************************/
} else if (conn_stat == 5) { // normal Start send status every 30 sec (just as an example)
//Serial.println("from section with tasks where WiFi/MQTT is required: ");
d = digitalRead(sensorPin);
e = 0;
f = 0;
if (lastSensor != d) {
statuschange = 1;
}
if (statuschange == 1) {
cycle = 1500; // look at the status every second after a change
interval = 200; // blinking freq 200 ms
} else {
cycle = 30000; // look at the status every second after a change
interval = 1000; // blinking freq 200 ms
}
e = digitalRead(sensorPin);
delay(10);
f = digitalRead(sensorPin);
delay(10);
if ((d == e) && (d == f)) {
Serial.print("Daylight: "); Serial.println(String(d));
mesg_1 = String(d);
Serial.print("Daylight: "); Serial.println(String(d));
Serial.println("Current status daylight sensor: " + String(d));
Serial.println("Current statuscode " + mesg_1); // MQTT can only transmit strings
Serial.print("home/livingroom/light-sensor, "); Serial.println(String(mesg_1));
} else {
// difference between two recordings // do new cycle
Serial.println(String(d) + String(e) + String(f));
Serial.println("no hit");
}
// remember time of last sent status message
o = digitalRead(optocPin);
p = -1;
q = -1;
if (lastOptoc != o) {
statuschange = 1;
}
p = digitalRead(optocPin);
delay(10);
q = digitalRead(optocPin);
delay(10);
if ((o == p) && (o == q)) {
Serial.print("Optocoupler: "); Serial.println(o);
Serial.println("Current status optocoupler " + String(o));
String toprint = "clientID: " + String(clientID);
Serial.println(toprint);
mesg_2 = String(o);
} else {
// difference between two recordings // do new cycle
Serial.println(String(o) + String(p) + String(q));
}
mesg = mesg_1;
mesg.concat(mesg_2);
Serial.println("Result of concatenated messages: " + mesg);
Serial.print("millis() = " + String(millis()));
Serial.print(", lastStatus = " + String(lastStatus));
Serial.print(", difference = " + String(millis() -lastStatus));
Serial.println(", Interval = " + String(interval));
String Telegram = daylight_topic;
Telegram += ", ";
Telegram.concat(mesg);
Serial.println("Test for Telegram = " + Telegram);
if (millis() - lastStatus > cycle) {
boolean rq = client.publish(daylight_topic, String(mesg).c_str());
if (rq == 1) { // send powerstatus to broker
Serial.println("##########################################################################");
Serial.print("Statusmessage: "); Serial.println(Status);
//Serial.print(Telegram); Serial.println(" sent!");
Serial.println("##########################################################################");
} else {
Serial.println("con_stat: " + String(conn_stat));
Serial.print("Daylight-sensor failed to send. ");
Serial.println("Reconnecting to MQTT Broker and trying again"); // If the message failed to send, we will try again, as the connection may have broken.
Serial.print("error : status should refresh automatically");
}
client.loop(); // give control to MQTT to send message to broker
lastStatus = millis();
//delay(100);
}
if (d + o == 1) {
statuschange = 0;
}
Serial.print("millis() = " + String(millis()));
Serial.print(", lastStatus = " + String(lastStatus));
Serial.print(", difference = " + String(millis() -lastStatus));
Serial.println(", Interval = " + String(interval));
lastSensor = d;
lastOptoc = o;
mesg = ""; mesg_1 = ""; mesg_2 = "";
d = 0; e = 0; f = 0;
o = 0; p = 0; q = 0;
client.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 **********************************/
// count the loops in disconnected status
if (WiFi.status() != WL_CONNECTED) {
loopCount++;
if (loopCount > 50) {
Serial.println("going to restart, because loopCount = " + String(loopCount) );
ESP.restart();
}
}
currentMillis = millis();
if (currentMillis - prevms >= interval) {
prevms = currentMillis; // save the last time you blinked the LED
ledState = not(ledState); // if the LED is off turn it on and vice-versa:
digitalWrite(LED, ledState); // set the LED with the ledState of the variable:
//delay(interval/2);
}
// end of section for tasks which should run regardless of WiFi/MQTT
}
}
Code: Select all
from __future__ import division
import paho.mqtt.client as mqtt
import RPi.GPIO as GPIO
import time
import subprocess
import sys
import os
from subprocess import call
from datetime import datetime
import pytz
import pymsgbox
MQTT_ADDRESS = '192.168.1.110'
MQTT_USER = '********'
MQTT_PASSWORD = '********'
MQTT_TOPIC = 'home/+/+'
tz_BKK = pytz.timezone('Asia/Bangkok')
oldident = ''
ItsNight = -2
Night = -2
PowerInfo = -2
datetime_BKK = datetime.now(tz_BKK)
timenow = str(datetime_BKK.strftime("%H:%M:%S"))
PowerInfo= 0
count = 0
text = ""
logfile = "/var/log/lighting.log" # define a logfile
debug = 1
loops = 0
class init():
global initialized
global loops
try:
def __init__(self, init_done, laps):
self.done = init_done
self.laps = laps
def ret_init(self):
initialized = self.done
loops = self.laps
print("Message from Class init: value = " + str(self.done))
print("Message from Class init: loops = " + str(loops))
print("Message from Class init: loop = " + str(self.laps))
except Exception as e:
print(e)
def write_logfile(entry):
count = 0
global logfile
with open(logfile, 'r') as f:
for line in f:
count += 1 # count the lines in the logfile
if (count > 60): # make sure the logfile is not growing endlessly
with open(logfile, 'r') as f_in:
data =f_in.read().splitlines(True)
with open(logfile, 'w') as f_out:
f_out.writelines(data[1:])
f = open(logfile,"a+")
f.write(entry)
f.close()
def on_connect(client, userdata, flags, rc):
""" The callback for when the client receives a CONNACK response from the server."""
print('Connected with result code ' + str(rc))
client.subscribe(MQTT_TOPIC)
def on_message(client, userdata, msg):
"""The callback for when a PUBLISH message is received from the server."""
global ItsNight
global PowerInfo
global Night
global oldident
global timenow
global datetime_BKK
global loops
text = ""
datetime_BKK = datetime.now(tz_BKK)
timenow = str(datetime_BKK.strftime("%H:%M:%S"))
if (loops > 0): # loops enables a delay to overcome a fuzzy daylight status
loops = loops - 1 # as long as loops > 0 no signal is being sent
try:
timenow = str(datetime_BKK.strftime("%H:%M:%S"))
ident=msg.topic[5:11]
# uncomment next line for debugging
# print(ident)
if (ident == 'entree'):
print('____________________ ENTREE _____________________\n')
obj = msg.topic[12:len(msg.topic)]
# entree is not active yet, it can do the same function as living room
# currently just manually controlled
print('ident 1 = ' + ident + ', topic: ' + obj + ', msg ' + msg.payload + "\n")
if (int(msg.payload) == 1):
print(timenow + " light is off ")
else:
print(timenow + " light is on")
elif (ident == 'garden'):
print('_____________________ GARDEN _____________________\n')
print('topic = ' + msg.topic + ', payload = ' + msg.payload)
if (int(msg.payload) == 1):
print(timenow + " It is dark\n")
else:
print(timenow + " Daylight in the garden\n")
if (int(msg.payload) == 1):
Night = 1
#if (ItsNight == 0):
# ItsNight += 1 # concat not possible
else:
ItsNight = 0
Night = 0
print('Lightsensor Westside: ' + str(Night))
print('global ItsNight ' + str(ItsNight))
print('\n')
elif (ident == 'bathro'):
print('____________________ BATHROOM ____________________\n')
print('topic = ' + msg.topic + ', payload = ' + msg.payload)
obj = msg.topic[14:len(msg.topic)]
# print(obj)
Inverter = int(msg.payload[0:1:1])
Waterlevel = int(msg.payload[1:2:1]);
print("Inverter = " + str(Inverter) + " , Waterlevel = " + str(Waterlevel))
if (Inverter == 0):
print(timenow + " running on the grid\n")
else:
print(timenow + " running on battery, water-heater \nand laundry-machine interrupted\n")
if (Waterlevel == 0):
print(timenow + " running out of water\n")
else:
print(timenow + " enough water\n")
elif (ident == 'living'):
obj = msg.topic[16:len(msg.topic)]
# controlling light in the pergola. It is on all night.
PowerInfo = int(msg.payload[1:2:1]);
ItsNight = int(msg.payload[0:1:1])
print("number of loops " + str(loops))
if (1 == 1):
print('___________________ LIVING-ROOM __________________\n')
print('topic = ' + msg.topic + ', payload = ' + msg.payload)
if ((ItsNight == 1) & (PowerInfo == 0)) | ((ItsNight == 0) & (PowerInfo == 1)):
loops = 0
if (ItsNight == 1):
text = timenow + ", No action required, ItsNight = " + str(ItsNight)
text += " and light is on (optoc): " + str(PowerInfo) + "\n"
else:
text = timenow + ", No action required, Daylight (ItsNight = " + str(ItsNight)
text += ") and light is off (optoc): " + str(PowerInfo) + "\n"
elif ((ItsNight == 0) & (PowerInfo == 0)) | ((ItsNight == 1) & (PowerInfo == 1)):
if (loops == 0):
loops = 3
subprocess.call(["/home/mikebkk/433Utils/RPi_utils/mycontrol1", "8"])
write_logfile(text)
if (ItsNight == 1):
text = timenow + ", action required, night & lamp off\n"
else:
text = timenow + ", action required, daylight & lamp on\n"
else:
pymsgbox.alert('Something went wrong!', 'Check this first')
print(text)
# in case the fire command fails it will be repeated until success
if (ident != 'living'):
print('topic = ' + msg.topic + ', payload = ' + msg.payload +"\n")
else:
print('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
print('ident ' + ident + ' no action yet')
print(timenow)
print(obj)
print('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
oldident = ident
except Exception as e:
print(e)
def main():
#mqtt_client = mqtt.Client()
mqtt_client = mqtt.Client("", True, mqtt.MQTTv31)
mqtt_client.username_pw_set(MQTT_USER, MQTT_PASSWORD)
mqtt_client.on_connect = on_connect
mqtt_client.on_message = on_message
mqtt_client.connect(MQTT_ADDRESS, 1883)
print(MQTT_ADDRESS)
# uncomment to verify
# print(str(mqtt_client) + ", user : " + str(MQTT_USER) + ", passwd: " + str(MQTT_PASSWORD))
mqtt_client.loop_forever()
if __name__ == '__main__':
print('MQTT to InfluxDB bridge')
main()
Re: The Proper Way to Reconnect to WiFi?
Hello, nice concept. I implemented it for PubSubClient. One question;urs_eppenberger wrote: ↑Sat Dec 16, 2017 5:02 pm
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.
To reconnect to wifi, you use WiFi.begin() again. Seems to be working, however I also found information on the net about reconnection should be automatically. That is, the WiFi lib is handling that already. Anyone knows the details / pros and cons?
Besides that, are there any downsides on check WiFi.status() every loop() cycle? Some examples introduce delays for this.
Who is online
Users browsing this forum: No registered users and 117 guests