第9章: 統合とロボット制御#

ここまでの部品を組み合わせ、OS の上でロボット制御タスクを走らせます。

サンプルコード#

  • ディレクトリ: docs/os-on-arduino/code/09_integration

  • ビルドと書き込み:

uv run pio run -d 09_integration -t upload
uv run pio device monitor -b 115200

コードを見る: docs/os-on-arduino/code/09_integration

ソースコード#

この章のスケッチです。ファイル名をクリックすると開きます。コードは原稿リポジトリから自動で同期されているので、ここに出ているものが常に最新です。

src/main.cpp — 165 行

GitHub で開く

  1// 08_integrated_os.ino
  2// 統合版自作OS
  3
  4#include "os_kernel.h"
  5#include "os_fault.h"
  6#include "os_display.h"
  7#include "shell.h"
  8#include "interpreter.h"
  9#include <Servo.h>
 10
 11// ハードウェアピン定義
 12// Arduino UNO R4 WiFi は基板上に "L" と印字された LED を持っています。
 13// 追加部品なしで動かせるよう、これを主 LED として使います。
 14#define LED1_PIN LED_BUILTIN
 15#define LED2_PIN 3
 16#define LED3_PIN 4
 17#define SERVO1_PIN 9
 18#define SERVO2_PIN 10
 19#define BUTTON_PIN 7
 20#define POT_PIN A0
 21
 22// グローバル変数(タスク間共有)
 23volatile int g_pot_value = 0;
 24volatile bool g_button_pressed = false;
 25Servo servo1, servo2;
 26
 27// ===========================================
 28// タスク定義
 29// ===========================================
 30
 31// タスク1: システムハートビート
 32void task_heartbeat(void)
 33{
 34    pinMode(LED1_PIN, OUTPUT);
 35    while (1) {
 36        digitalWrite(LED1_PIN, HIGH);
 37        os_sleep(100);
 38        digitalWrite(LED1_PIN, LOW);
 39        os_sleep(900);
 40    }
 41}
 42
 43// タスク2: センサ読み取り
 44// 注: ここではanalogRead()を使っているが、内部のレジスタ操作については
 45//     Chapter 12でADC14のレジスタを直接操作する方法を学ぶ。
 46void task_sensor(void)
 47{
 48    pinMode(POT_PIN, INPUT);
 49    pinMode(BUTTON_PIN, INPUT_PULLUP);
 50
 51    while (1) {
 52        g_pot_value = analogRead(POT_PIN);
 53        g_button_pressed = (digitalRead(BUTTON_PIN) == LOW);
 54        os_sleep(50);
 55    }
 56}
 57
 58// タスク3: サーボ制御(ポテンショメータ連動)
 59// 注: ここではServoライブラリ(またはServoPWM)を使っているが、
 60//     ソフトウェアPWMはコンテキストスイッチの影響でジッターする可能性がある。
 61//     Chapter 12でGPTタイマを使ったハードウェアPWMに改善する。
 62void task_servo(void)
 63{
 64    servo1.attach(SERVO1_PIN);
 65    servo2.attach(SERVO2_PIN);
 66
 67    while (1) {
 68        int angle = map(g_pot_value, 0, 1023, 0, 180);
 69        servo1.write(angle);
 70        servo2.write(180 - angle);  // 逆方向
 71        os_sleep(20);
 72    }
 73}
 74
 75// タスク4: LED表示(センサ値を3段階表示)
 76void task_led_indicator(void)
 77{
 78    pinMode(LED2_PIN, OUTPUT);
 79    pinMode(LED3_PIN, OUTPUT);
 80
 81    while (1) {
 82        int level = g_pot_value / 341;  // 0-2の3段階
 83
 84        digitalWrite(LED2_PIN, level >= 1);
 85        digitalWrite(LED3_PIN, level >= 2);
 86
 87        os_sleep(100);
 88    }
 89}
 90
 91// タスク5: ボタン監視(押すたびにイベント発生)
 92void task_button(void)
 93{
 94    bool last_state = false;
 95    int press_count = 0;
 96
 97    while (1) {
 98        if (g_button_pressed && !last_state) {
 99            // 押された瞬間
100            press_count++;
101            Serial.print("[Button] Pressed! Count: ");
102            Serial.println(press_count);
103        }
104        last_state = g_button_pressed;
105        os_sleep(20);
106    }
107}
108
109// ===========================================
110// セットアップ
111// ===========================================
112
113void setup()
114{
115    Serial.begin(115200);
116    while (!Serial) { }
117
118    // スタートアップメッセージ
119    Serial.println();
120    Serial.println("╔════════════════════════════════════════╗");
121    Serial.println("║     MiniOS for Arduino UNO R4 WiFi     ║");
122    Serial.println("║          Version 1.0                   ║");
123    Serial.println("╚════════════════════════════════════════╝");
124    Serial.println();
125
126    // 例外設定
127    fault_init();
128
129    // OS初期化
130    os_init();
131    interp_init();
132
133    Serial.println("Creating tasks...");
134
135    // タスク作成
136    os_create_task(task_heartbeat, "Heartbeat", 256);
137    os_create_task(task_sensor, "Sensor", 512);
138    os_create_task(task_servo, "Servo", 512);
139    os_create_task(task_led_indicator, "LED_Ind", 256);
140    os_create_task(task_button, "Button", 512);
141    os_create_task(shell_task, "Shell", 1024);
142    os_create_task(display_task, "Display", 512);
143
144    Serial.println();
145    Serial.println("Tasks created:");
146    for (int i = 0; i < os_get_task_count(); i++) {
147        Serial.print("  ");
148        Serial.print(i);
149        Serial.print(": ");
150        Serial.println(os_get_task_name(i));
151    }
152
153    Serial.println();
154    Serial.println("Type 'help' for available commands.");
155    Serial.println("Type 'run' to execute motion scripts.");
156    Serial.println();
157
158    // OS開始
159    os_start();
160}
161
162void loop()
163{
164    // 到達しない
165}
include/interpreter.h — 36 行

GitHub で開く

 1// interpreter.h
 2
 3#ifndef INTERPRETER_H
 4#define INTERPRETER_H
 5
 6#include <stdint.h>
 7
 8#define MAX_SCRIPT_LINES 32
 9#define MAX_LINE_LENGTH 32
10
11// 実行結果
12typedef enum {
13    EXEC_OK,
14    EXEC_ERROR,
15    EXEC_END
16} ExecResult;
17
18// インタプリタ初期化
19void interp_init(void);
20
21// スクリプトをロード(複数行を改行で区切って渡す)
22int interp_load(const char *script);
23
24// 1行実行
25ExecResult interp_step(void);
26
27// スクリプト全体を実行
28int interp_run(void);
29
30// 実行中かどうか
31bool interp_is_running(void);
32
33// インタプリタタスク
34void interpreter_task(void);
35
36#endif // INTERPRETER_H
include/os_display.h — 18 行

GitHub で開く

 1// os_display.h
 2
 3#ifndef OS_DISPLAY_H
 4#define OS_DISPLAY_H
 5
 6#include "os_kernel.h"
 7#include "Arduino_LED_Matrix.h"
 8
 9// 表示を初期化
10void display_init(void);
11
12// 表示を更新(定期的に呼ぶ)
13void display_update(void);
14
15// 表示タスク
16void display_task(void);
17
18#endif // OS_DISPLAY_H
include/os_fault.h — 46 行

GitHub で開く

 1/**
 2 * os_fault.h - Fault Handler Header
 3 * Chapter 6: Memory Protection
 4 */
 5
 6#ifndef OS_FAULT_H
 7#define OS_FAULT_H
 8
 9#include <stdint.h>
10
11#ifdef __cplusplus
12extern "C" {
13#endif
14
15// Fault information structure
16typedef struct {
17    uint32_t r0;
18    uint32_t r1;
19    uint32_t r2;
20    uint32_t r3;
21    uint32_t r12;
22    uint32_t lr;
23    uint32_t pc;
24    uint32_t xpsr;
25} FaultFrame;
26
27// Initialize fault handling
28void fault_init(void);
29
30// Get fault reason string
31const char* fault_get_reason(void);
32
33// Check if a fault occurred
34int fault_occurred(void);
35
36// Clear fault flag
37void fault_clear(void);
38
39// Print pending fault info (call from task context, not from ISR)
40void fault_print_pending(void);
41
42#ifdef __cplusplus
43}
44#endif
45
46#endif
include/os_kernel.h — 79 行

GitHub で開く

 1/**
 2 * os_kernel.h - Mini OS Kernel Header
 3 * Chapter 6: Memory Protection with HardFault Handler
 4 */
 5
 6#ifndef OS_KERNEL_H
 7#define OS_KERNEL_H
 8
 9#include <stdint.h>
10
11#ifdef __cplusplus
12extern "C" {
13#endif
14
15typedef enum {
16    TASK_READY,
17    TASK_RUNNING,
18    TASK_BLOCKED,
19    TASK_SUSPENDED,
20    TASK_TERMINATED
21} TaskState;
22
23typedef struct {
24    uint32_t *sp;
25    uint32_t *stack_base;
26    uint32_t stack_size;
27    TaskState state;
28    uint8_t id;
29    uint8_t priority;
30    char name[16];
31    void (*entry)(void);
32    uint32_t wake_tick;
33    uint32_t cpu_ticks;
34} TCB;
35
36#define MAX_TASKS 8
37#define DEFAULT_STACK_SIZE 512
38// タスク用スタックをまとめて置く領域。os_create_task() は
39// ここから要求されたぶんだけ切り出す。
40// 既定より大きなスタックを要求するタスク(シェルなど)があるため、
41// MAX_TASKS * DEFAULT_STACK_SIZE では足りない。余裕を持たせる。
42#define STACK_POOL_SIZE (MAX_TASKS * DEFAULT_STACK_SIZE + 2048)
43
44// スタックを埋めておく目印。どこまで書き換わったかで使用量が分かる。
45#define STACK_FILL_PATTERN 0xA5A5A5A5u
46#define IDLE_STACK_SIZE 256
47#define TIME_SLICE_MS 10
48
49void os_init(void);
50int os_create_task(void (*entry)(void), const char *name, uint32_t stack_size);
51void os_start(void);
52void os_yield(void);
53void os_sleep(uint32_t ms);
54
55void os_suspend_task(int id);
56void os_resume_task(int id);
57void os_terminate_task(int id);
58
59int os_get_task_count(void);
60const char* os_get_task_name(int id);
61TaskState os_get_task_state(int id);
62int os_get_current_task(void);
63int os_get_cpu_usage(int task_id);
64uint32_t os_get_task_cpu_ticks(int task_id);
65uint32_t os_get_tick(void);
66uint32_t os_get_stack_unused(int id);   // 一度も使われていないスタックのバイト数
67
68extern TCB g_tasks[MAX_TASKS];
69extern volatile int g_task_count;
70extern volatile int g_current_task;
71extern volatile int g_next_task;
72extern volatile uint32_t g_system_ticks;
73extern const uint32_t g_tcb_size;
74
75#ifdef __cplusplus
76}
77#endif
78
79#endif
include/shell.h — 23 行

GitHub で開く

 1/**
 2 * shell.h - Interactive Shell Header
 3 * Chapter 5: Interactive Shell
 4 */
 5
 6#ifndef SHELL_H
 7#define SHELL_H
 8
 9#ifdef __cplusplus
10extern "C" {
11#endif
12
13// Shell task entry point
14void shell_task(void);
15
16// Process a single command
17void shell_process_command(char *cmd);
18
19#ifdef __cplusplus
20}
21#endif
22
23#endif
src/interpreter.cpp — 288 行

GitHub で開く

  1// interpreter.cpp
  2
  3#include "interpreter.h"
  4#include "os_kernel.h"
  5#include <Arduino.h>
  6#include <string.h>
  7#include <stdlib.h>
  8#include <Servo.h>
  9
 10// スクリプト格納
 11static char g_script[MAX_SCRIPT_LINES][MAX_LINE_LENGTH];
 12static int g_script_lines = 0;
 13static int g_current_line = 0;
 14static bool g_running = false;
 15
 16// ループスタック
 17#define MAX_LOOP_DEPTH 4
 18static int g_loop_stack[MAX_LOOP_DEPTH];
 19static int g_loop_count[MAX_LOOP_DEPTH];
 20static int g_loop_depth = 0;
 21
 22// ハードウェア
 23static Servo g_servos[2];
 24static bool g_servo_attached[2] = {false, false};
 25
 26// サーボピン
 27#define SERVO_PIN_0 9
 28#define SERVO_PIN_1 10
 29
 30// モーター制御(ダミー実装)
 31static void motor_forward(int distance)
 32{
 33    Serial.print("[Motor] Forward ");
 34    Serial.print(distance);
 35    Serial.println(" units");
 36    // 実際のモーター制御はここに実装
 37    os_sleep(distance * 10);  // 仮の待機
 38}
 39
 40static void motor_backward(int distance)
 41{
 42    Serial.print("[Motor] Backward ");
 43    Serial.print(distance);
 44    Serial.println(" units");
 45    os_sleep(distance * 10);
 46}
 47
 48static void motor_turn(int angle)
 49{
 50    Serial.print("[Motor] Turn ");
 51    Serial.print(angle);
 52    Serial.println(" degrees");
 53    os_sleep(abs(angle) * 10);
 54}
 55
 56// サーボ制御
 57static void servo_set(int channel, int angle)
 58{
 59    if (channel < 0 || channel > 1) {
 60        Serial.println("[Error] Invalid servo channel");
 61        return;
 62    }
 63
 64    // 必要に応じてアタッチ
 65    if (!g_servo_attached[channel]) {
 66        int pin = (channel == 0) ? SERVO_PIN_0 : SERVO_PIN_1;
 67        g_servos[channel].attach(pin);
 68        g_servo_attached[channel] = true;
 69    }
 70
 71    // 角度を制限
 72    if (angle < 0) angle = 0;
 73    if (angle > 180) angle = 180;
 74
 75    g_servos[channel].write(angle);
 76
 77    Serial.print("[Servo] Ch");
 78    Serial.print(channel);
 79    Serial.print(" = ");
 80    Serial.print(angle);
 81    Serial.println(" deg");
 82}
 83
 84// LED制御
 85static void led_set(int pin, int state)
 86{
 87    pinMode(pin, OUTPUT);
 88    digitalWrite(pin, state ? HIGH : LOW);
 89
 90    Serial.print("[LED] Pin ");
 91    Serial.print(pin);
 92    Serial.print(" = ");
 93    Serial.println(state ? "ON" : "OFF");
 94}
 95
 96// 空白をスキップ
 97static const char* skip_spaces(const char *cursor)
 98{
 99    while (*cursor == ' ' || *cursor == '\t') cursor++;
100    return cursor;
101}
102
103// 数値を解析
104static int parse_int(const char **cursor_ptr)
105{
106    const char *cursor = skip_spaces(*cursor_ptr);
107    int sign = 1;
108    int value = 0;
109
110    if (*cursor == '-') {
111        sign = -1;
112        cursor++;
113    }
114
115    while (*cursor >= '0' && *cursor <= '9') {
116        value = value * 10 + (*cursor - '0');
117        cursor++;
118    }
119
120    *cursor_ptr = cursor;
121    return sign * value;
122}
123
124// 1行を実行
125static ExecResult execute_line(const char *line)
126{
127    const char *cursor = skip_spaces(line);
128
129    // 空行やコメント
130    if (*cursor == '\0' || *cursor == '#') {
131        return EXEC_OK;
132    }
133
134    // コマンドを取得
135    char cmd[16] = {0};
136    int i = 0;
137    while (*cursor && *cursor != ' ' && *cursor != '\t' && i < 15) {
138        cmd[i++] = *cursor++;
139    }
140
141    // コマンド実行
142    if (strcmp(cmd, "FORWARD") == 0) {
143        int dist = parse_int(&cursor);
144        motor_forward(dist);
145    }
146    else if (strcmp(cmd, "BACKWARD") == 0) {
147        int dist = parse_int(&cursor);
148        motor_backward(dist);
149    }
150    else if (strcmp(cmd, "TURN") == 0) {
151        int angle = parse_int(&cursor);
152        motor_turn(angle);
153    }
154    else if (strcmp(cmd, "SERVO") == 0) {
155        int ch = parse_int(&cursor);
156        int angle = parse_int(&cursor);
157        servo_set(ch, angle);
158    }
159    else if (strcmp(cmd, "LED") == 0) {
160        int pin = parse_int(&cursor);
161        int state = parse_int(&cursor);
162        led_set(pin, state);
163    }
164    else if (strcmp(cmd, "DELAY") == 0) {
165        int ms = parse_int(&cursor);
166        Serial.print("[Delay] ");
167        Serial.print(ms);
168        Serial.println(" ms");
169        os_sleep(ms);
170    }
171    else if (strcmp(cmd, "PRINT") == 0) {
172        cursor = skip_spaces(cursor);
173        Serial.print("[Print] ");
174        Serial.println(cursor);
175    }
176    else if (strcmp(cmd, "LOOP") == 0) {
177        if (g_loop_depth >= MAX_LOOP_DEPTH) {
178            Serial.println("[Error] Loop too deep");
179            return EXEC_ERROR;
180        }
181        int count = parse_int(&cursor);
182        g_loop_stack[g_loop_depth] = g_current_line;
183        g_loop_count[g_loop_depth] = count;
184        g_loop_depth++;
185    }
186    else if (strcmp(cmd, "END") == 0) {
187        if (g_loop_depth <= 0) {
188            Serial.println("[Error] END without LOOP");
189            return EXEC_ERROR;
190        }
191        g_loop_count[g_loop_depth - 1]--;
192        if (g_loop_count[g_loop_depth - 1] > 0) {
193            // ループ継続
194            g_current_line = g_loop_stack[g_loop_depth - 1];
195        } else {
196            // ループ終了
197            g_loop_depth--;
198        }
199    }
200    else {
201        Serial.print("[Error] Unknown command: ");
202        Serial.println(cmd);
203        return EXEC_ERROR;
204    }
205
206    return EXEC_OK;
207}
208
209void interp_init(void)
210{
211    g_script_lines = 0;
212    g_current_line = 0;
213    g_running = false;
214    g_loop_depth = 0;
215}
216
217int interp_load(const char *script)
218{
219    interp_init();
220
221    const char *cursor = script;
222    int line = 0;
223
224    while (*cursor && line < MAX_SCRIPT_LINES) {
225        int i = 0;
226        while (*cursor && *cursor != '\n' && i < MAX_LINE_LENGTH - 1) {
227            g_script[line][i++] = *cursor++;
228        }
229        g_script[line][i] = '\0';
230
231        // 空行でなければカウント
232        if (i > 0) {
233            line++;
234        }
235
236        if (*cursor == '\n') cursor++;
237    }
238
239    g_script_lines = line;
240    g_current_line = 0;
241
242    Serial.print("[Interpreter] Loaded ");
243    Serial.print(g_script_lines);
244    Serial.println(" lines");
245
246    return g_script_lines;
247}
248
249ExecResult interp_step(void)
250{
251    if (g_current_line >= g_script_lines) {
252        g_running = false;
253        return EXEC_END;
254    }
255
256    g_running = true;
257    ExecResult result = execute_line(g_script[g_current_line]);
258    g_current_line++;
259
260    return result;
261}
262
263int interp_run(void)
264{
265    Serial.println("\n[Interpreter] Starting script...");
266
267    int executed = 0;
268    while (g_current_line < g_script_lines) {
269        ExecResult result = interp_step();
270        if (result == EXEC_ERROR) {
271            Serial.println("[Interpreter] Script error, aborting");
272            return -1;
273        }
274        executed++;
275    }
276
277    Serial.print("[Interpreter] Script completed: ");
278    Serial.print(executed);
279    Serial.println(" commands executed");
280
281    g_running = false;
282    return executed;
283}
284
285bool interp_is_running(void)
286{
287    return g_running;
288}
src/os_display.cpp — 162 行

GitHub で開く

  1// os_display.cpp
  2
  3#include "os_display.h"
  4#include <Arduino.h>
  5
  6// LEDマトリクスオブジェクト
  7static ArduinoLEDMatrix matrix;
  8
  9// フレームバッファ(8行×12列)
 10static uint8_t frame[8][12];
 11
 12// CPU負荷履歴(12サンプル)
 13static uint8_t cpu_history[12];
 14static int history_index = 0;
 15
 16// タスク実行履歴(4タスク × 8サンプル)
 17#define TASK_HISTORY_LEN 8
 18static int task_history[MAX_TASKS][TASK_HISTORY_LEN];
 19static int task_history_index = 0;
 20
 21void display_init(void)
 22{
 23    matrix.begin();
 24
 25    // フレームバッファをクリア
 26    memset(frame, 0, sizeof(frame));
 27    memset(cpu_history, 0, sizeof(cpu_history));
 28    memset(task_history, -1, sizeof(task_history));
 29}
 30
 31// CPU負荷をパーセントから高さ(0-4)に変換
 32static int cpu_to_height(int cpu_percent)
 33{
 34    if (cpu_percent >= 80) return 4;
 35    if (cpu_percent >= 60) return 3;
 36    if (cpu_percent >= 40) return 2;
 37    if (cpu_percent >= 20) return 1;
 38    return 0;
 39}
 40
 41// フレームバッファにCPU負荷グラフを描画
 42static void draw_cpu_graph(void)
 43{
 44    // 上半分(行0-3)にCPU負荷を描画
 45    for (int col = 0; col < 12; col++) {
 46        int height = cpu_to_height(cpu_history[col]);
 47
 48        for (int row = 0; row < 4; row++) {
 49            // 下から上に向かって描画(行3が底、行0が頂上)
 50            frame[3 - row][col] = (row < height) ? 1 : 0;
 51        }
 52    }
 53}
 54
 55// フレームバッファにタスク実行状態を描画
 56static void draw_task_status(void)
 57{
 58    int task_count = os_get_task_count();
 59
 60    // 最大4タスク表示(行4-7)
 61    int display_tasks = (task_count > 4) ? 4 : task_count;
 62
 63    for (int t = 0; t < display_tasks; t++) {
 64        int row = 4 + t;
 65
 66        // 履歴を描画
 67        for (int i = 0; i < TASK_HISTORY_LEN; i++) {
 68            int hist_idx = (task_history_index + i + 1) % TASK_HISTORY_LEN;
 69            int col = i;
 70
 71            if (task_history[t][hist_idx] == t) {
 72                // このタスクが実行中だった
 73                frame[row][col] = 1;
 74            } else {
 75                frame[row][col] = 0;
 76            }
 77        }
 78
 79        // 現在の状態を示すインジケータ(右端)
 80        TaskState state = os_get_task_state(t);
 81        switch (state) {
 82            case TASK_RUNNING:
 83                frame[row][10] = 1;
 84                frame[row][11] = 1;
 85                break;
 86            case TASK_READY:
 87                frame[row][10] = 1;
 88                frame[row][11] = 0;
 89                break;
 90            case TASK_BLOCKED:
 91                frame[row][10] = 0;
 92                frame[row][11] = 1;
 93                break;
 94            default:
 95                frame[row][10] = 0;
 96                frame[row][11] = 0;
 97                break;
 98        }
 99    }
100}
101
102// 区切り線を描画
103static void draw_separator(void)
104{
105    // CPU負荷領域とタスク状態領域の間に区切り線
106    // 列8, 9を区切りとして使用
107    for (int row = 0; row < 8; row++) {
108        frame[row][8] = 0;
109        frame[row][9] = (row == 3) ? 1 : 0;  // 行3だけ点灯
110    }
111}
112
113void display_update(void)
114{
115    // CPU 使用率を更新(アイドル以外の割合)。
116    // os_get_cpu_usage() は呼ぶたびに基準時刻を更新するので、ここから呼ぶと
117    // シェルの ps の計測窓を壊してしまう。生カウンタから自前で差分を取る。
118    static uint32_t last_total = 0;
119    static uint32_t last_idle  = 0;
120
121    uint32_t total = os_get_tick();
122    uint32_t idle  = os_get_task_cpu_ticks(0);
123    uint32_t d_total = total - last_total;
124    uint32_t d_idle  = idle  - last_idle;
125    last_total = total;
126    last_idle  = idle;
127
128    int total_cpu = (d_total == 0) ? 0
129                  : (int)(100u - (d_idle * 100u) / d_total);
130
131    // 履歴に追加
132    cpu_history[history_index] = total_cpu;
133    history_index = (history_index + 1) % 12;
134
135    // 現在実行中のタスクを記録
136    int current = os_get_current_task();
137    if (current >= 0 && current < MAX_TASKS) {
138        task_history[current][task_history_index] = current;
139    }
140    task_history_index = (task_history_index + 1) % TASK_HISTORY_LEN;
141
142    // フレームバッファをクリア
143    memset(frame, 0, sizeof(frame));
144
145    // 各要素を描画
146    draw_cpu_graph();
147    draw_task_status();
148
149    // マトリクスに表示
150    matrix.renderBitmap(frame, 8, 12);
151}
152
153// 表示更新タスク
154void display_task(void)
155{
156    display_init();
157
158    while (1) {
159        display_update();
160        os_sleep(100);  // 100msごとに更新
161    }
162}
src/os_fault.cpp — 170 行

GitHub で開く

  1/**
  2 * os_fault.cpp - Fault Handler Implementation
  3 * Chapter 6: Memory Protection
  4 */
  5
  6#include "os_fault.h"
  7#include "os_kernel.h"
  8#include <Arduino.h>
  9
 10static volatile int g_fault_occurred = 0;
 11static volatile const char* g_fault_reason = "";
 12static volatile int g_faulted_task = -1;
 13
 14// Forward declarations for vector table override
 15extern "C" __attribute__((naked)) void HardFault_Handler(void);
 16extern "C" void PendSV_Handler(void);
 17extern "C" void SysTick_Handler(void);
 18
 19void fault_init(void)
 20{
 21    // Enable divide by zero trap
 22    SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk;
 23
 24    // Do NOT enable separate UsageFault/BusFault/MemFault handlers.
 25    // By leaving them disabled, all faults escalate to HardFault,
 26    // which we handle via the VTOR override above.
 27
 28    // The Arduino framework copies the vector table to RAM (VTOR points to RAM).
 29    // We must overwrite the HardFault entry in the RAM vector table directly.
 30    // Also override PendSV and SysTick entries for the OS scheduler.
 31    uint32_t *vtor = (uint32_t *)SCB->VTOR;
 32    vtor[3] = (uint32_t)HardFault_Handler;   // Vector #3 = HardFault
 33    vtor[14] = (uint32_t)PendSV_Handler;     // Vector #14 = PendSV
 34    vtor[15] = (uint32_t)SysTick_Handler;    // Vector #15 = SysTick
 35
 36    g_fault_occurred = 0;
 37    g_fault_reason = "";
 38}
 39
 40const char* fault_get_reason(void)
 41{
 42    return (const char*)g_fault_reason;
 43}
 44
 45int fault_occurred(void)
 46{
 47    return g_fault_occurred;
 48}
 49
 50void fault_clear(void)
 51{
 52    g_fault_occurred = 0;
 53    g_fault_reason = "";
 54}
 55
 56static void analyze_fault(void)
 57{
 58    uint32_t cfsr = SCB->CFSR;
 59
 60    // Check Usage Fault Status Register (UFSR)
 61    if (cfsr & SCB_CFSR_DIVBYZERO_Msk) {
 62        g_fault_reason = "Divide by zero";
 63    } else if (cfsr & SCB_CFSR_UNALIGNED_Msk) {
 64        g_fault_reason = "Unaligned access";
 65    } else if (cfsr & SCB_CFSR_UNDEFINSTR_Msk) {
 66        g_fault_reason = "Undefined instruction";
 67    } else if (cfsr & SCB_CFSR_INVSTATE_Msk) {
 68        g_fault_reason = "Invalid state (Thumb bit)";
 69    } else if (cfsr & SCB_CFSR_INVPC_Msk) {
 70        g_fault_reason = "Invalid PC";
 71    }
 72    // Check Bus Fault Status Register (BFSR)
 73    else if (cfsr & SCB_CFSR_PRECISERR_Msk) {
 74        g_fault_reason = "Precise data bus error";
 75    } else if (cfsr & SCB_CFSR_IMPRECISERR_Msk) {
 76        g_fault_reason = "Imprecise data bus error";
 77    } else if (cfsr & SCB_CFSR_IBUSERR_Msk) {
 78        g_fault_reason = "Instruction bus error";
 79    }
 80    // Check Memory Management Fault Status Register (MMFSR)
 81    else if (cfsr & SCB_CFSR_DACCVIOL_Msk) {
 82        g_fault_reason = "Data access violation";
 83    } else if (cfsr & SCB_CFSR_IACCVIOL_Msk) {
 84        g_fault_reason = "Instruction access violation";
 85    }
 86    else {
 87        g_fault_reason = "Unknown fault";
 88    }
 89
 90    // Clear fault flags
 91    SCB->CFSR = cfsr;
 92}
 93
 94// Safe landing pad for terminated tasks after fault recovery
 95extern "C" void _fault_task_sink(void)
 96{
 97    while (1) {
 98        __WFI();
 99    }
100}
101
102// Flag to defer fault message printing to a safe context
103static volatile int g_fault_pending_print = 0;
104static volatile uint32_t g_fault_pc = 0;
105
106extern "C" void HardFault_Handler_C(FaultFrame *frame)
107{
108    g_fault_occurred = 1;
109    g_faulted_task = g_current_task;
110    g_fault_pc = frame->pc;
111
112    analyze_fault();
113
114    // Terminate the faulted task and redirect PC to safe sink
115    if (g_faulted_task > 0) {
116        os_terminate_task(g_faulted_task);
117
118        // Rewrite the stacked PC to a safe infinite loop so that
119        // returning from the fault doesn't re-execute the faulting
120        // instruction. The SysTick scheduler will switch away from
121        // this terminated task on the next tick.
122        frame->pc = (uint32_t)_fault_task_sink;
123        frame->xpsr = 0x01000000;  // Thumb bit set
124
125        // Defer printing to next safe context
126        g_fault_pending_print = 1;
127        return;
128    }
129
130    // If idle task or no task crashed, halt
131    while (1) { }
132}
133
134// Call this from a task (e.g., shell) to print deferred fault info
135void fault_print_pending(void)
136{
137    if (!g_fault_pending_print) return;
138    g_fault_pending_print = 0;
139
140    Serial.println();
141    Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
142    Serial.println("!!!       FAULT DETECTED      !!!");
143    Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
144    Serial.println();
145    Serial.print("Task ");
146    Serial.print(g_faulted_task);
147    Serial.print(" (");
148    Serial.print(os_get_task_name(g_faulted_task));
149    Serial.println(") crashed!");
150    Serial.print("Reason: ");
151    Serial.println((const char*)g_fault_reason);
152    Serial.print("PC at fault: 0x");
153    Serial.println(g_fault_pc, HEX);
154    Serial.println("Task terminated. Other tasks continue.");
155    Serial.println();
156}
157
158extern "C" __attribute__((naked)) void HardFault_Handler(void)
159{
160    __asm volatile (
161        "   TST     LR, #4              \n"  // Check which stack was used
162        "   ITE     EQ                  \n"
163        "   MRSEQ   R0, MSP             \n"  // MSP if LR bit 2 is 0
164        "   MRSNE   R0, PSP             \n"  // PSP if LR bit 2 is 1
165        "   PUSH    {LR}                \n"  // Save EXC_RETURN
166        "   BL      HardFault_Handler_C \n"  // Call C handler (preserves LR on stack)
167        "   POP     {LR}                \n"  // Restore EXC_RETURN
168        "   BX      LR                  \n"  // Return from exception properly
169    );
170}
src/os_kernel.cpp — 266 行

GitHub で開く

  1/**
  2 * os_kernel.cpp - Mini OS Kernel Implementation
  3 * Chapter 6: Memory Protection
  4 */
  5
  6#include "os_kernel.h"
  7#include <Arduino.h>
  8#include <string.h>
  9
 10TCB g_tasks[MAX_TASKS];
 11volatile int g_task_count = 0;
 12volatile int g_current_task = -1;
 13volatile int g_next_task = -1;
 14volatile uint32_t g_system_ticks = 0;
 15
 16const uint32_t g_tcb_size = sizeof(TCB);
 17
 18static uint32_t g_stack_pool[STACK_POOL_SIZE / 4];
 19static uint32_t g_stack_used;   // 切り出し済みのワード数
 20
 21static void idle_task(void) { while (1) __WFI(); }
 22
 23// タスク関数が return したときの受け皿。例外フレームの LR スロットは
 24// ここを指す (第3章 3.4)。EXC_RETURN を置く場所ではない。
 25static void task_exit(void)
 26{
 27    g_tasks[g_current_task].state = TASK_TERMINATED;
 28    os_yield();
 29    while (1) { }  // ここには到達しない
 30}
 31
 32static uint32_t* init_task_stack(uint32_t *stack_top, void (*entry)(void))
 33{
 34    uint32_t *sp = stack_top;
 35    *(--sp) = 0x01000000;
 36    *(--sp) = (uint32_t)entry;
 37    *(--sp) = (uint32_t)task_exit;  // LR: タスクが return したときの戻り先
 38    *(--sp) = 0; *(--sp) = 0; *(--sp) = 0; *(--sp) = 0; *(--sp) = 0;
 39    // FPU を使ったタスクは例外フレームが拡張される(FP レジスタ込み)。
 40    // どちらのフレームで中断したかは EXC_RETURN の値に現れるため、
 41    // タスクごとに EXC_RETURN も保存する。初期値は「PSP・基本フレーム」。
 42    *(--sp) = 0xFFFFFFFD;
 43    *(--sp) = 0; *(--sp) = 0; *(--sp) = 0; *(--sp) = 0;
 44    *(--sp) = 0; *(--sp) = 0; *(--sp) = 0; *(--sp) = 0;
 45    return sp;
 46}
 47
 48void os_init(void)
 49{
 50    memset(g_tasks, 0, sizeof(g_tasks));
 51    g_task_count = 0;
 52    g_current_task = -1;
 53    g_next_task = -1;
 54    g_system_ticks = 0;
 55    os_create_task(idle_task, "idle", IDLE_STACK_SIZE);
 56}
 57
 58int os_create_task(void (*entry)(void), const char *name, uint32_t stack_size)
 59{
 60    if (g_task_count >= MAX_TASKS) return -1;
 61    int id = g_task_count;
 62    TCB *task = &g_tasks[id];
 63    task->id = id;
 64    task->state = TASK_READY;
 65    task->priority = id;
 66    task->entry = entry;
 67    task->wake_tick = 0;
 68    task->cpu_ticks = 0;
 69    strncpy(task->name, name, sizeof(task->name) - 1);
 70    if (stack_size == 0) {
 71        stack_size = DEFAULT_STACK_SIZE;
 72    }
 73    uint32_t words = (stack_size + 3) / 4;
 74    if (g_stack_used + words > STACK_POOL_SIZE / 4) {
 75        return -1;          // プールが尽きた。黙って縮めず、失敗として返す
 76    }
 77    g_stack_used += words;
 78    task->stack_size = stack_size;
 79    task->stack_base = &g_stack_pool[g_stack_used];   // スタックは上端から下へ伸びる
 80
 81    // 目印で埋めておく。あとで書き換わっていない範囲を数えれば、
 82    // そのタスクがスタックを何バイト使ったかが分かる。
 83    for (uint32_t i = 0; i < words; i++) {
 84        task->stack_base[-(int32_t)(i + 1)] = STACK_FILL_PATTERN;
 85    }
 86
 87    task->sp = init_task_stack(task->stack_base, entry);
 88    g_task_count++;
 89    return id;
 90}
 91
 92void os_schedule(void)
 93{
 94    for (int i = 0; i < g_task_count; i++) {
 95        if (g_tasks[i].state == TASK_BLOCKED && g_system_ticks >= g_tasks[i].wake_tick) {
 96            g_tasks[i].state = TASK_READY;
 97        }
 98    }
 99
100    int next = g_current_task;
101    do {
102        next = (next + 1) % g_task_count;
103        if (g_tasks[next].state == TASK_READY || g_tasks[next].state == TASK_RUNNING) break;
104    } while (next != g_current_task);
105
106    if (next != g_current_task || g_current_task < 0) {
107        if (g_current_task >= 0 && g_tasks[g_current_task].state == TASK_RUNNING) {
108            g_tasks[g_current_task].state = TASK_READY;
109        }
110        g_tasks[next].state = TASK_RUNNING;
111        g_next_task = next;
112        SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
113    }
114}
115
116void os_yield(void) { os_schedule(); }
117void os_sleep(uint32_t ms) {
118    if (g_current_task > 0) {
119        g_tasks[g_current_task].state = TASK_BLOCKED;
120        g_tasks[g_current_task].wake_tick = g_system_ticks + ms;
121        os_schedule();
122    }
123}
124void os_suspend_task(int id) {
125    if (id > 0 && id < g_task_count) {
126        g_tasks[id].state = TASK_SUSPENDED;
127        if (id == g_current_task) os_schedule();
128    }
129}
130void os_resume_task(int id) {
131    if (id > 0 && id < g_task_count && g_tasks[id].state == TASK_SUSPENDED) {
132        g_tasks[id].state = TASK_READY;
133    }
134}
135void os_terminate_task(int id) {
136    if (id > 0 && id < g_task_count) {
137        g_tasks[id].state = TASK_TERMINATED;
138        if (id == g_current_task) os_schedule();
139    }
140}
141
142uint32_t os_get_tick(void) { return g_system_ticks; }
143int os_get_task_count(void) { return g_task_count; }
144const char* os_get_task_name(int id) { return (id >= 0 && id < g_task_count) ? g_tasks[id].name : ""; }
145TaskState os_get_task_state(int id) { return (id >= 0 && id < g_task_count) ? g_tasks[id].state : TASK_TERMINATED; }
146int os_get_current_task(void) { return g_current_task; }
147
148void os_start(void)
149{
150    NVIC_SetPriority(PendSV_IRQn, 0xFF);
151    SysTick_Config(SystemCoreClock / 1000 * TIME_SLICE_MS);
152    g_current_task = -1;
153    g_next_task = 0;
154    // Do NOT switch CONTROL to PSP here — PSP is not yet initialized.
155    // PendSV returns with EXC_RETURN=0xFFFFFFFD, which automatically
156    // switches thread mode to PSP after loading the first task's context.
157    SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
158    while (1) { }
159}
160
161extern "C" void SysTick_Handler(void) {
162    // 実際に経過した時間を、いま走っていたタスクの取り分として計上する。
163    // os_schedule() 側で数えると os_yield() の回数まで混ざってしまう。
164    if (g_current_task >= 0) {
165        g_tasks[g_current_task].cpu_ticks += TIME_SLICE_MS;
166    }
167    g_system_ticks += TIME_SLICE_MS;
168    os_schedule();
169}
170
171extern "C" __attribute__((naked)) void PendSV_Handler(void)
172{
173    __asm volatile (
174        "   MRS     R0, PSP              \n"
175        "   LDR     R1, =g_current_task  \n"
176        "   LDR     R2, [R1]             \n"
177        "   CMP     R2, #0               \n"
178        "   BLT     _skip_save           \n"
179        // FPU を使って中断したタスク(EXC_RETURN の bit4 が 0)は、
180        // ハードウェアが積まない S16-S31 もここで退避する
181        "   TST     LR, #0x10            \n"
182        "   IT      EQ                   \n"
183        "   VSTMDBEQ R0!, {S16-S31}      \n"
184        // R4-R11 と EXC_RETURN(LR) を保存する
185        "   STMDB   R0!, {R4-R11, LR}    \n"
186        "   LDR     R3, =g_tasks         \n"
187        "   LDR     R4, =g_tcb_size      \n"
188        "   LDR     R4, [R4]             \n"
189        "   MUL     R2, R2, R4           \n"
190        "   ADD     R3, R3, R2           \n"
191        "   STR     R0, [R3]             \n"
192        "_skip_save:                     \n"
193        "   LDR     R1, =g_next_task     \n"
194        "   LDR     R2, [R1]             \n"
195        "   LDR     R1, =g_current_task  \n"
196        "   STR     R2, [R1]             \n"
197        "   LDR     R3, =g_tasks         \n"
198        "   LDR     R4, =g_tcb_size      \n"
199        "   LDR     R4, [R4]             \n"
200        "   MUL     R2, R2, R4           \n"
201        "   ADD     R3, R3, R2           \n"
202        "   LDR     R0, [R3]             \n"
203        "   LDMIA   R0!, {R4-R11, LR}    \n"
204        // FPU を使って中断していたタスクなら S16-S31 も復元する
205        "   TST     LR, #0x10            \n"
206        "   IT      EQ                   \n"
207        "   VLDMIAEQ R0!, {S16-S31}      \n"
208        "   MSR     PSP, R0              \n"
209        "   BX      LR                   \n"
210    );
211}
212
213// CPU使用率を取得(パーセント)
214// 直前にこの関数を呼んだ時点からの差分で計算する。
215// ps のようにタスクごとにループで呼ぶため、基準時刻もタスクごとに持つ。
216// CPU 使用カウンタの生値。os_get_cpu_usage() と違って状態を持たないので、
217// 何度呼んでも他の呼び手の計測窓を壊さない (第4章 4.7)。
218uint32_t os_get_task_cpu_ticks(int task_id)
219{
220    if (task_id < 0 || task_id >= g_task_count) {
221        return 0;
222    }
223    return g_tasks[task_id].cpu_ticks;
224}
225
226int os_get_cpu_usage(int task_id)
227{
228    if (task_id < 0 || task_id >= g_task_count) {
229        return -1;
230    }
231
232    static uint32_t last_total[MAX_TASKS] = {0};
233    static uint32_t last_task_ticks[MAX_TASKS] = {0};
234
235    uint32_t total = g_system_ticks;
236    uint32_t delta_total = total - last_total[task_id];
237
238    if (delta_total == 0) {
239        return 0;
240    }
241
242    uint32_t delta_task = g_tasks[task_id].cpu_ticks - last_task_ticks[task_id];
243
244    last_total[task_id] = total;
245    last_task_ticks[task_id] = g_tasks[task_id].cpu_ticks;
246
247    return (delta_task * 100) / delta_total;
248}
249
250// 一度も書き換えられていないスタック領域のバイト数を返す。
251// 0 に近いほど危険で、0 なら溢れている可能性が高い。
252uint32_t os_get_stack_unused(int id)
253{
254    if (id < 0 || id >= g_task_count) {
255        return 0;
256    }
257    TCB *task = &g_tasks[id];
258    uint32_t words = task->stack_size / 4;
259    uint32_t *low = task->stack_base - words;
260
261    uint32_t i = 0;
262    while (i < words && low[i] == STACK_FILL_PATTERN) {
263        i++;
264    }
265    return i * 4;
266}
src/shell.cpp — 237 行

GitHub で開く

  1/**
  2 * shell.cpp - Interactive Shell Implementation
  3 * Chapter 5: Interactive Shell
  4 */
  5
  6#include "shell.h"
  7#include "os_kernel.h"
  8#include "os_fault.h"
  9#include "interpreter.h"
 10#include <Arduino.h>
 11#include <string.h>
 12
 13#define CMD_BUF_SIZE 64
 14
 15static char cmd_buf[CMD_BUF_SIZE];
 16static int cmd_pos = 0;
 17
 18static void cmd_run(const char *arg);
 19
 20static const char* state_to_str(TaskState state)
 21{
 22    switch (state) {
 23        case TASK_READY:      return "READY";
 24        case TASK_RUNNING:    return "RUNNING";
 25        case TASK_BLOCKED:    return "BLOCKED";
 26        case TASK_SUSPENDED:  return "SUSPEND";
 27        case TASK_TERMINATED: return "TERM";
 28        default:              return "?";
 29    }
 30}
 31
 32static void cmd_help(void)
 33{
 34    Serial.println("Available commands:");
 35    Serial.println("  ps      - List all tasks");
 36    Serial.println("  kill N  - Suspend task N");
 37    Serial.println("  exec N  - Resume task N");
 38    Serial.println("  info    - System information");
 39    Serial.println("  reboot  - Reboot system");
 40    Serial.println("  help    - Show this help");
 41}
 42
 43static void cmd_ps(void)
 44{
 45    Serial.println();
 46    Serial.println("ID  NAME           STATE      CPU%");
 47    Serial.println("--  ----           -----      ----");
 48
 49    for (int i = 0; i < os_get_task_count(); i++) {
 50        Serial.print(i);
 51        Serial.print("   ");
 52
 53        const char *name = os_get_task_name(i);
 54        Serial.print(name);
 55        for (int j = strlen(name); j < 15; j++) Serial.print(" ");
 56
 57        Serial.print(state_to_str(os_get_task_state(i)));
 58        for (int j = strlen(state_to_str(os_get_task_state(i))); j < 10; j++) Serial.print(" ");
 59
 60        Serial.print(os_get_cpu_usage(i));
 61        Serial.println("%");
 62    }
 63    Serial.println();
 64}
 65
 66static void cmd_kill(int id)
 67{
 68    if (id <= 0) {
 69        Serial.println("Error: Cannot kill idle task (ID 0)");
 70        return;
 71    }
 72    if (id >= os_get_task_count()) {
 73        Serial.println("Error: Invalid task ID");
 74        return;
 75    }
 76
 77    os_suspend_task(id);
 78    Serial.print("Task ");
 79    Serial.print(id);
 80    Serial.print(" (");
 81    Serial.print(os_get_task_name(id));
 82    Serial.println(") suspended.");
 83}
 84
 85static void cmd_exec(int id)
 86{
 87    if (id <= 0 || id >= os_get_task_count()) {
 88        Serial.println("Error: Invalid task ID");
 89        return;
 90    }
 91
 92    os_resume_task(id);
 93    Serial.print("Task ");
 94    Serial.print(id);
 95    Serial.print(" (");
 96    Serial.print(os_get_task_name(id));
 97    Serial.println(") resumed.");
 98}
 99
100static void cmd_info(void)
101{
102    Serial.println();
103    Serial.println("=== System Information ===");
104    Serial.print("System Ticks:  ");
105    Serial.println(os_get_tick());
106    Serial.print("Uptime:        ");
107    Serial.print(os_get_tick() / 1000);
108    Serial.println(" s");
109    Serial.print("Task Count:    ");
110    Serial.println(os_get_task_count());
111    Serial.print("Time Slice:    ");
112    Serial.print(TIME_SLICE_MS);
113    Serial.println(" ms");
114    Serial.println();
115}
116
117static void cmd_reboot(void)
118{
119    Serial.println("Rebooting...");
120    delay(100);
121    NVIC_SystemReset();
122}
123
124void shell_process_command(char *cmd)
125{
126    // Trim whitespace
127    while (*cmd == ' ') cmd++;
128    char *end = cmd + strlen(cmd) - 1;
129    while (end > cmd && *end == ' ') *end-- = '\0';
130
131    if (strlen(cmd) == 0) return;
132
133    // Parse command
134    if (strcmp(cmd, "help") == 0 || strcmp(cmd, "?") == 0) {
135        cmd_help();
136    } else if (strcmp(cmd, "ps") == 0) {
137        cmd_ps();
138    } else if (strncmp(cmd, "kill ", 5) == 0) {
139        int id = atoi(cmd + 5);
140        cmd_kill(id);
141    } else if (strncmp(cmd, "exec ", 5) == 0) {
142        int id = atoi(cmd + 5);
143        cmd_exec(id);
144    } else if (strncmp(cmd, "run", 3) == 0) {
145        const char *arg = cmd + 3;
146        while (*arg == ' ') arg++;
147        cmd_run(arg);
148    } else if (strcmp(cmd, "info") == 0) {
149        cmd_info();
150    } else if (strcmp(cmd, "reboot") == 0) {
151        cmd_reboot();
152    } else {
153        Serial.print("Unknown command: ");
154        Serial.println(cmd);
155        Serial.println("Type 'help' for available commands.");
156    }
157}
158
159// シリアルから 1 行読み取る(本書 8.4「シェルとの統合」)
160static void read_line(char *buf, int size)
161{
162    int pos = 0;
163    memset(buf, 0, size);
164
165    while (1) {
166        if (Serial.available()) {
167            char ch = Serial.read();
168
169            if (ch == '\r' || ch == '\n') {
170                Serial.println();
171                return;
172            } else if (ch == '\b' || ch == 127) {
173                if (pos > 0) {
174                    pos--;
175                    buf[pos] = '\0';
176                    Serial.print("\b \b");
177                }
178            } else if (pos < size - 1) {
179                buf[pos++] = ch;
180                Serial.print(ch);
181            }
182        }
183        os_sleep(10);
184    }
185}
186
187// run コマンド: 引数があれば 1 行スクリプト、無ければ複数行の入力モード
188static void cmd_run(const char *arg)
189{
190    static char script[MAX_SCRIPT_LINES * MAX_LINE_LENGTH];
191    static char line[MAX_LINE_LENGTH];
192
193    if (arg && *arg) {
194        interp_load(arg);
195        interp_run();
196        return;
197    }
198
199    Serial.println("Enter script (empty line to execute):");
200    script[0] = '\0';
201
202    while (1) {
203        Serial.print(". ");
204        read_line(line, MAX_LINE_LENGTH);
205        if (line[0] == '\0') break;
206
207        if (strlen(script) + strlen(line) + 2 >= sizeof(script)) {
208            Serial.println("Error: script too long");
209            return;
210        }
211        strcat(script, line);
212        strcat(script, "\n");
213    }
214
215    if (script[0] != '\0') {
216        interp_load(script);
217        interp_run();
218    }
219}
220
221void shell_task(void)
222{
223    Serial.println();
224    Serial.println("Mini OS Shell");
225    Serial.println("Type 'help' for commands.");
226
227    interp_init();
228
229    while (1) {
230        // 保留中のフォルト情報をタスク文脈で表示する (第6章 6.3)
231        fault_print_pending();
232
233        Serial.print("> ");
234        read_line(cmd_buf, CMD_BUF_SIZE);
235        shell_process_command(cmd_buf);
236    }
237}

実機での出力#

Arduino UNO R4 WiFi の実機で実際に取得した出力です(macOS / Windows の 両方で同じ結果を確認しています)。手元の出力と見比べてください。

ps

ID  NAME           STATE      CPU%
--  ----           -----      ----
0   idle           READY     100%
1   Heartbeat      BLOCKED   0%
2   Sensor         BLOCKED   0%
3   Servo          BLOCKED   0%
4   LED_Ind        BLOCKED   0%
5   Button         BLOCKED   0%
6   Shell          RUNNING   0%
7   Display        BLOCKED   0%

> 

つまずきやすいポイント#

(読者からの質問に応じて随時追記します。質問は GitHub Issues へどうぞ)