第35回 if else文

タイトル画像

今回はタイマーの残り時間が少なくなったら動作を変える制御をします。

目次

条件判断文

前回の記事で「if文」を新しく習得しましたが、if文のように条件を判断して処理を変える制御構文を「条件判断文」と呼んでいます。

「条件判断文」は他のプログラミング言語でも出てきますので、ぜひ使いこなせるようにしておきましょう。

ところで、前回習得した「if文」はまだちょっと力不足なんです。今回はキッチンタイマーの機能を追加することにより新しい知識を習得します。

キッチンタイマーの追加機能

前回の記事で、残り時間の目安をLEDの色で表現しました。例えば1分タイマーにした時は、以下のような動作をします。

  1. タイマーをスタートした時、緑色LEDが点灯する
  2. 20秒経過すると、緑色LEDが消えて、黄色LEDが点灯する
  3. 40秒経過すると、黄色LEDが消えて、赤色LEDが点灯する

残り20秒になると赤色LEDが点灯して設定時間が近づいていることがわかります。このようにLEDで残り時間を表現するのもいいですが、タイマー設定時間になる直前に音で知らせてくれるのもいいかもしれません。

そこで今回の記事では、タイマーの残り時間が5秒以下の場合、青色LEDの点滅に合わせてスピーカーを「ピッ」と鳴らして音でも知らせたいと思います。

スケッチでやりたいこと

「タイマーの残り時間が5秒になったら」というのは条件判断文、つまりif文で実現できそうです。

この動作はスケッチの以下の部分を変更すれば実現できす。

// TIMER_JIKAN分の回数を数える
for( count=0; count<TIMER_JIKAN; count++) {

  // 残り時間表現用LEDの制御
  if( count == KIIRO_JIKAN ) {
    digitalWrite(LED_MIDORI, LOW);
    digitalWrite(LED_KIIRO,  HIGH);
  }

  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秒に1回青色LEDをピカッと点灯

現在のこのような動作をするスケッチを「タイマーの残り時間が5秒以下の場合、青色LEDの点滅に合わせてスピーカーを「ピッ」と鳴らす」というように変更したいわけです。

このように変更するには、上の(3)の動作を以下のように変更すれば実現できます。

以下の(1)から(3)の処理をTIMER_JIKAN分繰り返す
 1) もしタイマーがKIIRO_JIKANだったら緑色LEDを消して黄色LEDをつける
 2) もしタイマーがAKA_JIKANだったら黄色LEDを消して赤色LEDをつける
 3-1) もし残り時間が5秒以下だったら、1秒に1回青色LEDをつけて、スピーカーを鳴らす
 3-2) そうでなければ、1秒に1回青色LEDをつける

このような動作をする場合、(3-1)の動作で「もし〜ならば」なので「if文」が使えます。では(3-2)の「そうでなければ」はどうすればよいでしょうか。

if else文

先ほどの例では、「もし〜ならば〇〇する。そうでなければ□□する」という動作をしたいわけですが、このような動作はキッチンタイマーに限らず多くの場面で出てきます。

このような目的のために「if else文」という構文が用意されています。

if-else文

この書き方は処理が1つの場合ですが、処理が複数ある場合は複数の処理を中括弧で囲みます。一般的には以下のような改行をして書く場合が多いです。( } else { を1行で書く)

if( 条件式 ) {
  処理1;
  処理2;
} else {
  処理3;
  処理4;
}

このif else文を使用して、先ほどのスケッチを日本語交じりで書くと、以下のようにすれば実現できそうです。

// TIMER_JIKAN分の回数を数える
for( count=0; count<TIMER_JIKAN; count++) {

  // 残り時間表現用LEDの制御
  if( count == KIIRO_JIKAN ) {
    digitalWrite(LED_MIDORI, LOW);
    digitalWrite(LED_KIIRO,  HIGH);
  }

  if( count == AKA_JIKAN ){
    digitalWrite(LED_KIIRO, LOW);
    digitalWrite(LED_AKA, HIGH);
  }

  if( 残り時間が5秒以下だったら )
    1秒に1回青色LEDをつけて、スピーカーを鳴らす;
  else
    1秒に1回青色LEDをつける;

}

それでは、実際にスケッチを考えてみましょう。

スケッチにしてみよう

最初に「残り時間が5秒以下だったら」という部分をどのようにすればよいか考えてみます。

キッチンタイマーのスケッチでは変数countで時間を数えていますが、このcountは残り時間ではありません。キッチンタイマーをスタートすると、変数countは0から始めて1秒に1ずつ増えていきます。つまりcountは経過時間です。

もしcountが残り時間であれば「残り時間が5秒以下だったら」という部分は以下のように書けます。

if( count <= 5 )

ところがcountは経過時間なのでこのように書けません。どのように書けばよいでしょうか。

一般的に考えると難しくなってしまいますので、例としてタイマー時間が30秒の時を考えてみます。

例えば10秒経過したときのケースで考えてみます。残り時間は、タイマー時間の30秒から経過時間10秒を引いた結果の20秒になります。

もう少し一般的に考えてみると、残り時間は

(残り時間) = (タイマー時間) - (経過時間)

となります。これが残り時間になりますので、スケッチで書くと、残り時間は以下の式で表すことができます。

TIMER_JIKAN - count

これが5秒以下、という判定をすればよいわけですから、if文は以下のように書けばよいことがわかります。

if( (TIMER_JIKAN - count) <= 5 )

かなりややこしくなってきましたよね。でも、このややこしい部分は必ず理解してから先に進むようにしてください。

プログラミングの難しさはこのようなところにあると思います。やりたいことをすぐにスケッチに書けない場合、どのようにすれば命令を組み合わせて実現できるかを考えることが重要になってきます。

いろいろな方にプログラミングを教えていると、プログラミングができるようになるかは、このややこしい部分が理解できるかにかかっているように感じています。ぜひこのややこしい部分を自分のものにしてから先に進んでください。

完成したスケッチ

ここまでできれば、あとは残り時間が5秒以下とそうでない場合の動作をスケッチに書けばOKです。

今までの内容をまとめて完成させたスケッチを以下に示します。

/*
 * キッチンタイマー
 * 
 * 内容: スイッチ、LED、スピーカーを使ったキッチンタイマー
 * 変更履歴:
 *   2019. 8.11: 新規作成
 *   2019. 8.15: スタートスイッチ処理を追加
 *   2019. 8.17: スイッチ関連の#define追加
 *   2019. 9. 8: 点滅回数カウント追加
 *               最終的に繰り返し処理をfor文で作成
 *   2019. 9.16: スケッチ動作開始時にLEDを点滅
 *   2019.10. 5: アラーム音追加
 *               動作開始時とタイマー時間の時に青色LEDを点灯するように変更
 *   2019.10.13: 残り時間LED制御追加
 *   2019.10.14: 残り時間が5秒以下になったらスピーカーを鳴らす動作を追加
 */

// 秒を表現する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 23    // スイッチを接続している端子番号
#define SWITCH_OFF 1 // スイッチOFFの時のdigitalReadの値
#define SWITCH_ON  0 // スイッチONの時のdigitalReadの値

// タイマー時間設定(単位:秒)
#define TIMER_JIKAN 30

// 残り時間を表現するLEDの制御時間
#define KIIRO_JIKAN TIMER_JIKAN / 3
#define AKA_JIKAN   TIMER_JIKAN / 3 * 2

// アラーム音関連
#define SPEAKER 18  // スピーカーの端子番号
#define ALARM   880 // アラーム音の音程(単位:Hz)


void setup() {
  // 端子の設定
  pinMode(BYOU_LED,   OUTPUT);   // 青色LED接続端子設定
  pinMode(LED_MIDORI, OUTPUT);   // 緑色LED接続端子設定
  pinMode(LED_KIIRO,  OUTPUT);   // 緑色LED接続端子設定
  pinMode(LED_AKA,    OUTPUT);   // 緑色LED接続端子設定
  pinMode(SWITCH, INPUT_PULLUP); // スイッチ接続端子の設定

  // 秒のLED(青色LED)を点灯する
  digitalWrite(BYOU_LED, HIGH);

  // スイッチが押されるまで待つ
  while(digitalRead(SWITCH) == SWITCH_OFF) {
  }

  // タイマー開始時に緑色LEDを点灯
  digitalWrite(LED_MIDORI, HIGH);
}


void loop() {
  // for文で回数を数えるために使用する変数
  uint8_t count;

  // TIMER_JIKAN分の回数を数える
  for( count=0; count<TIMER_JIKAN; count++) {

    // 残り時間表現用LEDの制御
    if( count == KIIRO_JIKAN ) {
      digitalWrite(LED_MIDORI, LOW);
      digitalWrite(LED_KIIRO,  HIGH);
    }

    if( count == AKA_JIKAN ){
      digitalWrite(LED_KIIRO, LOW);
      digitalWrite(LED_AKA, HIGH);
    }

    // 残り時間に応じて処理を変える
    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( 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 残り時間計算方法変更
通知の設定
通知タイミング
guest
0 コメント
本文中にフィードバック
全てのコメントを見る
目次