FreeRTOS Queue



The objective of this post is to give an introduction to FreeRTOS queues, using the ESP32 and the Arduino core.

Queues are very useful for inter-task communication, allowing to send messages from one task to another safely in terms of concurrency [1]. They are typically used as FIFOs (First In First Out) [1], meaning that new data is inserted at the back of the queue and consumed from the front.

Nonetheless, FreeRTOS has a very rich queue API, which offers a lot more functionality. You can check the queue API here.

One very important aspect to keep in mind is that the data that is inserted in the queue is copied rather that only a reference to it being stored [1]. This means that if we send an integer to the queue, its value will be actually copied and if we change the original value after that no problem should occur.

Nonetheless, we can still insert pointers to data as elements of our queue, which is useful specially if the messages to exchange are big. In this case, the pointer will be copied to the queue, not the message itself, and thus we need to guarantee that it is not changed. But this is a more advanced situation which we are not going to cover here.

Other important behavior to keep in mind is that the insertion in a full queue or the consumption of an empty queue can be made blocking calls for a specified amount of time [1] (this amount of time is a parameter of the API).

QueueHandle_t queue;
void setup() {
  queue = xQueueCreate( 10, sizeof( int ) );
  if(queue == NULL){
    Serial.println("Error creating the queue");
void loop() {
  if(queue == NULL)return;
  for(int i = 0; i<10; i++){
    xQueueSend(queue, &i, portMAX_DELAY);
  int element;
  for(int i = 0; i<10; i++){
    xQueueReceive(queue, &element, portMAX_DELAY);

If the queue is successfully create then a handle to the newly created queue is returned. If the queue cannot be created then 0 is returned.
uxQueueLength: The maximum number of items that the queue can contain.
uxItemSize: The number of bytes each item in the queue will require. Items are queued by copy, not by reference, so this is the number of bytes that will be copied for each posted item. Each item on the queue must be the same size.

pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL.
xQueue: The handle to the queue on which the item is to be posted.
pvItemToQueue: A pointer to the item that is to be placed on the queue. The size of the items the queue will hold was defined when the queue was created, so this many bytes will be copied from pvItemToQueue into the queue storage area.
xTicksToWait: The maximum amount of time the task should block waiting for space to become available on the queue, should it already be full. The call will return immediately if this is set to 0 and the queue is full. The time is defined in tick periods so the constant portTICK_PERIOD_MS should be used to convert to real time if this is required.

pdTRUE if an item was successfully received from the queue, otherwise pdFALSE.
xQueue: The handle to the queue from which the item is to be received.
pvBuffer: Pointer to the buffer into which the received item will be copied.
xTicksToWait: The maximum amount of time the task should block waiting for an item to receive should the queue be empty at the time of the call. xQueueReceive() will return immediately if xTicksToWait is zero and the queue is empty. The time is defined in tick periods so the constant portTICK_PERIOD_MS should be used to convert to real time if this is required.

similar to xQueueReceive without remove element from queue

xTaskHandle task;
xQueueHandle queue;
byte tpin=13;

void taskFunction(void* parameter){
    int val1=0;
    int* val=&val1;
    xQueueReceive(queue,&val,100);//wait the queue for 100 ms if not queue recived it will print 0

void setup(){
    Serial.println("task created");
  queue=xQueueCreate(5,sizeof(void*));//queue contain 5 integer items

void loop(){
  int val1=touchRead(tpin);
  int* val=&val1;
  xQueueSend(queue,&val,portMAX_DELAY);// if queue is full it will wait for portMAX_DELAY to available space
Tagged :

ESP32 FreeRTOS Tasks



The main job of all operating systems is to run and coordinate user tasks. Like many operating systems, the basic unit of work in FreeRTOS is the task. FreeRTOS uses a Task Control Block (TCB) to represent each task.

Task Control Block (TCB)

The TCB is defined in tasks.c like this:

typedef struct tskTaskControlBlock
  volatile portSTACK_TYPE *pxTopOfStack;                  /* Points to the location of
                                                             the last item placed on 
                                                             the tasks stack.  THIS 
                                                             MUST BE THE FIRST MEMBER 
                                                             OF THE STRUCT. */
  xListItem    xGenericListItem;                          /* List item used to place 
                                                             the TCB in ready and 
                                                             blocked queues. */
  xListItem    xEventListItem;                            /* List item used to place 
                                                             the TCB in event lists.*/
  unsigned portBASE_TYPE uxPriority;                      /* The priority of the task
                                                             where 0 is the lowest 
                                                             priority. */
  portSTACK_TYPE *pxStack;                                /* Points to the start of 
                                                             the stack. */
  signed char    pcTaskName[ configMAX_TASK_NAME_LEN ];   /* Descriptive name given 
                                                             to the task when created.
                                                             Facilitates debugging 
                                                             only. */

  #if ( portSTACK_GROWTH > 0 )
    portSTACK_TYPE *pxEndOfStack;                         /* Used for stack overflow 
                                                             checking on architectures
                                                             where the stack grows up
                                                             from low memory. */

  #if ( configUSE_MUTEXES == 1 )
    unsigned portBASE_TYPE uxBasePriority;                /* The priority last 
                                                             assigned to the task - 
                                                             used by the priority 
                                                             inheritance mechanism. */

} tskTCB;

The TCB stores the address of the stack start address in pxStack and the current top of stack in pxTopOfStack. It also stores a pointer to the end of the stack in pxEndOfStack to check for stack overflow if the stack grows “up” to higher addresses. If the stack grows “down” to lower addresses then stack overflow is checked by comparing the current top of stack against the start of stack memory in pxStack.

The TCB stores the initial priority of the task in uxPriority and uxBasePriority. A task is given a priority when it is created, and a task’s priority can be changed. If FreeRTOS implements priority inheritance then it uses uxBasePriority to remember the original priority while the task is temporarily elevated to the “inherited” priority. (See the discussion about mutexes below for more on priority inheritance.)

Each task has two list items for use in FreeRTOS’s various scheduling lists. When a task is inserted into a list FreeRTOS doesn’t insert a pointer directly to the TCB. Instead, it inserts a pointer to either the TCB’s xGenericListItem or xEventListItem. These xListItem variables let the FreeRTOS lists be smarter than if they merely held a pointer to the TCB. We’ll see an example of this when we discuss lists later.

A task can be in one of four states: running, ready to run, suspended, or blocked. You might expect each task to have a variable that tells FreeRTOS what state it’s in, but it doesn’t. Instead, FreeRTOS tracks task state implicitly by putting tasks in the appropriate list: ready list, suspended list, etc. The presence of a task in a particular list indicates the task’s state. As a task changes from one state to another, FreeRTOS simply moves it from one list to another.


Create a new task with a specified affinity.
This function is similar to xTaskCreate, but allows setting task affinity in SMP system.
pdPASS if the task was successfully created and added to a ready list, otherwise an error code defined in the file projdefs.h
pvTaskCode: Pointer to the task entry function. Tasks must be implemented to never return (i.e. continuous loop).
pcName: A descriptive name for the task. This is mainly used to facilitate debugging. Max length defined by configMAX_TASK_NAME_LEN – default is 16.
usStackDepth: The size of the task stack specified as the number of bytes.
pvParameters: Pointer that will be used as the parameter for the task being created.
uxPriority: The priority at which the task should run. Systems that include MPU support can optionally create tasks in a privileged (system) mode by setting bit portPRIVILEGE_BIT of the priority parameter. For example, to create a privileged task at priority 2 the uxPriority parameter should be set to ( 2 | portPRIVILEGE_BIT ).
pvCreatedTask: Used to pass back a handle by which the created task can be referenced.
xCoreID: If the value is tskNO_AFFINITY, the created task is not pinned to any CPU, and the scheduler can run it on any core available. Values 0 or 1 indicate the index number of the CPU which the task should be pinned to. Specifying values larger than (portNUM_PROCESSORS – 1) will cause the function to fail.


similar to xTaskCreatePinnedToCore but it will work with core 0 like main loop function
Create a new task and add it to the list of tasks that are ready to run.
Internally, within the FreeRTOS implementation, tasks use two blocks of memory. The first block is used to hold the task’s data structures. The second block is used by the task as its stack. If a task is created using xTaskCreate() then both blocks of memory are automatically dynamically allocated inside the xTaskCreate() function. (see If a task is created using xTaskCreateStatic() then the application writer must provide the required memory. xTaskCreateStatic() therefore allows a task to be created without using any dynamic memory allocation.


Remove a task from the RTOS real time kernel’s management.
The task being deleted will be removed from all ready, blocked, suspended and event lists.
INCLUDE_vTaskDelete must be defined as 1 for this function to be available. See the configuration section for more information.
The idle task is responsible for freeing the kernel allocated memory from tasks that have been deleted. It is therefore important that the idle task is not starved of microcontroller processing time if your application makes any calls to vTaskDelete ().
Memory allocated by the task code is not automatically freed, and should be freed before the task is deleted.

create two tasks with different priorities

xTaskHandle task,task2;

void taskFunction(void* parameter){
    Serial.println("task 1");
  vTaskDelete(task); //delete task once you break the loop dont forget to free the memory
void taskFunction2(void* parameter){
    Serial.println("task 2");

void setup(){
  //at first time resume first but because it has lower priority 
    in the next Time it will be resumed after task2

void loop(){

if value of core is tskNO_AFFINITY, the created task is not pinned to any CPU, and the scheduler can run it on any core available

void taskFunction(void* parameter){
    vTaskDelay(1000);//similar to delay with arduino

void setup(){
    Serial.println("task created and schedualed with ready list ");

vTaskDelay and vTaskDelayUntil
vTaskDelay () will cause a task to block for the specified number of ticks from the time vTaskDelay () is called. It is therefore difficult to use vTaskDelay () by itself to generate a fixed execution frequency as the time between a task starting to execute and that task calling vTaskDelay () may not be fixed [the task may take a different path though the code between calls, or may get interrupted or preempted a different number of times each time it executes].
Whereas vTaskDelay () specifies a wake time relative to the time at which the function is called, vTaskDelayUntil () specifies the absolute (exact) time at which it wishes to unblock.

void vTaskFunction( void * pvParameters )
TickType_t xLastWakeTime;
const TickType_t xFrequency = 10;

    // Initialise the xLastWakeTime variable with the current time.
    xLastWakeTime = xTaskGetTickCount ();
    for( ;; )
        // Wait for the next cycle.
        vTaskDelayUntil( &xLastWakeTime, xFrequency );

        // Perform action here.

eRunning = 0
A task is querying the state of itself, so must be running.
The task being queried is in a read or pending ready list.
The task being queried is in the Blocked state.
The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out.
The task being queried has been deleted, but its TCB(task control block) has not yet been freed.


void vAFunction( void )
TaskHandle_t xHandle;

 // Create a task, storing the handle.
 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );

 // Use the handle to suspend the created task.
 vTaskSuspend( xHandle );

 // The created task will not run during this period, unless
 // another task calls vTaskResume( xHandle ).

 // Resume the suspended task ourselves.
 vTaskResume( xHandle );

 // The created task will once again get microcontroller processing
 // time in accordance with its priority within the system.

Suspends the scheduler without disabling interrupts.
Context switches will not occur while the scheduler is suspended.
After calling vTaskSuspendAll () the calling task will continue to execute without risk of being swapped out until a call to xTaskResumeAll () has been made.
API functions that have the potential to cause a context switch (for example, vTaskDelayUntil(), xQueueSend(), etc.) must not be called while the scheduler is suspended.

void vTask1( void * pvParameters )
 for( ;; )
     // Task code goes here.

     // ...

     // At some point the task wants to perform a long operation during
     // which it does not want to get swapped out.  It cannot use
     // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the
     // operation may cause interrupts to be missed - including the
     // ticks.

     // Prevent the real time kernel swapping out the task.
     vTaskSuspendAll ();

     // Perform the operation here.  There is no need to use critical
     // sections as we have all the microcontroller processing time.
     // During this time interrupts will still operate and the kernel
     // tick count will be maintained.

     // ...

     // The operation is complete.  Restart the kernel.
     xTaskResumeAll ();

The count of ticks since vTaskStartScheduler was called
The number of tasks that the real time kernel is currently managing. This includes all ready, blocked and suspended tasks. A task that has been deleted but not yet freed by the idle task will also be included in the count.
It is not valid to call xTaskGetIdleTaskHandle() before the scheduler has been started.

The Idle Task
The idle task is created automatically when the RTOS scheduler is started to ensure there is always at least one task that is able to run. It is created at the lowest possible priority to ensure it does not use any CPU time if there are higher priority application tasks in the ready state.
The idle task is responsible for freeing memory allocated by the RTOS to tasks that have since been deleted. It is therefore important in applications that make use of the vTaskDelete() function to ensure the idle task is not starved of processing time. The idle task has no other active functions so can legitimately be starved of microcontroller time under all other conditions.
It is possible for application tasks to share the idle task priority (tskIDLE_PRIORITY). See the configIDLE_SHOULD_YIELD configuration parameter for information on how this behaviour can be configured.

RTOS (Real Time Operating System)

What is Real Time Operating System (RTOS)- How it works?

                When we hear the word “Operating System” the first ones that come to our mind are those we experience/use in our day to day life, say, Windows XP, Linux, Ubuntu, Windows 7 for Computer systems, Android for mobiles and many more . We mainly know that operating systems are for computers. It is a fact that most of the digital electronic devices run some sort of operating systems inside. There are many operating systems developed for micro controllers too. But here it is familiar as REAL TIME OPERATING SYSTEM. The phrase ‘REAL TIME’ indicates that the response of the operating systems is quick. Microcontrollers don’t have much space for code. Thus the operating systems have less scope to be advanced. They try to provide at least the minimum scope of threading, scheduling and monitoring of multiple tasks for small systems.

                Usually, Real Time Operating Systems are a segment or a part of the whole program that decides the next task, task priority, handles the task messages and coordinates all of the tasks. An RTOS is a complex concept. I’d like to discuss about the concept of State Machine. Here is an implementation of what you can merrily call a state machine.

{ case 1: //Code for Task 1;
state= 2;
case 2: //Code for Task 2;
state= 3;
case 3: //Code for Task 3;
state= 4;
case 4: //Code for Task 4;

As you can see from the code, there is a provision of changing the execution sequence. And it can be further modified and made complex. The programmer can modify and place decision making statements (Like if, if-else, switch-case) to switch the task. And the flow of execution can be logically determined.

                A Real time operating system handles some tasks or routines to be run. The kernel of the operating system assigns CPU attention to a particular task for a period of time. It also checks the task priority, arranges the massages from tasks and schedules.

The basic functionalities an RTOS are:

  • Scheduler
  • RTOS Services
  • Synchronization and messaging

The Scheduler

Tasks, can have three states.

  • Ready to run:  When task have all the resources to run, but not in running state. It’s called a ready to run task. It’s the state before running.
  • Running  – When a task is executing. It is known as running.
  • Blocked – When a task doesn’t have enough resources to run, it is sent to a blocked state.

        To schedule a task, three techniques are adapted.

  • Co-operative scheduling:  In this scheme, a task runs, until it completes its execution.
  • Round Robin Scheduling: Each task is assigned a fixed time slot in this scheme. The task needs to complete its execution. Otherwise, the task may lose its flow, and data generated or it would have to wait for its next turn.
  • Preemptive Scheduling:  This scheduling scheme includes priority dependent time allocation. Usually in programs, 256 priority level is generally used. Each task is assigned an unique priority level. While some system may support more priority levels, and multiple tasks may have same priorities.

The Kernel takes care of the task. It involves the following

  • Creating a task
  • Deleting a task
  • Changing the priority of the task
  • Changing state of the task

RTOS Services

The heart of every operating system is called ‘kernel’. Tasks are relived of monitoring the hardware. It’s the responsibility of the kernel to manage and allocate the resources. As tasks cannot acquire CPU attention all the time, the kernel must also provide some more services. These includes,

  • Interrupt handling services
  • Time services
  • Device management services
  • Memory management services
  • Input-output services


Messaging provides a means of communication with other system and between the tasks. The messaging services includes

  • Semaphores
  • Event flags
  • Mailboxes
  • Pipes
  • Message queues

Semaphores are used to synchronize access to shared resources, such as common data areas. Event flags are used to synchronize the inter-task activities. Mailboxes, pipes, and message queues are used to send messages among tasks.