
私はArduinoの専門家ではありませんが、プレートは長い間使っていません。使ったことのあるのは、ツールとして、既に作成されたコードをコピー&ペーストする程度で、仕組みを深く理解するほどの興味はなく、ただ動作させて便利に使ってみようという意図だけでした。今年のクリスマスは、LEDとHC-SR04超音波センサーを使って、キリスト降誕のシーンを少しアレンジしました。そして、何をしなければならないか観察するために立ち止まりました。
同じ信号から2つのLEDをそれぞれ違う動作をさせたかったんです。おっと、Arduinoをいじり始めたら最初に遭遇するであろう制限の一つにすぐに遭遇してしまいました。そんなに複雑にする必要はありません。LEDをいくつか使っただけですから、やりたいことを正しく実行できないことはお分かりいただけると思います。
まず最初に明確にしておきたいのは、Arduinoにはマルチタスク機能は存在せず、2つのタスクを並行して処理することはできないということです。しかし、まるで同時に動作しているかのように高速に呼び出しを行うテクニックは存在します。
もう少し詳しく説明します。クリスマスにキリスト降誕の乗り物に乗りました。娘たちが近づくと、特定の誕生ライトが点灯するようにしたかったのです。複雑なことは何もありませんでした。近接センサーの値に応じて、2本のLEDライトが異なる動作をするようにしたかったのです。
誰かが 10 cm 以内に近づいたときに次の動作をさせたいと思いました。
- 星へと続く光の枝は10秒間点灯し続けます
- 家の中に入るもう一つのランプは、降誕像から離れた瞬間から 10 秒間点灯し続けます。
簡単そうに見えますが、実は大きな問題を引き起こす可能性があります。Arduinoはマルチタスクに対応していないため、命令を次々に実行してしまうのです。

超音波センサーHC-SR04と2つのLEDを使ってアセンブリを作成しました。それぞれのLEDはキリスト降誕の場面の枝に相当します。最初の部分は、LOOP内での超音波センサーの設定と動作を構成するためのものなので、今は重要ではありませんが、これは何千ものサイトで見つけることができます。おそらく、後日さらに調査すれば特別なものを作成するかもしれません。今のところは、ここにあります(出典を記載できなくて申し訳ありませんが、どこから入手したか覚えていません)。
ArduinoのHC-SR04コード
int ledPin1=8; int ledPin2=7; long distancia; long tiempo; long currenttime; long timepast; void setup() { // デジタルピン LED_BUILTIN を出力として初期化します。 pinMode(ledPin1, OUTPUT); pinMode(ledPin2, OUTPUT); Serial.begin(9600); pinMode(3, OUTPUT); /*ピン 9 を出力としてアクティブ化: 超音波パルス用*/ pinMode(2, INPUT); /*ピン 8 を入力としてアクティブ化: 超音波反跳時間*/ } // ループ関数は永久に何度も実行されます void loop() { digitalWrite(3,LOW); /* センサー安定化*/ delayMicroseconds(5); digitalWrite(3, HIGH); /* 超音波パルスを送信*/ delayMicroseconds(10); tiempo=pulseIn(2, HIGH); /* 入力パルスの長さを測定する関数。超音波パルスの送信からセンサーが反射を受信するまでの経過時間を測定します。つまり、ピン 12 が反射の受信を開始して HIGH になってから、反射の受信を停止して LOW になるまでの時間が、受信パルスの長さです。*/ distancia= int(0.017*tiempo); /*整数値を取得する距離を計算する式*/ /*シリアル モニターによるセンチメートルの監視*/ Serial.println("Distancia "); Serial.println(distancia); Serial.println(" cm"); delay(1000);
これは超音波センサーによって測定された距離をチェックして保存します
LEDS動作ソリューション
最初に思いつくのは、遅延を入れることです。なぜかは分かりませんが、初心者は皆delay()を思いつきます。しかし、delay()を使うと選択肢がかなり狭まります。delay()を使うとボードが動かなくなり、その間は何もできなくなるからです。解決策はmillis()を使うことです。
そこで、if文とカウンタを使ったシンプルな解決策を見つけました。ある先生が言っていたように、if文をいくつも並べればどんなプログラムでも書けるんです。でも、もちろん現実はそれほどエレガントではありません。
// 距離が 10 未満の場合は、2 つの LED をオンにして、milis() で時間をカウントし始めます。if (distancia < 10){ digitalWrite(ledPin1, HIGH); digitalWrite(ledPin2, HIGH); currenttime=millis(); } // 距離が 10 より大きい場合は、スイッチ オンからの時間をチェックし、それがより大きい場合は LED1 をオフにします。if (distancia > 10){ timepast=millis()-currenttime; digitalWrite(ledPin1, LOW); // 距離が 10000 より大きい場合は、LED2 をオフにします。if(timepast>10000){ digitalWrite(ledPin2,LOW); } } }
このコードの意図は、Arduinoがこれら3つのif文を常に実行し続けることです。これにより、Arduinoはそれらを非常に高速に処理し、複数の処理を同時に行っているように見えます。しかし、ご存知の通り、実際には文を一つずつ実行し続けています。
距離が測定され、距離変数に格納されると、if が評価されます。
- まず、距離が目標の10センチメートル未満かどうかを確認します。もしそうであれば、2つのLEDを点灯し、ミリ()で時間をカウントし始めます。
- 距離が10cmを超える場合は、2番目のステップに進みます。このステップが成立した場合、経過時間を計算し、距離のみに依存するLED 1を消灯します。
- カウンタが起動してから10秒以上経過しているかどうかをチェックし、そうであればピン2をオフにする。
- そしてループは続く。そしてまた。
結局、解決策はうまくいったものの、この問題は多くの人が経験しているに違いなく、もっと正統的な解決策があるはずだと確信しました。そこで探し始めたところ、Adafruitの開発者たちがオブジェクト指向プログラミングを用いて開発した優れた解決策(もちろん他にもあるでしょう)を見つけました。それは、定義済みのクラスを持つオブジェクトを作成し、素早く呼び出しを行うことで、プログラム内で同じコードを何百回も繰り返す必要がないようにするというものです。
結局のところ、これは if + カウンターを使用した私のソリューションと「同じ」ですが、はるかにエレガントで、はるかに読みやすく、はるかに効率的なコードを提供します。
Arduinoにおけるマルチタスク管理は、必然的に時間管理につながります。当初、これは記事の内容に合わないと考えていましたが、非常に興味深いテーマだと思います。
Arduinoの時間関数
前述したように、delay() との依存関係は大きいと思います。おそらく、初心者の人は、点滅から信号機、LED のオン/オフ操作まで、あらゆる例でこの関数を見ているからでしょう。
遅延処理には大きな問題があります。それは、delay()を呼び出して一定時間待機させると、すべてが停止してしまうことです。ボードはセンサーを読み取ったり、文章の実行を続けたり、何も実行したりせず、指定した時間が経過するまで待機するだけです。もちろん、ボードを複数の用途で同時に使用したい場合は、これは現実的ではありません。
millis()やdelayMicroseconds()も確認する必要があります。また、プログラムの実行開始からのマイクロ秒数を返すmicros()もあります。
遅滞なく瞬きする
動作を確認するための良い例として、伝説の Blink を遅延なしで実行してみましょう。主な違いは、遅延 () 関数を使わずに milis () 関数を使って操作間の時間を計測できるようにすることです。
https://github.com/spitstecをチェックして「後で忘れて」
役に立つかどうかはわかりませんが、TaskManager(https://github.com/drsteveplatt/TaskManager)を試してみてください。これは軽量なタスクスワッパーで、タスク間の切り替えに約20マイクロ秒しかかかりません。私はnano上で10個以上の独立したタスクを実行したことがあります。例えば、多数のLEDが点滅するプログラム、Canonカメラのタイムラプスコントローラー、キーボード/MIDI(fluxamasynth)/スイッチパネルインターフェースなどです。(すべて異なるプログラムで、同時に実行したわけではありません。)