To overcome this I attempted to implement the ESP Watchdog Timer with the intention of rebooting in the event of a timeout. Eventually I found an example which compiled on the current version of the Arduino and the timer does kick in when the device times out. However the Watchdog Code then fails to reboot leaving the device hanging as before.
I chose to run the example code I had found on one of the devices on which my code was failing and it rebooted correctly after a timeout.
I then started to add into the example portions of my code starting with the includes of the 5 libraries which it used:-
Code: Select all
#include <WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <PubSubClient.h>
#include <esp_sleep.h>
I removed all 5 libraries again and added them back one by one.
With esp_sleep.h added the reboot worked perfectly but with any or all of the others the reboot failed.
I've included my actual application code and the code for the example.
Please don't be too critical of my code. I'm 76 years old and was programming professionally for about 30 of those. Legacy techniques still pervade my brain. If anyone can spot where my code fails to handle failure of the network connection I would appreciate it. As far as the fact that the Watchdog timer is affected by the presence of the other libraries even though they are not called would appear to be some sort of incompatibility.
My Code
Code: Select all
// this version attempts to improve failed connection handling
//and set sleeptime via MQTT message
#define RELEASE "Live 04112024"
#include <WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <PubSubClient.h>
#include <esp_sleep.h>
#include "esp_task_wdt.h"
//3 seconds WDT
#define WDT_TIMEOUT 30000
//if 1 core doesn't work, try with 2
#define CONFIG_FREERTOS_NUMBER_OF_CORES 1
#define CONFIG_ESP_SYSTEM_PANIC CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT
esp_task_wdt_config_t twdt_config = {
.timeout_ms = WDT_TIMEOUT,
.idle_core_mask = (1 << CONFIG_FREERTOS_NUMBER_OF_CORES) - 1, // Bitmask of all cores
.trigger_panic = true,
};
// Define Sleep Duration
#define uS_TO_S_FACTOR 1000000 // Conversion factor for microseconds to seconds
long SECONDS_TO_SLEEP = 60; // Time ESP32 will go to sleep (in seconds)
String hostName = "Moisture Sensor ************";
const int RSSI_MAX = -50; // define maximum strength of signal in dBm
const int RSSI_MIN = -100; // define minimum strength of signal in dBm
const int displayEnc = 1; // set to 1 to display Encryption or 0 not to display
int strongest = 0;
String strongestNetwork;
String allowedNetworks[4] = { "PLUSNET-6S27-2.4", "zyxel-2.4", "BTHub3-MZJZ", "PLUSNET-RP7X-2.4" };
const int noAllowedNetworks = 4;
#define timeSeconds 2
const char* password = "11shelley";
const char* MQTT_username = "Don";
const char* MQTT_password = "11Shelley";
const int moisturePin = 34;
const int batteryPin = 36;
String statusMsg;
String printString;
int actionVal;
WiFiClient espClient;
PubSubClient mqttClient(espClient);
const char* mqtt_server = "192.168.1.215"; // Service running on Linux
WiFiServer server(80);
long now = millis();
const long utcOffsetInSeconds = 0; // for summertime add 3600;
char daysOfTheWeek[7][12] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", utcOffsetInSeconds);
String req;
long lastReconnectAttempt = 0;
IPAddress ip;
String ipAddress;
int count = 0;
bool gotUniqueNumber = false;
bool gotPlantName = false;
bool gotSecondsToSleep = false;
String uniqueNumber;
String plantName;
int AirValue;
int WaterValue;
void panic(const char* message) {
Serial.println(message); // Print the registers and reboot the ESP32
esp_restart();
}
void setup() {
Serial.begin(115200);
Serial.println("Starting...");
print_wakeup_reason();
pinMode(27, OUTPUT);
digitalWrite(27, HIGH);
esp_task_wdt_deinit(); //wdt is enabled by default, so we need to deinit it first
esp_task_wdt_init(&twdt_config); //enable panic so ESP32 restarts
esp_task_wdt_add(NULL); //add current thread to WDT watch
setupWiFi();
esp_task_wdt_reset();
delay(1000); // Added to repeatedly reset the Watch Dog Timer
setupMQTT();
esp_task_wdt_reset();
delay(1000); // Added to repeatedly reset the Watch Dog Timer
setupTimeClient();
esp_task_wdt_reset();
delay(1000); // Added to repeatedly reset the Watch Dog Timer
requestDeviceInfo();
delay(2000); // Wait for the device info to be received
esp_task_wdt_reset();
delay(1000); // Added to repeatedly reset the Watch Dog Timer
}
void setupWiFi() {
WiFi.mode(WIFI_STA);
WiFi.hostname(hostName.c_str());
WiFi.disconnect();
delay(2000);
int n = WiFi.scanNetworks();
if (n == 0) {
Serial.println("no networks found");
} else {
for (int i = 0; i < n; ++i) {
printString = "";
PrintLn();
printString = WiFi.SSID(i) + " " + WiFi.RSSI(i);
PrintLn();
boolean validNetwork = false;
for (int j = 0; j < noAllowedNetworks; j++) {
if (WiFi.SSID(i) == allowedNetworks[j]) {
validNetwork = true;
if (WiFi.RSSI(i) < strongest) {
strongestNetwork = WiFi.SSID(i);
strongest = WiFi.RSSI(i);
}
}
}
if (!validNetwork) {
printString = WiFi.SSID(i) + " " + WiFi.RSSI(i) + " is not an allowed Network";
PrintLn();
}
}
}
delay(5000);
WiFi.scanDelete();
printString = "Connecting to " + strongestNetwork;
PrintLn();
WiFi.begin(strongestNetwork, password);
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println(F("Connection Failed! Rebooting..."));
enterDeepSleep();
}
Serial.println(F("WiFi connected"));
Serial.println(WiFi.localIP());
}
void setupMQTT() {
mqttClient.setServer(mqtt_server, 1883);
mqttClient.setCallback(mqttCallback);
if (mqttClient.connect(ipAddress.c_str(), MQTT_username, MQTT_password)) {
Serial.println("Connected to MQTT Broker");
mqttClient.subscribe("Moisture/#");
mqttClient.subscribe("Moisture/ipAddress/#");
}
}
void print_wakeup_reason() {
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
switch (wakeup_reason) {
case ESP_SLEEP_WAKEUP_EXT0: Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1: Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER: Serial.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD: Serial.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP: Serial.println("Wakeup caused by ULP program"); break;
default: Serial.printf("Wakeup was not caused by deep sleep: %d\n", wakeup_reason); break;
}
}
void setupTimeClient() {
timeClient.setTimeOffset(utcOffsetInSeconds);
timeClient.begin();
timeClient.forceUpdate();
}
void requestDeviceInfo() {
ipAddress = WiFi.localIP().toString();
String requestTopic = "Moisture/RequestDeviceInfo";
mqttClient.publish(requestTopic.c_str(), ipAddress.c_str());
requestTopic = "Moisture/Release";
mqttClient.publish(requestTopic.c_str(), RELEASE);
}
void readAndPublishSensorData() {
int moistureValue = analogRead(moisturePin);
Serial.print("Raw Moisture: ");
Serial.println(String(moistureValue));
delay(500);
int rawBatteryValue = analogRead(batteryPin);
Serial.print("Raw adc: ");
Serial.println(String(rawBatteryValue));
float sensivity = (4.2 / 4095.0);
float maxVoltage = 4.2;
float batteryVoltage = rawBatteryValue * sensivity;
printString = "Voltage: " + String(batteryVoltage) + "V";
PrintLn();
int batteryPercentage = int((batteryVoltage / maxVoltage) * 100.0);
printString = "Battery percentage: " + String(batteryPercentage) + "%";
PrintLn();
int percentageHumidity = map(moistureValue, WaterValue, AirValue, 100, 0);
printString = "Moistness " + String(moistureValue);
PrintLn();
printString = "Moisture% " + String(percentageHumidity);
PrintLn();
String baseTopic = "Moisture/" + uniqueNumber + "/";
publishMQTT((baseTopic + "Time/").c_str(), timeClient.getFormattedTime());
publishMQTT((baseTopic + "Moistness/").c_str(), String(moistureValue));
publishMQTT((baseTopic + "PercentMoistness/").c_str(), String(percentageHumidity));
publishMQTT((baseTopic + "BatteryVoltage/").c_str(), String(batteryVoltage));
publishMQTT((baseTopic + "PercentVoltage/").c_str(), String(batteryPercentage));
printString = baseTopic;
PrintLn();
}
void enterDeepSleep() {
// esp_sleep_enable_timer_wakeup(SECONDS_TO_SLEEP * uS_TO_S_FACTOR);
uint64_t seconds_to_sleep = SECONDS_TO_SLEEP;
uint64_t us_to_s_factor = uS_TO_S_FACTOR;
esp_sleep_enable_timer_wakeup(seconds_to_sleep * us_to_s_factor);
Serial.println("Setup ESP32 to sleep for every " + String(SECONDS_TO_SLEEP) + " Seconds");
esp_deep_sleep_start();
}
void mqttCallback(char* topic, byte* message, unsigned int length) {
printString = "Message arrived on topic: " + String(topic) + ". Message: ";
String messageText;
for (int i = 0; i < length; i++) {
messageText += (char)message[i];
}
printString += messageText;
PrintLn();
String topicStr = String(topic);
if (topicStr.endsWith("/UniqueNumber")) {
uniqueNumber = messageText;
String uniqueTopic = "Moisture/" + uniqueNumber + "/#";
mqttClient.subscribe(uniqueTopic.c_str());
gotUniqueNumber = true;
delay(500);
} else if (topicStr.endsWith("/AirValue")) {
AirValue = messageText.toInt();
} else if (topicStr.endsWith("/WaterValue")) {
WaterValue = messageText.toInt();
} else if (topicStr.endsWith("/SecondsToSleep")) {
SECONDS_TO_SLEEP = messageText.toInt();
gotSecondsToSleep = true;
delay(500);
} else if (topicStr.endsWith("/PlantName")) {
plantName = messageText.toInt();
gotPlantName = true;
delay(500);
}
}
boolean reconnect() {
if (mqttClient.connect(ipAddress.c_str())) {
mqttClient.publish("outTopic", "hello world");
mqttClient.subscribe("Moisture/#");
WiFi.disconnect();
delay(12000);
ESP.restart();
}
return mqttClient.connected();
}
void publishMQTT(const char* topic, const String& payload) {
mqttClient.publish(topic, payload.c_str());
}
void Print() {
Serial.print(printString);
}
void PrintLn() {
printString = timeClient.getFormattedTime() + " - " + printString;
Serial.println(printString);
}
void loop() {
esp_task_wdt_reset();
delay(1000); // Added to repeatedly reset the Watch Dog Timer
if (!mqttClient.connected()) {
long now = millis();
if (reconnect()) {
} else {
enterDeepSleep();
}
}
mqttClient.loop();
if (gotUniqueNumber == true && gotPlantName == true && gotSecondsToSleep == true) {
printString = "Got all data..." + uniqueNumber + " " + plantName + " " + SECONDS_TO_SLEEP;
PrintLn();
readAndPublishSensorData();
delay(2000);
enterDeepSleep();
}
}
float mapf(float x, float in_min, float in_max, float out_min, float out_max) {
float a = x - in_min;
float b = out_max - out_min;
float c = in_max - in_min;
return a * b / c + out_min;
}
[Codebox=cpp file=Untitled.cpp][/Codebox]
This is the code for the example
Code: Select all
//#include <WiFi.h>
//#include <NTPClient.h>
//#include <WiFiUdp.h>
//#include <PubSubClient.h>
#include <esp_sleep.h>
#include <esp_task_wdt.h>
//3 seconds WDT
#define WDT_TIMEOUT 3000
//if 1 core doesn't work, try with 2
#define CONFIG_FREERTOS_NUMBER_OF_CORES 2
#define CONFIG_ESP_SYSTEM_PANIC CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT
esp_task_wdt_config_t twdt_config = {
.timeout_ms = WDT_TIMEOUT,
.idle_core_mask = (1 << CONFIG_FREERTOS_NUMBER_OF_CORES) - 1, // Bitmask of all cores
.trigger_panic = true,
};
void setup() {
Serial.begin(115200);
Serial.println("Configuring WDT...");
esp_task_wdt_deinit(); //wdt is enabled by default, so we need to deinit it first
esp_task_wdt_init(&twdt_config); //enable panic so ESP32 restarts
esp_task_wdt_add(NULL); //add current thread to WDT watch
}
int i = 0;
int last = millis();
void loop() {
// resetting WDT every 2s, 5 times only
if (millis() - last >= 2000 && i < 5) {
Serial.println("Resetting WDT...");
esp_task_wdt_reset();
delay(1000);
last = millis();
i++;
if (i == 5) {
Serial.println("Stopping WDT reset. CPU should reboot in 3s");
}
}
}