1

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.

4
  • That's a lot of code. Did it previously work and now it doesn't? What did you change? Commented Oct 22, 2024 at 22:32
  • All your tasks seem to be running at the same priority. This means that if one of them happens to spin in a loop, it will starve out all the others, which will never run. Unless you have defined configUSE_PREEMPTION to be 1. Commented Oct 22, 2024 at 22:36
  • @pmacfarlane the only change I did was removing the void loop() function bcuz I didn't want the whole system delayed if a module has an error. Before that, the code runs perfectly. Commented Oct 23, 2024 at 0:54
  • ... See instructables.com/FreeRTOS-With-Arduino-02-Task-Switching and instructables.com/FreeRTOS-With-Arduino-03-Task-Idle-Hook for example. Commented Oct 23, 2024 at 6:29

2 Answers 2

0

You need at least an empty loop() function:

void loop()
{
   // FreeRTOS idle hook
}

The FreeRTOS idle thread invokes loop() as an idle hook, and the default supplied when you do not provide an override is clearly unsuitable (probably a busy-wait trap).

The loop() may be used for non real-time background work, but must not block indefinitely as that will starve the watchdog. Generally any significant work should be deferred to a RTOS thread with appropriate priority.

Note that this is not specifically a FreeRTOS issue, but rather an artefact of using FreeRTOS under the Arduino framework.

Sign up to request clarification or add additional context in comments.

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.