今回はタイマーの残り時間が少なくなったら動作を変える制御をします。
制御文
前回の記事ではif
を使って、ある条件が成立しているときに指示を実行する、という制御をしました。
他にもwhile
やfor
を使って、処理の流れを制御してきましたよね。
このように処理の流れを制御するものを「制御文」と呼ぶことがあります。
ところで、前回習得したif
の使い方ではまだちょっと力不足なんです。
そこで今回の記事では、キッチンタイマーにさらに機能を追加することにより新しい知識を習得します。
キッチンタイマーの追加機能
前回の記事で、残り時間の目安をLEDの色で表現しました。
例えば1分タイマーにした時は、次の動作をします。
- タイマーをスタートした時、緑色LEDが点灯する
- 20秒経過すると、緑色LEDが消えて、黄色LEDが点灯する
- 40秒経過すると、黄色LEDが消えて、赤色LEDが点灯する
赤色LEDが点灯すると、設定時間が近づいているな、ということがわかります。
でもキッチンタイマーを使うときって、ずっと残り時間を目視で確認しているわけではないですよね。
そこで今回の記事では、上の動作に追加して「残り時間が5秒以下のときは、青色LEDの『ピカッ、ピカッ』という点滅に合わせて、スピーカーを『ピッ、ピッ』と鳴らして音でも知らせる」という機能を追加したいと思います。
スケッチでやりたいこと
「タイマーの残り時間が5秒のとき」というのはif
で実現できそうですよね。
この動作はスケッチのタイマー時間をカウントしている部分を変更すれば実現できるはずです。
// TIMER_JIKAN分の回数を数える
for(uint8_t count=0; count<TIMER_JIKAN; count++) {
// 残り時間が3分の2のとき、緑色をOFF、黄色をONにする
if( count == KIIRO_JIKAN ) {
digitalWrite(LED_MIDORI, LOW);
digitalWrite(LED_KIIRO, HIGH);
}
// 残り時間が3分の1のとき、黄色をOFF、赤色をONにする
if( count == AKA_JIKAN ){
digitalWrite(LED_KIIRO, LOW);
digitalWrite(LED_AKA, HIGH);
}
// 1秒に1回青色LEDを点滅する
digitalWrite(BYOU_LED, HIGH);
delay(BYOU_ON);
digitalWrite(BYOU_LED, LOW);
delay(BYOU_OFF);
}
最初からスケッチ書こうとすると難しくなってしまいますので、まずは日本語でどうすればよいか考えてみましょう。
上のスケッチを日本語でまとめると次のような処理になっています。
次の(1)から(3)の処理をTIMER_JIKAN分繰り返す
(1) もしタイマーがKIIRO_JIKANだったら緑色LEDを消して黄色LEDをつける
(2) もしタイマーがAKA_JIKANだったら黄色LEDを消して赤色LEDをつける
(3)1秒間かけて青色LEDを1回ピカッと点灯
このように動作をするスケッチを「タイマーの残り時間が5秒以下のとき、青色LEDの「ピカッ」という点滅に合わせてスピーカーを「ピッ」と鳴らす」というように変更したいわけです。
このように変更するには、上の(3)の動作を次のように変更できればいいはずです。
次の(1)から(3)の処理をTIMER_JIKAN分繰り返す
(1) もしタイマーがKIIRO_JIKANだったら緑色LEDを消して黄色LEDをつける
(2) もしタイマーがAKA_JIKANだったら黄色LEDを消して赤色LEDをつける
(3) もし残り時間が5秒以下だったら(3-1)の処理をして、そうでない場合は(3-2)の処理をする
(3-1) 1秒間かけて青色LEDを1回「ピカッ」と点灯して、さらに同時にスピーカーも「ピッ」と鳴らす
(3-2) 1秒間かけて青色LEDを1回「ピカッ」と点灯する(スピーカーは鳴らさない)
このような動作をしたい場合、(3)の処理の前半では「もし〜ならば」という処理をするのでif
が使えますよね。
でも(3)の処理の後半では「そうでない場合は」という処理が必要です。
そこで、今回の記事では(3)の処理のように、
もし〜ならば〇〇の処理をする。そうでなければ△△の処理をする
という処理の書き方を習得します。
if 〜 else 〜
「もし〜ならば〇〇の処理をする。そうでなければ△△の処理をする」という処理は今回のキッチンタイマーに限らず、とても多くの場面で必要な処理です。
C++言語では、直球でこのような処理をかける仕組みが用意されています。
なんだか説明するまでもない、という感じですよね。
上の説明では指示が1つの場合ですが、指示が複数ある場合は次のように波括弧で囲めばOKです。
if( 条件 ) {
処理1;
処理2;
} else {
処理3;
処理4;
}
この仕組みを利用して、これから作成したいスケッチを日本語交じりで書くと、次のようにすれば実現できそうです。
// TIMER_JIKAN分の回数を数える
for(uint8_t count=0; count<TIMER_JIKAN; count++) {
// 残り時間が3分の2のとき、緑色をOFF、黄色をONにする
if( count == KIIRO_JIKAN ) {
digitalWrite(LED_MIDORI, LOW);
digitalWrite(LED_KIIRO, HIGH);
}
// 残り時間が3分の1のとき、黄色をOFF、赤色をONにする
if( count == AKA_JIKAN ){
digitalWrite(LED_KIIRO, LOW);
digitalWrite(LED_AKA, HIGH);
}
// -------- 今回の記事で実装したい処理 --------
if( 残り時間が5秒以下のとき ) {
1秒間かけて青色LEDを1回「ピカッ」と点灯して、さらに同時にスピーカーも「ピッ」と鳴らす
} else {
1秒間かけて青色LEDを1回「ピカッ」と点灯する(スピーカーは鳴らさない)
}
}
それでは、実際にスケッチを考えてみましょう!
スケッチを考えてみよう!
最初に「残り時間が5秒以下だったら」という部分をどのようにすればよいか考えてみます。(すでに残り時間の考え方については残り時間LED制御のところで出てきましたので、解説が不要でしたら次のセクションのスケッチを確認してみてくだい)
キッチンタイマーのスケッチでは変数count
で時間を計測していますが、このcount
は経過時間です。
つまり「残り時間が5秒以下のとき」という上限は「countが5以下のとき」と書くことはできません。
いきなりスケッチでどのように書けば良いか考えるとややこしくなってしまいますので、例としてタイマー時間が30秒の時を考えてみます。
例えば10秒経過したとき、「残り時間」は「タイマー時間の30秒から、経過時間10秒を引いた値の20秒」になります。
このように計算するわけなので、一般的に考えてみると、残り時間は
「残り時間」 = 「タイマー時間」- 「経過時間」
という式で計算できることがわかります。
ということは、スケッチで書くと「残り時間」は次の式で表すことができますよね。
TIMER_JIKAN - count;
「残り時間が5秒以下のとき」という判定をすればよいわけですから、スケッチのif
の条件は次のように書けばよいことがわかります。
if( (TIMER_JIKAN - count) <= 5 )
かなりややこしくなってきましたよね。
if 〜 else 〜
や「変数」など、単体ではそれほど難しくないと思いますが、何かの処理を実現するために、どのように計算や指示を組み合わせれば良いか、となると急に難しく感じますよね。このあたりがプログラミングが難しく感じる一つの要因なのかな、と思います。
最初は誰でも難しく感じますが、慣れていけば「あっ、このパターンかな」という感じでだんだん複雑なスケッチが書けるようになってきます。
ぜひこの先もキッチンタイマーの製作を通して電子工作やプログラミングに慣れていただければと思います。
完成したスケッチ
スケッチの骨格ができましたので、先ほどのの日本語で書いた部分をスケッチとして書いてみましょう!
今までの内容をまとめて、次のようなスケッチにしてみました。
/*
キッチンタイマー
内容: スイッチ、LED、スピーカーを使ったキッチンタイマー
変更履歴:
2024.11.25: 新規作成
2024.12.01: スイッチが押されたらLED点滅開始
2024.12.02: スイッチ関連の#define追加
2024.12.05: 点滅回数カウント追加
2024.12.06: 繰り返し処理をforに変更
2024.12.13: アラーム音追加・動作開始時とタイマー時間の時に青色LEDを点灯
2024.12.15: 残り時間のLED制御を追加
2024.12.17: 残り時間が少なくなったら音も鳴らす制御を追加
*/
// 秒を表現するLED関連の定義
#define BYOU_LED 12 // 秒を表現するLEDの接続端子
#define BYOU_ON 50 // 秒を表現するLEDを点灯している時間(単位:ミリ秒)
#define BYOU_OFF (1000 - BYOU_ON) // 秒を表現するLEDを消している時間(単位:ミリ秒)
// 残り時間を表現するLED関連(緑色、黄色、赤色LED)
#define LED_MIDORI 8 // 緑色LEDの端子番号
#define LED_KIIRO 6 // 黄色LEDの端子番号
#define LED_AKA 4 // 赤色LEDの端子番号
// タイマースタートスイッチ関連の定義
#define SWITCH A5 // スイッチを接続している端子名
#define SWITCH_OFF 1 // スイッチOFFの時のdigitalReadの値
#define SWITCH_ON 0 // スイッチONの時のdigitalReadの値
// タイマー時間設定(LEDの点滅回数)
#define TIMER_JIKAN 30
// 残り時間を表現するLEDの制御時間
#define KIIRO_JIKAN 10 // 残り時間3分の2
#define AKA_JIKAN 20 // 残り時間3分の1
// アラーム音関連
#define SPEAKER A0 // スピーカーの端子番号
#define ALARM 880 // アラーム音の音程
void setup() {
// 端子の設定
// スピーカー接続端子はtoneの指示で自動的に出力設定になるので設定していません
pinMode(BYOU_LED, OUTPUT); // 秒表現のLED接続端子の出力設定
pinMode(SWITCH, INPUT_PULLUP); // スイッチ接続端子をプルアップ設定
pinMode(LED_MIDORI, OUTPUT); // 緑色LED接続端子設定
pinMode(LED_KIIRO, OUTPUT); // 緑色LED接続端子設定
pinMode(LED_AKA, OUTPUT); // 緑色LED接続端子設定
// 秒のLED(青色LED)を点灯する
digitalWrite(BYOU_LED, HIGH);
// スイッチが押されるまで何もしないで待つ
while( digitalRead(SWITCH) == SWITCH_OFF ) {
}
// タイマー開始時に緑色LEDを点灯
digitalWrite(LED_MIDORI, HIGH);
}
void loop() {
// TIMER_JIKAN分の回数を数える
for(uint8_t count=0; count<TIMER_JIKAN; count++) {
// 残り時間が3分の2のとき、緑色をOFF、黄色をONにする
if( count == KIIRO_JIKAN ) {
digitalWrite(LED_MIDORI, LOW);
digitalWrite(LED_KIIRO, HIGH);
}
// 残り時間が3分の1のとき、黄色をOFF、赤色をONにする
if( count == AKA_JIKAN ){
digitalWrite(LED_KIIRO, LOW);
digitalWrite(LED_AKA, HIGH);
}
// 残り時間が5秒以下かそうでないかで処理を変える
if( (TIMER_JIKAN-count) <= 5 ) {
// 残り時間が5秒以下のときは、1秒に1回青色LEDを点滅すると同時に音を鳴らす
digitalWrite(BYOU_LED, HIGH);
tone(SPEAKER, ALARM);
delay(BYOU_ON);
digitalWrite(BYOU_LED, LOW);
noTone(SPEAKER);
delay(BYOU_OFF);
} else {
// そうでなければ、1秒に1回青色LEDを点滅する(音は鳴らさない)
digitalWrite(BYOU_LED, HIGH);
delay(BYOU_ON);
digitalWrite(BYOU_LED, LOW);
delay(BYOU_OFF);
}
}
// 時間になったので秒のLED(青色LED)を点灯する
digitalWrite(BYOU_LED, HIGH);
// アラーム音を3回鳴らす
for(uint8_t count=0; count<3; count++) {
// ピ
tone(SPEAKER, ALARM);
delay(60);
noTone(SPEAKER);
delay(60);
// ピ
tone(SPEAKER, ALARM);
delay(60);
noTone(SPEAKER);
delay(60);
// ピーッ
tone(SPEAKER, ALARM);
delay(100);
noTone(SPEAKER);
delay(600);
}
// 何もしないで待つ
while( true ) {
}
}
ミニチャレンジ課題
前回記事の「チャレンジ課題2-2」では、残り時間が5秒以下になったら赤色LEDも青色LEDと一緒に点滅するようにしました。
解答例ではif
のみを使用しましたが、スケッチはちょっと読みづらい状態です。
そこで、今回のif 〜 else 〜
を使用してスケッチを書き直してみてください。
ミニチャレンジ課題解答例
if 〜 else 〜
を使用して5秒以下とそうでない場合の処理を大きく次のように分けてみました。
// 1秒に1回点滅処理をする
// 残り時間が少なくなったら赤色LEDも青色LEDと同じように点滅する
// (残り時間に応じて大きく処理を分ける)
if( count >= TENMETSU_JIKAN ) {
// 残り時間が少ない場合(5秒以下の場合)、青色LEDと赤色LEDを制御
digitalWrite(BYOU_LED, HIGH);
digitalWrite(LED_AKA, HIGH);
delay(BYOU_ON);
digitalWrite(BYOU_LED, LOW);
digitalWrite(LED_AKA, LOW);
delay(BYOU_OFF);
} else {
// そうでない場合、青色LEDのみ制御
digitalWrite(BYOU_LED, HIGH);
delay(BYOU_ON);
digitalWrite(BYOU_LED, LOW);
delay(BYOU_OFF);
}
このようにスケッチを書き直してみました。
/*
チャレンジ課題2-2解答例
内容: 残り時間が一定時間以下になったら、赤色LEDも「ピカっ」と点滅する
*/
// 秒を表現するLED関連の定義
#define BYOU_LED 12 // 秒を表現するLEDの接続端子
#define BYOU_ON 50 // 秒を表現するLEDを点灯している時間(単位:ミリ秒)
#define BYOU_OFF (1000 - BYOU_ON) // 秒を表現するLEDを消している時間(単位:ミリ秒)
// 残り時間を表現するLED関連(緑色、黄色、赤色LED)
#define LED_MIDORI 8 // 緑色LEDの端子番号
#define LED_KIIRO 6 // 黄色LEDの端子番号
#define LED_AKA 4 // 赤色LEDの端子番号
// タイマースタートスイッチ関連の定義
#define SWITCH A5 // スイッチを接続している端子名
#define SWITCH_OFF 1 // スイッチOFFの時のdigitalReadの値
#define SWITCH_ON 0 // スイッチONの時のdigitalReadの値
// タイマー時間設定(LEDの点滅回数)
#define TIMER_JIKAN 30
// 残り時間を表現するLEDの制御時間
#define KIIRO_JIKAN 10 // 残り時間3分の2
#define AKA_JIKAN 20 // 残り時間3分の1
// 赤色LEDを点滅する時間
// 残り時間が5秒なので、経過時間は(TIMER_JIKAN-5)で計算する
// 赤色LEDは、この経過時間以上になったら点滅処理をすることにする
#define TENMETSU_JIKAN (TIMER_JIKAN - 5)
// アラーム音関連
#define SPEAKER A0 // スピーカーの端子番号
#define ALARM 880 // アラーム音の音程
void setup() {
// 端子の設定
// スピーカー接続端子はtoneの指示で自動的に出力設定になるので設定していません
pinMode(BYOU_LED, OUTPUT); // 秒表現のLED接続端子の出力設定
pinMode(SWITCH, INPUT_PULLUP); // スイッチ接続端子をプルアップ設定
pinMode(LED_MIDORI, OUTPUT); // 緑色LED接続端子設定
pinMode(LED_KIIRO, OUTPUT); // 緑色LED接続端子設定
pinMode(LED_AKA, OUTPUT); // 緑色LED接続端子設定
// 秒のLED(青色LED)を点灯する
digitalWrite(BYOU_LED, HIGH);
// スイッチが押されるまで何もしないで待つ
while( digitalRead(SWITCH) == SWITCH_OFF ) {
}
// タイマー開始時に残り時間LEDを全て点灯する
digitalWrite(LED_MIDORI, HIGH);
digitalWrite(LED_KIIRO, HIGH);
digitalWrite(LED_AKA, HIGH);
}
void loop() {
// TIMER_JIKAN分の回数を数える
for(uint8_t count=0; count<TIMER_JIKAN; count++) {
// 残り時間が3分の2のとき、緑色をOFFにする(黄色と赤色LEDは変更しない)
if( count == KIIRO_JIKAN ) {
digitalWrite(LED_MIDORI, LOW);
}
// 残り時間が3分の1のとき、黄色をOFFにする(赤色LEDは変更しない)
if( count == AKA_JIKAN ){
digitalWrite(LED_KIIRO, LOW);
}
// 1秒に1回点滅処理をする
// 残り時間が少なくなったら赤色LEDも青色LEDと同じように点滅する
// (残り時間に応じて大きく処理を分ける)
if( count >= TENMETSU_JIKAN ) {
// 残り時間が少ない場合(5秒以下の場合)、青色LEDと赤色LEDを制御
digitalWrite(BYOU_LED, HIGH);
digitalWrite(LED_AKA, HIGH);
delay(BYOU_ON);
digitalWrite(BYOU_LED, LOW);
digitalWrite(LED_AKA, LOW);
delay(BYOU_OFF);
} else {
// そうでない場合、青色LEDのみ制御
digitalWrite(BYOU_LED, HIGH);
delay(BYOU_ON);
digitalWrite(BYOU_LED, LOW);
delay(BYOU_OFF);
}
}
// 時間になったので秒のLED(青色LED)を点灯する
digitalWrite(BYOU_LED, HIGH);
// アラーム音を3回鳴らす
for(uint8_t count=0; count<3; count++) {
// ピ
tone(SPEAKER, ALARM);
delay(60);
noTone(SPEAKER);
delay(60);
// ピ
tone(SPEAKER, ALARM);
delay(60);
noTone(SPEAKER);
delay(60);
// ピーッ
tone(SPEAKER, ALARM);
delay(100);
noTone(SPEAKER);
delay(600);
}
// 何もしないで待つ
while( true ) {
}
}
更新履歴
日付 | 内容 |
---|---|
2019.10.14 | 新規投稿 |
2021.8.27 | 新サイトデザイン対応 |
2022.2.17 | 残り時間計算方法変更 |
2024.12.17 | 説明内容変更 スケッチの端子名など変更 ミニチャレンジ課題追加 |