In the last tutorial, we have seen how a single task can receive data from multiple tasks using a single Queue of structure type. In this tutorial, we will learn to use FreeRTOS queue set. Firstly, the question is why we need queue sets? Sometimes, as a programmer, we have to use separate Queues for receiving different types of data from different task resources.

Why We need to use Queue Sets?

For instance, if we want to integrated third party or external codes into application and it requires a dedicated queue to receive data from that third-party code.

Let suppose, we have a single data receiving task and multiple data sending tasks. Each sending task writes data to different queues but having a single data receiving task how we can differentiate that from which queue data is received. Therefore, we can resolve this issue by using ‘queue set’.

In summary, by using queue sets, a single task can receive data (same or different type) from multiple queues without the need of task polling to determine which holds data.

Use Queue Sets If Necessary

Note: It is recommended that not to use FreeRTOS queue sets if application design does not require to receive data from third party codes. Because FreeRTOS queue set makes program less efficient. Without the need of third-party code, as we have seen in last tutorial, we can achieve same functionality to receiving data from multiple resources by passing structure as an item to the queue.

Enable FreeRTOS Queue Sets

By default, queue set function is disabled in FreeRTOS configuration file. In order to set it, go to library folder of FreeRTOS in Arduino IDE folder and open FreeRTOSConfig fil like this:

After that set compile time configuration constant “configUSE_QUEUE_SETS” to one as shown in this diagram.

Also, before learning more about how to create queue sets using FreeRTOS and Arduino, you should know how to create queues:

How to Create Queue Set

Before creating a queue set, we should also create QueueSetHandle_t type reference handle. Because sets are handled by QueueSetHandle_t type handlers.

The return value of this function is a reference hander to the queue set. The input argument determines the number of queues handles it can hold. For example, if we have two queues and each queue contains 2 elements. Hence, the queue set can hold four items total ( 2 queues multiply by 2 items of each queue). Therefore, for this example, we should set uxEventQueueLength value to 4 by passing this value to xQueueCreateSet() function.

// define hanlder for queue set with name of "xQueueSet"
static QueueSetHandle_t xQueueSet = NULL;
// Create Queue_Set with that can hold two queues and 2 element each
xQueueSet = xQueueCreateSet( 2 * 2 );

The first argument to xQueueAddToSet() is the handle of the queue to which want to add and the second argument is a handle of the queue set to which the queue is being added. You can find more information on this function on the FreeRTOS link.

For example, these line adds two queues such as xQueue1 and xQueue2 to the xQueueSet.

Note: We should create xQueue1 and xQueue2 queues before adding them to the set using xQueueAddToSet() function.

Reading Data from the Queue Set

xQueueSelectFromSet() reads a queue handle from the queue set. For example, the first argument to this function is a handle of the queue from which data is received and the second is the waiting time. The waiting time defines the maximum time for which it should wait for the data from sender tasks.

This line block on the queue set to wait for one of the queues in the set to contain data.

FreeRTOS Queue Set Example with Arduino

In this example, we create two sender tasks and one receiver task. The sender tasks send a string to the receiver task using two separate queues. By using queue sets, the receiver task decides which of the queue from two holds data.

First, we include libraries of FreeRTOS and queue managemet.

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

Create two variable of type QueueHandle_t that are used as a refernce for two queues.

The last thing inside the Arduino setup function is receiver task creation and starting a scheduler. We create one receiver task with a priority higher than the sender tasks. It reads data from the queue set and determines which of the queue contains data.

Sender Tasks Definiton

These functions define sender tasks. Task1 uses xQueue1 to send a character string to the receiver task using a character to a pointer. It uses a block time of 100ms. Therefore, it sends string after every 100ms.

The second sending Task2 uses xQueue2 to send a character pointer to the receiving task every 200 milliseconds. The character pointers are set to point to a string that identifies the sending task.

This function defines the receiver task. It reads data from the queue set by determining from which of two queues data is written.

Whenever, a sender task writes a string to one of the queues, the handle of that queue is also passed to the queue set. Inside the receiving task, xQueueSelectFromSet() reads the handles from the queue set. By reading the handles of each queue, it determines which of the queue holds data. Therefore, it reads the data from the queue directly. After that “vReceiverTask” reads data from that queue using xQueueReceive() and prints its value on Arduino serial monitor.

Output Result

After uploading code, open Arduino serial monitor and observe the output. You will see output like this on serial monitor.

As you can see from the output diagram that “vReceiverTask” receives strings from both Task1 and Task2. The block time used by Task1() is half of the block time used by Task2(), causing the strings sent by Task1() to be printed out twice as often as those sent by Task2().