ArduinoにOSだと!?
イベント・ループで動作するArduinoでマルチタスク処理を実現できる素晴らしき存在。それがリアルタイムOSことRTOS!!(ざっくりな理解ですみません)
Linux等の汎用OSと違い、ローエンドなマイコン上でも動く機能限定版の超軽量OSです。
私の目指す組み込みの世界で主に利用されているそうで、今回は学習の初めの一歩を記事にしてみようと思います。
Arduino用のライブラリがあるようなので早速#Include
して使ってみようではありませんか!
実験のネタを考える
例えばこんなコードがあったとします。
for (;;)
{
digitalWrite(LED, HIGH);
delay(1000);
digitalWrite(LED, LOW);
delay(1000);
}
はい、普通のLEDチカチカコードです。
ただし、このコードだけでは無限ループの中でずっと1秒間隔で電気流したり止めたりしてるだけですから、回路設計を間違えて居たりLEDそのものの不具合があった場合に、意図した輝度より暗い又は明るすぎる状態でLチカしていることを、このプログラムで動いているArduinoには判別できません。
人間の目で確認するか、輝度センサでも使って基準値を外れていないか確認する必要があります。
そこで今回はFreeRTOS
を使ってマルチタスクを実装し、Arduino自身に「LEDがちゃんと光ってるか確認させる」という機能を通じて、リアルタイムOSの動きを見てみたいと思います。
では行こう。
実装するタスクは2つ
- LEDを1秒間隔で点灯させる無限ループ
- 輝度センサをLEDの状態(輝度)を監視し、異常な輝度になったらアラートを吐く
これらを平行して動作させます。
ハード側の仕様
- LED側に可変抵抗を挟んで任意に輝度を調節できるようにする。(疑似的にLEDの調子を狂わせる)
作ってみた
LEDに輝度センサをピッタリ当てて最大輝度を監視させます。
可変抵抗をドライバでひねってやれば、LEDの輝度を意図的に上げ下げできます。
材料
- Arduino UNO
- Cdsセル(輝度センサ)
- 可変抵抗
- LED
- カーボン抵抗(10kΩ)
コード書いた
FreeRTOS公式のサンプルコードを参考に、RTOSな処理を実装していきます。
#include <Arduino_FreeRTOS.h>
#define LED 2
void TaskBlink(void *pvParameters);
void TaskChecker(void *pvParameters);
しょっぱなでタスクを宣言します。
今回はLチカタスクと輝度チェックタスクです。
void setup()
{
Serial.begin(9600);
xTaskCreate(TaskBlink, "Blink", 128, NULL, 2, NULL);
xTaskCreate(TaskChecker, "Checker", 128, NULL, 1, NULL);
}
void loop()
{
}
Arduinoおなじみのセットアップとループ。
xTaskCreate(TaskBlink, "Blink", 128, NULL, 2, NULL);
この記述の2
とある所、こちらでタスクの優先度を設定します。
0~2の3段階で設定。説明によるとこの記述により、設定した優先度に合わせたタスクスケジューラが開始するとのこと。
Lチカの安定駆動が最優先!監視機能は優先度を一段階下げます。
いつものloop
関数は今回は空白です。全て作成したタスクが処理するからだそうです。なんだかいつもと違うぞ!
unsigned char led_state;
void TaskBlink(void *pvParameters)
{
(void)pvParameters;
pinMode(LED, OUTPUT);
for (;;)
{
digitalWrite(LED, HIGH);
led_state = HIGH;
vTaskDelay(1000 / portTICK_PERIOD_MS);
digitalWrite(LED, LOW);
led_state = LOW;
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
こちらはLチカタスク。
led_state
は後で出てくる監視タスク用の変数です。
vTaskDelay(1000 / portTICK_PERIOD_MS);
こいつがdelay()
にあたりますが、記述から分かる通りタスクそのものを待機させるようです。
void TaskChecker(void *pvParameters)
{
(void)pvParameters;
int value = analogRead(A0);
int i = 0;
for (;;)
{
value = analogRead(A0);
Serial.println(value);
delay(100);
i++;
if (i == 30)
{
if ((value < 700) && (led_state == HIGH))
{
Serial.println("くらい");
i = 0;
}
else if ((value > 900) && (led_state == HIGH))
{
Serial.println("明るすぎ!");
i = 0;
}
else
{
i = 0;
}
}
vTaskDelay(1);
}
}
最後にこちらがLED輝度監視タスクのコードです。
いい塩梅の輝度を700~899(目視での目分量です)として、それ以外の輝度かつLEDが点灯中である旨を検知したらシリアルモニタに警告メッセージを出力します。
今回は実験なので可変抵抗でLEDの輝度を自分で変えて、LEDの異常が検知出来れば成功です。
こちらのラストを飾るvTaskDelay(1);
は読み取りの安定を図る為に記述しています。
この記述を端折ると吐き出されるデータが飛び飛びになったりして読み取りが不安定になってしまいました。
## 準備はOK!動かしてみよう!
またもガビガビgifで見づらいのですが・・・
LEDの輝度を指定値外にするとちゃんと判別してシリアルモニタに出力してくれる様子がうかがえます。
タイムスタンプを付けてシリアル出力を見てもちゃんと2つのタスクが同時に動いていますね!すごい。
タスク1に記述したLED点滅と、タスク2に記述した輝度センサによる監視が平行して同時に動作しました。今回のゴール達成です!
・・・同時に動作しているのは分かりましたが、肝心のLEDを1秒ごとに点灯するタスクがどうも正確に1秒を刻んでいないようです。Serial.print()
の処理の関係かArduino的にそういうものなのか、私では確実な原因はわかりませんでしたが5~15msecほど遅れて行っている様子でした。
今回は「プログラムから見えないハードの異常を監視させる」ことが目標なので厳密には詰めませんでしたが、Lチカタスク自体が正確に動いていることを保証するために、ここは次回に向けてチューニングしていかなければいけないポイントですね。
次の課題へ
今回はArduino向けのFreeRTOSを利用してマルチタスクSUGEEE出来ました。しかし、これでFreeRTOSの概要が掴めたとはとてもじゃありませんが言えません。
RTOSを使うメリットとして、タスク間でのデータのやり取りができるという事を知りました。この機能を勉強するため、次回の企画は今回のシステムを発展させて、「監視タスクから輝度異常アラートを受け取り、Lチカタスクが自分で輝度を修正する」システムに改良してみようと思います。
セマフォとキューですね!(覚えたて)
タスク同士で助け合えるなんて素敵!習得に向けて勉強勉強。
終わりに
実際に動かしてみると意図した動作と違っていたり、問題と出くわす場面が多々ありますね。
RTOSに関しては純粋な好奇心から、Arduinoのライブラリを駆使してカジュアルに入門してみましたが、やはりOSの話なので、使いこなすには難易度が高いと痛感。
行く行くは質の高い発信が出来るよう精進して参りますので、この記事にたどり着かれた諸先輩方には是非アドバイスを頂ければと存じます。