ESP32 Arduino: Creating a FreeRTOS task

The objective of this post is to explain how to launch tasks with the FreeRTOS functions.


Introduction

The objective of this post is to explain how to launch tasks with the FreeRTOS functions. Since this will introduce some complex concepts, we will start by a very simple example where we will create two tasks that will print some “Hello World” messages and then delete them.

For most of the functionalities that we are going to use in this tutorial, you can check the .h file here. Please check this previous post for an introduction on FreeRTOS and tasks.


The setup and loop code

We will start our setup function by opening a serial connection, in order to be able to get the output of our testing program. This will be our regular Arduino function.

Serial.begin(112500);
delay(1000);

Then, we will create the tasks, with a call to the xTaskCreate function. The arguments to this function are the following [1]:

TaskCode: In this argument, we need to pass a pointer to the function that will implement the task. We will create two functions, TaskOne and TaskTwo, which we will define latter and will be passed in this argument.

TaskName: The name of the task, in a string. We will use “TaskOne” and “TaskTwo”.

StackDepth: The size of the stack of the task, specified as the number of variables it can hold (not the number of bytes). There is no simple way of determining the size of the task [2], although some calculations can be made. In this simple example, we will pass a value that is big enough.

Parameter: Pointer to a parameter that the task function can receive. It needs to be of type (void *) [2]. In this case, for simplicity of the code, we will not use it, so we pass NULL in the argument.

Priority: Priority of the task. We will create both tasks with the same priority.

TaskHandle: Returns an handle that can be used for latter reference of the task on calls to functions (for example, to delete a task or change its priority) [2]. Also, for this simple example, we are not going to use it, so it will be NULL.

This function returns pdPASS on success or an error code that can be seen here [1]. For now, we will assume that the tasks will be created without any problem, so we are not going to do any error checking. Naturally, for a real case scenario application, we would need to do that to confirm the tasks were created.

So, check bellow the full setup function, already with the calls create the two different tasks.

void setup() {

  Serial.begin(112500);
  delay(1000);

  xTaskCreate(
                    taskOne,          /* Task function. */
                    "TaskOne",        /* String with name of task. */
                    10000,            /* Stack size in words. */
                    NULL,             /* Parameter passed as input of the task */
                    1,                /* Priority of the task. */
                    NULL);            /* Task handle. */

  xTaskCreate(
                    taskTwo,          /* Task function. */
                    "TaskTwo",        /* String with name of task. */
                    10000,            /* Stack size in words. */
                    NULL,             /* Parameter passed as input of the task */
                    1,                /* Priority of the task. */
                    NULL);            /* Task handle. */

}

In the main loop, we won’t do anything because our tasks will implement all the functionality. So let’s just put a delay.

void loop() {
   delay(1000);
}


The task functions

Now, we only need to specify the functions for the tasks. Remember that we want to create a task implemented by function TaskOne and other implemented by function TaskTwo.

Note that task are implemented with regular functions and they only need to follow a predefined function prototype [3]. So, they must return void and they must receive as input an argument of type (void *) [3]. Check an example bellow.

void taskOne( void * parameter )

A very important thing to take in consideration is that this function should not return. So, they must not contain a return statement or execute until the end of the code [3]. Instead, they should be explicitly deleted [3].

To delete a task from inside its own code, we just need  to call the vTaskDelete function. This function receives as input the handle of the task to be deleted [4] (remember the previously mentioned argument of the xTaskCreate that we are not going to use). Nevertheless, if we pass NULL as input the calling task will be deleted [4], which is what we want, since we are going to call it from the task’s own code.

vTaskDelete( NULL );

Other that these particularities, we are going to implement the two functions in a very simple way. Basically, we are going to do a simple loop where we are going to print a “Hello World” from each task and, after all the iterations of the loop, we will print a message stating the task will end.

Check the full code for this tutorial bellow, with both our tasks implemented.

void setup() {

  Serial.begin(112500);
  delay(1000);

  xTaskCreate(
                    taskOne,          /* Task function. */
                    "TaskOne",        /* String with name of task. */
                    10000,            /* Stack size in words. */
                    NULL,             /* Parameter passed as input of the task */
                    1,                /* Priority of the task. */
                    NULL);            /* Task handle. */

  xTaskCreate(
                    taskTwo,          /* Task function. */
                    "TaskTwo",        /* String with name of task. */
                    10000,            /* Stack size in words. */
                    NULL,             /* Parameter passed as input of the task */
                    1,                /* Priority of the task. */
                    NULL);            /* Task handle. */

}

void loop() {
  delay(1000);
}

void taskOne( void * parameter )
{

    for( int i = 0;i<10;i++ ){

        Serial.println("Hello from task 1");
        delay(1000);
    }

    Serial.println("Ending task 1");
    vTaskDelete( NULL );

}

void taskTwo( void * parameter)
{

    for( int i = 0;i<10;i++ ){

        Serial.println("Hello from task 2");
        delay(1000);
    }
    Serial.println("Ending task 2");
    vTaskDelete( NULL );

}


Running the code

To run the code, simply upload it to the ESP32 board using the Arduino IDE. You should get a result similar to figure 1, with both “Hello World” messages being printed to the serial console. In the end, also both the termination messages should be printed.

ESP32 Create FreeRTOS Task

Figure 1 – Output of the program.

Note that both tasks were running in parallel, which is way the prints from each task are mixed. Naturally, the time of execution for each one of the tasks was decided by the RTOS scheduler.


Related Posts


References

[1] http://esp32.info/docs/esp_idf/html/dd/d3c/group__xTaskCreate.html

[2] http://www.freertos.org/Documentation/161204_Mastering_the_FreeRTOS_Real_Time _Kernel-A_Hands-On_Tutorial_Guide.pdf   [Page 50]

[3] http://www.freertos.org/Documentation/161204_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf [Page 46]

[4] http://www.freertos.org/a00126.html

Advertisements
This entry was posted in ESP32 and tagged , , , , . Bookmark the permalink.

12 Responses to ESP32 Arduino: Creating a FreeRTOS task

  1. Pingback: ESP32 Arduino: Passing a variable as argument of a FreeRTOS task | techtutorialsx

  2. Pingback: ESP32 Arduino: Getting FreeRTOS tasks priority | techtutorialsx

  3. Pingback: ESP32: Get Task execution core | techtutorialsx

  4. Pingback: ESP32: Running code on a specific core | techtutorialsx

  5. Pingback: ESP32: FreeRTOS counting semaphores | techtutorialsx

  6. Pingback: ESP32: Dual core execution speedup | techtutorialsx

  7. Luke says:

    Nice tutorial.
    Let me understand it better: in the setup() function you create the two tasks, but you never call the vTaskStartScheduler();
    Is the scheduler invoked behind the scene, out of the setup function?

    Another question: you’re calling the Serial.println method from inside the two tasks. Is this method re-entrant? it seems so, looking at the output on the serial console.

    Thanks for your kind answer.

    Liked by 1 person

    • antepher says:

      Hi! Thank you very much for the feedback 🙂

      Yes it is not needed, but I’m not 100% sure if it is called in the Arduino core initializations or if it is enabled by default. I haven’t yet explored much that initialization part of the Arduino core.

      Since the Serial port is a shared resource, it should have had a lock to ensure that there is no concurrent access. Nonetheless, since this was an introductory tutorial to tasks in FreeRTOS, I did not wan’t to complicate it with synchronization primitives.

      If I’m not mistaken, the implementation of the Serial.println function doesn’t have any blocking calls and writes the bytes to a buffer to be then sent asynchronously. So all the characters are written to the buffer by the first task and then when it calls the delay it wields and gives the CPU to the other task, which writes its string to the serial port, and so on.

      And that’s why we are seeing this print pattern. Nonetheless, the Arduino core has many abstraction layers until it reaches the bare metal (and implementations change over time), so the best approach is actually performing the exclusive access synchronization.

      Hope to have helped.

      Best regards,
      Nuno Santos

      Like

  8. programmer131 says:

    thank you for this nice tutorial.

    Liked by 1 person

  9. Pingback: ESP32 Arduino: Communication between tasks using FreeRTOS queues | techtutorialsx

  10. Pingback: ESP32 Arduino: FreeRTOS queues performance test | techtutorialsx

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s