Wemos D1 Mini ESP32 fails to reboot after Watchdog Timeout

DonEvans
Posts: 3
Joined: Mon Sep 23, 2024 10:41 am

Wemos D1 Mini ESP32 fails to reboot after Watchdog Timeout

Postby DonEvans » Sat Nov 09, 2024 3:02 pm

I have a number of Wemos D1 Mini ESP32 running on Battery Power each one collecting Moisture data from a single Capacitive Soil Moisture Sensor and transmitting the data back to a server using MQTT. The application only requires at the most, 2 readings per day from the sensor so the D1 Mini is put into Deep Sleep for the intervening period. For testing and debugging purposes the sleep interval is obtained from the server each time the processor awakens. I have observed that under certain circumstances that connection to the network fails in a way that is not handled properly by my code and the processor is left in an uncontrolled running condition until the battery is depleted.

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>
on running the example again the Watchdog timer was triggered correctly but once again failed to reboot.

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");
      }
  }
}

aliarifat794
Posts: 196
Joined: Sun Jun 23, 2024 6:18 pm

Re: Wemos D1 Mini ESP32 fails to reboot after Watchdog Timeout

Postby aliarifat794 » Sun Nov 10, 2024 9:06 am

You can try

Code: Select all


esp_task_wdt_init(WDT_TIMEOUT / 1000, true);  // Timeout in seconds
esp_task_wdt_add(NULL);  // Add the main loop task


This keeps the setup simple and often avoids conflicts with other library-initialized tasks.

DonEvans
Posts: 3
Joined: Mon Sep 23, 2024 10:41 am

Re: Wemos D1 Mini ESP32 fails to reboot after Watchdog Timeout

Postby DonEvans » Sun Nov 10, 2024 9:35 am

esp_task_wdt_init(WDT_TIMEOUT / 1000, true); // Timeout in seconds
On Arduino IDE 2.3.3 this generates a compilation error due to updates in the esp_task_wdt_h library.

C:\Users\xxxAppData\Local\Temp\.arduinoIDE-unsaved20241010-55776-14jr3r0.x6wb\sketch_nov10a\sketch_nov10a.ino: In function 'void setup()':
C:\Users\xxx\AppData\Local\Temp\.arduinoIDE-unsaved20241010-55776-14jr3r0.x6wb\sketch_nov10a\sketch_nov10a.ino:25:32: error: invalid conversion from 'int' to 'const esp_task_wdt_config_t*' [-fpermissive]
25 | esp_task_wdt_init(WDT_TIMEOUT / 1000, true); // Timeout in seconds
C:\Users\xxx\AppData\Local\Temp\.arduinoIDE-unsaved20241010-55776-14jr3r0.x6wb\sketch_nov10a\sketch_nov10a.ino:25:19: error: too many arguments to function 'esp_err_t esp_task_wdt_init(const esp_task_wdt_config_t*)'
25 | esp_task_wdt_init(WDT_TIMEOUT / 1000, true); // Timeout in seconds
| ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from C:\Users\xxx\AppData\Local\Temp\.arduinoIDE-unsaved20241010-55776-14jr3r0.x6wb\sketch_nov10a\sketch_nov10a.ino:6:
C:\Users\xxx\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.3-a0f798cf\esp32/include/esp_system/include/esp_task_wdt.h:47:11: note: declared here
47 | esp_err_t esp_task_wdt_init(const esp_task_wdt_config_t *config);
| ^~~~~~~~~~~~~~~~~

exit status 1

Compilation error: invalid conversion from 'int' to 'const esp_task_wdt_config_t*' [-fpermissive]

Who is online

Users browsing this forum: No registered users and 71 guests