E15. FreeRTOS Tasks and Dual-Core Basics
Run two jobs at the same time: a task blinks one LED while loop() blinks another at a different speed.
Learning outcomes
- Say what a task is: a second loop that runs at the same time as loop()
- Start a task with xTaskCreate
- Use delay() in a task without stopping loop()
- Choose between millis() and a task
Parts and preparation
ESP32 DevKit, USB cable, two LEDs with 220-330 ohm resistors on GPIO 2 and GPIO 4 (or the on-board LED plus one external LED).
Before power: inspect wiring, confirm supply voltage and ensure all connected circuits share GND.
What you will do
On the Uno, delay() stops everything, so you learned millis() to do two things at once. The ESP32 has another way: tasks.
The sketch starts a task that blinks the LED on GPIO 4 once a second, while loop() blinks the LED on GPIO 2 four times faster. Both use plain delay().
What is a task?
The ESP32 runs a small operating system called FreeRTOS. It shares the processor between tasks, switching between them many times a second. Your loop() is already one task.
A task you write looks like a function with a while (true) loop inside, so it never ends. When a task calls delay(), only that task waits - the others keep running. A classic ESP32 also has two processor cores, so two tasks can really run at the same moment.
| Part of the call | Meaning |
|---|---|
| blinkTask | The function the task runs |
| "blink" | A name for the task (any short text) |
| 2048 | Memory for the task - raise it if the board keeps restarting |
| NULL, 1, NULL | Copy as they are (no input, normal priority, no handle) |
Task or millis()?
millis() in one loop() is still a good choice for simple jobs, and you already know it. Tasks help when one job is slow - such as waiting for a website - and another must keep going.
Be careful when two tasks use the same variable or the same Serial port at once. Bigger projects pass data between tasks with a FreeRTOS queue - a topic for after this course.
Wiring and safe build sequence
- GPIO 2 -> 220-330 ohm -> LED -> GND (or the on-board LED)
- GPIO 4 -> 220-330 ohm -> LED -> GND
Worked sketch: Two LEDs, two speeds
Download .inoWhat this sketch is for: The GPIO 2 LED blinks fast and the GPIO 4 LED blinks slowly, and neither one waits for the other.
const int fastLed = 2; // blinked by loop()
const int slowLed = 4; // blinked by the task
void blinkTask(void* parameter) { // the task (copy this first line as it is)
pinMode(slowLed, OUTPUT); // the slow LED pin is an output
while (true) { // a task never ends, so it loops for ever
digitalWrite(slowLed, HIGH); // slow LED on
delay(1000); // wait 1 second - only this task waits
digitalWrite(slowLed, LOW); // slow LED off
delay(1000); // wait 1 second
}
}
void setup() { // runs once
pinMode(fastLed, OUTPUT); // the fast LED pin is an output
xTaskCreate(blinkTask, "blink", 2048, NULL, 1, NULL); // start the task running
}
void loop() { // loop() is a task too
digitalWrite(fastLed, HIGH); // fast LED on
delay(250); // wait a quarter of a second
digitalWrite(fastLed, LOW); // fast LED off
delay(250); // wait a quarter of a second
}How the code works
- void* parameter is how FreeRTOS writes 'this task takes no input that matters' - copy it as it is.
- while (true) keeps the task alive. A task function must never reach its closing bracket.
- Try it the Uno way: move the slow LED code into loop(). The two LEDs then take turns instead of blinking independently.
Test and record evidence
Practical evidence checklist
Common faults and checks
- Slow LED never lights: check its wiring on GPIO 4, and that xTaskCreate is inside setup().
- Board keeps restarting: raise 2048 to 4096.
- Only one LED on your board: use the on-board LED for GPIO 2 and add one external LED on GPIO 4.
Check your understanding
Q1. What is a task?
Show answer
A function that runs at the same time as loop(), shared out by FreeRTOS.
Q2. What operating system runs under ESP32 sketches?
Show answer
FreeRTOS.
Q3. Which command starts a task?
Show answer
xTaskCreate.
Q4. When a task calls delay(), what happens to loop()?
Show answer
Nothing - loop() keeps running. Only the task waits.
Q5. Why does a task use while (true)?
Show answer
So it never ends.
Q6. What should you try if the board keeps restarting?
Show answer
Give the task more memory: raise 2048 to 4096.
Q7. When is millis() in loop() still a good choice?
Show answer
For simple jobs that do not have to wait for something slow.