FreeRTOS on Xiao ESP32S3
by tech_nickk in Circuits > Microcontrollers
22 Views, 0 Favorites, 0 Comments
FreeRTOS on Xiao ESP32S3

Introduction to FreeRTOS
FreeRTOS is a real-time operating system (RTOS) designed specifically for microcontrollers and small microprocessors. It provides a reliable foundation for developing applications that require precise timing and task management on resource-constrained devices.
All the Example Codes in this Guide can be found on this repo: FreeRTOS on Xiao ESP32S3
What is FreeRTOS?
FreeRTOS is a lightweight operating system that enables multitasking on microcontrollers. It allows developers to create applications that can execute multiple tasks concurrently while managing system resources efficiently. The "Free" in FreeRTOS refers to its open-source nature, available under the MIT license, making it accessible for both commercial and personal projects.
The Need for FreeRTOS in Embedded Systems
FreeRTOS addresses critical challenges in embedded system development that simple sequential programming cannot effectively handle. Here's why it's necessary:
The Problem with Traditional Programming
In traditional "superloop" microcontroller programming, code runs sequentially in an infinite loop. This approach has several limitations:
- Timing Issues: When multiple tasks need different timing requirements, managing them becomes increasingly complex with delay functions.
- Responsiveness Problems: Long-running tasks block everything else. For example, if you're reading a sensor that takes 1 second, the system can't respond to other events during that time.
- Code Complexity: As projects grow, managing multiple device interactions in a single loop becomes unwieldy and error-prone.
- Resource Contention: Without proper synchronization, accessing shared resources can lead to data corruption or unpredictable behavior.
Benefits of Using FreeRTOS
FreeRTOS solves these problems through:
1. True Multitasking
Rather than one task blocking others, FreeRTOS allows multiple tasks to run concurrently. While one task waits for something (like a sensor reading), others can continue executing.
2. Deterministic Timing
FreeRTOS provides mechanisms to ensure time-critical operations happen when they should. Task priorities ensure high-priority operations get CPU time when needed.
3. Simplified Program Structure
Instead of complex state machines in a single loop, you can write:
- One task for handling connectivity
- Another for sensor readings
- A third for user interface elements Each task becomes simpler and more focused.
4. Resource Management
FreeRTOS provides semaphores, mutexes, and queues to safely share resources and communicate between tasks, preventing data corruption and race conditions.
5. Power Efficiency
FreeRTOS can put the processor into sleep modes when tasks are inactive, reducing power consumption—critical for battery-powered devices.
Real-World Example: IoT Sensor Node
Consider a device that:
- Reads multiple sensors
- Processes the data
- Connects to Wi-Fi
- Sends data to a server
- Manages a display
- Handles user inputs
Without an RTOS, timing becomes a nightmare—you'd need complex state machines, careful timing calculations, and would still face responsiveness issues.
With FreeRTOS, you can:
- Create separate tasks for each function
- Assign appropriate priorities
- Let the scheduler handle the timing
- Use queues to pass data between components
- Implement power-saving strategies
When FreeRTOS is Essential
FreeRTOS becomes particularly necessary for:
- Time-sensitive applications: Industrial controls, medical devices, or automotive systems where precise timing is critical.
- Complex interactions: Systems managing multiple peripherals, communications, and user interfaces simultaneously.
- Resource-constrained devices: When you need to maximize efficiency on devices with limited processing power.
- Reliable operation: Applications where system stability and predictable behavior are non-negotiable.
For the Xiao ESP32-S3 specifically, FreeRTOS is valuable because the board's dual-core processor and connectivity features (Wi-Fi, Bluetooth) are perfectly complemented by an RTOS that can effectively distribute tasks across cores and manage complex communication stacks.
Common Applications
- IoT Devices: Managing sensors, connectivity, and data processing concurrently.
- Industrial Automation: Handling multiple control processes with timing guarantees.
- Consumer Electronics: Managing user interfaces while performing background operations.
- Medical Devices: Ensuring reliable operation with predictable timing for critical functions.
- Automotive Systems: Managing multiple control systems with different priorities.
Getting Started with Xiao ESP32-S3
The Seeed Studio Xiao ESP32-S3 is a compact yet powerful development board featuring:
- ESP32-S3 dual-core processor
- 8MB PSRAM and 8MB flash memory
- Wi-Fi and Bluetooth connectivity
- USB Type-C with native USB support
- Multiple GPIO pins in a small form factor
Supplies
- XIAO ESP32S3
- Two LEDS
- TWO 330 Ohms Resistors
- DHT 22 Sensor
- Jumper Wires Breadboard
Setting Up the Development Environment
- Install the Arduino IDE from arduino.cc
Add ESP32 board support:
- Open Arduino IDE
- Go to File > Preferences
- Add https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json to Additional Board Manager URLs
- Go to Tools > Board > Boards Manager
- Search for "esp32" and install the latest version
- Add ESP32 board support:Open Arduino IDEGo to File > PreferencesAdd https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json to Additional Board Manager URLsGo to Tools > Board > Boards ManagerSearch for "esp32" and install the latest version
Select the correct board:
- Go to Tools > Board > ESP32 Arduino
- Select "XIAO_ESP32S3"
- Select the correct board:Go to Tools > Board > ESP32 ArduinoSelect "XIAO_ESP32S3"
Install FreeRTOS library:
- FreeRTOS comes pre-installed with the ESP32 Arduino core
Example 1: Blinking Two LEDs Simultaneously
This example demonstrates how to create two independent tasks, each controlling an LED with different blinking patterns.
Schematic
Code
Check out the WOKWI Simulation here: WOKWI Simulation
You can create a copy of and practice with it, Like blinking the two LEDs at different rates or even adding more LEDs
Key Concepts in Example 1
Includes and Definitions
- The code includes the necessary FreeRTOS header files to access task creation and management functions.
- LED pins are defined as constants, with LED1_PIN using the built-in LED on the ESP32-S3 (pin 2) and LED2_PIN representing an external LED connected to pin 3.
Task Handles
- These variables store references to the created tasks. They're used to identify and control the tasks if needed later (e.g., for suspending, resuming, or deleting tasks).
Task 1: LED1 Blinking Function
- This function configures LED1_PIN as an output pin.
- It enters an infinite loop (while(1)) which is standard for FreeRTOS tasks that need to run continuously.
Inside the loop:
- It turns the LED on with digitalWrite(LED1_PIN, HIGH)
- Waits for 500 milliseconds using vTaskDelay(500 / portTICK_PERIOD_MS)
- Turns the LED off with digitalWrite(LED1_PIN, LOW)
- Waits for another 500 milliseconds
- Inside the loop:It turns the LED on with digitalWrite(LED1_PIN, HIGH)
- Waits for 500 milliseconds using vTaskDelay(500 / portTICK_PERIOD_MS)
- Turns the LED off with digitalWrite(LED1_PIN, LOW)
- Waits for another 500 milliseconds
- The vTaskDelay function is crucial here. It yields control back to the FreeRTOS scheduler, allowing other tasks to run during the delay. The parameter portTICK_PERIOD_MS is a constant that converts milliseconds to FreeRTOS tick periods.
Task 2: LED2 Blinking Function
This function is structurally identical to Task 1, but:
- It controls LED2_PIN instead
- It uses a longer delay of 1000 milliseconds (1 second), creating a different blinking pattern
- This function is structurally identical to Task 1, but:It controls LED2_PIN insteadIt uses a longer delay of 1000 milliseconds (1 second), creating a different blinking pattern
Setup Function
- The setup function initializes Serial communication at 115200 baud rate and waits for 1 second.
It then creates two tasks using xTaskCreate(), which has several parameters:
- Task Function: The function to be executed by the task (blinkLED1Task or blinkLED2Task)
- Task Name: A descriptive name for debugging purposes
- Stack Size: The amount of memory (in bytes) allocated for the task's stack (2048 bytes here)
- Task Parameters: Data passed to the task (NULL means no parameters)
- Task Priority: A number from 0-24 where higher numbers mean higher priority; both tasks have the same priority (1)
- Task Handle: A pointer to store the task's handle, used for later management
- It then creates two tasks using xTaskCreate(), which has several parameters:Task Function: The function to be executed by the task (blinkLED1Task or blinkLED2Task)Task Name: A descriptive name for debugging purposesStack Size: The amount of memory (in bytes) allocated for the task's stack (2048 bytes here)Task Parameters: Data passed to the task (NULL means no parameters)Task Priority: A number from 0-24 where higher numbers mean higher priority; both tasks have the same priority (1)Task Handle: A pointer to store the task's handle, used for later management
Loop Function
- The loop function is largely empty because the FreeRTOS scheduler is now in control of executing tasks.
- Including vTaskDelay() in the loop prevents the CPU from wasting cycles in an empty loop.
How the Multitasking Works:
- When the ESP32-S3 starts, it runs the setup() function which creates the two LED blinking tasks.
- The FreeRTOS scheduler takes over and starts executing both tasks concurrently.
- Task 1 runs until it hits vTaskDelay(), then the scheduler suspends it temporarily.
- The scheduler then runs Task 2 until it hits its own vTaskDelay().
- As tasks enter and exit their delay states, the scheduler intelligently switches between them.
- From the user's perspective, both LEDs appear to blink independently and simultaneously.
This is a perfect example of how FreeRTOS enables multitasking on a single-core or multi-core microcontroller, allowing multiple operations to run concurrently without complex manual timing management.
Example 2: Continuous Data Collection With Internet Reconnection
This example demonstrates collecting sensor data continuously, storing it locally when internet connection is lost, and sending it once the connection is restored.
Schematic
Code
Check out the WOKWI Simulation here: WOKWI Simulation
You can create a copy of and practice with it
Key Concepts in Example 2
- Task Synchronization: Using a mutex (SemaphoreHandle_t) to protect shared resources.
- Inter-task Communication: Using a queue (QueueHandle_t) to pass data between tasks.
- Resource Management: Managing local storage for data when connection is unavailable.
- Multiple Task Priorities: Giving connection management a higher priority than data collection and sending.
Advanced FreeRTOS Concepts
Task States in FreeRTOS
- Running: The task is currently executing.
- Ready: The task is ready to run but waiting for CPU time.
- Blocked: The task is waiting for an event (e.g., delay timeout, semaphore).
- Suspended: The task is not available for scheduling.
- Deleted: The task has been deleted but not removed from memory.
Memory Management
FreeRTOS provides memory allocation functions that are designed to be deterministic and avoid fragmentation:
- pvPortMalloc(): Allocate memory
- vPortFree(): Free allocated memory
Tips for Efficient FreeRTOS Applications
- Use Static Allocation: When possible, use static allocation instead of dynamic allocation to avoid fragmentation and allocation failures.
- Choose Task Priorities Carefully: Assign higher priorities to time-critical tasks, but be mindful of priority inversion issues.
- Avoid Blocking in High-Priority Tasks: High-priority tasks should not block for long periods as they prevent lower-priority tasks from running.
- Use Appropriate Stack Sizes: Allocate enough stack space to prevent overflow but not so much that you waste RAM.
- Leverage Event-Driven Programming: Use FreeRTOS notification events instead of polling when possible.
- Consider Tick Rates: Configure the FreeRTOS tick rate appropriately for your application's timing requirements.
- Monitor CPU Usage: Use FreeRTOS's built-in statistics gathering to identify bottlenecks and optimize task performance.
Conclusion
If you like this guide, give it a thumbs up, follow me for more fun projects and guides, and drop a comment if you successfully build a project with FreeRTOS and XIAO, I’d love to see it!