キッチンタイマーのスケッチを変更して、「スイッチが押されたらLEDの点滅を開始する」という動作にします。
習得した項目の確認
前回までの記事で、青色LEDを1秒に1回、ピカッ、ピカッ、と光らせることができました。また、スイッチの状態を読み取ることもできました。
今まで習得したことを利用して、さらに複雑なスケッチを作成していきます。もし、次の項目で「あれ?、どんな内容だっけ?」というものがありましたら、この段階で復習しておきましょう!
- setupとloopの役割(第10回・第11回)
- pinMode(第10回)
- delay(第11回)
- delay(第11回)
- digitalRead(第20回)
- Serial.begin(第21回)
- Serial.println / Serial.print(第21回)
実現したいこと
一般的なキッチンタイマーにはスタートボタンがありますよね。
前回までに作成したキッチンタイマーのスケッチでは、スケッチをArduinoボードに送るとすぐに青色LEDの点滅が始まってしまいます。そこで、スイッチを押したら点滅を開始するようにスケッチを変更したいと思います。
今まで習得したことで次のことは実現できています。
- 青色LEDを1秒に1回点滅する
- スイッチの状態を調べる
これから実現したいことは「スイッチが押されたら青色LEDの点滅を開始する」という動作です。この動作を実現するには、「何かをしたら何かをする」ということができれば良さそうです。
今回のメインテーマは、「何かをしたら何かをする」というスケッチを書くことです。
「何かをしたら何かをする」という動作は、電子工作のいろいろな場面で出てきますので、習得できれば作れるものの範囲が広がります。今回も是非じっくり取り組んでみてください。
それではこれから新しい知識を習得して、スイッチが押されたらLEDの点滅を開始するようにスケッチを変更していきましょう。
「何かをしたら、何かの処理をする」?
電子工作の世界では、「何かをしたら、何かの処理をする」という場面はたくさんありそうですよね。
今回作成する「スイッチが押されたら、LEDの点滅を開始する」という動作もそうですし、他にも「気温が25度を超えたら、リモコン信号を発生してエアコンをONにする」など、いくらでもありそうです。
でも、プログラミングの世界では、「ある動作をしたら、何かの処理をする」という動作の指示を書くのは意外に難しいんです。
これから詳しく説明していきますが、イメージとしてはC++言語で用意されている指示をうまく組み合わせて、結果として「ある動作をしたら、何かの処理をする」という動作になるようにスケッチを書いていきます。
と説明されてもよくわからないと思いますので、具体的にみていきましょう!
説明の進め方
C++言語では、「ある条件が成立している間、ある処理をする」という制御を行う仕組みが用意されています。
この仕組みを使って「何かをしたら、何かの処理をする」という動作を実現します。(今回は「スイッチが押されたら、LEDの点滅を開始する」という動作になるようにスケッチに書いていきます)
なんだか上の太線文字の2つの動作は似ているようで違うような、、、という感じでややこしくなってきましたよね。今はさっぱり理解できない、という感じだと思います。順を追ってじっくり読み解いていけば理解できますのでご安心ください。
この後の説明ですが、次のように進めていきますので、「何の説明をしているの?」と迷子にならないようにしてください!
- 「ある条件が成立している間、ある処理をする」という処理の書き方
- ❶の仕組み利用して「スイッチが押されたら、LEDの点滅を開始する」というスケッチを書く
❶ 「ある条件が成立している間、ある処理をする」という処理の書き方
C++言語では「ある条件が成立している間、ある処理をする」という制御を実現するために、「while」が用意されています。
最初にこのwhileの書き方を確認しましょう。
「指示」の部分は実際にスケッチを書くときの注意点がありますが、細かい内容については記事の後半で説明します。
whileは「条件」が成立している間、「指示」をずっと何度も何度も繰り返す、という制御を実現します。
実際にArduinoボードは次のようにwhileを処理します。
- Arduinoボードは「条件」を判定します。
条件が成立していれば❷、成立していなければ❸に進みます - 「命令」を実行します。命令の実行後、❶に戻ります
- 「命令」は実行せず、whileの次に書かれている指示に進みます(whileの処理を終えます)
whileの処理はそれほど複雑ではないと思いますが、これを使って「スイッチが押されたら、LEDの点滅を開始する」という動作をするのはちょっと難しいんです。
というのはArduinoボードは、whileを処理するときに「条件」が成立しているかどうか判定しますが、この判定は、判定を行う時点の状態が条件に合うかどうかです。
whileの「条件」の内容が「スイッチON」であれば「判定時点でスイッチがONかOFFか」という条件だけで、「スイッチが押されたら」という動作の判定ではないんです。
かなりややこしい話になってしましましたが、whileの「条件」はあくまで判定時点の「状態の判断」であって、「動作」ではない、という点がポイントです。
「スイッチが押されたら」という動作を実現するには発想を変える必要があります。
❷ 「スイッチが押されたら、LEDの点滅を開始する」というスケッチ
それでは実際にwhileを使って「スイッチが押されたら、LEDの点滅を開始する」というスケッチはどのようなものか見ていきましょう。
最初は日本語混じりで説明しますが、次のスケッチを実行すると「スイッチが押されたら、LEDの点滅を開始する」という動作になります。
void setup() {
while(スイッチがOFF)
何もしない
}
void loop() {
// LEDを点滅 (pinModeの設定は省略)
digitalWrite(12, HIGH);
delay(50);
digitalWrite(12, LOW);
delay(950);
}
Arduinoボードはこのスケッチを以下のように実行します。
- Arduinoボードの動作開始後、setupの処理をします。
- whlieの「条件」を判定します。条件は「スイッチがOFF」かどうかです。
「スイッチがOFF」つまり条件が成立していれば❸に進みます。
「スイッチがON」つまり条件が不成立であれば❹に進みます。 - 条件が成立していますので「指示」を実行しますが、「指示」は「何もしない」なので何もせず、❷に戻ります。
- 条件が不成立ですので、whileの処理を終えて次に進みます。
- setupはwhile以降、指示がありません。次にloopに進み、LEDの点滅を繰り返します。
結果的な動作として、「スイッチが押されたら、LEDの点滅を開始する」ということになります。
全体の動作をまとめると、setupのwhileでスイッチがOFFの間、何もしないをずっと繰り返します。言い換えると、setup部分ではスイッチが押されるまで何もしないで待機する、という動作になります。
この動作状態のとき、スイッチがONになると条件が不成立になるのでloopに進み、LEDの点滅を開始します。
結果として「スイッチが押されたら、LEDの点滅を開始する」という動作が実現できます。
日常生活と考え方が逆?のようなイメージですので、ちょっと?のところもあるかもしれません。Arduinoボードの気持ちになってスケッチがどのように処理されるかじっくり確認してみてください。
whileの「指示」の注意点
次にwhileの書き方をもう少し詳しく説明します。
whileは、条件が成立している間、while(条件)
のすぐ次に書いてある「1つの指示」を繰り返し実行します。whileの直後の1つの指示だけです。(しつこくてすみません…)
while(条件) // この条件が成立している間
指示; // この1つの指示を実行する
例えば次のように書くと、whileは条件が成立している間、「指示1」だけずっと繰り返します。「指示2」はwhileの処理が終わったあとに実行されます。
while(条件) // この条件が成立している間
指示1; // この1つの指示を実行する
指示2; // この指示はwhileとは関係ないので、whileの処理終了後に実行される
では、実行したい指示が2つ以上ある場合はどのように書けばいいのでしょうか?
そのような場合は、「 { 」と「 } 」で複数の指示を一つのかたまりにして次のように書きます。
while(条件) {
指示1;
指示2;
指示3;
}
「 { 」「 } 」で指示を括っていますので、上のようにインデントを入れるようにします。whileが条件成立次に実行する指示の内容が(少し)わかりやすくなります。
細かい話になりますが、人によってはwhileを以下のように書く場合もあります。(最初の「 { 」をwhileの次の行に書いている)
while(条件)
{
指示1;
指示2;
指示3;
}
どちらが正しいというわけではありませんが、このシリーズでは、最初の方の書き方を採用しています。理由は、Arduino IDEで新規ファイルを作成した場合、setupやloopの「 { 」は最初の書き方になっていますので、それに合わせたためです。
while部分のスケッチ作成
先ほどのスケッチでは次のように日本語混じりで書きましたので、これから実際のスケッチを書いていきます。
void setup() {
while(スイッチがOFF)
何もしない
}
最初に「何もしない」部分の書き方、その次に「スイッチがOFF」の条件の書き方を説明していきます。
「何もしない」部分の書き方
最初は「何もしない」部分の書き方です。書き方は2通りあります。
ひとつの書き方は以下のように「;」だけを書いて指示がないことを表現する方法です。
while(条件)
;
C++言語では改行やスペースは無視されますので、次のようなスケッチを見かけることもあります。
while(条件);
while(条件)という指示に見えてしまいますが、何もしないwhileである点に注意してください。
もうひとつの書き方は次のように「 { 」「 } 」の中に指示がないことを表現する方法です。
while(条件) {
}
なお、指示がないことを表現するのであれば、次のように「 ; 」と書いても問題ありませんが、この書き方は今まで見かけたことがありません。
while(条件) {
;
}
Arduinoのサンプルスケッチを見ると、最初の書き方も比較的みられますが、このシリーズでは次の書き方で進めます。
while(条件) {
}
次はwhileの条件の部分に書く「スイッチがOFF」という書き方をみていきましょう。
「スイッチがOFF」という条件の書き方
次は、「スイッチがOFF」という条件の書き方です。
スイッチ状態の確認
「スイッチがOFF」という条件を書くには、まずはスイッチ状態を知る必要があります。
スイッチの状態はdigitalRead
で調べることができましたよね。シリアルモニタでdigitalReadの戻り値を確認した結果、次のことがわかりました。
digitalReadの戻り値 | 意味 |
1 | スイッチがOFF |
0 | スイッチがON |
先ほどのwhile
の「条件」は「スイッチがOFF」でしたよね。ということは、「条件」の部分は「digitalReadの値が1」ということです。
while( digitalReadの戻り値が1 ) {
}
これから、「digitalReadの値が1かどうか」確認しますが、このようにある値とある値を比較することが多くあります。
そこで、何かと何かを比較する方法を確認します。
比較演算子
2つの値を比較をするために、C++言語では「比較演算子」というものが用意されています。
「比較演算子」というと意識高い感じですが、要するに2つの値を比較する記号、ということです。
それではどのような記号が用意されているか確認しましょう。
「 < 」とか「 > 」は数学で出てきましたよね。直感的な記号なのでわかりやすいと思います。
「以上」「以下」は数学では「 ≧ 」「 ≦ 」という記号でしたが、キーボードではこの記号は入力できないので、バラして「 >= 」「 <= 」が使われています。(記号の順番に注意してください)
等しいか異なるかという記号は数学では、「 = 」と「 ≠ 」でしたよね。プログラミングでは、「 = 」は他の目的で使用されるので(第23回以降の記事で出てきます)、ちょっとヤケになった感じで「 == 」が使われています。また「≠」は半角文字にこの記号はないので、これもちょっとヤケになった感じで「 != 」が使われています。
異なる記号については、半角文字に縦棒「 | 」がありますので、「 | = 」の方がまだマシなのではないか、という気もします。でも「 | = 」は別の目的で使用されるので「!=」が使われています。
それではキッチンタイマーのスケッチの検討に戻りましょう。
whileの条件部分完成
完成していないところは、次の「digitalReadの戻り値が1」の部分でした。
while( digitalReadの戻り値が1 ) {
}
「スイッチがOFF」ということは、digitalRead(SWITCH)
の値が「1」ということですので、この部分は次のように書くことができます。
while( digitalRead(SWITCH) == 1 ) {
}
よくやってしまいがちなのが「等しい」は「 = 」という先入観があるので、次のように書いてしまうことがあります。
while( digitalRead(SWITCH) = 1 ) { // "=="ではなく"="と書いてしまった!
}
これはエラーになりますので注意してください。
それでは、スイッチを押したら青色LEDの点滅を開始する、というスケッチに仕上げましょう。
/*
キッチンタイマー
内容: スイッチ、LED、スピーカーを使ったキッチンタイマー
変更履歴:
2024.11.25: 新規作成
2024.12.01: スイッチが押されたらLED点滅開始
*/
// 秒を表現するLED関連の定義
#define BYOU_LED 12 // 秒を表現するLEDの接続端子
#define BYOU_ON 50 // 秒を表現するLEDを点灯している時間(単位:ミリ秒)
#define BYOU_OFF (1000 - BYOU_ON) // 秒を表現するLEDを消している時間(単位:ミリ秒)
// タイマースタートスイッチ関連の定義
#define SWITCH A5 // スイッチを接続している端子名
void setup() {
// 端子の設定
pinMode(BYOU_LED, OUTPUT); // 秒表現のLED接続端子の出力設定
pinMode(SWITCH, INPUT_PULLUP); // スイッチ接続端子をプルアップ設定
// スイッチが押されるまで何もしないで待つ
while( digitalRead(SWITCH) == 1 ) {
}
}
void loop() {
// 秒表現LEDを点滅する
digitalWrite(BYOU_LED, HIGH);
delay(BYOU_ON);
digitalWrite(BYOU_LED, LOW);
delay(BYOU_OFF);
}
LED点滅のスケッチに対して、次の項目を追加しています。
- スイッチ接続端子の#define定義
- スイッチ接続端子のpinModeによるプルアップ設定
- whileによるスイッチが押されるまで待つ処理
スケッチを作成したら、Arduinoボードに送ります。送り終わると何も反応しませんが、スイッチを押すと青色LEDが点滅を開始します。
スケッチの量が増えてきましたので、コメントを追加しています。コメントを頼りにご確認いただければと思います。もしわからないところがありましたら、コメント欄からご質問ください。
【補足】「何もしない」スピード
whileを使用して「スイッチが押されるまで待つ」というスケッチを作成しました。
while( digitalRead(SWITCH) == 1 ) {
}
スイッチが押されるまで何もしないとはいえ、Arduinoボードはスケッチのこの部分で、A5端子の電圧計の値を確認して、1と比較して、等しければまたA5端子の電圧計の値を確認して、、、ということをずっと繰り返しています。
スイッチが押されるまで、何度も何度もA5端子の電圧計を確認するのですが、1秒間にどのぐらい確認していると思いますか?
せいぜい1秒間に数十回でしょうか、いやいやすごいスピードで動いてるわけだから1秒間に数百回でしょうか。
実は1秒間にだいたい100万回程度、電圧計を確認しては1と比較して、ということをしています。すごい早いですね。
次回はwhileについてさらに詳しく理解していきます。
更新履歴
日付 | 内容 |
---|---|
2019.8.16 | 新規投稿 |
2021.8.22 | 新サイトデザイン対応 |
2024.12.1 | Arduino IDE2に変更 whileの説明内容変更 等価演算子、関係演算子の用語補足 |
アルディーノIDEでのプログラムは分かったのですが、アルディーノUNO R3を、使っているのでブレッドボードをどのように配線を繋げればいいのかわからないです。
現在はプログラムをエラーなく実行できるのですが、スイッチを押す押さない関係なく、常にLEDがピカピカ設定した秒数消えたりついたりします。ブレッドボードの画像や、やり方を教えてほしいです。
ご質問どうもありがとうございます。
Arduino UNOの場合、Microとはピン番号が異なります。
例えばスイッチをA5端子に接続している場合、内部の番号は19番になります。
(Microの場合は23番)
そこで、最初にスイッチの番号を#defineで定義しているところを23から19に変更していただければと思います。
また、A5端子に接続している場合、次のように接続すればA5-スイッチ-GND端子に接続されることになります。
不明点がありましたら追加でご質問いただければと思います。
すごく分かったです!
コメントどうもありがとうございます。
プログラミングはわかりやすく説明するのがなかなか難しいこともありますので、わからないことがありましたらコメントいただければと思います。
大変参考になり5分タイマーを作成し、目薬点滴時に便利に使っており、ありがとうございます。またさらにボタン追加し1分タイマーも織り込みたいのですが、いろいろ試しましたがうまくいきません。何かヒントをいただけたらとよろしくお願いいたします。
ご質問どうもありがとうございます。
このシリーズではボタン1個を用意してスタートボタンに使用していますが、空いている端子にもう1つボタンを追加して、「5分タイマー用ボタン」「1分タイマー用ボタン」を用意する、というイメージでしょうか。
プログラミングの方法は色々とありますが、すみません、実装しやすい方法はこのシリーズでは触れていない文法を使った方がいいかもしれません。
まずその文法ですが、while文を抜ける(強制的に終了させる)「break」というものです。具体例を見た方が理解が早いと思います。break文は以下のように使用します。
このプログラムは、while( ture )としていますので、永遠繰り返すことになっています。ただ、while構文の中で、スイッチ1の状態を確認して、スイッチ1が押された場合、「break;」を実行しますが、このbreakはwhile文を抜ける(while文を終了する)命令となっています。このbreak文を使用して、2つのスイッチの状態をチェックしよう、というわけです。
Arduinoにスイッチ1とスイッチ2の2つのスイッチを接続して、それぞれのスイッチを1分タイマーと5分タイマーのスタートボタンに割り当てる場合、以下のようなプログラムがわかりやすいのではないかと思います。
(loop関数内に書きます)
以上ですが、プログラミングに慣れていないと、ちょっとわかりづらいところもあるかもしれません。不明点ございましたらお手数ですがコメントいただけると幸いです。
実は、このシリーズはわかりづらいところもあるので、そろそろ記事を書き直そうとしておりました。基礎編1では必要ないかと思っていましたが、今回ご質問いただいた内容を考慮して、while文でのbreakの使い方は説明しておりませんので、記事書き直しの際に追加説明しようと思います。
ご質問いただいてどうもありがとうございました。