The objective of this ESP32 Arduino Tutorial is to explain how to insert content in the front and back of a FreeRTOS queue. The tests of this ESP32 tutorial were performed using a DFRobot’s ESP32 module device integrated in a ESP32 development board.
Introduction
The objective of this post is to explain how to insert content in the front and back of a FreeRTOS queue. For an introductory tutorial on FreeRTOS queues on the ESP32, please consult this post.
Although queues are typically used for inter task communication, we will perform our tests in the main loop function, since our aim is to compare the insertion in the back vs the insertion in the front and the corresponding API calls needed.
Note that in the previous tutorial about queues we used the xQueueSend function to insert items in the queue. This function only allows to insert items in the back of the queue and it is still available for compatibility with previous versions of FreeRTOS [1].
So, for having more control on where to insert the items, we now have access to the xQueueSendToBack and xQueueSendToFront functions, which allow us to insert both in the back and front of the queue, respectively. We will test both on this tutorial.
The tests of this ESP32 tutorial were performed using a DFRobot’s ESP32 module device integrated in a ESP32 development board.
The code
In order for us to easily compare the different methods of insertion available on the FreeRTOS API, we will create two queues, one for insertion in the front and another for insertion in the back. These will be stored in two different global variables that we will declare bellow.
QueueHandle_t queueBack;
QueueHandle_t queueFront;
Moving to the setup function, we will open a serial connection so we can latter print the results. Upon executing the code, we will then be able to analyze it on the Arduino IDE serial monitor.
Next we will initialize each queue with a call to the xQueueCreate function. This function receives as first parameter the maximum number of items a queue can hold at a given time and as second the size of each item, specified in bytes. We will use simple queues of integers with a maximum of 10 items. Please consult the previous post for more details on how to use this function.
This function will return an handle to the queue created. So for each call, we will store the result in our global variables. In case the queue could not be allocated, then the function will return NULL. So we will also check for this case and print a message if any of the queues failed to initialize. In our test it will be irrelevant which one fails and we should abort the execution in either cases.
void setup() {
Serial.begin(115200);
queueBack = xQueueCreate( 10, sizeof( int ) );
queueFront = xQueueCreate( 10, sizeof( int ) );
if(queueBack == NULL || queueFront == NULL){
Serial.println("Error creating one of the queues");
}
}
Moving on to the Arduino main loop, we will do the actual insertion of items in each queue. In the first one, we will use the xQueueSendToBack function, which will insert the items in the back of the queue. In the second one we will use the xQueueSendToFront function, which will insert the items in the front of the queue.
Note that both functions receive exactly the same arguments, being the only difference the position on the queue where the item is added.
So, these functions receive as input the queue handle, a pointer to the item to be inserted (which will be copied, not just referenced) and a time interval (in ticks) that the task should wait in case the queue is full.
We will do the insertion in a loop, in order to insert 10 elements in each queue, which is the maximum number of items they can hold, as we specified in the initialization of each one.
So we pass our global queue handles to each of the functions, the pointer to the integer that holds the current iteration number and for the last argument we can pass 0, since or program will be designed in such a way that an insertion in a full queue never happens. Just as a note, a value of 0 ticks means that the task will not block and keep executing if the queue is full.
for(int i = 0; i<10; i++){
xQueueSendToBack(queueBack, &i, 0);
xQueueSendToFront(queueFront, &i, 0);
}
Next we will do the consumption of the items with a call to the xQueueReceive function. Note that in the case of the consumption of items from the queue, there is no option to consume either from the back or the front of the queue. Thus, the consumption will always be done from the front of the queue.
We will need a buffer to store the consumed items, so before reading from the queue we will declare an integer variable. Next, we will simply consume the items from both queues in two different loops.
As for the call to the xQueueReceive function, it receives as first input the handle to the queue we want to consume, as second a pointer to the buffer to where the consumed item will be copied, and as third the number of ticks to wait in case the queue is empty. We will again pass zero to this last parameter.
We will first read from the queue where new elements are being inserted in the back. So, it should act as a FIFO (First In First Out), and thus the elements should be printed in the same order they were inserted.
Next we will read from the queue where the elements are being inserted in the front. This leads to a LIFO (Last In First Out) behavior, where the last inserted elements are the ones to be consumed first.
int element;
Serial.println("Back queue:");
for(int i = 0; i<10; i++){
xQueueReceive(queueBack, &element, 0);
Serial.print(element);
Serial.print("|");
}
Serial.println();
Serial.println("Front queue:");
for(int i = 0; i<10; i++){
xQueueReceive(queueFront, &element, 0);
Serial.print(element);
Serial.print("|");
}
The full source code can be seen bellow. Note that we added both a small delay between each iteration of the loop and a null checking for each of the queues.
QueueHandle_t queueBack;
QueueHandle_t queueFront;
void setup() {
Serial.begin(115200);
queueBack = xQueueCreate( 10, sizeof( int ) );
queueFront = xQueueCreate( 10, sizeof( int ) );
if(queueBack == NULL || queueFront ==NULL){
Serial.println("Error creating one of the queues");
}
}
void loop() {
if(queueBack == NULL || queueFront == NULL )return;
for(int i = 0; i<10; i++){
xQueueSendToBack(queueBack, &i, 0);
xQueueSendToFront(queueFront, &i, 0);
}
int element;
Serial.println("Back queue:");
for(int i = 0; i<10; i++){
xQueueReceive(queueBack, &element, 0);
Serial.print(element);
Serial.print("|");
}
Serial.println();
Serial.println("Front queue:");
for(int i = 0; i<10; i++){
xQueueReceive(queueFront, &element, 0);
Serial.print(element);
Serial.print("|");
}
Serial.println();
Serial.println("--------------");
delay(1000);
}
Testing the code
As usual, to test the code, simply compile it and upload it using the Arduino IDE. Then, open the Serial Monitor and check for the results.
You should get a result similar to figure 1. Note that the elements of the first queue are being printed in the same order they are inserted, and the elements of the second queue are reversed, as expected.
Figure 1 – Output of the program for inserting in the back and front of the FreeRTOS queue.