ESP32 Tutorial – Using xTask


Introduction

In the ESP32 IDF (Espressif IoT Development Framework), FreeRTOS is the real-time operating system core for managing multitasking. Task is one of the core concepts in FreeRTOS. Each task is an independent execution thread and can run concurrently. Below is a detailed description of the role of FreeRTOS tasks in ESP32.

Difference Between xTask & vTask

In FreeRTOS, the xTask and vTask prefixes are used to distinguish different types of API functions. Understanding their differences will help you use these functions correctly.

1. xTask function
The xTask prefix is ​​typically used for functions that return a value. This value usually represents the execution result of the function, or some status information.
Return value type: Usually BaseType_t (indicating success or failure of the operation), or an indicator type (such as TaskHandle_t).

2. vTask function
The vTask prefix is ​​used for functions that do not return any value (void type). Such functions usually perform some operation without telling the caller the result of the operation.
Return value type: void (no return value).

Task – Create

1. Overview
In FreeRTOS, a task is an independent thread of execution code. Each task has its own stack, priority, status, and execution time slice. Tasks run under the FreeRTOS scheduler, which is responsible for switching between different tasks.

2. Create
Task creation uses the xTaskCreate function, whose basic syntax is as follows...
BaseType_t xTaskCreate(
    TaskFunction_t pvTaskCode,       
    const char * const pcName,       
    uint16_t usStackDepth,           
    void *pvParameters,              
    UBaseType_t uxPriority,          
    TaskHandle_t *pxCreatedTask
);
Detailed description of each parameter:
pvTaskCode: Pointer to the task function. This function defines the behavior of the task, that is, what the task performs.
pcName: The name of the task. This name is mainly used to identify the task during debugging, making it easy to view and track the execution of the task. The name does not have to be unique.

usStackDepth: Specifies the size of the task stack. The unit is "word" (word), not byte (byte). On most platforms, a word is usually 4 bytes. The stack size must be large enough to ensure that the task can run properly.

pvParameters: Parameters passed to the task. This indicator will be passed to the task function when the task is started. This allows you to pass specific parameters to the task when it is started.

uxPriority: The priority of the task. FreeRTOS schedules tasks according to this priority, and tasks with higher priority will be executed before low-priority tasks. If multiple tasks have the same priority, they are executed in turn based on time slices.

pxCreatedTask: Pointer to the task handle variable. After the task is successfully created, this variable will save the handle of the task. If you do not need to reference this task later, you can set this parameter to NULL.

Return value:
This function returns a value of type BaseType_t. If the task is created successfully, pdPASS is usually returned. If the creation fails (for example, out of memory), an error code such as errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY is returned.
void taskFunction(void *pvParameter) {
    while (1) {
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void app_main() {
    // Create a new task with the following parameters:
    // taskFunction: The function that defines the task's operations.
    // "TaskName": The name of the task for debugging purposes.
    // 2048: The stack size for the task, in words (not bytes).
    // NULL: The parameter passed to the task function (no parameters needed here).
    // 5: The priority of the task (higher numbers indicate higher priority).
    // NULL: Task handle (not needed in this case, so passing NULL).
    xTaskCreate(taskFunction, "TaskName", 2048, NULL, 5, NULL);
}

Task – Priority

Task priority determines the order in which tasks are executed. Higher priority tasks will get CPU time more often than lower priority tasks. Priorities in FreeRTOS range from 0 (lowest priority) to configMAX_PRIORITIES - 1 (highest priority).
#define tskIDLE_PRIORITY 0    // The priority level assigned to the idle task
#define configMAX_PRIORITIES  5  // The maximum priority level available for tasks

Task – Stack

Each task has a stack that stores local variables and function call information during task execution. The size of the stack is specified when the task is created and must be large enough to accommodate all the data required for task execution.
#define STACK_SIZE 2048 // Task stack size (in words)

void app_main() {
    xTaskCreate(taskFunction, "TaskName", STACK_SIZE, NULL, 5, NULL);
}

Task – Time Slicing

FreeRTOS uses time slicing (round-robin scheduling) and priority-based scheduling to switch between tasks. When a task's time slice expires, or when a higher-priority task becomes ready to run, the scheduler performs a context switch to transfer control to the appropriate task.

vTaskDelay & vTaskDelayUntil

vTaskDelay is used to put a task to sleep for a period of time.
vTaskDelay(pdMS_TO_TICKS(1000));
vTaskDelayUntil causes tasks to run at fixed intervals to avoid time drift.
TickType_t xLastWakeTime = xTaskGetTickCount();
vTaskDelayUntil(&xLastWakeTime, pdMS_TO_TICKS(1000));

Task – Delete

Delete yourself.
void taskFunction(void *pvParameter) {
    // Perform task operations
    vTaskDelete(NULL); // Delete the current task
}
Removed by other tasks.
void app_main() {
    TaskHandle_t taskHandle;
    xTaskCreate(taskFunction, "TaskName", 2048, NULL, 5, &taskHandle);
    // Delete the task
    vTaskDelete(taskHandle);
}

Install VSCode & ESP-IDF VSCode Extension

Make sure you have installed and configured the ESP-IDF development environment. You can also refer to the article the ESP32 Tutorial – ESP-IDF With VSCode.

Create A New ESP32 Project

We can use VSCode's IDF plug-in to create a new ESP32 project. Please refer to ESP32 Tutorial – How To Create An ESP32 Project With VSCode.

xTask & vTask Example

#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <stdio.h>

// Task to blink an LED
void ledBlinkTask(void *pvParameters) {
    while (1) {
        // Simulate LED on
        printf("LED ON\n");
        vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 milliseconds

        // Simulate LED off
        printf("LED OFF\n");
        vTaskDelay(pdMS_TO_TICKS(500)); // Delay for 500 milliseconds
    }
}

// Task to monitor system status
void monitorTask(void *pvParameters) {
    while (1) {
        // Simulate monitoring
        printf("Monitoring system...\n");
        vTaskDelay(pdMS_TO_TICKS(2000)); // Delay for 2 seconds
    }
}

// Task that runs and then deletes itself
void selfDeletingTask(void *pvParameters) {
    printf("Self-Deleting Task is running...\n");

    // Delay for 3 seconds
    vTaskDelay(pdMS_TO_TICKS(3000));

    printf("Self-Deleting Task will delete itself now.\n");

    // Delete this task
    vTaskDelete(NULL); // NULL means delete the calling task
}

void app_main() {
    // Create LED blink task with priority 3
    xTaskCreate(ledBlinkTask, "LED Blink Task", 2048, NULL, 3, NULL);

    // Create monitoring task with priority 2
    xTaskCreate(monitorTask, "Monitor Task", 2048, NULL, 2, NULL);

    // Create a self-deleting task with priority 1
    xTaskCreate(selfDeletingTask, "Self-Deleting Task", 2048, NULL, 1, NULL);
}

Compile, Flash & Monitor

Find the "ESP32-IDF: Build, Flash and Monitor" ICON in VSCode to execute and burn.

Results

We can view the printing results on the terminal, as follows...
LED ON
LED OFF
LED ON
LED OFF
Monitoring system...
LED ON
LED OFF
LED ON
LED OFF
Self-Deleting Task is running...
Monitoring system...
LED ON
LED OFF
LED ON
LED OFF
Self-Deleting Task will delete itself now.
Monitoring system...
LED ON
LED OFF
...

Conclusion

In ESP32 IDF, FreeRTOS tasks are the foundation for building complex, versatile embedded applications. Through tasks, you can effectively manage concurrent operations, improve system response speed, optimize resource usage, and achieve modular and maintainable code structures. The flexibility and power of FreeRTOS make it a key tool for embedded development on the ESP32.