Can't execute codes in loop() function under "Arduino UNO R3 + Arduino_FreeRTOS.h" multi-tasking environment

I have three independent tasks depicted in Fig-1, which have equal priorities and are running concurrently very well being responsive to interrupt button K1 and Pot1 under "Arduino UNO R3 + Arduino_FreeRTOS.h" multi-tasking environment. The tasks are:
(1) LED2 freely blinks at 4 sec interval,
(2) LED1's blink rate depends on ADC-Ch1 voltage, and
(3) L (built-in LED of UNOR3) blinks five times at 1-sec interval when interrupt button K1 is pressed.

The complete sketch is given below, which I have prepared by consulting the examples of the Arduino IDE.

Now, I want to blink LED3 freely of Fig-1 at 1-sec interval by including the following codes in the loop() function. The result is this that the system becomes hanged (no activities of LED1, LED2, and LED3) after the uploading of the sketch.

  digitalWrite(12, HIGH);
  vTaskDelay(1000 / portTICK_PERIOD_MS );
  digitalWrite(12, LOW);
  vTaskDelay(1000 / portTICK_PERIOD_MS );

I would appreciate to receive your guidance (solution hints along with underlying theory) to make the above codes working in the loop() function.

led4Sw1FRTOS
Figure-1:

Complete Sketch:

#include <Arduino_FreeRTOS.h>
#include <queue.h>
#include <semphr.h>

QueueHandle_t integerQueue;
SemaphoreHandle_t interruptSemaphore;
#define INT0 2

// define three tasks for Blink & AnalogRead
void TaskBlinkL( void *pvParameters ); //blinks L at 1 sec interval
void TaskBlinkLED2( void *pvParameters ); //blinks LED2 at 2 sec interval
void TaskAnalogReadA1( void *pvParameters );  //task that publiseh value of A1-pin in Queue
void TaskPwm( void *pvParameters ); //blink rate of LED1 depends on value of A1-pin

void setup()
{
  Serial.begin(9600);

  while (!Serial)
  {
    ; // wait for serial port to connect.
  }
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT); //output line for LED3
  pinMode(13, OUTPUT); //built-in LED (L)
  pinMode(INT0, INPUT_PULLUP);
  //----initialize PWM (Mode 14 FPWM) at DPin-9 -----
  pinMode(9, OUTPUT); //Ch-A
  TCCR1A = 0x00;   //reset
  TCCR1B = 0x00;   //TC1 reset and OFF
  //fOC1A/B = clckSys/(N*(1+ICR1); Mode-14 FPWM; OCR1A controls duty cycle
  // 1 Hz = 16000000/(256*(1+ICR1) N=1,8,64,256,1024==> ICR1 = 62499
  // 20 Hz ; ICR1 = 3124
  TCCR1A |= (1 << WGM11); //Mode-14 Fast PWM
  TCCR1B |= (1 << WGM13) | (1 << WGM12);    //Mode-14 Fast PWM
  TCCR1A |= (1 << COM1A1) | (0 << COM1A0);  //Non-invert: HIGH-LOW

  ICR1 = 62499;   // TOP for 1Hz frequnecy
  OCR1A = 31250;  //= 50% duty cycle
  TCNT1 = 0;

  TCCR1B |= (1 << CS12);//TC1 statrt with N = 256;
  //--------------------------------------------
  // Now set up tasks to run independently.
  interruptSemaphore = xSemaphoreCreateBinary();
  if (interruptSemaphore != NULL)
  {
    attachInterrupt(digitalPinToInterrupt(2), interruptHandler, LOW);// Attach interrupt
  }
  //-----------------------------------------------

  integerQueue = xQueueCreate(
                   10,
                   sizeof (int));  //Queue length, queue item size

  if (integerQueue != NULL)
  {
    xTaskCreate( // Create task that consumes the queue if it was created.
      TaskPwm, // Task function
      "PWMLED1", // A name just for humans
      128,
      NULL,
      1, // Priority
      NULL);

    xTaskCreate(  // Create task that publish data in the queue if it was created.
      TaskAnalogReadA1,
      "AnalogReadA1",
      128,  // Stack size
      NULL,
      1,  // Priority
      NULL );
  }
  //----------------------------------------------------

  xTaskCreate(
    TaskBlinkL,
    "BlinkL",   // A name just for humans
    128,
    NULL,
    1,  // Priority
    NULL );

  xTaskCreate(
    TaskBlinkLED2,
    "BlinkLED2",
    128,
    NULL,
    1, // Priority
    NULL );
  // Now the task scheduler, which takes over control
  //of scheduling individual tasks, is automatically started.
}

void loop()  //called upon by Task Idle of FreeRTOS
{
  /*digitalWrite(12, HIGH);
  vTaskDelay(1000 / portTICK_PERIOD_MS );
  digitalWrite(12, LOW);
  vTaskDelay(1000 / portTICK_PERIOD_MS );*/
}

//---------------------------------------------

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

  while (true) // A Task shall never return or exit.
  {
    digitalWrite(11, HIGH);
    vTaskDelay( 2000 / portTICK_PERIOD_MS ); // wait for 2 second
    digitalWrite(11, LOW);
    vTaskDelay( 2000 / portTICK_PERIOD_MS ); // wait for 2 second
  }
}

//--------------------------------------------
void TaskAnalogReadA1(void *pvParameters)
{
  (void) pvParameters;

  while (true)
  {
    int adcValue = analogRead(A1);
    xQueueSend(integerQueue, &adcValue, portMAX_DELAY);
    vTaskDelay(1);// One tick delay (15ms) in between reads for stability
  }
}

void TaskPwm(void * pvParameters)
{
  (void) pvParameters;
  int valueFromQueue = 0;

  while (true)
  {
    if (xQueueReceive(integerQueue, &valueFromQueue, portMAX_DELAY) == pdPASS)
    {
      Serial.print("ADC-A1 value received via Queue: ");
      Serial.println(valueFromQueue, DEC); //0 to 1023
      Serial.println("===================");
      ICR1 = map(valueFromQueue, 0, 1023, 62499, 3124); //1Hz (FCCW) - 20Hz(FCW)
      OCR1A = ICR1 / 2; //maintains 50% duty cycle
      vTaskDelay(1000 / portTICK_PERIOD_MS );
    }
  }
}
//------------------------------

void interruptHandler()
{
  xSemaphoreGiveFromISR(interruptSemaphore, NULL);
}
//-------------------------------

void TaskBlinkL(void *pvParameters)
{
  (void) pvParameters;

  while (true)
  {
    if (xSemaphoreTake(interruptSemaphore, portMAX_DELAY) == pdPASS)
    {
      for (int i = 0; i < 5; i++)
      {
        digitalWrite(13, HIGH);
        vTaskDelay(500 / portTICK_PERIOD_MS ); //500 ms delay
        digitalWrite(13, LOW);
        vTaskDelay(500 / portTICK_PERIOD_MS );
      }
    }
  }
}

Very strange, sorry you got stuck..
Have you added some prints into setup to see how far it gets..
Don't know anything about timers, so won't be any help there, sorry..
The only thing that strikes me as a strange design pattern TaskAnalogReadA1 and TaskPwm..
TaskAnalogReadA1 does an analogRead and stores this into a Q for TaskPwm to process..
And it does this every 15 ms, Q which has a len of 10 ints, is filled in 150ms..
Now TaskPwm comes along, pops an int off the Q, does some serial prints, some math for the timer, adjusts the timer (i think??), then sits and spins for 1000ms..

This blocks TaskAnalogReadA1, the values you are seeing in TaskPwm are old..

curious, what happens if you crank that vTaskDelay up too 1000ms in TaskAnalogReadA1..
Can you then start blinking the led in the loop??

How much memory is left??

good luck.. ~q

1 Like

I have tried increasing the delay, but the blinking codes under loop() function are not working.

The sketch of post #1 takes --

10936 bytes of flash and
445 bytes of RAM

Please, look into the following simple case with two independent tasks (LED2 at DPin-11 blinks at 2-sec interval and LED3 at DPin-12 blinks at 4-sec interval) and they work very well as long as there are no blinking codes in the loop() function.

The activities of the LEDs are stopped when I upload a sketch with blinking codes (L at DPin-13 to blink at 1-sec interval) in the loop() function. This is not the case for ESP32 (Fig-1) where the loop() function is existing very well with other tasks.

Do you think that there could be some limitations (for example: does not allow loop function to co-exist somoothly with other tasks) in the design of the Arduino_FreeRTOS.h Library? It is made by an individual and NOT by Real Time Engineers Company, the owner of FreeRTOS.

The following is an excerpt from the webpage of the above link:
"The FreeRTOS idle task is used to run the loop() function whenever there is no unblocked FreeRTOS task available to run. In the trivial case, where there are no configured FreeRTOS tasks, the loop() function will be run exactly as normal, with the exception that a short scheduler interrupt will occur every 15 milli-seconds ."

#include <Arduino_FreeRTOS.h>

TaskHandle_t myTaskA;
TaskHandle_t myTaskB;


void setup()
{
  pinMode(11, OUTPUT); //LED2
  pinMode(12, OUTPUT);  //LED3
  pinMode(13, OUTPUT);  //L

  xTaskCreate(myTaskFunctionA, "MyTaskA", 128, NULL, 1, &myTaskA);
  xTaskCreate(myTaskFunctionB, "MyTaskB", 128, NULL, 1, &myTaskB);
}

void loop()//Led-L 
{
  /*digitalWrite(13, HIGH);
  vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 ms
  digitalWrite(13, LOW);
  vTaskDelay(pdMS_TO_TICKS(500));*/
}

void myTaskFunctionA(void *pvParameters)//LED2
{
  for (;;)
  {
    digitalWrite(11, HIGH);
    vTaskDelay(pdMS_TO_TICKS(1000)); // Delay for 1 second
    digitalWrite(11, LOW);
    vTaskDelay(pdMS_TO_TICKS(1000)); 
  }
}

void myTaskFunctionB(void *pvParameters)//LED3
{
  for (;;)
  {
    digitalWrite(12, HIGH);
    vTaskDelay(pdMS_TO_TICKS(2000)); // Delay for 2 second
    digitalWrite(12, LOW);
    vTaskDelay(pdMS_TO_TICKS(2000)); 
  }
}


Figure-1:

I've used RTOS only with the ESP32 which does not have the same resource limitations as the Uno. If you suspect it is the way the loop() is configured then simply put nothing other than a short delay in it (say 10ms) and create a third RTOS task with the 4 statements that you would have put in the loop().
Can you also load your code into Wokwi so others can copy and play with it ?

1 Like

I will try as I occasionally play with Wokwi.

ChatGPT has also offered me similar advice.

Why are you using queues and tasks? it's over complicated. Is this is a requirement of an assigment or something?

Otherwise, you could just keep track of the status of each led and the last time it changed. Then in the loop you compare with the current time and depending of the state and the other conditions, switch off or on.

It is an assignment to myself to understand the vocabularies and principles of multi-tasking using Arduino UNO R3 and Arduino_FreeRTOS.h platform.

1 Like

For LED3 you could create another tasks like the one of LED2.
Or include in that tasks the manamegent of both leds, changing one or the other or both LEDs after the right delays.

HI @GolamMostafa

On the Phillip Stevens AVR port of FreeRTOS, it's possible to implement the idle task hook either within the loop(), or alternatively as a vApplicationIdleHook() function (keeping loop() empty):

void vApplicationIdleHook()
{
  // Do something here when there's nothing better to do
  // ...
}

However, there are limitations on the use of the idle task:

  1. The first is that this task should never be blocked or suspended.
  2. The second being that the idle task must be allowed to run a "reasonable time" after calling a vTaskDelete() function. This is because the idle task in involved in internal kernel resource housekeeping behind the scenes.

Adding a vTaskDelay() function within the idle task is essentially blocking it.

1 Like

My intention is to understand the reasons why the blinking codes are not working in the loop() function. I will stop running after it once it is established that Arduino_FreeRTOS.h does not support the smooth co-existence of loop() function with other independent tasks in Arduino UNO R3 (ATmega328P) architecture.

Hi @GolamMostafa

The AVR port of FreeRTOS does support the smooth co-existance of the loop() function together with other independent taks, but it's necessary to follow the rules and guidance laid out in the "Mastering the FreeRTOS Real Time Kernel" guide.

The guide (irrespective of the microcontroller that's used) stipulates that:

Therefore calling the vTaskDelay() function in the loop() breaks rule 1, as the loop() function is the idle task hook.

If you were instead to use a non-blocking Arduino millis() function to implement a "Blink" sketch within the loop, the code would function as intended, for example:

#include <Arduino_FreeRTOS.h>

#define LED_INTERVAL 500

TaskHandle_t myTaskA;
TaskHandle_t myTaskB;

uint32_t currentMillis, previousMillis;

void setup()
{
  pinMode(11, OUTPUT); //LED2
  pinMode(12, OUTPUT);  //LED3
  pinMode(13, OUTPUT);  //L

  xTaskCreate(myTaskFunctionA, "MyTaskA", 128, NULL, 1, &myTaskA);
  xTaskCreate(myTaskFunctionB, "MyTaskB", 128, NULL, 1, &myTaskB);
  previousMillis = millis();
}

void loop()//Led-L 
{
  static bool toggle = false;
  
  currentMillis = millis();
  if (currentMillis - previousMillis >= LED_INTERVAL)
  {
    if (toggle)
    {
      digitalWrite(13, HIGH);
    }
    else
    {
      digitalWrite(13, LOW);
    }
    toggle = !toggle;
    previousMillis = currentMillis;
  }
}

void myTaskFunctionA(void *pvParameters)//LED2
{
  for (;;)
  {
    digitalWrite(11, HIGH);
    vTaskDelay(pdMS_TO_TICKS(1000)); // Delay for 1 second
    digitalWrite(11, LOW);
    vTaskDelay(pdMS_TO_TICKS(1000)); 
  }
}

void myTaskFunctionB(void *pvParameters)//LED3
{
  for (;;)
  {
    digitalWrite(12, HIGH);
    vTaskDelay(pdMS_TO_TICKS(2000)); // Delay for 2 second
    digitalWrite(12, LOW);
    vTaskDelay(pdMS_TO_TICKS(2000)); 
  }
}
1 Like

@GolamMostafa
Sorry for asking that - what the program did you use to generate the Figure-1 in post #3?

I think that FreeRTOS executes the loop() function in the idle time, when it's not executing anything in the tasks. Could be that vTaskDelay calls loop again and gets starting all the time. Try to print something before calling vTaskDelay.

So, I understand that you can execute things in the loop, non blocking. But maybe makes no sense to call vTaskDelay, as it is already in 'idle' time, and actually it is not a task.

You could for example do something like this (not tested):

void loop()  // non blocking loop
{
  curr_millis = millis();
  if(  curr_millis - prev_millis >= 1000){
    digitalWrite( LED3, high_low);
    high_low = !high_low;  // switch led state
    prev_millis = curr_millis;
  }
}

But, as you are using several tasks, maybe would be more consistent to use the same system for all leds. And let the loop empty, or for other housekeeping tasks not related to the peripherals management.

I have manually created the diagram under Microsoft Visio application.

1 Like

Very high quality looking.
Are you a painter || designer ?

1 Like

Your opinion is in line what I have read (quoted below) in the Arduino FreeRTOS's website. I have tried to conceptualize/express the essence of the quote with the help of Fig-1.

"Secondly, the FreeRTOS idle task is used to run the loop() function whenever there is no unblocked FreeRTOS task available to run. In the trivial case, where there are no configured FreeRTOS tasks, the loop() function will be run exactly as normal, with the exception that a short scheduler interrupt will occur every 15 milli-seconds (configurable). "

multitask-2
Fgure-1:

Thank you for the good words. Academically and professionally, I am an Electrical Engineer.

wow, ok..
curious then, did you try to make another thread for the loop led blinking..
still don't quite get why it's a solid freeze??
i would more expect it to just violate what you were expecting to see..
makes me wonder though, I do see people deleting the loop task in esp32..
been fighting something myself lately, think i'm gonna move everything out of the loop and see if it make a difference, i would think not so, but seeing what you're noticing here, all bets are off..

interesting.. ~q

1 Like

Thank you very much for your efforts to offer me an adjusted sketch in which the blinking codes work well under the hood of millis() function.

My simple curiosity is to know why the blinking codes work well in the loop() function of ESP32 without the need of millis() function (Fig-1 of post #3) but NOT in the case of Arduino UNO R3.

Hi @GolamMostafa

It probably has something to do with fact that the ESP32's loop() function is itself implemented as a FreeRTOS task:

void loopTask(void *pvParameters)
{
    setup();
    for(;;) {
#if CONFIG_FREERTOS_UNICORE
        yieldIfNecessary();
#endif
        if(loopTaskWDTEnabled){
            esp_task_wdt_reset();
        }
        loop();
        if (serialEventRun) serialEventRun();
    }
}

...where as on the AVR implementation it's not:

void vApplicationIdleHook( void )
{
    loop();                     // the normal Arduino loop() function is run here.
    if (serialEventRun) serialEventRun();
}

FreeRTOS is essentially baked into the ESP32 core code, while on other microcontrollers implementations this isn't necessarily the case.