Back to blogEmbedded Systems

Handling FreeRTOS Queues and Tasks on Dual-Core ESP32

6 min read·Aug 2026

Handling FreeRTOS queues and tasks on dual-core ESP32 microcontrollers is essential when writing reliable production firmware. Moving beyond monolithic loop() architectures prevents blocking operations during network handshakes and ensures critical sensors sample deterministically.

1. Pinning tasks to specific CPU cores

The ESP32 features two Xtensa LX6/LX7 cores (Core 0 and Core 1). Core 0 typically manages underlying WiFi and Bluetooth radio stacks, while Core 1 executes Arduino user code. Use xTaskCreatePinnedToCore() to assign time-critical sampling tasks explicitly to Core 1 with high priority.

2. Thread-safe inter-task communication with FreeRTOS queues

Never share global variables directly between concurrently executing FreeRTOS tasks without synchronization. FreeRTOS Queues provide FIFO, thread-safe memory buffers that transfer data by value:

  • Initialize the queue buffer using xQueueCreate(queue_length, sizeof(DataType)).
  • Push telemetry data from sensor tasks using xQueueSend(queue, &data, portMAX_DELAY).
  • Consume data in telemetry publishing tasks with xQueueReceive(queue, &buffer, timeout).

3. Mutexes vs Semaphores for shared bus protection

When multiple tasks access a single hardware bus (such as I2C or SPI), wrap transactions inside FreeRTOS Mutexes (xSemaphoreCreateMutex()). This eliminates race conditions and sensor corruption. For peripheral wiring considerations, review our guide on I2C, SPI, and UART protocols[cite: 2].

4. Avoiding Task Watchdog Timer (TWDT) panics

FreeRTOS tasks must yield execution back to lower-priority kernel handlers. Infinite loops without vTaskDelay(pdMS_TO_TICKS(10)) trigger the Task Watchdog Timer, causing unexpected system panics and core reboots.

When to get help

If you'd rather have this handled directly, see our Firmware Architecture Services for scope and turnaround times.