ESP32 with FreeRTOS Queues: Inter-Task Communication (Arduino IDE)
Learn how to use FreeRTOS queues for safe and efficient communication between tasks on the ESP32, using the Arduino IDE. Queues allow you to exchange data between tasks in a safe way.
- Suitable forB.Tech/Diploma IoT/ESP32 students
- Build time1-2 weeks
- LevelBeginner
- Available atAjvika Labs, Kakinada
About this project
In this guide, you’ll learn how to use FreeRTOS queues for safe and efficient communication between tasks on the ESP32, using the Arduino IDE. Queues allow you to exchange data between tasks in a safe way. We’ll cover the basic principles of how queues work and walk you through three practical examples to show how to pass data between tasks.
New to FreeRTOS?
Start with this tutorial:
ESP32 with FreeRTOS (Arduino IDE)—Getting Started Guide: Creating Tasks
Table of Contents
In this tutorial, we’ll cover the following subjects:
FreeRTOS Communication Between Tasks
What are FreeRTOS Queues?
FreeRTOS Queue Basics
Example 1: Using Queues to Control LED Brightness
Example 2: FreeRTOS Queues – Sharing Data Between Three Tasks
Example 3: Display Sensor Data on a Screen (Sharing a Data Structure)
FreeRTOS Communication Between Tasks
In FreeRTOS there are different ways for inter-task communication and synchronization of data between tasks, suited for different purposes.
Queues
: allow tasks to send and receive data, like sensor readings, button states, or other data, as we’ll see in the examples covered in this tutorial. Here we’ll focus on FreeRTOS queues.
Semaphores
are like signaling tools in FreeRTOS used to coordinate tasks. They are often used to indicate that an event has occurred or a resource is ready. They don’t carry data, just a count (or flag) to trigger actions after something has happened. We’ll cover this in a future tutorial.
Mutexes
(mutual exclusions) are used to protect shared resources by allowing only one task to access them at a time. This prevents conflicts in concurrent operations that need to access the same data. Unlike queues, mutexes focus on resource access rather than data transfer. We’ll cover this in a future tutorial.
What are FreeRTOS Queues?
FreeRTOS queues are thread-safe data structures that allow tasks to send and receive data for synchronized communication in a multitasking environment.
They ensure that data is passed safely between tasks without conflicts or data loss.
Queues are especially useful when one task needs to send data and another task needs to handle it later, like a sensor task sending readings to a logging task or to be processed to display on a screen. This way, each task can run at its own speed without interfering with the other.
FreeRTOS Queue Basics
Here are some basic concepts about creating and handling queues on your Arduino IDE code for the ESP32:
Creating a Queue
Use the
xQueueCreate(size, item_size)
to create a queue—
size
corresponds to the number of items that can be on the queue and
item_size
is the bytes size of heap allocated for each item on the queue.
Sending Data to a Queue
To send data to a queue use the
xQueueSend()
to add data to the queue. Or use
xQueueSendFromISR()
if sending the data from an ISR (interrupt service routine).
Receive Data from a Queue
The
xQueueReceive()
function reads data from the queue, if available.
Example 1: Using Queues to Control LED Brightness
In this example, we’ll create two tasks. One task will send data to a queue. The other task will read data from the queue.
We’ll control the LED brightness using a potentiometer. This is a basic example, you’ve probably seen before, to learn about
analog reading
and
PWM
. In this case, we’ll use that basic example as a starting point to explain queues.
We’ll create two tasks with the following names:
potTask
: reads the value from the potentiometer and sends it to a queue.
LEDBrightnessTask
: checks if there are values to receive from the queue and adjusts the LED brightness accordingly.
Parts Required
For this project, you need the following parts:
ESP32 Board
of your choice – read
Best ESP32 Development Boards
LED
220 Ohm resistor
Potentiometer
Breadboard
Jumper wires
You can use the preceding links or go directly to
MakerAdvisor.com/tools
to find all the parts for your projects at the best price!
Wiring the Circuit
For this example, wire an LED and a potentiometer to your ESP32 board. We’re connecting the LED to GPIO 2 and the potentiometer to GPIO 15.
Code – Control LED Brightness (Queues Basic Example)
The following code creates two tasks. One reads the values from the potentiometer and sends them to a queue. The other task reads the values from the queue and controls the LED brightness accordingly.
You can copy the following code to the Arduino IDE and upload it to your board.
/*
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#define POT_PIN 15
#define LED_PIN 2
#define PWM_FREQ 5000
#define PWM_RESOLUTION 12 // 12-bit (0–4095)
#define QUEUE_SIZE 5
// Create an handle for the queue
QueueHandle_t potQueue = NULL;
void potTask(void *parameter) {
for (;;) {
uint16_t potValue = analogRead(POT_PIN); // Read 0–4095
xQueueSend(potQueue, &potValue, portMAX_DELAY); // Send to queue
Serial.printf(\"potTask: Sent pot value %u\
\", potValue);
vTaskDelay(100 / portTICK_PERIOD_MS); // 100ms
}
}
void LEDBrightnessTask(void *parameter) {
for (;;) {
uint16_t potValue;
if (xQueueReceive(potQueue, &potValue, portMAX_DELAY)) {
uint16_t brightness = potValue;
ledcWrite(LED_PIN, brightness);
Serial.printf(\"LEDBrightnessTask: Set brightness %u\
\", brightness);
}
}
}
void setup() {
Serial.begin(115200);
// Setup PWM for LED
ledcAttach(LED_PIN, PWM_FREQ, PWM_RESOLUTION);
// Create queue (5 items, each uint16_t)
potQueue = xQueueCreate(QUEUE_SIZE, sizeof(uint16_t));
if (potQueue == NULL) {
Serial.println(\"Failed to create queue!\");
while (1);
}
// Create tasks
xTaskCreatePinnedToCore(
potTask,
\"potTask\",
3000, // Task stack
NULL,
1,
NULL,
1 // Core 1
);
xTaskCreatePinnedToCore(
LEDBrightnessTask,
\"LEDBrightnessTask\",
3000,
NULL,
1,
NULL,
1
);
}
void loop() {
// Empty
}
How Does the Code Work?
Let’s take a look at the code to see how it works.
Define the LED and Potentiometer GPIOs
First define the GPIO pins for the LED and for the potentiometer.
#define POT_PIN 15
#define LED_PIN 2
PWM Properties
Then, define the PWM frequency and resolution to adjust the LED brightness. We’re setting 12-bit resolution (the same default resolution for the analog reading).
#define PWM_FREQ 5000
#define PWM_RESOLUTION 12 // 12-bit (0–4095)
If you’re new to PWM with the ESP32, check this tutorial to learn more:
ESP32 PWM with Arduino IDE (Analog Output)
FreeRTOS Queue Size
Define the size of the FreeRTOS queue. This is the number of items that can the queue can hold. In this case, we’re setting it to 5.
#define QUEUE_SIZE 5
In our case, this is a good estimate because the items sent to the queue will be almost imediately consumed by the other task.
Queue Handle
Create an handle for the queue. This is how we’ll refer to our queue throughout our code. We’ll refer to the queue as
potQueue
// Create an handle for the queue
QueueHandle_t potQueue = NULL;
potTask Function
Create a function for the pontentiometer task:
potTask
. This is the task that will read from the pontentiometer.
void potTask(void *parameter) {
for (;;) {
uint16_t potValue = analogRead(POT_PIN); // Read 0–4095
xQueueSend(potQueue, &potValue, portMAX_DELAY); // Send to queue
Serial.printf(\"potTask: Sent pot value %u\
\", potValue);
vTaskDelay(100 / portTICK_PERIOD_MS); // 100ms
}
}
We start by reading the value from the potentiometer and save it in a
uint16_t
type of variable called
potValue
uint16_t potValue = analogRead(POT_PIN); // Read 0–4095
To learn mor
New to FreeRTOS?
Start with this tutorial:
ESP32 with FreeRTOS (Arduino IDE)—Getting Started Guide: Creating Tasks
Table of Contents
In this tutorial, we’ll cover the following subjects:
FreeRTOS Communication Between Tasks
What are FreeRTOS Queues?
FreeRTOS Queue Basics
Example 1: Using Queues to Control LED Brightness
Example 2: FreeRTOS Queues – Sharing Data Between Three Tasks
Example 3: Display Sensor Data on a Screen (Sharing a Data Structure)
FreeRTOS Communication Between Tasks
In FreeRTOS there are different ways for inter-task communication and synchronization of data between tasks, suited for different purposes.
Queues
: allow tasks to send and receive data, like sensor readings, button states, or other data, as we’ll see in the examples covered in this tutorial. Here we’ll focus on FreeRTOS queues.
Semaphores
are like signaling tools in FreeRTOS used to coordinate tasks. They are often used to indicate that an event has occurred or a resource is ready. They don’t carry data, just a count (or flag) to trigger actions after something has happened. We’ll cover this in a future tutorial.
Mutexes
(mutual exclusions) are used to protect shared resources by allowing only one task to access them at a time. This prevents conflicts in concurrent operations that need to access the same data. Unlike queues, mutexes focus on resource access rather than data transfer. We’ll cover this in a future tutorial.
What are FreeRTOS Queues?
FreeRTOS queues are thread-safe data structures that allow tasks to send and receive data for synchronized communication in a multitasking environment.
They ensure that data is passed safely between tasks without conflicts or data loss.
Queues are especially useful when one task needs to send data and another task needs to handle it later, like a sensor task sending readings to a logging task or to be processed to display on a screen. This way, each task can run at its own speed without interfering with the other.
FreeRTOS Queue Basics
Here are some basic concepts about creating and handling queues on your Arduino IDE code for the ESP32:
Creating a Queue
Use the
xQueueCreate(size, item_size)
to create a queue—
size
corresponds to the number of items that can be on the queue and
item_size
is the bytes size of heap allocated for each item on the queue.
Sending Data to a Queue
To send data to a queue use the
xQueueSend()
to add data to the queue. Or use
xQueueSendFromISR()
if sending the data from an ISR (interrupt service routine).
Receive Data from a Queue
The
xQueueReceive()
function reads data from the queue, if available.
Example 1: Using Queues to Control LED Brightness
In this example, we’ll create two tasks. One task will send data to a queue. The other task will read data from the queue.
We’ll control the LED brightness using a potentiometer. This is a basic example, you’ve probably seen before, to learn about
analog reading
and
PWM
. In this case, we’ll use that basic example as a starting point to explain queues.
We’ll create two tasks with the following names:
potTask
: reads the value from the potentiometer and sends it to a queue.
LEDBrightnessTask
: checks if there are values to receive from the queue and adjusts the LED brightness accordingly.
Parts Required
For this project, you need the following parts:
ESP32 Board
of your choice – read
Best ESP32 Development Boards
LED
220 Ohm resistor
Potentiometer
Breadboard
Jumper wires
You can use the preceding links or go directly to
MakerAdvisor.com/tools
to find all the parts for your projects at the best price!
Wiring the Circuit
For this example, wire an LED and a potentiometer to your ESP32 board. We’re connecting the LED to GPIO 2 and the potentiometer to GPIO 15.
Code – Control LED Brightness (Queues Basic Example)
The following code creates two tasks. One reads the values from the potentiometer and sends them to a queue. The other task reads the values from the queue and controls the LED brightness accordingly.
You can copy the following code to the Arduino IDE and upload it to your board.
/*
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#define POT_PIN 15
#define LED_PIN 2
#define PWM_FREQ 5000
#define PWM_RESOLUTION 12 // 12-bit (0–4095)
#define QUEUE_SIZE 5
// Create an handle for the queue
QueueHandle_t potQueue = NULL;
void potTask(void *parameter) {
for (;;) {
uint16_t potValue = analogRead(POT_PIN); // Read 0–4095
xQueueSend(potQueue, &potValue, portMAX_DELAY); // Send to queue
Serial.printf(\"potTask: Sent pot value %u\
\", potValue);
vTaskDelay(100 / portTICK_PERIOD_MS); // 100ms
}
}
void LEDBrightnessTask(void *parameter) {
for (;;) {
uint16_t potValue;
if (xQueueReceive(potQueue, &potValue, portMAX_DELAY)) {
uint16_t brightness = potValue;
ledcWrite(LED_PIN, brightness);
Serial.printf(\"LEDBrightnessTask: Set brightness %u\
\", brightness);
}
}
}
void setup() {
Serial.begin(115200);
// Setup PWM for LED
ledcAttach(LED_PIN, PWM_FREQ, PWM_RESOLUTION);
// Create queue (5 items, each uint16_t)
potQueue = xQueueCreate(QUEUE_SIZE, sizeof(uint16_t));
if (potQueue == NULL) {
Serial.println(\"Failed to create queue!\");
while (1);
}
// Create tasks
xTaskCreatePinnedToCore(
potTask,
\"potTask\",
3000, // Task stack
NULL,
1,
NULL,
1 // Core 1
);
xTaskCreatePinnedToCore(
LEDBrightnessTask,
\"LEDBrightnessTask\",
3000,
NULL,
1,
NULL,
1
);
}
void loop() {
// Empty
}
How Does the Code Work?
Let’s take a look at the code to see how it works.
Define the LED and Potentiometer GPIOs
First define the GPIO pins for the LED and for the potentiometer.
#define POT_PIN 15
#define LED_PIN 2
PWM Properties
Then, define the PWM frequency and resolution to adjust the LED brightness. We’re setting 12-bit resolution (the same default resolution for the analog reading).
#define PWM_FREQ 5000
#define PWM_RESOLUTION 12 // 12-bit (0–4095)
If you’re new to PWM with the ESP32, check this tutorial to learn more:
ESP32 PWM with Arduino IDE (Analog Output)
FreeRTOS Queue Size
Define the size of the FreeRTOS queue. This is the number of items that can the queue can hold. In this case, we’re setting it to 5.
#define QUEUE_SIZE 5
In our case, this is a good estimate because the items sent to the queue will be almost imediately consumed by the other task.
Queue Handle
Create an handle for the queue. This is how we’ll refer to our queue throughout our code. We’ll refer to the queue as
potQueue
// Create an handle for the queue
QueueHandle_t potQueue = NULL;
potTask Function
Create a function for the pontentiometer task:
potTask
. This is the task that will read from the pontentiometer.
void potTask(void *parameter) {
for (;;) {
uint16_t potValue = analogRead(POT_PIN); // Read 0–4095
xQueueSend(potQueue, &potValue, portMAX_DELAY); // Send to queue
Serial.printf(\"potTask: Sent pot value %u\
\", potValue);
vTaskDelay(100 / portTICK_PERIOD_MS); // 100ms
}
}
We start by reading the value from the potentiometer and save it in a
uint16_t
type of variable called
potValue
uint16_t potValue = analogRead(POT_PIN); // Read 0–4095
To learn mor
Technologies used
Hardware components
- ESP32
- Arduino
Software & tools
- Arduino IDE
What you get
- Working project / hardware kit, tested before delivery
- Complete source code with explanation
- Project demo and guidance for your viva
- Project report, PPT and IEEE paper support (on request)
Delivery & Payment
Delivery takes a minimum of two weeks from the date your inquiry is confirmed.
Payment is in two parts:
• 50% advance to start the project
• Remaining 50% before delivery
Note:
• Documentation and PPT are not included by default
• If you need documentation/PPT, it can be arranged for extra time and cost
We'll confirm the exact timeline and cost with you after reviewing your requirements.