If you're diving into embedded systems or real-time programming, FreeRTOS (Free Real-Time Operating System) is a must-learn tool. In this guide, we’ll explore key RTOS concepts and how to implement them using FreeRTOS on an ESP32 board through the Arduino IDE. Whether you're a beginner or looking to expand your ESP32 project with multitasking features, this tutorial has you covered.
Downloading and Setting Up FreeRTOS
To begin, head over to FreeRTOS.org and click Download FreeRTOS. This will give you access to the complete source code.
While it's downloading, navigate to Documentation > Kernel > Getting Started. The instructions will walk you through running demo applications. If your development board isn’t directly supported, visit the "Create your own FreeRTOS project" page.
Minimum Requirements to Include FreeRTOS in Your Project:
-
FreeRTOS kernel source files (
.c
and.h
) -
A heap memory management file (choose from the available options)
-
Assignment of a microcontroller timer for the RTOS tick
Also, it’s a good idea to download the "Mastering the FreeRTOS Real Time Kernel" and the FreeRTOS Reference Manual from the Books section for comprehensive help.
Understanding FreeRTOS vs FreeRTOS+ and the Demo Examples
Once downloaded, extract the FreeRTOS zip file and locate the /FreeRTOS
directory. You’ll find two important libraries:
-
FreeRTOS Kernel – The task scheduler only.
-
FreeRTOS+ – Kernel with additional drivers (e.g., TCP/IP).
Look into the Demo
folder for sample projects. For instance, GCC examples include a FreeRTOSConfig.h
file, which defines critical system parameters. Be sure to review main.c
to see how tasks are declared and the library is integrated.
ESP32 and FreeRTOS: What You Need to Know
The ESP32 microcontroller, commonly found on boards like the Adafruit Feather Huzzah32, uses a modified version of FreeRTOS inside ESP-IDF (Espressif IoT Development Framework).
Most ESP32 boards feature dual-core processors using Symmetric Multiprocessing (SMP)—two cores sharing memory and resources. Espressif’s version of FreeRTOS is customized to support this architecture.
🔥 Note: In this series, we’ll simplify by using only one core to avoid the complexities of multi-core programming in the early stages.
Installing ESP32 Support in Arduino IDE
-
Open Arduino IDE.
-
Go to File > Preferences.
-
Add this to the Additional Board Manager URLs:
https://dl.espressif.com/dl/package_esp32_index.json
4. Go to Tools > Board > Board Manager.
5. Search for ESP32 and install the latest Espressif Systems package.
Understanding FreeRTOSConfig.h for ESP32
The ESP32 board uses its own FreeRTOSConfig.h
file, located under:
This file reveals system-wide RTOS settings. For example:
-
Maximum priorities = 25
-
Minimum task stack size = 768 bytes
Always check this config file to understand what options are available and how they affect your system.
Writing a Blinky Program with FreeRTOS on ESP32
Let’s get hands-on and write a blinking LED program using FreeRTOS. Note that you don't need the above libraries to compile the code here.
Key Concepts:
-
A task is a function managed by the RTOS scheduler.
-
Use
vTaskDelay()
instead ofdelay()
for non-blocking waits. -
The tick timer is a hardware interrupt that helps FreeRTOS manage timing and task switching. By default, 1 tick = 1 ms.
Code Solution:
#include <Arduino.h>
const int LED_PIN = 2; // or any GPIO pin you prefer
void TaskBlink(void *pvParameters) {
pinMode(LED_PIN, OUTPUT);
while (true) {
digitalWrite(LED_PIN, HIGH);
vTaskDelay(500 / portTICK_PERIOD_MS);
digitalWrite(LED_PIN, LOW);
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
void setup() {
// Create a task and pin it to Core 1
xTaskCreatePinnedToCore(
TaskBlink, // Task function
"Blink Task", // Name of the task
1024, // Stack size in bytes
NULL, // Parameter to pass
1, // Task priority (0-24)
NULL, // Task handle (optional)
1 // Core number (0 or 1)
);
}
void loop() {
// Leave empty or add future tasks
}
Video tutorial
Experiment Ideas
-
Try changing
rate_1
andrate_2
to see how the blinking pattern changes. -
Assign different priorities to the tasks and observe how it affects the LED behavior.
-
Add more tasks (e.g., reading a sensor or printing to Serial) to experiment with multitasking.
-
Set priority of the LED tasks to 0. If the LED stops blinking, think about why that happens (hint: priority conflict with
loop()
task).
Code Solution:
// Use only core 1 for demo purposes
#if CONFIG_FREERTOS_UNICORE
static const BaseType_t app_cpu = 0;
#else
static const BaseType_t app_cpu = 1;
#endif
// LED rates
static const int rate_1 = 500; // ms
static const int rate_2 = 323; // ms
// Pins
static const int led_pin = LED_BUILTIN;
// Our task: blink an LED at one rate
void toggleLED_1(void *parameter) {
while(1) {
digitalWrite(led_pin, HIGH);
vTaskDelay(rate_1 / portTICK_PERIOD_MS);
digitalWrite(led_pin, LOW);
vTaskDelay(rate_1 / portTICK_PERIOD_MS);
}
}
// Our task: blink an LED at another rate
void toggleLED_2(void *parameter) {
while(1) {
digitalWrite(led_pin, HIGH);
vTaskDelay(rate_2 / portTICK_PERIOD_MS);
digitalWrite(led_pin, LOW);
vTaskDelay(rate_2 / portTICK_PERIOD_MS);
}
}
void setup() {
// Configure pin
pinMode(led_pin, OUTPUT);
// Task to run forever
xTaskCreatePinnedToCore( // Use xTaskCreate() in vanilla FreeRTOS
toggleLED_1, // Function to be called
"Toggle 1", // Name of task
1024, // Stack size (bytes in ESP32, words in FreeRTOS)
NULL, // Parameter to pass to function
1, // Task priority (0 to configMAX_PRIORITIES - 1)
NULL, // Task handle
app_cpu); // Run on one core for demo purposes (ESP32 only)
// Task to run forever
xTaskCreatePinnedToCore( // Use xTaskCreate() in vanilla FreeRTOS
toggleLED_2, // Function to be called
"Toggle 2", // Name of task
1024, // Stack size (bytes in ESP32, words in FreeRTOS)
NULL, // Parameter to pass to function
1, // Task priority (0 to configMAX_PRIORITIES - 1)
NULL, // Task handle
app_cpu); // Run on one core for demo purposes (ESP32 only)
// If this was vanilla FreeRTOS, you'd want to call vTaskStartScheduler() in
// main after setting up your tasks.
}
void loop() {
// Do nothing
// setup() and loop() run in their own task with priority 1 in core 1
// on ESP32
}
Processor Core Selection
Blinking Rates and Pin Setup
rate_1
and rate_2
) in milliseconds, and specify the LED pin (typically the built-in LED).Task 1: Blink LED at rate_1
toggleLED_1
, but uses a different delay (rate_2
), so the blinking is out of phase and creates a unique visual pattern.Setup Function: Creating Tasks
Here we use xTaskCreatePinnedToCore()
to create two tasks that run on the same core (app_cpu
). Both tasks have the same priority (1) and run forever, blinking the same LED at their respective rates.
✅ Note: If you’re using plain FreeRTOS on a non-ESP32 system, use
xTaskCreate()
instead. And don’t forget to callvTaskStartScheduler()
in yourmain()
function to start task execution.
loop() Function
setup()
and loop()
run in their own FreeRTOS task. Since all tasks (including the default loop()
task) are set to priority 1, the core's time is shared among them. Key Differences: Vanilla FreeRTOS vs ESP-IDF
Feature | Vanilla FreeRTOS | ESP32 FreeRTOS (ESP-IDF) |
---|---|---|
Cores | Single core | Dual-core SMP supported |
Task Creation | xTaskCreate() | xTaskCreatePinnedToCore() |
Startup | Requires vTaskStartScheduler() | Called automatically |
Stack Size | In "words" | In bytes |
Delay | vTaskDelay() (ticks) | Same, but supports Arduino environment |
Test Your Skills: Create Multiple Tasks
As a challenge, create a second FreeRTOS task that blinks another LED at a different interval. This will demonstrate task concurrency and time slicing in action.
Conclusion
Congratulations! You've just built your first FreeRTOS task on an ESP32 using Arduino IDE. While it's just a blinking LED, you've taken your first steps into the world of real-time systems, task scheduling, and multithreaded programming.
As you progress, consider exploring:
-
Task prioritization
-
Context switching
-
Inter-task communication
-
Semaphore and mutex management
📌 Stay tuned for the next tutorial where we'll dive into task prioritization and context switching in FreeRTOS.