This tutorial is about how to use FreeRTOS structured queue to receive data from multiple resources. In the last tutorial, we have learned to use message queues with FreeRTOS API and Arduino. But in that example, we used a single queue to receive and send data from single tasks. But in real-time operating systems, it is also possible that a single task receives data from multiple resources by using a single queue. However, the multiple sender tasks can write data to a single queue, but the issue is that the receive task should know from which task data came from.

If you are just starting with FreeRTOS with Arduino, you can read this getting started guide:

Why do we used queues Structure?

Why is it necessary for the receiver task to the know the source of data?

Because otherwise there is no other way, the receiver can differentiate data resources and can perform a meaningful operation on that data. The simplest solution is to use structures data type to write and read data from a single queue. The structure should contain a source of data and the actual value that we want to communicate with the receiver task.

Structure Queue Example to Receive Data from Multiple Resources

This block diagram demonstrates an example to transfer structure to a single queue.

We create an example with Arduino that measure temperature and light sensor value from A0 and A1 channels of Arduino, respectively. After that, prints sensors value on 16×2 LCD.

First, we create a queue that can hold the structure. This structure elements consist of data value and tag of resource (such as analog channel number)

In this example, we create two sender tasks such as Temperature and LDR.

The temperature sender reads the LM35 sensor value from analog channel A0 of Arduino IDE. After that, it writes structure to the queue that contains ADC value and channel number (A0).

Similarly, a light-dependent resistor task measures light intensity with the ADC channel (A1) and writes data in a structured format.

LCD task (Receiver) also reads this structure data and processes data to display sensor value after identifying the source.

Implementation of FreeRTOS structure Queue in Arduino

For demonstration, we implement an example using Arduino. Let’s create a demo with two analog sensors and one LCD display as shown in the circuit diagram below:

In the setup function, we create three tasks that use structure_queue to write and read data. Firstly, “TaskLCD” is created that reads structure elements from the queue and it has the highest priority. Conversely, TaskTempReadPin0() and TaskLightReadPin0() pins are created with equal priority and lower than the receiver task. I will explain the reason later on.

Receiver Task

“TaskLcd” is a receiver task that reads structure data from struct_queue. The receiving task has the highest priority. Therefore, at the start of program execution, it will run first, but it will enter the blocking state. Because the queue will be empty. Hence, it will execute as soon as when one of the senders will write data to the queue. This task always expects to receive data, therefore, we specify blocking time to the maximum value that is “portMAX_DELAY”.

Sender Tasks

These are the definitions of two sender tasks. As we mentioned earlier, writer tasks have equal priority but lower than receiver tasks. Therefore, when a high priority task will be in the blocking state, then one of the low priority tasks will execute by following time sharing or time-slicing scheduling algorithm. Because FreeRTOS is based on Prioritized Pre-emptive Scheduling with Time Slicing.

Program Time Execution Pattern

Now lets disucss the excution pattern of the program according to tasks priorities:

T1

Receiver Task starts to execute, but enters the blocking state because the queue is empty.

T2

The queue is empty, therefore, one of the low priority tasks starts to execute, because both senders have equal priority. Let’s suppose, AnalogReadPin0 starts to execute and as soon as it writes data to the queue, the high priority task ( receiver) pre-empt it and it will not be able to complete its further execution such as printing “Channel_0” on the serial monitor.

Now the Receiver task being the highest priority one enters the running state and completes its execution such as display temperature on LCD and printing values on the serial monitor. After that, it again goes to a blocking state, because of the queue is empty again.

T3

Now two low priority tasks are available to execute, but this time, the second sender task “AnalogReadPin1” will enter the running state. Because both sender tasks have equal priority and the FreeRTOS scheduler follows a time-sharing algorithm for equal priority tasks. Hence, “AnalogReadPin1” starts to execute.

But as soon as it writes the value of the LDR sensor to the structure queue, “Displaydata” will pre-empt it, because, it is the highest priority task in the blocked state waiting for the data. Therefore, it again enters the running state and completes its execution such as displaying light sensor value on LCD and printing values on the serial monitor. After that, it again goes to a blocking state, because of the queue is empty again.

T4

Now again two sender tasks are ready to execute, but due to time-sharing scheduling for equal priority tasks, “AnalogReadPin0” will start to execute and display “channel_0” on Arduino serial monitor.

T5

After that “AnalogReadPin0” sender task will execute because the receiver task is in a blocking state waiting for the data. Hence, sender2 will display “Channel_2” on the Arduino serial monitor. After that, the program again starts to follow execution from T1.

This is the reason we see first LcdTask output on Arduino serial monitor than sender tasks as shown below: