July 16, 2025
Understanding Queue Sets in FreeRTOS

Understanding Queue Sets in FreeRTOS

FreeRTOS provides a variety of synchronization mechanisms and inter-process communication (IPC) tools, among which Queue Sets stand out as a versatile and powerful feature for managing multiple queues. Queue Sets allow tasks to wait for multiple queues (or other synchronization objects, like event groups or semaphores) to be available for reading or writing, simplifying complex synchronization patterns and improving system efficiency. In this article, we will explore what Queue Sets are, how they work, and how to use them effectively in FreeRTOS.

What is a Queue Set?

A Queue Set in FreeRTOS is a high-level abstraction that groups multiple queues (and other synchronization objects like semaphores or event groups) into a single “set” to monitor their state. A task can then block and wait for any of the items in the set to become available for reading or writing, without having to poll each individual queue separately.

Queue Sets enable tasks to efficiently wait for multiple events without blocking unnecessarily. For example, a task might need to wait for data from several different sensors (each represented by a queue) or await events from different parts of the system (e.g., semaphores or event groups). Instead of using multiple xQueueReceive() or xSemaphoreTake() calls with timeouts, a Queue Set allows the task to monitor all relevant synchronization objects in one go, making the code cleaner and more efficient.

Key Benefits of Queue Sets

  • Reduced Complexity: Instead of managing multiple queues, semaphores, or event groups individually, you can handle them together in a single Queue Set, reducing the complexity of synchronization logic.
  • Efficient Blocking: A task can block until one of the queues or synchronization objects in the set is ready, avoiding unnecessary polling or busy-waiting.
  • Centralized Event Handling: Instead of writing multiple event-handling functions or using nested logic to check each synchronization object, a Queue Set simplifies the process by allowing you to handle all events centrally.
  • Supports Multiple Objects: Queue Sets can contain queues, semaphores, and event groups, giving you a flexible way to group different types of synchronization objects together

Implementing Oueue sets

Oueue set Api

  • xQueueCreateSet():Creates a queue set that can contain queues and/or semaphores. It returns a handle to the created set.
  • xQueueAddToSet():Adds a queue or semaphore to the queue set. Once added, the queue can be monitored for data or events.
  • xQueueSelectFromSet():This function blocks the calling task until one of the queues or semaphores in the set becomes available. It returns the handle to the object that is ready.

Example:

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_log.h"

// Define queue set size
#define QUEUE_SET_SIZE 3

// Create handles for queues and queue set
QueueHandle_t queue1, queue2, queue3;
QueueSetHandle_t queueSet;

void producerTask(void *pvParameters) {
    int data = 0;
    while (1) {
        // Send data to queue1, queue2, or queue3
        if (data % 3 == 0) {
            xQueueSend(queue1, &data, portMAX_DELAY);
            ESP_LOGI("Producer", "Sent to queue1: %d", data);
        } else if (data % 3 == 1) {
            xQueueSend(queue2, &data, portMAX_DELAY);
            ESP_LOGI("Producer", "Sent to queue2: %d", data);
        } else {
            xQueueSend(queue3, &data, portMAX_DELAY);
            ESP_LOGI("Producer", "Sent to queue3: %d", data);
        }
        
        data++;
        vTaskDelay(pdMS_TO_TICKS(1000));  // Wait for 1 second
    }
}

void consumerTask(void *pvParameters) {
    int receivedData;
    QueueSetMemberHandle_t activatedQueue;
    while (1) {
        // Wait for any queue in the queue set to become available
        activatedQueue = xQueueSelectFromSet(queueSet, portMAX_DELAY);

        // Check which queue has data available
        if (activatedQueue == queue1) {
            xQueueReceive(queue1, &receivedData, 0);  // Receive data from queue1
            ESP_LOGI("Consumer", "Received from queue1: %d", receivedData);
        } else if (activatedQueue == queue2) {
            xQueueReceive(queue2, &receivedData, 0);  // Receive data from queue2
            ESP_LOGI("Consumer", "Received from queue2: %d", receivedData);
        } else if (activatedQueue == queue3) {
            xQueueReceive(queue3, &receivedData, 0);  // Receive data from queue3
            ESP_LOGI("Consumer", "Received from queue3: %d", receivedData);
        }
    }
}

void app_main(void) {
    // Create three queues with a length of 5 items each
    queue1 = xQueueCreate(5, sizeof(int));
    queue2 = xQueueCreate(5, sizeof(int));
    queue3 = xQueueCreate(5, sizeof(int));

    // Check for queue creation failure
    if (queue1 == NULL || queue2 == NULL || queue3 == NULL) {
        ESP_LOGE("Main", "Queue creation failed!");
        return;
    }

    // Create a queue set to hold the queues
    queueSet = xQueueCreateSet(QUEUE_SET_SIZE);

    // Add the queues to the set
    xQueueAddToSet(queue1, queueSet);
    xQueueAddToSet(queue2, queueSet);
    xQueueAddToSet(queue3, queueSet);

    // Create producer and consumer tasks
    xTaskCreate(producerTask, "Producer", 2048, NULL, 2, NULL);
    xTaskCreate(consumerTask, "Consumer", 2048, NULL, 2, NULL);
}

Explanation: In this , we create three queues (queue1, queue2, queue3), each capable of holding 5 integers. We then create a queue set (queueSet) using xQueueCreateSet() to monitor all three queues simultaneously. The queues are added to the set with xQueueAddToSet(), allowing us to wait for any of them to receive data. The producer task sends data to one of the queues every second based on a simple pattern, while the consumer task uses xQueueSelectFromSet() to block until one of the queues has data available, retrieves it, logs the data, and continues waiting for the next available queue

Conclusion

Queue Sets in FreeRTOS are a powerful tool for managing multiple synchronization objects like queues, semaphores, and event groups in a single abstraction. They allow tasks to wait for events on multiple objects without the need for complex polling logic, resulting in more efficient and scalable applications.

Leave a Reply

Your email address will not be published. Required fields are marked *