アフィリエイト広告
アフィリエイト広告

Arduino のマルチタスク (1) ステートマシン

これまでにやってみた 7 セグメント LED のダイナミック点灯でも、トライアックによる電力制御でも、Arduino で何かをやろうと思うと、いろいろな処理を同時に進行させないといけないわけです。
スケッチを書きながら、ごく自然にそんなことをやっていたように思うわけだけど、これがいわゆる「マルチタスク」ってやつなんだと気が付いた今日この頃。

マルチタスクとクラス

Arduino では、いろいろな処理をマルチタスクで休みなく連続して実行し、プログラムは終了させないことが必要。
たとえばよく言われる「delay() を使わない」ってのは、delay() 中も Arduino は delay() という仕事をやっていて、その仕事中は他の処理はできない、だから、そこで止まってしまう。
ふぅむ、まぁわかっているけど、面倒くさい話なんだよねぇ (;´Д`)

でも、それをやんないと Arduino はまともに仕事をしてくれないわけだし、このあたりの考え方はちょっとしっかり理解しておきたいよねぇ、ってのが、今回の話です。

で、話は変わるんだけど、「クラス」ってのを使ったプログラムを勉強したいなぁと、思ってた。
「クラス」ってのは C++ なの? C にはないのか。Arduino でクラスを使ったスケッチを見たことがあるので、使えるんだよね。そのスケッチってのが、ひとつの Arduino でいくつもの電球をそれぞれパワーコントロールするってものだった。お、これ、マルチタスクじゃん。

教科書やサイトをつらつら眺めてみたけど、クラスに出てくるのはリンゴとか車とか、なんのこっちゃ? チンプンカンプンわからん (;´Д`)

そんなおりに出会ったサイトが、ここ。

Multi-tasking the Arduino - Part 1
Once you have mastered the basic blinking leds, simple sensors and buzzing motors, it’s time to move on to bigger and better projects. That usually involves com...

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 を点滅させるので、外部回路は無しです。

  1. // Blink Without delay()
  2. const bool LED_ON = HIGH;
  3. const bool LED_OFF = LOW;
  4. const byte ledPin = LED_BUILTIN;
  5. const unsigned long interval = 1000; // LED blinking interval (ms)
  6. void setup() {
  7.   pinMode(ledPin, OUTPUT);
  8. }
  9. void loop() {
  10.   static bool ledState = LED_OFF;
  11.   static unsigned long previousMillis = 0;
  12.   unsigned long currentMillis = millis();
  13.   if (interval < currentMillis - previousMillis) {
  14.     previousMillis = currentMillis;
  15.     ledState = (LED_OFF == ledState) ? LED_ON : LED_OFF;
  16.     digitalWrite(ledPin, ledState);
  17.   }
  18. }

良いか悪いかはわからんのですが、こんな感じ。これまでにもやったことのある方法ですので、難しくはないですね。

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」を作ってみます。

  1. // Flash without delay
  2. const bool LED_ON = HIGH;
  3. const bool LED_OFF = LOW;
  4. const byte ledPin = LED_BUILTIN;
  5. const unsigned long onTime = 250; // LED on time (ms)
  6. const unsigned long offTime = 750; // LED off time (ms)
  7. void setup() {
  8.   pinMode(ledPin, OUTPUT);
  9. }
  10. void loop() {
  11.   static bool ledState = LED_OFF;
  12.   static unsigned long previousMillis = 0;
  13.   unsigned long currentMillis = millis();
  14.   if ((LED_ON == ledState) && (onTime <= currentMillis - previousMillis)) {
  15.     ledState = LED_OFF;
  16.     previousMillis = currentMillis;
  17.     digitalWrite(ledPin, ledState);
  18.   }
  19.   else if ((LED_OFF == ledState) && (offTime <= currentMillis - previousMillis)) {
  20.     ledState = LED_ON;
  21.     previousMillis = currentMillis;
  22.     digitalWrite(ledPin, ledState);
  23.   }
  24. }

LED の状態を追跡する変数と、最後の変更がいつ行なわれたかを追跡する変数。これが「ステート」です。
状態を調べて、次の変更を決定するコードが「マシン」で、ループのたびにマシンが実行されます。


ということで、今回は「ステートマシン」の概念を勉強しました。わかったかな? うーん、まぁね (^_^;)

タイトルとURLをコピーしました