これまでにやってみた 7 セグメント LED のダイナミック点灯でも、トライアックによる電力制御でも、Arduino で何かをやろうと思うと、いろいろな処理を同時に進行させないといけないわけです。
スケッチを書きながら、ごく自然にそんなことをやっていたように思うわけだけど、これがいわゆる「マルチタスク」ってやつなんだと気が付いた今日この頃。
マルチタスクとクラス
Arduino では、いろいろな処理をマルチタスクで休みなく連続して実行し、プログラムは終了させないことが必要。
たとえばよく言われる「delay() を使わない」ってのは、delay() 中も Arduino は delay() という仕事をやっていて、その仕事中は他の処理はできない、だから、そこで止まってしまう。
ふぅむ、まぁわかっているけど、面倒くさい話なんだよねぇ (;´Д`)
でも、それをやんないと Arduino はまともに仕事をしてくれないわけだし、このあたりの考え方はちょっとしっかり理解しておきたいよねぇ、ってのが、今回の話です。
で、話は変わるんだけど、「クラス」ってのを使ったプログラムを勉強したいなぁと、思ってた。
「クラス」ってのは C++ なの? C にはないのか。Arduino でクラスを使ったスケッチを見たことがあるので、使えるんだよね。そのスケッチってのが、ひとつの Arduino でいくつもの電球をそれぞれパワーコントロールするってものだった。お、これ、マルチタスクじゃん。
教科書やサイトをつらつら眺めてみたけど、クラスに出てくるのはリンゴとか車とか、なんのこっちゃ? チンプンカンプンわからん (;´Д`)
そんなおりに出会ったサイトが、ここ。
Arduino でマルチタスクを行なうために、クラスを使ったスケッチを紹介してくれています。
ニーズにドンピシャじゃぁないですか。こちらを参考に、勉強してみましょう。
delay() を捨てて、millis() を使う
The first thing you need to do is stop using delay().
https://learn.adafruit.com/multi-tasking-the-arduino-part-1/ditch-the-delay
まずは解説されているのは delay() を捨てること。
スケッチ例にある L チカの基本「Blink」と、サーボモータを動かす「Sweep」の 2 つのスケッチを組み合わせると、Blink と Sweep は交互に実行されるだけで、LED とサーボモータを同時に動作させることはできないのです。それは、delay() を使っているから。
じゃぁどうするか。そのヒントになるのが、スケッチ例の「Blink without Delay」なわけ。
スケッチ例にある Blink without Delay をコピーアンドペーストするだけじゃ身につかないので、自分なりに書き直しています。
内蔵 LED を点滅させるので、外部回路は無しです。
- // Blink Without delay()
- const bool LED_ON = HIGH;
- const bool LED_OFF = LOW;
- const byte ledPin = LED_BUILTIN;
- const unsigned long interval = 1000; // LED blinking interval (ms)
- void setup() {
- pinMode(ledPin, OUTPUT);
- }
- void loop() {
- static bool ledState = LED_OFF;
- static unsigned long previousMillis = 0;
- unsigned long currentMillis = millis();
- if (interval < currentMillis - previousMillis) {
- previousMillis = currentMillis;
- ledState = (LED_OFF == ledState) ? LED_ON : LED_OFF;
- digitalWrite(ledPin, ledState);
- }
- }
良いか悪いかはわからんのですが、こんな感じ。これまでにもやったことのある方法ですので、難しくはないですね。
BinkWithoutDelay illustrates a very important concept known as a State Machine.
https://learn.adafruit.com/multi-tasking-the-arduino-part-1/using-millis-for-timing
ポイントは、最後に LED の状態が更新された時刻 previousMillis を記憶しておいて、次のループで現在時刻 currentMillis をチェックし、更新する時刻かどうかを判断するということ。
状態を記憶する「ステート」と次の動作を決定する「マシン」とで、「ステートマシン」と呼ぶのだそうです。
Let’s look at a slightly more interesting blink variant that has a different on-time and off-time. We’ll call this one “FlashWithoutDelay”.
https://learn.adafruit.com/multi-tasking-the-arduino-part-1/using-millis-for-timing
オンとオフの時間が異なる Flash を作る
LED のオンとオフの時間が異なる「Flash without delay」を作ってみます。
- // Flash without delay
- const bool LED_ON = HIGH;
- const bool LED_OFF = LOW;
- const byte ledPin = LED_BUILTIN;
- const unsigned long onTime = 250; // LED on time (ms)
- const unsigned long offTime = 750; // LED off time (ms)
- void setup() {
- pinMode(ledPin, OUTPUT);
- }
- void loop() {
- static bool ledState = LED_OFF;
- static unsigned long previousMillis = 0;
- unsigned long currentMillis = millis();
- if ((LED_ON == ledState) && (onTime <= currentMillis - previousMillis)) {
- ledState = LED_OFF;
- previousMillis = currentMillis;
- digitalWrite(ledPin, ledState);
- }
- else if ((LED_OFF == ledState) && (offTime <= currentMillis - previousMillis)) {
- ledState = LED_ON;
- previousMillis = currentMillis;
- digitalWrite(ledPin, ledState);
- }
- }
LED の状態を追跡する変数と、最後の変更がいつ行なわれたかを追跡する変数。これが「ステート」です。
状態を調べて、次の変更を決定するコードが「マシン」で、ループのたびにマシンが実行されます。
ということで、今回は「ステートマシン」の概念を勉強しました。わかったかな? うーん、まぁね (^_^;)