Arduino ESP32 FreeRTOS 2: How to use Task Parameter - Task Priorities - Task Handle - Idle Task Hook

1. Introduction
- In order to create a task in FreeRTOS, we use this API below. It has some parameters that we knew in Arduino ESP32 FreeRTOS 1. In this tutorial we will learn how to use other parameters: "void *pvParameters" and "UBaseType_t uxPriority" and "TaskHandle_t *pxCreatedTask"
BaseType_t xTaskCreate( TaskFunction_t pvTaskCode,
                        const char * const pcName,
                        uint16_t usStackDepth,
                        void *pvParameters,
                        UBaseType_t uxPriority,
                        TaskHandle_t *pxCreatedTask )

2. Demos
2.1.1 Task Parameter
- We use "void *pvParameters" to transfer input to TaskFunction_t. For example we have a task function: "void hello_world_task(void* param)" so "void *pvParameters" is "void* param". In this demo, when we create a task and pass parameter to it, this parameter will be transfered to task function and we print this parameter to Terminal.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void setup() {

  Serial.begin(112500);
  char task1Param[12] = "task1Param";
  /* we create a new task here */
  xTaskCreate(
      task1,           /* Task function. */
      "task1",        /* name of task. */
      10000,                    /* Stack size of task */
      (void *)task1Param,                     /* parameter of the task */
      1,                        /* priority of the task */
      NULL);                    /* Task handle to keep track of created task */
}

/* the forever loop() function is invoked by Arduino ESP32 loopTask */
void loop() {

}
/* this function will be invoked when additionalTask was created */
void task1( void * parameter )
{
  Serial.println((char *)parameter);
  /* loop forever */
  for(;;){
    Serial.println("task1 is running");
    delay(1000);
  }
  /* delete a task when finish, 
  this will never happen because this is infinity loop */
  vTaskDelete( NULL );
}
2.1.2 Result

Figure: How Task Parameter work
2.2.1 Task Priorities
- There are 3 types of scheduling: Co-operative Scheduling and Prioritized Pre-emptive Scheduling with time slicing and without time slicing.
+ Prioritized Pre-emptive Scheduling with time slicing: task with higher priority will pre-empt the tasks that have lower priority. And tasks that have the same priority will run in turn every time tick interrupt occurs (RTOS has a timer interrupt to measure the time, every time the interrupt occurs RTOS will check that it is time to unblock or wake a task).
+ Prioritized Pre-emptive Scheduling without time slicing: task with higher priority will pre-empt the tasks that have lower priority. And tasks that have the same priority will not run in turn every time tick interrupt occurs (the task that is running, will continue running until it is pre-empted by high priority task).
+ Co-operative Scheduling: context switch occur when running task change from Running state to Blocked state or call taskYIELD(). The next running task is the task that has highest priority and is in Running state.
2.2.1.1 Demo for Prioritized Pre-emptive Scheduling with time slicing
- Currently, Arduino ESP32 FreeRTOS configuration use Prioritized Pre-emptive Scheduling with time slicing so we just make demo for this type of scheduling. We create 2 tasks: task1 has priority is 1, task2 has priority is 4. task1 will print the strings "task1 is running" and "task1 is ending" to terminal. task2 will print the strings "task2 is running" and "task2 is ending" to terminal. So most of the time you will see the output on terminal is come from task2.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
void setup() {

  Serial.begin(112500);
  char task1Param[12] = "taskParam";
  /* we create a new task here */
  xTaskCreate(
      task1,           /* Task function. */
      "task1",        /* name of task. */
      10000,                    /* Stack size of task */
      (void *)task1Param,                     /* parameter of the task */
      1,                        /* priority of the task */
      NULL);                    /* Task handle to keep track of created task */
  /* let task1 run first then create task2 */
  xTaskCreate(
      task2,           /* Task function. */
      "task2",        /* name of task. */
      10000,                    /* Stack size of task */
      (void *)task1Param,                     /* parameter of the task */
      4,                        /* priority of the task */
      NULL);                    /* Task handle to keep track of created task */
}

/* the forever loop() function is invoked by Arduino ESP32 loopTask */
void loop() {

}
/* this function will be invoked when additionalTask was created */
void task1( void * parameter )
{
  Serial.println((char *)parameter);
  /* loop forever */
  for(;;){
    Serial.println("task1 is running");
    Serial.println("task1 is ending");
  }
  /* delete a task when finish, 
  this will never happen because this is infinity loop */
  vTaskDelete( NULL );
}

/* this function will be invoked when additionalTask was created */
void task2( void * parameter )
{
  Serial.println((char *)parameter);
  /* loop forever */
  for(;;){
    Serial.println("task2 is running");
    Serial.println("task2 is ending");
  }
  /* delete a task when finish, 
  this will never happen because this is infinity loop */
  vTaskDelete( NULL );
}
2.2.1.2 Result
 Figure: Demo for Prioritized Pre-emptive Scheduling with time slicing
2.2.2 Task Handle
- Task Handle keep the reference of created task so we can operate on the created task such as delete it, change priority of it.
2.2.2.1 Demo
- Let 's re-use the Demo in 2.2.1.1 and change requirement a little bit. When task2 run 20 times, we will change the priority of task1 to 4 and task2 to 1 so now you will see on terminal, most of the time the output is from task1.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/* variables to hold instances of tasks*/
TaskHandle_t t1 = NULL;
TaskHandle_t t2 = NULL;
int count = 0;

void setup() {

  Serial.begin(112500);
  char task1Param[12] = "taskParam";
  /* we create a new task here */
  xTaskCreate(
      task1,           /* Task function. */
      "task1",        /* name of task. */
      10000,                    /* Stack size of task */
      (void *)task1Param,                     /* parameter of the task */
      1,                        /* priority of the task */
      &t1);                    /* Task handle to keep track of created task */
  /* let task1 run first then create task2 */
  xTaskCreate(
      task2,           /* Task function. */
      "task2",        /* name of task. */
      10000,                    /* Stack size of task */
      (void *)task1Param,                     /* parameter of the task */
      4,                        /* priority of the task */
      &t2);                    /* Task handle to keep track of created task */
}

/* the forever loop() function is invoked by Arduino ESP32 loopTask */
void loop() {

}
/* this function will be invoked when additionalTask was created */
void task1( void * parameter )
{
  Serial.println((char *)parameter);
  /* loop forever */
  for(;;){
    Serial.println("task1 is running");
    Serial.println("task1 is ending");
  }
  /* delete a task when finish, 
  this will never happen because this is infinity loop */
  vTaskDelete( NULL );
}

/* this function will be invoked when additionalTask was created */
void task2( void * parameter )
{
  Serial.println((char *)parameter);
  /* loop forever */
  for(;;){
    count++;
    /* if count is 20 then we swap the priority of 2 tasks */
    if(count == 20){
      vTaskPrioritySet( t1, 4 );
      vTaskPrioritySet( t2, 1 );
    }
    Serial.println("task2 is running");
    Serial.println("task2 is ending");
  }
  /* delete a task when finish, 
  this will never happen because this is infinity loop */
  vTaskDelete( NULL );
}
2.2.2.2 Result
 Figure: using task handle to change priorities
2.2.3 Idle Task Hook Function
- The Idle task (background task) is the task:
+ Create by scheduler.
+ Has the lowest priority (priority is 0)
+ Will be in Running state when all  created tasks are in Blocked state.
- It is possible to hook a function in the Idle task. This function will be invoked when the Idle task run.
- We can use this task to:
+ Do some thing in background.
+ Measure spare time of system.
+ Put the micro controller in low power mode when there is nothing to do.
+ Clean up resources when tasks are deleted.
Note: In the task hook function do not do any thing that block the Idle task.
- In order to use task hook function just implement the function that has the prototype like below in the code:
"bool vApplicationIdleHook( void )" and then call "esp_register_freertos_idle_hook(vApplicationIdleHook)" to register that function to the core.
2.2.3.1 Demo
- Create a task hook function and whenever it is called it will increase the counter by 1 and in the created task function just print the counter.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include "esp_freertos_hooks.h"
int counter = 0;

void setup() {

  Serial.begin(112500);
  esp_register_freertos_idle_hook(vApplicationIdleHook);
  char task1Param[12] = "task1Param";
  /* we create a new task here */
  xTaskCreate(
      task1,           /* Task function. */
      "task1",        /* name of task. */
      10000,                    /* Stack size of task */
      (void *)task1Param,                     /* parameter of the task */
      1,                        /* priority of the task */
      NULL);                    /* Task handle to keep track of created task */
}

/* the forever loop() function is invoked by Arduino ESP32 loopTask */
void loop() {

}
/* this function will be invoked when additionalTask was created */
void task1( void * parameter )
{
  Serial.println((char *)parameter);
  /* loop forever */
  for(;;){
    Serial.print("task1 is running and counter : ");
    Serial.println(counter);
    if(counter > 30000)
    {
      counter = 0;  
    }
    /* block this task so that Idle task has chance to run */
    delay(10);
  }
  /* delete a task when finish, 
  this will never happen because this is infinity loop */
  vTaskDelete( NULL );
}

/* define task hook function */
bool vApplicationIdleHook(void)
{
  counter++;
}
2.2.3.2 Result
 Figure: counter is increased

Post a Comment

1 Comments

Joe said…
Greetings,

I've been enjoying your esp32 tutorials, especially the FreeRTOS series. I've tried to run your example 2.2.3 Idle Task Hook Function, but when I run it the blocking delay never causes the vApplicatinIdleHook to be run, leaving the counter forever at zero. I've tried different delay periods, and also set the counter reset value to much greater that 30000, but nothing I've tried seems to get the idle task to run. I see the same behavior on three different esp32 boards from three different manufacturers. Could you run your demo on your system again and see if perhaps something in the core libraries has changed. Any advice you can give to get your demo working for me is much appreciated.

Joe