This project demonstrates and compares two fundamental FreeRTOS delay mechanisms: vTaskDelay() and vTaskDelayUntil().
The goal is to help embedded developers understand the difference between relative timing and absolute timing in real-time systems.
Project OverviewReal-time applications depend on predictable task execution. Even small timing errors can accumulate and cause system instability over time.
This demo visualizes timing drift by running two concurrent FreeRTOS tasks on an Arduino UNO, each toggling an LED using a different delay method. A logic analyzer is used to measure and compare their real execution periods.
The FreeRTOS library is included as usual:
#include <Arduino_FreeRTOS.h>Hardware SetupThe project can be simulated using Wokwi Simulator and consists of:
- Arduino UNO running FreeRTOS
- Red LED on Pin 8 → vTaskDelay
- Green LED on Pin 9 → vTaskDelayUntil
- Logic Analyzer to observe timing behavior
ℹ️ LEDs alone cannot show timing drift accurately — the logic analyzer reveals what the eye cannot see.
Task BehaviorTask Using vTaskDelay (Relative Timing)
This task introduces execution time before applying a delay. Because the delay is relative, execution time accumulates as timing drift.
void TaskDelayDemo(void *pvParameters) {
pinMode(LED_DELAY_PIN, OUTPUT);
for (;;) {
digitalWrite(LED_DELAY_PIN, !digitalRead(LED_DELAY_PIN));
vTaskDelay(EXECUTION_TIME_MS / portTICK_PERIOD_MS); // Simulated workload
vTaskDelay(TASK_DELAY_MS / portTICK_PERIOD_MS); // Relative delay
}
}Task Using vTaskDelayUntil (Absolute Timing)
This task uses a fixed reference time, ensuring precise periodic execution, regardless of execution time.
void TaskDelayUntilDemo(void *pvParameters) {
TickType_t xLastWakeTime = xTaskGetTickCount();
pinMode(LED_DELAYUNTIL_PIN, OUTPUT);
for (;;) {
digitalWrite(LED_DELAYUNTIL_PIN, !digitalRead(LED_DELAYUNTIL_PIN));
vTaskDelay(EXECUTION_TIME_MS / portTICK_PERIOD_MS); // Same workload
vTaskDelayUntil(&xLastWakeTime, TASK_DELAY_MS / portTICK_PERIOD_MS);
}
}Task Creation and Scheduler Control
Both tasks are created with the same priority to ensure a fair comparison.
void setup() {
xTaskCreate(TaskDelayDemo, "vTaskDelay", 128, NULL, 1, NULL);
xTaskCreate(TaskDelayUntilDemo, "vTaskDelayUntil", 128, NULL, 1, NULL);
}
void loop() {
// RTOS scheduler takes control
}Logic Analyzer ResultsObserved waveform behavior:
- Red trace (Pin 8): Gradual drift, measured period ≈ 2.1 s
- Green trace (Pin 9): Stable execution at exactly 2.0 s
This clearly shows how execution time affects relative delays but is compensated in absolute delays.
Key Insights
vTaskDelay (Relative Timing)
- Delay starts after task execution
- Execution time adds drift each cycle
- Best for non-critical timing
Use cases: LED blinking, UI feedback, simple delays
vTaskDelayUntil (Absolute Timing)
- Delay calculated from a fixed reference
- Maintains constant periodicity
- Compensates for execution time
Use cases: Sensor sampling, motor control, communication protocols
Real-World Impact- Sensor sampling: Drift causes inaccurate timestamps
- Motor control: Inconsistent cycles create vibration
- Communication: Timing violations break protocols
- Power management: Drift skews energy calculations
- ✅ Use vTaskDelay when timing precision is not critical
- ✅ Use vTaskDelayUntil for real-time, periodic tasks
- ❌ Avoid vTaskDelay in control loops or sampling systems
- Understand relative vs absolute task timing
- Learn how execution time causes drift
- Choose the correct FreeRTOS delay API for real-time designs
This project provides a clear, measurable demonstration of why vTaskDelayUntil is essential for real-time embedded systems.
That's all!If you have any questions or suggestions, don’t hesitate to leave a comment below.


_ztBMuBhMHo.jpg?auto=compress%2Cformat&w=48&h=48&fit=fill&bg=ffffff)

Comments