embeddedesp32freertos

Embedded ESP32 FreeRTOS: Task watchdog got triggered


I'm trying to run 6 tasks in FreeRTOS on my embedded ESP32 board and got this error:

E (51687) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (51687) task_wdt:  - IDLE0 (CPU 0)
E (51687) task_wdt: Tasks currently running:
E (51687) task_wdt: CPU 0: Read Modbus
E (51687) task_wdt: CPU 1: Connect MQTT
E (51687) task_wdt: Aborting.

The program will then reboot. I also can't read Modbus data as it returns timeout error, eventhough I can read it in superloop code. Here's my code:

#include <Arduino.h>
#include <ModbusRTUClient.h>
#include <ArduinoRS485.h>
#include <PubSubClient.h>
#include <RtcDS3231.h>
#include <iostream>
#include <string>
#include <Wire.h>
#include <SPI.h>
#include "FS.h"
#include "SD.h"

#define TINY_GSM_MODEM_SIM7600
#define SIM_RXD       32
#define SIM_TXD       33
#define PUSH_INTERVAL 60000

using namespace std;

#include <TinyGsmClient.h>

HardwareSerial SerialAT(1);
TinyGsm modem(SerialAT);
TinyGsmClient client(modem);
PubSubClient mqtt(client);
RtcDS3231 <TwoWire> Rtc(Wire);  
RtcDateTime now = Rtc.GetDateTime();
RtcDateTime compiled; // Time at which the program is compiled

float volume, ullage;
char logString[200];
char monitorString[200];
String speed, latitude, altitude, longitude;
// GPRS credentials
const char apn[] = "v-internet";
const char gprsUser[] = "";
const char gprsPass[] = "";
// WiFi credentials
const char* ssid = "VIETCIS";
const char* password = "0976168268";
// MQTT credentials
const char* topic = "";
const char* broker = "";
const char* clientID = "";
const char* brokerUser = "";

// Task handles
static TaskHandle_t lteTaskHandle = NULL;
static TaskHandle_t gpsTaskHandle = NULL;
static TaskHandle_t modbusTaskHandle = NULL;
static TaskHandle_t rtcTaskHandle = NULL;
static TaskHandle_t sdTaskHandle = NULL;
static TaskHandle_t mqttTaskHandle = NULL;

// Task delay times
const TickType_t lteDelay = pdMS_TO_TICKS(5000);
const TickType_t gpsDelay = pdMS_TO_TICKS(1000);
const TickType_t modbusDelay = pdMS_TO_TICKS(1000);
const TickType_t rtcDelay = pdMS_TO_TICKS(60000);
const TickType_t sdDelay = pdMS_TO_TICKS(5000);
const TickType_t mqttDelay = pdMS_TO_TICKS(1000);

// Task prototypes
void connectLTE(void *pvParameters);
void readGPS(void *pvParameters);
void readModbus(void *pvParameters);
void checkRTC(void *pvParameters);
void logToSD(void *pvParameters);
void connectMQTT(void *pvParameters);

//Function prototypes
void appendFile(fs::FS &fs, const char * path, const char * message);
void writeFile(fs::FS &fs, const char * path, const char * message);
void mqttCallback(char* topic, byte* message, unsigned int len);
void parseGPS(String gpsData);
String getValue(String data, char separator, int index);

void setup() {
  // Initialize serial communication
  Serial.begin(115200);
  SerialAT.begin(115200, SERIAL_8N1, 32, 33);
  vTaskDelay(pdMS_TO_TICKS(3000));

  Serial.println("Initializing modem...");
  modem.restart();

  // Enable GPS
  Serial.println("Enabling GPS...");
  modem.sendAT("+CGPS=1,1");  // Start GPS in standalone mode
  modem.waitResponse(10000L);
  Serial.println("Waiting for GPS data...");
  vTaskDelay(pdMS_TO_TICKS(500));

  // Print out compile time
  Serial.print("Compiled: ");
  Serial.print(__DATE__);
  Serial.println(__TIME__);
  vTaskDelay(pdMS_TO_TICKS(500));

  // Initialize the Modbus RTU client
  while (!ModbusRTUClient.begin(9600)) {
    Serial.println("Failed to start Modbus RTU Client!");
    Serial.println("Trying to reconnect");
    ModbusRTUClient.begin(9600);
  }
  vTaskDelay(pdMS_TO_TICKS(500));

  // Initialize DS3231 communication
  Rtc.Begin();
  compiled = RtcDateTime(__DATE__, __TIME__);
  Serial.println();
  // Disable unnecessary functions of the RTC DS3231
  Rtc.Enable32kHzPin(false);
  Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone);

  // Initialize the microSD card
  if(!SD.begin(5)){
    Serial.println("Card Mount Failed");
    return;
  }
  uint8_t cardType = SD.cardType();
  if(cardType == CARD_NONE){
    Serial.println("No SD card attached");
    return;
  }
  // Check SD card type
  Serial.print("SD Card Type: ");
  if(cardType == CARD_MMC){
    Serial.println("MMC");
  } else if(cardType == CARD_SD){
    Serial.println("SDSC");
  } else if(cardType == CARD_SDHC){
    Serial.println("SDHC");
  } else {
    Serial.println("UNKNOWN");
  }
  // Check SD card size
  uint64_t cardSize = SD.cardSize() / (1024 * 1024);
  Serial.printf("SD Card Size: %lluMB\n", cardSize);
  vTaskDelay(pdMS_TO_TICKS(500));
  // If the log.txt file doesn't exist
  // Create a file on the SD card and write the data labels
  File file = SD.open("/log.txt");
  if(!file) {
    Serial.println("File doens't exist");
    Serial.println("Creating file...");
    writeFile(SD, "/log.txt", "Date,Time,Latitude, Longitude,\
                              Speed(km/h),Altitude(m), Volume(l),Ullage(l)\n");
  }
  else {
    Serial.println("File already exists");  
  }
  file.close();

  // Initialize MQTT broker
  mqtt.setServer(broker, 1883);
  mqtt.setCallback(mqttCallback);

  Serial.println("FAFNIR TANK LEVEL");

  // Create FreeRTOS tasks
  xTaskCreate(connectLTE, "Connect LTE", 2048, NULL, 1, &lteTaskHandle);
  xTaskCreate(readGPS, "Read GPS", 2048, NULL, 1, &gpsTaskHandle);
  xTaskCreate(readModbus, "Read Modbus", 1280, NULL, 1, &modbusTaskHandle);
  xTaskCreate(checkRTC, "Check RTC", 2048, NULL, 1, &rtcTaskHandle);
  xTaskCreate(logToSD, "Log to SD", 3072, NULL, 1, &sdTaskHandle);
  xTaskCreate(connectMQTT, "Connect MQTT", 2048, NULL, 1, &mqttTaskHandle);

  vTaskDelete(NULL);
}

void loop() {

}

void connectLTE(void *pvParameters){
  while(1){
    Serial.print("Connecting to APN: ");
    Serial.println(apn);
    if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
    Serial.println("Fail to connect to LTE network");
    ESP.restart();
    } else {
    Serial.println("OK");
    }

    if (modem.isGprsConnected()) {
      Serial.println("GPRS connected");
    }
    vTaskDelay(lteDelay);
  }
}

void readGPS(void *pvParameters){
  while(1){
    modem.sendAT("+CGPSINFO");
    if (modem.waitResponse(10000L, "+CGPSINFO:") == 1) {
      String gpsData = modem.stream.readStringUntil('\n');

      // Check if the data contains invalid GPS values
      if (gpsData.indexOf(",,,,,,,,") != -1) {
        Serial.println("Error: GPS data is invalid (no fix or no data available).");
      } else {
        Serial.println("Raw GPS Data: " + gpsData);
        // Call a function to parse the GPS data if valid
        parseGPS(gpsData);
      }
      vTaskDelay(pdMS_TO_TICKS(5000));
    } else {
      Serial.println("GPS data not available or invalid.");
    }
    vTaskDelay(gpsDelay);
  }
}

void readModbus(void *pvParameters){
  while(1){
    volume = ModbusRTUClient.holdingRegisterRead<float>(4, 0x0036, BIGEND);
    if (volume < 0) {
      Serial.print("Failed to read volume: ");
      Serial.println(ModbusRTUClient.lastError());
    } else {
      Serial.println("Volume: " + String(volume));
    }
    vTaskDelay(pdMS_TO_TICKS(100));

    ullage = ModbusRTUClient.holdingRegisterRead<float>(4, 0x003C, BIGEND);
    if (ullage < 0) {
      Serial.print("Failed to read ullage: ");
      Serial.println(ModbusRTUClient.lastError());
    } else {
      Serial.println("Ullage: " + String(ullage));
    }
    vTaskDelay(modbusDelay);
  }
}

void checkRTC(void *pvParameters){
  while(1){
    // Validate RTC date and time
    if (!Rtc.IsDateTimeValid())
    {
      if ((Rtc.LastError()) != 0)
      {
        Serial.print("RTC communication error = ");
        Serial.println(Rtc.LastError());
      }
      else
      {
        Serial.println("RTC lost confidence in the DateTime!");
        Rtc.SetDateTime(compiled);
      }
    }
    // Ensure the RTC is running
    if (!Rtc.GetIsRunning())
    {
      Serial.println("RTC was not actively running, starting now");
      Rtc.SetIsRunning(true);
    }
    // Compare RTC time with compile time
    if (now < compiled)
    {
      Serial.println("RTC is older than compile time! (Updating DateTime)");
      Rtc.SetDateTime(compiled);
    }
    else if (now > compiled)
    {
      Serial.println("RTC is newer than compiled time! (as expected)");
    }
    else if (now == compiled)
    {
      Serial.println("RTC is the same as compile time! (not expected but acceptable)");
    }
    vTaskDelay(rtcDelay);
  }
}

void logToSD(void *pvParameters){
  while(1){
    char datestring[20];
    char timestring[20];
    snprintf_P(datestring,
              countof(datestring),
              PSTR("%02u/%02u/%04u"),
              now.Month(),
              now.Day(),
              now.Year());
    snprintf_P(timestring,
              countof(timestring),
              PSTR("%02u:%02u:%02u"),
              now.Hour(),
              now.Minute(),
              now.Second());
    snprintf(logString, sizeof(logString), "%s,%s,%s,%s,%s,%s,%f,%f\n",
            datestring, timestring, latitude, longitude, speed, altitude, volume, ullage);
    appendFile(SD, "/log.txt", logString);
    snprintf(monitorString, sizeof(monitorString), "%s %s\nLatitude: %s\nLongtitude: %s\
            \nSpeed: %s(km/h)\nAltitude: %s(m)\nVolume: %.1f(l)\nUllage: %.1f(l)",
            datestring, timestring, latitude, longitude, speed, altitude, volume, ullage);
    mqtt.publish(topic, monitorString);
    vTaskDelay(sdDelay);
  }
}

void connectMQTT(void *pvParameters){
  while(1){
    while (!mqtt.connected()) {
      Serial.print("Attempting MQTT connection...");
      // Attempt to connect
      if (mqtt.connect(clientID)) {
        Serial.println("Connected");
        // Subscribe
        mqtt.subscribe(topic);
      } else {
        Serial.print("Failed, rc=");
        Serial.print(mqtt.state());
        Serial.println("Try again in 5 seconds");
        vTaskDelay(mqttDelay);
      }
    }
    mqtt.loop();
    vTaskDelay(pdMS_TO_TICKS(100));
  }
}

void parseGPS(String gpsData){
  // Split the string by commas
  int index = 0;
  latitude = getValue(gpsData, ',', 0) + getValue(gpsData, ',', 1);
  longitude = getValue(gpsData, ',', 2) + getValue(gpsData, ',', 3);
  altitude = getValue(gpsData, ',', 6);
  speed = getValue(gpsData, ',', 7);
}

String getValue(String data, char separator, int index) {
  int found = 0; // Found separator
  int strIndex[] = {0, -1}; // Array to hold the start and end index of the value
  int maxIndex = data.length() - 1; // Get the maximum index of the string

  // Loop ends when it reaches the end of the string and there are more separators than indices
  for (int i = 0; i <= maxIndex && found <= index; i++) {
    if (data.charAt(i) == separator || i == maxIndex) {
      found++;
      strIndex[0] = strIndex[1] + 1;
      strIndex[1] = (i == maxIndex) ? i+1 : i;
    }
  }
  return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}

void mqttCallback(char* topic, byte* message, unsigned int len) {
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String messageTemp;
  
  for (int i = 0; i < len; i++) {
    Serial.print((char)message[i]);
    messageTemp += (char)message[i];
  }
  Serial.println();
}

void writeFile(fs::FS &fs, const char * path, const char * message) {
  Serial.printf("Writing file: %s\n", path);

  File file = fs.open(path, FILE_WRITE);
  if(!file) {
    Serial.println("Failed to open file for writing");
    return;
  }
  if(file.print(message)) {
    Serial.println("File written");
  } else {
    Serial.println("Write failed");
  }
  file.close();
}

void appendFile(fs::FS &fs, const char * path, const char * message){
  Serial.printf("Appending to file: %s\n", path);

  File file = fs.open(path, FILE_APPEND);
  if(!file){
    Serial.println("File does not exist, creating file...");
    file = fs.open(path, FILE_WRITE);  // Create the file
    if(!file){
      Serial.println("Failed to create file");
      return;
    }
  }
  if(file.print(message)){
      Serial.println("Message appended");
  } else {
    Serial.println("Append failed");
  }
  file.close();
}

I have tried adding more non-blocking vTaskDelay and changing the priority of the tasks but the same error happens. I'm still new to FreeRTOS. Any help is appreciated.


Solution

  • Use esp_err_t esp_task_wdt_init(uint32_t timeout_seconds, bool panic) to configure watchdog timeout in setup(). The range of timeout is 0-60 secs. panic sets the alarm:true if alarm is needed, otherwise it's false