Chapter 10: The Same Thing with FreeRTOS#
Sample code#
Directory:
docs/os-on-arduino/code/10_freertos
uv run pio run -d 10_freertos -t upload
uv run pio device monitor -b 115200
The real-FreeRTOS examples are in 10_freertos_real, one environment (the build
configuration picked with -e) per example. Replace integrated with basic,
preemptive, task_control, semaphore or queue for the others.
uv run pio run -d 10_freertos_real -e integrated -t upload
Source code#
The sketch for this chapter. Click a file name to expand. The code is mirrored automatically from the manuscript repository, so what you see here is always current.
docs/os-on-arduino/code/10_freertos#
src/main.cpp — 229 lines
1/**
2 * Chapter 10: FreeRTOS Implementation
3 *
4 * Demonstrates how to implement the same functionality using FreeRTOS.
5 * Shows the mapping between our custom OS and FreeRTOS APIs.
6 *
7 * Note: Arduino Uno R4 WiFi has FreeRTOS support through Arduino_FreeRTOS library.
8 * This example shows the concepts - actual FreeRTOS requires the library.
9 */
10
11#include <Arduino.h>
12#include "Arduino_LED_Matrix.h"
13#include "servo_pwm.h"
14
15// This is a simplified demonstration showing FreeRTOS concepts
16// For actual FreeRTOS, include <Arduino_FreeRTOS.h>
17
18ArduinoLEDMatrix matrix;
19ServoPWM servo;
20
21// Arduino UNO R4 WiFi は基板上に "L" と印字された LED を持っています。
22// 追加部品なしで動かせるよう、これを主 LED として使います。
23#define LED1_PIN LED_BUILTIN
24#define LED2_PIN 3
25#define SERVO_PIN 9
26
27// Simulated FreeRTOS-like API
28// In real FreeRTOS, these would be the actual FreeRTOS functions
29
30typedef void (*TaskFunction_t)(void*);
31
32struct FakeTask {
33 TaskFunction_t func;
34 const char* name;
35 void* param;
36 unsigned long delay_until;
37 bool suspended;
38};
39
40#define MAX_FAKE_TASKS 8
41FakeTask fake_tasks[MAX_FAKE_TASKS];
42int fake_task_count = 0;
43
44// xTaskCreate equivalent
45void xTaskCreate(TaskFunction_t func, const char* name, int stack, void* param, int priority, void* handle)
46{
47 if (fake_task_count < MAX_FAKE_TASKS) {
48 fake_tasks[fake_task_count].func = func;
49 fake_tasks[fake_task_count].name = name;
50 fake_tasks[fake_task_count].param = param;
51 fake_tasks[fake_task_count].delay_until = 0;
52 fake_tasks[fake_task_count].suspended = false;
53 fake_task_count++;
54 Serial.print("[FreeRTOS] Task created: ");
55 Serial.println(name);
56 }
57}
58
59// vTaskDelay equivalent (simulated)
60unsigned long vTaskDelay_ms = 0;
61void vTaskDelay(unsigned long ticks)
62{
63 vTaskDelay_ms = millis() + ticks;
64}
65
66// vTaskDelayUntil equivalent
67void vTaskDelayUntil(unsigned long* lastWake, unsigned long period)
68{
69 *lastWake += period;
70 while (millis() < *lastWake) {
71 // In real FreeRTOS, this would yield to scheduler
72 delay(1);
73 }
74}
75
76// ============================================
77// FreeRTOS-style Task Implementations
78// ============================================
79
80// In real FreeRTOS, tasks are functions that never return
81// and use vTaskDelay() or vTaskDelayUntil() for timing
82
83void vTaskLED1(void* param)
84{
85 // Real FreeRTOS task would have:
86 // while(1) {
87 // digitalWrite(LED1_PIN, !digitalRead(LED1_PIN));
88 // vTaskDelay(pdMS_TO_TICKS(200));
89 // }
90 (void)param;
91 static bool state = false;
92 state = !state;
93 digitalWrite(LED1_PIN, state);
94}
95
96void vTaskLED2(void* param)
97{
98 (void)param;
99 static bool state = false;
100 state = !state;
101 digitalWrite(LED2_PIN, state);
102}
103
104void vTaskServo(void* param)
105{
106 (void)param;
107 static int angle = 0;
108 angle = (angle + 10) % 180;
109 servo.write(angle);
110}
111
112void vTaskSerial(void* param)
113{
114 (void)param;
115 static int count = 0;
116 Serial.print("[FreeRTOS Task] Count: ");
117 Serial.println(count++);
118}
119
120void vTaskDisplay(void* param)
121{
122 (void)param;
123 static uint8_t frame[8][12];
124 static int pos = 0;
125
126 memset(frame, 0, sizeof(frame));
127
128 // Moving dot animation
129 frame[3][pos] = 1;
130 frame[4][pos] = 1;
131 pos = (pos + 1) % 12;
132
133 matrix.renderBitmap(frame, 8, 12);
134}
135
136// ============================================
137// Comparison: Our OS vs FreeRTOS
138// ============================================
139
140void show_api_comparison(void)
141{
142 Serial.println();
143 Serial.println("============================================");
144 Serial.println(" Our OS vs FreeRTOS Comparison");
145 Serial.println("============================================");
146 Serial.println();
147 Serial.println("Task Creation:");
148 Serial.println(" Our OS: os_create_task(func, name, stack)");
149 Serial.println(" FreeRTOS: xTaskCreate(func, name, stack, param, priority, handle)");
150 Serial.println();
151 Serial.println("Delay:");
152 Serial.println(" Our OS: os_sleep(ms)");
153 Serial.println(" FreeRTOS: vTaskDelay(pdMS_TO_TICKS(ms))");
154 Serial.println();
155 Serial.println("Task Control:");
156 Serial.println(" Our OS: os_suspend_task(id) / os_resume_task(id)");
157 Serial.println(" FreeRTOS: vTaskSuspend(handle) / vTaskResume(handle)");
158 Serial.println();
159 Serial.println("Semaphores (FreeRTOS only):");
160 Serial.println(" xSemaphoreCreateBinary()");
161 Serial.println(" xSemaphoreTake(sem, timeout)");
162 Serial.println(" xSemaphoreGive(sem)");
163 Serial.println();
164 Serial.println("Queues (FreeRTOS only):");
165 Serial.println(" xQueueCreate(length, itemSize)");
166 Serial.println(" xQueueSend(queue, &item, timeout)");
167 Serial.println(" xQueueReceive(queue, &item, timeout)");
168 Serial.println();
169 Serial.println("============================================");
170 Serial.println();
171}
172
173// Simulated scheduler loop
174unsigned long task_timers[MAX_FAKE_TASKS] = {0};
175unsigned long task_periods[] = {200, 500, 100, 1000, 150};
176
177void fake_scheduler_run(void)
178{
179 unsigned long now = millis();
180 for (int i = 0; i < fake_task_count && i < 5; i++) {
181 if (!fake_tasks[i].suspended && now - task_timers[i] >= task_periods[i]) {
182 task_timers[i] = now;
183 fake_tasks[i].func(fake_tasks[i].param);
184 }
185 }
186}
187
188void setup()
189{
190 Serial.begin(115200);
191 while (!Serial) { }
192
193 Serial.println();
194 Serial.println("=== FreeRTOS Concepts Demo ===");
195 Serial.println();
196 Serial.println("This demonstrates FreeRTOS-style task creation.");
197 Serial.println("Note: For actual FreeRTOS, use Arduino_FreeRTOS library.");
198 Serial.println();
199
200 // Initialize hardware
201 pinMode(LED1_PIN, OUTPUT);
202 pinMode(LED2_PIN, OUTPUT);
203 servo.attach(SERVO_PIN);
204 matrix.begin();
205
206 // Create tasks FreeRTOS-style
207 xTaskCreate(vTaskLED1, "LED1", 128, NULL, 1, NULL);
208 xTaskCreate(vTaskLED2, "LED2", 128, NULL, 1, NULL);
209 xTaskCreate(vTaskServo, "Servo", 256, NULL, 2, NULL);
210 xTaskCreate(vTaskSerial, "Serial", 512, NULL, 1, NULL);
211 xTaskCreate(vTaskDisplay, "Display", 256, NULL, 1, NULL);
212
213 Serial.println();
214
215 // Show API comparison
216 show_api_comparison();
217
218 Serial.println("Starting FreeRTOS-style scheduler...");
219 Serial.println();
220
221 // In real FreeRTOS, you would call: vTaskStartScheduler();
222 // The scheduler never returns in FreeRTOS
223}
224
225void loop()
226{
227 // Simulated FreeRTOS scheduler
228 fake_scheduler_run();
229}
include/servo_pwm.h — 61 lines
1/**
2 * Simple PWM-based Servo Control for Arduino Uno R4 WiFi
3 *
4 * Since the standard Servo library is not available in PlatformIO for Renesas,
5 * this provides a simple software PWM implementation.
6 */
7
8#ifndef SERVO_PWM_H
9#define SERVO_PWM_H
10
11#include <Arduino.h>
12
13class ServoPWM {
14private:
15 int pin;
16 int angle;
17 bool attached;
18
19 void writeMicroseconds(int us) {
20 if (!attached) return;
21 // Standard servo pulse: 500-2500us for 0-180 degrees
22 // Period: 20ms (50Hz)
23 digitalWrite(pin, HIGH);
24 delayMicroseconds(us);
25 digitalWrite(pin, LOW);
26 }
27
28public:
29 ServoPWM() : pin(-1), angle(90), attached(false) {}
30
31 void attach(int p) {
32 pin = p;
33 pinMode(pin, OUTPUT);
34 attached = true;
35 }
36
37 void detach() {
38 attached = false;
39 }
40
41 void write(int ang) {
42 if (!attached) return;
43 angle = constrain(ang, 0, 180);
44 // Map angle (0-180) to pulse width (500-2500us)
45 int us = map(angle, 0, 180, 500, 2500);
46 writeMicroseconds(us);
47 }
48
49 int read() {
50 return angle;
51 }
52
53 // Call this in loop() to maintain servo position
54 void refresh() {
55 if (!attached) return;
56 int us = map(angle, 0, 180, 500, 2500);
57 writeMicroseconds(us);
58 }
59};
60
61#endif // SERVO_PWM_H
docs/os-on-arduino/code/10_freertos_real#
src/basic.cpp — 35 lines
1// 第10章 10.3「FreeRTOSの基本」の掲載例
2#include <Arduino.h>
3#include <Arduino_FreeRTOS.h>
4
5// タスク関数
6void task_led(void *pvParameters)
7{
8 pinMode(2, OUTPUT);
9
10 for (;;) { // FreeRTOSではwhile(1)の代わりにfor(;;)が慣例
11 digitalWrite(2, !digitalRead(2));
12 vTaskDelay(pdMS_TO_TICKS(500)); // 500ms待機
13 }
14}
15
16void setup()
17{
18 // タスク作成
19 xTaskCreate(
20 task_led, // タスク関数
21 "LED", // タスク名
22 128, // スタックサイズ(ワード単位)
23 NULL, // パラメータ
24 1, // 優先度
25 NULL // タスクハンドル
26 );
27
28 // スケジューラ開始
29 vTaskStartScheduler();
30}
31
32void loop()
33{
34 // FreeRTOS使用時は到達しない
35}
src/integrated.cpp — 103 lines
1// freertos_integrated.ino(第10章 10.10 の掲載例)
2#include <Arduino.h>
3#include <Arduino_FreeRTOS.h> // semphr.h などもここから取り込まれる
4#include <Servo.h>
5
6// 基板上の「L」LED。追加部品なしで動かせるようにこれを使います
7#define LED_PIN LED_BUILTIN
8#define SERVO_PIN 9
9#define POT_PIN A0
10
11// 共有データ
12// R4 同梱の FreeRTOS はミューテックスが無効なので、
13// バイナリセマフォを「1 個の鍵」として使って排他する (10.7 参照)
14volatile int g_pot_value = 0;
15SemaphoreHandle_t xDataMutex = NULL;
16
17// サーボ
18Servo servo;
19
20// タスク: センサ読み取り
21void task_sensor(void *pvParameters)
22{
23 for (;;) {
24 int value = analogRead(POT_PIN);
25
26 if (xSemaphoreTake(xDataMutex, portMAX_DELAY) == pdTRUE) {
27 g_pot_value = value;
28 xSemaphoreGive(xDataMutex);
29 }
30
31 vTaskDelay(pdMS_TO_TICKS(50));
32 }
33}
34
35// タスク: サーボ制御
36void task_servo(void *pvParameters)
37{
38 servo.attach(SERVO_PIN);
39
40 for (;;) {
41 int value;
42 if (xSemaphoreTake(xDataMutex, portMAX_DELAY) == pdTRUE) {
43 value = g_pot_value;
44 xSemaphoreGive(xDataMutex);
45 }
46
47 int angle = map(value, 0, 1023, 0, 180);
48 servo.write(angle);
49
50 vTaskDelay(pdMS_TO_TICKS(20));
51 }
52}
53
54// タスク: LED点滅
55void task_led(void *pvParameters)
56{
57 pinMode(LED_PIN, OUTPUT);
58
59 for (;;) {
60 digitalWrite(LED_PIN, !digitalRead(LED_PIN));
61 vTaskDelay(pdMS_TO_TICKS(500));
62 }
63}
64
65// タスク: 状態表示
66void task_monitor(void *pvParameters)
67{
68 for (;;) {
69 Serial.println("=== FreeRTOS Status ===");
70 Serial.print("Tick: ");
71 Serial.println(xTaskGetTickCount());
72 Serial.print("Free heap: ");
73 Serial.println(xPortGetFreeHeapSize());
74 Serial.print("Pot value: ");
75 Serial.println(g_pot_value);
76 Serial.println();
77
78 vTaskDelay(pdMS_TO_TICKS(2000));
79 }
80}
81
82void setup()
83{
84 Serial.begin(115200);
85 while (!Serial) { }
86
87 Serial.println("=== FreeRTOS Integrated Demo ===\n");
88
89 // 排他用のバイナリセマフォを作成し、最初は「空き」にしておく
90 xDataMutex = xSemaphoreCreateBinary();
91 xSemaphoreGive(xDataMutex);
92
93 // タスク作成
94 xTaskCreate(task_sensor, "Sensor", 128, NULL, 2, NULL);
95 xTaskCreate(task_servo, "Servo", 128, NULL, 2, NULL);
96 xTaskCreate(task_led, "LED", 128, NULL, 1, NULL);
97 xTaskCreate(task_monitor, "Monitor", 256, NULL, 1, NULL);
98
99 // スケジューラ開始
100 vTaskStartScheduler();
101}
102
103void loop() { }
src/preemptive.cpp — 48 lines
1// freertos_preemptive.ino(第10章 10.4 の掲載例)
2#include <Arduino.h>
3#include <Arduino_FreeRTOS.h>
4
5void task_led1(void *pvParameters)
6{
7 pinMode(2, OUTPUT);
8 for (;;) {
9 digitalWrite(2, !digitalRead(2));
10 vTaskDelay(pdMS_TO_TICKS(100));
11 }
12}
13
14void task_led2(void *pvParameters)
15{
16 pinMode(3, OUTPUT);
17 for (;;) {
18 digitalWrite(3, !digitalRead(3));
19 vTaskDelay(pdMS_TO_TICKS(300));
20 }
21}
22
23// 重い計算タスク(yield不要!)
24void task_heavy(void *pvParameters)
25{
26 pinMode(4, OUTPUT);
27 for (;;) {
28 volatile long sum = 0;
29 for (long i = 0; i < 1000000; i++) {
30 sum += i;
31 }
32 digitalWrite(4, !digitalRead(4));
33 // vTaskDelayなしでもプリエンプションで切り替わる
34 }
35}
36
37void setup()
38{
39 Serial.begin(115200);
40
41 xTaskCreate(task_led1, "LED1", 128, NULL, 1, NULL);
42 xTaskCreate(task_led2, "LED2", 128, NULL, 1, NULL);
43 xTaskCreate(task_heavy, "Heavy", 256, NULL, 1, NULL);
44
45 vTaskStartScheduler();
46}
47
48void loop() { }
src/queue.cpp — 54 lines
1// 第10章 10.8「キュー(メッセージパッシング)」の掲載例
2#include <Arduino.h>
3#include <Arduino_FreeRTOS.h> // queue.h などもここから取り込まれる
4
5QueueHandle_t xQueue = NULL;
6
7typedef struct {
8 int sensorId;
9 int value;
10} SensorData;
11
12void task_sensor(void *pvParameters)
13{
14 for (;;) {
15 SensorData data;
16 data.sensorId = 1;
17 data.value = analogRead(A0);
18
19 // キューに送信
20 xQueueSend(xQueue, &data, portMAX_DELAY);
21
22 vTaskDelay(pdMS_TO_TICKS(100));
23 }
24}
25
26void task_processor(void *pvParameters)
27{
28 SensorData received;
29
30 for (;;) {
31 // キューから受信
32 if (xQueueReceive(xQueue, &received, portMAX_DELAY) == pdTRUE) {
33 Serial.print("Sensor ");
34 Serial.print(received.sensorId);
35 Serial.print(": ");
36 Serial.println(received.value);
37 }
38 }
39}
40
41void setup()
42{
43 Serial.begin(115200);
44
45 // キュー作成(10要素)
46 xQueue = xQueueCreate(10, sizeof(SensorData));
47
48 xTaskCreate(task_sensor, "Sensor", 128, NULL, 1, NULL);
49 xTaskCreate(task_processor, "Processor", 256, NULL, 2, NULL);
50
51 vTaskStartScheduler();
52}
53
54void loop() { }
src/semaphore.cpp — 44 lines
1// 第10章 10.7「セマフォの例」の掲載例
2#include <Arduino.h>
3#include <Arduino_FreeRTOS.h> // semphr.h などもここから取り込まれる
4
5SemaphoreHandle_t xSemaphore = NULL;
6volatile int sharedData = 0;
7
8void task_producer(void *pvParameters)
9{
10 for (;;) {
11 sharedData++;
12
13 // セマフォを与える(シグナル)
14 xSemaphoreGive(xSemaphore);
15
16 vTaskDelay(pdMS_TO_TICKS(500));
17 }
18}
19
20void task_consumer(void *pvParameters)
21{
22 for (;;) {
23 // セマフォを待つ
24 if (xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdTRUE) {
25 Serial.print("Received: ");
26 Serial.println(sharedData);
27 }
28 }
29}
30
31void setup()
32{
33 Serial.begin(115200);
34
35 // バイナリセマフォ作成
36 xSemaphore = xSemaphoreCreateBinary();
37
38 xTaskCreate(task_producer, "Producer", 128, NULL, 1, NULL);
39 xTaskCreate(task_consumer, "Consumer", 128, NULL, 1, NULL);
40
41 vTaskStartScheduler();
42}
43
44void loop() { }
src/task_control.cpp — 62 lines
1// freertos_task_control.ino(第10章 10.5 の掲載例)
2#include <Arduino.h>
3#include <Arduino_FreeRTOS.h>
4
5TaskHandle_t ledTaskHandle = NULL;
6
7void task_led(void *pvParameters)
8{
9 pinMode(2, OUTPUT);
10 for (;;) {
11 digitalWrite(2, !digitalRead(2));
12 vTaskDelay(pdMS_TO_TICKS(200));
13 }
14}
15
16void task_shell(void *pvParameters)
17{
18 for (;;) {
19 if (Serial.available()) {
20 String cmd = Serial.readStringUntil('\n');
21 cmd.trim();
22
23 if (cmd == "suspend") {
24 if (ledTaskHandle != NULL) {
25 vTaskSuspend(ledTaskHandle); // タスク停止
26 Serial.println("LED task suspended");
27 }
28 }
29 else if (cmd == "resume") {
30 if (ledTaskHandle != NULL) {
31 vTaskResume(ledTaskHandle); // タスク再開
32 Serial.println("LED task resumed");
33 }
34 }
35 else if (cmd == "info") {
36 // タスク情報表示
37 Serial.print("Free heap: ");
38 Serial.println(xPortGetFreeHeapSize());
39
40 Serial.print("Tick count: ");
41 Serial.println(xTaskGetTickCount());
42 }
43 }
44 vTaskDelay(pdMS_TO_TICKS(10));
45 }
46}
47
48void setup()
49{
50 Serial.begin(115200);
51 while (!Serial) { }
52
53 Serial.println("=== FreeRTOS Task Control Demo ===");
54 Serial.println("Commands: suspend, resume, info");
55
56 xTaskCreate(task_led, "LED", 128, NULL, 1, &ledTaskHandle);
57 xTaskCreate(task_shell, "Shell", 256, NULL, 2, NULL);
58
59 vTaskStartScheduler();
60}
61
62void loop() { }
On the board#
10_freertos running with the monitor open. The tasks are written against a
FreeRTOS-style API (xTaskCreate and so on) and run by a simple scheduler inside
loop(); the [FreeRTOS Task] task increments Count once a second.#
The real-FreeRTOS integrated example, flashed with
uv run pio run -d 10_freertos_real -e integrated -t upload (-e names an environment, i.e. a
build configuration, in platformio.ini). Every 2 s it prints the FreeRTOS
tick count, the free heap and the A0 reading. With nothing connected to A0, Pot value
floats around 200; with a potentiometer it follows the knob (0–1023).#
Pitfalls#
(Collected from reader questions — ask via GitHub Issues.)