Best way to have 30 minutes repetitive tasks with FreeRTOS?

I am trying to get a 30 minutes repetitive task to send data over MQTT in a project of mine but I cannot get it to even reach 10 minutes (600000 milliseconds) without the device being reset and data never sent.

Hardware is an ESP32 WROOM dev board and I use Arduino IDE to program it.

Here is the code that gives me a headache as I cannot find an example for long delays/pauses on the internet so far.

void TaskIOADA(void *pvParameters) {  // This is a task.
  (void)pvParameters;

  TickType_t xLastWakeTime;
  const TickType_t xFrequency = 300000; //5 minutes cannot reach 10 minutes (600000) ==> reset and data never sent

    // Initialise the xLastWakeTime variable with the current time.
  xLastWakeTime = xTaskGetTickCount();
  for (;;){

vTaskDelayUntil( &xLastWakeTime, xFrequency/ portTICK_PERIOD_MS );
  io.run();
.... Other stuff being done there....

If someone knows how to get this task to run every 30 minutes, I would be grateful!
It has to be a task as I am running multiple different task on this device like blinking an LED + reading an analog sensor which could trigger the LED to get solid and stop readings for an extended period of time after closing a timed valve then reopening the timed valve + reading periodically 2 other sensors + sending data over MQTT every 30 minutes.

What is the underlying type, it may be too small.

If the task exits after doing his job you can relaunch the task every 30 minutes.

No. That's a snippet only. Please post the entire code or figure it out Yourself.

That's milliseconds.

Correct, my bad, that is milliseconds indeed.

The task runs just fine if set to 5 minutes but not at least 10.

Try
const TickType_t xFrequency = 32766
and
const TickType_t xFrequency = 32770

You didn't include the code used to create the task, nor any messages that were printed when restarting.

I don't have an ESP32 handy, but this worked in Wokwi

#include <esp_task_wdt.h>

void setup() {
  Serial.begin(115200);
  Serial.println("Hello, ESP32!");
  Serial.print(portTICK_PERIOD_MS);
  Serial.println(" portTICK_PERIOD_MS");
  xTaskCreateUniversal(
    [](void *pvParameters) {
      constexpr TickType_t MINUTES = 60 * 1000 / portTICK_PERIOD_MS;
      constexpr TickType_t xFrequency = 10 * MINUTES;
      TickType_t xLastWakeTime = xTaskGetTickCount();

      for ( ;; ) {
        vTaskDelayUntil( &xLastWakeTime, xFrequency );
        Serial.print(millis());
        Serial.println(" task");
      }
    }, "Infrequent",
    CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE, nullptr,
    uxTaskPriorityGet(nullptr), nullptr, ARDUINO_RUNNING_CORE);

}

void loop() {
  Serial.print(millis());
  Serial.println(" loop");
  delay(4567);
}

Understood, but you are using a task that 'never ends' you call it once and then it executes stay dormant and executes again...
You can simply call the task that executes an then exits, after the 30 minuts period you recall that task.... ( this way if you need to change the period... )

If I may add ( its a personal preference/consideration... ) the mqtt task has his reasons ( you could bind the task to a core different from the one used by sensor and so on ), as it may you may need to rejoing the net, the server, something unexpected...

...but a task for a 'simple task' as flashing a led or operating a valve it seems an unnecessary complication ( simply wondering how do these task communicate each other ) this can be simply done in the main task ( the loop ) using as many finite machine as needed each state machine do a simple task an then returns immediately, advancing to a different state when necessary ( you could add as many as needed of states machines and of states )

I don't understand why everyone makes such a problem of this.

#define ONE_MINUTE (1000UL * 60UL)

void myTask(void *param)
{
  for(;;)
  {
    do something

    delay(30UL * ONE_MINUTE);
  }
}

The delay() function is here: https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-misc.c#L202
It is:

void delay(uint32_t ms) {
  vTaskDelay(ms / portTICK_PERIOD_MS);
}

And portTICK_PERIOD_MS is 1 on a ESP32.

I have not tested it for 30 minutes, that takes too long :yawning_face:

Believe me I have tried millis and it is just not feasible with my project, all examples you find everywhere are blinking LEDs only which is not helping at all. Operating a valve in a task makes a lot of sense since it will have to be actuated one way (open or closed) for a specific amount of time then switched off, then triggered according to external events, the valve status will be indicated by the LED.

Why a task to blink an LED? To not interfere with the other tasks as this LED will change state according to different status.

I agree with the task recall but I have yet to grasp the concept, all examples are too vague for me.

  1. That is simply not true. There are literally thousands of examples in threads on this site that do all sorts of things.

  2. If you understand how that code gets the blinking function to be called at intervals, then you can just replace that led line with anything you want as long as it is non-blocking.

I've been around this forum for 13 years now. I've seen this sentiment so many times. Believe me when I tell you that trying to use tasks and RTOS to get around learning how to write code that doesn't block is actually the harder thing to do. Because even with the RTOS you still need to understand the idea behind the other thing. And skipping that step and jumping straight to RTOS just becomes confusing. You're left with the options of copying working code and being satisfied or trying to change it and being frustrated because you don't understand it.

Believe me when I tell you from experience, this is easier the other way. It doesn't sound like it would be. But in reality the easier path for you lies in that direction. It's still a little hard, but not nearly as rough as the path you have chosen.

My point exactly. It's because you still haven't learned the vital lessons that come along with writing non-blocking that things like this still confuse you. Once you learn the non-blocking way, then if you still want to use RTOS you will at least understand how it works behind the scenes and you will better be able to take advantage of it.

I wished it worked but it crashes when above 5 minutes, below is the error stack for 10 minutes:

2:18:14.020 -> [600059][E][NetworkClient.cpp:315] setSocketOption(): fail on 0, errno: 9, "Bad file number"
12:18:14.020 -> 0
12:18:14.020 -> [600068][E][NetworkClient.cpp:315] setSocketOption(): fail on 0, errno: 9, "Bad file number"
12:18:14.063 -> [600076][E][NetworkClient.cpp:315] setSocketOption(): fail on 0, errno: 9, "Bad file number"
12:18:14.063 -> [600085][E][NetworkClient.cpp:315] setSocketOption(): fail on 0, errno: 9, "Bad file number"
12:18:14.063 -> [600093][E][NetworkClient.cpp:315] setSocketOption(): fail on 0, errno: 9, "Bad file number"
12:18:14.086 -> [600101][E][NetworkClient.cpp:315] setSocketOption(): fail on 0, errno: 9, "Bad file number"
12:18:14.627 -> Guru Meditation Error: Core  0 panic'ed (Unhandled debug exception). 
12:18:14.627 -> Debug exception reason: Stack canary watchpoint triggered (IO Adafruit) 
12:18:14.627 -> Core  0 register dump:
12:18:14.627 -> PC      : 0x40089cc8  PS      : 0x00060f36  A0      : 0x80138e54  A1      : 0x3ffb2a60  
12:18:14.627 -> A2      : 0x3ffb2a90  A3      : 0x00000000  A4      : 0x000000d8  A5      : 0x3ffb2b40  
12:18:14.659 -> A6      : 0x3ffba000  A7      : 0x0000000d  A8      : 0x8012c2ac  A9      : 0x3ffb2d50  
12:18:14.659 -> A10     : 0xffffffc4  A11     : 0x3ffb2d7c  A12     : 0x00000030  A13     : 0x3ffb2efc  
12:18:14.659 -> A14     : 0x00000000  A15     : 0x00000018  SAR     : 0x00000010  EXCCAUSE: 0x00000001  
12:18:14.659 -> EXCVADDR: 0x00000000  LBEG    : 0x40089cc8  LEND    : 0x40089cd3  LCOUNT  : 0x00000001  
12:18:14.691 -> 
12:18:14.691 -> 
12:18:14.691 -> Backtrace: 0x40089cc5:0x3ffb2a60 0x40138e51:0x3ffb2a70 0x401349e8:0x3ffb2a90 0x40131474:0x3ffb2b90 0x4012fe09:0x3ffb2bb0 0x4012ff0a:0x3ffb2c20 0x4012ff4e:0x3ffb2cd0 0x401300b1:0x3ffb2d50 0x4012c2a9:0x3ffb2d70 0x4012c3ea:0x3ffb2f20 0x400d6e55:0x3ffb2f70 0x400d6404:0x3ffb3080 0x400d6531:0x3ffb30d0 0x400d6632:0x3ffb3140 0x400d5ca5:0x3ffb3160 0x400d5ba1:0x3ffb3180 0x400d5c59:0x3ffb31a0 0x400d4d72:0x3ffb31c0 0x400d4de1:0x3ffb31e0 0x400d4e5d:0x3ffb3200 0x400d2fec:0x3ffb3220 0x4008ed3a:0x3ffb32b0
12:18:14.725 -> 
12:18:14.725 -> 
12:18:14.725 -> 
12:18:14.725 -> 
12:18:14.725 -> ELF file SHA256: 8f1734796684a647
12:18:14.725 -> 
12:18:14.994 -> Rebooting...

And here is the error stack from 6 minutes onwards:

13:34:33.191 -> Guru Meditation Error: Core  0 panic'ed (Unhandled debug exception). 
13:34:33.191 -> Debug exception reason: Stack canary watchpoint triggered (IO Adafruit) 
13:34:33.191 -> Core  0 register dump:
13:34:33.191 -> PC      : 0x40089cc8  PS      : 0x00060f36  A0      : 0x80138e54  A1      : 0x3ffb2a60  
13:34:33.223 -> A2      : 0x3ffb2a90  A3      : 0x00000000  A4      : 0x000000d8  A5      : 0x3ffb2b40  
13:34:33.223 -> A6      : 0x3ffba000  A7      : 0x0000000d  A8      : 0x8012c2ac  A9      : 0x3ffb2d50  
13:34:33.223 -> A10     : 0xffffffc4  A11     : 0x3ffb2d7c  A12     : 0x00000030  A13     : 0x3ffb2efc  
13:34:33.223 -> A14     : 0x00000000  A15     : 0x00000018  SAR     : 0x00000010  EXCCAUSE: 0x00000001  
13:34:33.223 -> EXCVADDR: 0x00000000  LBEG    : 0x40089cc8  LEND    : 0x40089cd3  LCOUNT  : 0x00000001  
13:34:33.255 -> 
13:34:33.255 -> 
13:34:33.255 -> Backtrace: 0x40089cc5:0x3ffb2a60 0x40138e51:0x3ffb2a70 0x401349e8:0x3ffb2a90 0x40131474:0x3ffb2b90 0x4012fe09:0x3ffb2bb0 0x4012ff0a:0x3ffb2c20 0x4012ff4e:0x3ffb2cd0 0x401300b1:0x3ffb2d50 0x4012c2a9:0x3ffb2d70 0x4012c3ea:0x3ffb2f20 0x400d6e55:0x3ffb2f70 0x400d6404:0x3ffb3080 0x400d6531:0x3ffb30d0 0x400d6632:0x3ffb3140 0x400d5ca5:0x3ffb3160 0x400d5ba1:0x3ffb3180 0x400d5c59:0x3ffb31a0 0x400d4d72:0x3ffb31c0 0x400d4de1:0x3ffb31e0 0x400d4df6:0x3ffb3200 0x400d2fec:0x3ffb3220 0x4008ed3a:0x3ffb32b0
13:34:33.289 -> 
13:34:33.289 -> 
13:34:33.289 -> 
13:34:33.289 -> 
13:34:33.289 -> ELF file SHA256: fe5ba08526b889a6
13:34:33.289 -> 
13:34:33.560 -> Rebooting...

So I do understand that everything lies in the number of ticks that seems to overflow something but I don't understand how to get around. I have tried your example with success using 5 minutes but any value above 5 minutes crashes the device and resets.

Can you show a small but working sketch that has the problem. I think that the problem is somewhere else.

Do you have the newest versions of the boards and the libraries ?

What is wrong with the Arduino delay() function as I mentioned in post #11. Did I miss something ?

OP has been asked many times and has not yet seemed to notice. I think it may be time to mute this thread and let this one go. Who knows without the code and OP is not getting the hint that the problem isn't where they think it is.

The delay() function blocks everything else when called. All my libraries and boards up to date. This is the portion of the code which is the problem. Any value above 5 minutes resets the device.

No, not when Arduino is on top of FreeRTOS or Mbed. The multitasking system keeps on running.
In my post #11 I gave a link the the source code of delay() on a ESP32 and even the code itself.


We have a saying on this forum: "The problem is in the part that you are not showing".
I made up "Koepel's law": "Fifty percent chance that the problem is in the section that was ignored from the beginning, because you thought that the problem could not be caused by that section"
There is a website for snippets: https://snippets-r-us.com/
Then some of us look at the code and your results and that is an indication that the problem is probably somewhere else.
I just follow my feeling, my feeling says that you have to prove that there is problem with a sketch that we can try.

It has been answered now but here is an ESP32 RTOS traffic light application using delay() statements where a counter in one task is running during delay() statements in another task.

This:

Debug exception reason: Stack canary watchpoint triggered

implies that you are consuming memory until the stack overflows. Either give the task more stack space or check for memory leaks etc.

EDIT
You can print out regularly the current stack size of a task using the following example:

Serial.printf("wiHandler() - Free Stack Space (2): %d \n", uxTaskGetStackHighWaterMark(NULL));

and the heap as well

char buf[120] = { 0 };
. . .
sprintf(buf, " stack HWM: %d", uxTaskGetStackHighWaterMark(NULL));
Serial.println(buf);
sprintf(buf, " free heap now: %d", esp_get_free_internal_heap_size());
Serial.println(buf);
sprintf(buf, " free heap min: %d", esp_get_minimum_free_heap_size());
Serial.println(buf);

Here we go, I removed everything else not related to this specific issue from the big project and still get the reset.


#if CONFIG_FREERTOS_UNICORE
#define ARDUINO_RUNNING_CORE 0
#else
#define ARDUINO_RUNNING_CORE 1
#endif


// Sensor libs
#include <Adafruit_Sensor.h>
#include "Adafruit_SHT4x.h"
#include <Wire.h>
#include <Adafruit_INA219.h>
Adafruit_INA219 ina219;
Adafruit_SHT4x sht4 = Adafruit_SHT4x();

// set up the IOT feeds
#include "config.h"
AdafruitIO_Feed *temperature = io.feed("decktemp");
AdafruitIO_Feed *humidity0 = io.feed("deckhumid");
AdafruitIO_Feed *Volts = io.feed("deckvolts");
AdafruitIO_Feed *mA = io.feed("deckmA");
AdafruitIO_Feed *Load = io.feed("deckLoad");

void TaskIO_ADA(void *pvParameters);
TaskHandle_t taskio_ada_task_handle;

void setup() {

  Serial.begin(115200);

    while(! Serial);
  
  xTaskCreate(
    TaskIOADA, "IO Adafruit" 
    ,
    2048  
    ,
    NULL 
    ,
    1 
    ,
    &taskio_ada_task_handle
  );

//initiate INA219 with highest sensitivity
  uint32_t currentFrequency;
  ina219.begin();
  ina219.setCalibration_16V_400mA();

  // initialize SHT40
    Serial.println("Adafruit SHT4x test");
  if (! sht4.begin()) {
    Serial.println("Couldn't find SHT4x");
    while (1) delay(1);
  }
  Serial.println("Found SHT4x sensor");
  Serial.println("Serial number 0x");
  Serial.println(sht4.readSerial(), HEX);
  
  sht4.setPrecision(SHT4X_HIGH_PRECISION);
  sht4.setHeater(SHT4X_NO_HEATER);
 
  // connect to io.adafruit.com
  Serial.print("..............Connecting to Adafruit IO");
  io.connect();

  // wait for a connection
  while(io.status() < AIO_CONNECTED) {
    Serial.print(".");
    vTaskDelay(500/ portTICK_PERIOD_MS);
  }

  // we are connected
  Serial.println();
  Serial.println(io.statusText());
}

void loop() {

}

/*--------------------------------------------------*/
/*---------------------- Tasks ---------------------*/
/*--------------------------------------------------*/


void TaskIOADA(void *pvParameters) {  // This is a task.
  (void)pvParameters;

  TickType_t xLastWakeTime;
  //const TickType_t xFrequency = 300000; //5 minutes cannot reach 10 minutes ==> reset and data never sent
  constexpr TickType_t MINUTES = 60 * 1000 / portTICK_PERIOD_MS;
  constexpr TickType_t xFrequency = 6 * MINUTES;
    // Initialise the xLastWakeTime variable with the current time.
  xLastWakeTime = xTaskGetTickCount();
  for (;;){

vTaskDelayUntil( &xLastWakeTime, xFrequency);
  io.run();

// Get temperature & Humidity
  sensors_event_t humidity, temp;
  sht4.getEvent(&humidity, &temp);// populate temp and humidity objects with fresh data
  
// Print temperature
  Serial.print("Temperature: "); Serial.print(temp.temperature); Serial.println(" °C");
  Serial.print("Humidity: "); Serial.print(humidity.relative_humidity); Serial.println(" % rH");

  // save temperature to Adafruit IO
  temperature->save(temp.temperature);

  // save humidity to Adafruit IO
  humidity0->save(humidity.relative_humidity);
  

//Reading Voltage/Current  
  float shuntv = 0;
  float busv = 0;
  float cur_mA = 0;
  float loadv = 0;

  shuntv = ina219.getShuntVoltage_mV();
  busv = ina219.getBusVoltage_V();
  cur_mA = ina219.getCurrent_mA();
  loadv = busv + (shuntv / 1000);
  
// Print Bus Voltage
   Serial.print("Bus Voltage:   "); Serial.print(busv); Serial.println(" V");

   // save Volts to Adafruit IO
   Volts->save(busv);

//Print Load Voltage
  Serial.print("Load Voltage:  "); Serial.print(loadv); Serial.println(" V");

  //save Load to Adafruit IO
  Load->save(loadv);

// Print Load Current  
  Serial.print("Current:       "); Serial.print(cur_mA); Serial.println(" mA");

  // save Amp to Adafruit IO
   mA->save(cur_mA);
  Serial.println("");
  }
}

Here is the error stack with 6 minutes set:

5:00:40.296 -> Guru Meditation Error: Core  0 panic'ed (Unhandled debug exception). 
15:00:40.296 -> Debug exception reason: Stack canary watchpoint triggered (IO Adafruit) 
15:00:40.296 -> Core  0 register dump:
15:00:40.296 -> PC      : 0x4008d817  PS      : 0x00060036  A0      : 0x4008f19f  A1      : 0x3ffb89b0  
15:00:40.345 -> A2      : 0x3ffc4058  A3      : 0x3ffc4238  A4      : 0x00000001  A5      : 0x3ffc4238  
15:00:40.345 -> A6      : 0x00000001  A7      : 0x00000000  A8      : 0x3ffc4060  A9      : 0x3ffb8990  
15:00:40.345 -> A10     : 0x3ffb91ac  A11     : 0x3ffb91ac  A12     : 0x00000000  A13     : 0x3ffb8188  
15:00:40.345 -> A14     : 0x80000001  A15     : 0xb33fffff  SAR     : 0x00000010  EXCCAUSE: 0x00000001  
15:00:40.361 -> EXCVADDR: 0x00000000  LBEG    : 0x40089ad4  LEND    : 0x40089ae2  LCOUNT  : 0x00000000  
15:00:40.361 -> 
15:00:40.361 -> 
15:00:40.361 -> Backtrace: 0x4008d814:0x3ffb89b0 0x4008f19c:0x3ffb89e0 0x4008f14c:0xa5a5a5a5 |<-CORRUPTED
15:00:40.361 -> 
15:00:40.361 -> 
15:00:40.361 -> 
15:00:40.361 -> 
15:00:40.361 -> ELF file SHA256: ba42d56f3685516f
15:00:40.361 -> 
15:00:40.591 -> Rebooting...

Here is the result with 5 minutes:

15:07:30.024 -> ........
15:07:35.137 -> Adafruit IO connected.
15:12:29.059 -> Temperature: 26.04 °C
15:12:29.059 -> Humidity: 49.18 % rH
15:12:29.059 -> Bus Voltage:   12.00 V
15:12:29.059 -> Load Voltage:  12.00 V
15:12:29.059 -> Current:       16.40 mA
15:12:29.094 ->