Lab: Multi-Task Firmware with FreeRTOS
Course 1 · Module 4 Type: Hands-on lab (create tasks, then add queue / mutex) Suggested time: 2.5–3.5 hours
Read first: Lesson · Cheatsheet · ← Table of Contents · ← M03 · M05 →
Note: the snippets in this lab use the API of the TESAIoT Bitstream firmware, which is not yet open source. See detail and equivalent examples in the public SDK in the note at the top of the lesson Multi-task Firmware with FreeRTOS
Useful references during the lab
Section titled “Useful references during the lab”| Document | Use when |
|---|---|
| Lesson snippets | xTaskCreate, queue, mutex, event group |
| M03 Driver APIs | LED / button / UART / I²C |
| TESAIoT Developer Hub | System / Real-Time examples |
| FreeRTOS xTaskCreate | Checking the parameters |
Lab Goals
Section titled “Lab Goals”Once complete, you will:
- Have created at least two tasks running at different periods
- Use
vTaskDelay(pdMS_TO_TICKS(...))instead of busy-waiting - Send an event through at least one queue path
- Use a mutex (or
cm55_i2c_manager_i2c_lock) when sharing a resource - (Recommended) use an event group or a binary semaphore as a ready flag
Prerequisites
Section titled “Prerequisites”- Have passed Lab M03 (at least GPIO + UART)
- An example project that can call
cm55_initialize/cm55_start_scheduler - Know the point at which the project allows
xTaskCreate(after init / in a callback)
Don’t call
vTaskStartScheduler()yourself again if the project already usescm55_start_scheduler()
Lab A — Two blink rates (required)
Section titled “Lab A — Two blink rates (required)”- Create a task
LAB_BLINK_SLOWthat blinks an LED at a 500 ms period - Create a task
LAB_BLINK_FASTthat blinks another LED (or switches colour) at a 200 ms period - Use slightly different priorities (such as idle+1 and idle+2) and observe the behaviour
led_controller_toggle(LED_RED);vTaskDelay(pdMS_TO_TICKS(500));Pass when: you see two distinct rhythms on the board, with no long while busy-delay inside a single task
Lab B — Button → Queue → UART task (required)
Section titled “Lab B — Button → Queue → UART task (required)”- Create a short-message queue (
xQueueCreate) - Create a task that pulls from the queue and calls
printf/LOG_INFO - In
cm55_button_on_pressed(or wherever the button event fires),xQueueSenda message, such asbtn\r\n
Pass when: pressing the button shows a message on serial, while the blink task from Lab A keeps running
Lab C — Shared resource lock (required)
Section titled “Lab C — Shared resource lock (required)”Choose at least one:
C1 Your own mutex
Section titled “C1 Your own mutex”- Create with
xSemaphoreCreateMutex - Have two tasks share printing, or share a status variable, under Take/Give
C2 The SDK’s I²C lock
Section titled “C2 The SDK’s I²C lock”- Read a sensor in one task
- Wrap it with
cm55_i2c_manager_i2c_lock/unlock(or the lock API your project has)
Pass when: you can explain why locking is needed, and demonstrate there is no random bus contention breaking things
Lab D — Ready flag (recommended)
Section titled “Lab D — Ready flag (recommended)”- Create with
xEventGroupCreate - Have a setup task set the “READY” bit once init is done
- Have another task call
xEventGroupWaitBitsbefore starting its work
Or use a binary semaphore as a “now ready” signal instead.
Pass when: the waiting task does not start its work before the ready signal
Lab E — Timing notes (short write-up)
Section titled “Lab E — Timing notes (short write-up)”Answer briefly (5–8 lines):
- What would you risk by putting
printfinside an ISR? - What would happen to the system if the heaviest task’s priority stayed at maximum all the time?
- How does a queue’s
depth × sizeof(item)affect RAM?
Troubleshooting
Section titled “Troubleshooting”| Symptom | Approach |
|---|---|
xTaskCreate returns something other than pdPASS |
The heap is exhausted · the stack is too large · called before the system is ready |
| The task doesn’t run | cm55_start_scheduler not called yet · a priority/name clash · the task’s loop ended and wasn’t recreated |
| A HardFault when pressing the button | A blocking API is inside an ISR · check for FromISR |
| UART output is garbled / hangs | Missing a mutex / recursive lock around printf |
| Random I²C NACKs | Missing a bus lock when several tasks use it |
Submit checklist
Section titled “Submit checklist”- Lab A passed
- Lab B passed
- Lab C passed
- (Recommended) Lab D
- Lab E fully answered
- The API table in rtos-patterns.md filled in
Lesson · Cheatsheet · Table of Contents · M05 →
Cite this lesson
If you teach from this lesson or reuse it in slides or documents, credit it with the text below. If you changed it, add (adapted) after the title.
"Lab: Multi-Task Firmware with FreeRTOS" from TESA Open Knowledge by the Thai Embedded Systems Association (TESA), https://github.com/tesaiot/tesa-qualification-program, licensed under CC BY-NC 4.0
Thai attribution: "แล็บ: เฟิร์มแวร์หลาย task ด้วย FreeRTOS" จาก TESA Open Knowledge โดยสมาคมสมองกลฝังตัวไทย (Thai Embedded Systems Association: TESA) https://github.com/tesaiot/tesa-qualification-program สัญญาอนุญาต CC BY-NC 4.0
Lesson link: https://tesaiot.github.io/tesa-qualification-program/en/courses/firmware-sdk-edge-ai/m04-rtos/l02-lab/
This lesson adapts the source below; keep its credit too.
https://github.com/drsanti/TESAIoT-Courses/blob/287c21814ba8c75f693136616dcd270349a15966/C1/M04/lab.md · Original content by Asst. Prof. Dr. Santi Nuratch (ผศ.ดร.สันติ นุราช), KMUTT. Course 1 (C1/) of drsanti/TESAIoT-Courses. TESA funded the work and holds the rights; published here under CC BY-NC 4.0. The upstream repository carries no licence file. Text kept faithful; structure, front matter, quizzes and notes added by TESA Open Knowledge.
TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · CC BY-NC 4.0
Content is licensed CC BY-NC 4.0. Reuse it non-commercially and credit the Thai Embedded Systems Association (TESA) every time. · How to cite TESA