第33回 if

タイマーの残り時間に応じて、前回追加したLEDを制御します。

目次

残り時間LEDの制御

前回追加した残り時間を表現するLEDは以下のように制御することにします。

  1. タイマーがスタートしたら緑色LEDをつける
  2. 残り時間が3分の2になったら、緑色LEDを消して黄色LEDをつける
  3. 残り時間が3分の1になったら、黄色LEDを消して赤色LEDをつける

このような制御は今まで習得したC++言語の文法では実現できませんので、今回は新しい文法を習得します。

条件判断「if」

残り時間表現用のLED制御のように「もし〜〜だったら、〇〇する」という制御はいろいろな場面で必要です。

例えば明るさセンサで部屋の明るさを測定して「明るさが□□以下になったら電気をつける」など、「〜になったら〇〇する」という制御は電子工作ではとても多く出てきます。

このような制御をするためにifというものが用意されています。

「if」は日本語で「もし」という意味です。「もし、条件が成立したら指示を実行する」という意味になります。

先ほどの残り時間を表現するLEDの制御はもし残り時間が3分の2という条件が成立していたら緑色LEDを消して黄色LEDをつけるというように、ifを使ってスケッチを作成できます。

それでは、if文の使い方を詳しくみていきましょう。

例えば、変数countが10の時に何らかの指示を実行したい場合は次のように書きます。

if( count == 10 )
 何らかの指示;

また、countが3未満の時に何らかの指示を実行したい場合は次のようになります。

if( count < 3 )
 何らかの指示;

条件式の書き方はwhileforと全く同じですので、忘れてしまったらwhile(第22回)やfor(第26回)の説明を確認してみてください。


また、条件が成立したときに複数の指示を実行する場合は、次のように波括弧で囲みます。

if( count == 10 ) {
 何らかの指示1;
 何らかの指示2;
}

ifの説明はこのぐらいなのですが、Arduinoボードがどのように処理しているかについてもう少し詳しくみていきましょう。


Arduinoボードはスケッチに書いてあるifを処理するとき、最初に「条件の式を評価」します。

評価するとその条件の式が「真」なのか「偽」なのかという結論が出ます。

Arduinoボードは評価した結果が「真」であれば「指示」を実行、「偽」であれば「指示」は実行せずに次の処理に進みます


ここからちょっとややこしくなります。

Arduinoボードは「真」は「0以外の数値」、「偽」は「0」として扱うんでしたよね。

そこで、プログラマによってはifの条件の部分を次のように書く人もいます。

if( jyoutai ) {
 指示;
}

ifの条件の部分には変数だけが書かれています。

この場合、変数jyoutaiが0であれば偽ですので「指示」は実行されません。

jyoutai0以外であれば真ですので「指示」は実行されます

つまり、上のスケッチは以下のような意味を持っていることがわかります。

if( jyoutai != 0 ) {
 指示;
}

ちょっと難しく感じるかもしれませんが、このような考え方を身につけると他の人が書いたスケッチが読みやすくなると思います。

さらに他のプログラミング言語でも同じような考え方、書き方が出てきますので、C++言語でしっかり習得すれば他のプログラミング言語の習得が容易になります。

残り時間に応じてLED制御する

ifの使い方がわかったところで、最初に説明したように残り時間表示用LEDを制御するスケッチを追加しましょう。

具体的には以下の動作をするようにスケッチを追加します。

  1. タイマーがスタートしたら緑色LEDをつける
  2. 残り時間が3分の2になったら、緑色LEDを消して黄色LEDをつける
  3. 残り時間が3分の1になったら、黄色LEDを消して赤色LEDをつける

それでは、スケッチに必要な処理を追加していきましょう!

追加したLEDの#define定義追加と出力設定(setup部分)

最初に追加したLEDを制御できるように端子番号の#defineを追加します。

// 残り時間を表現するLED関連(緑色、黄色、赤色LED)
#define LED_MIDORI 8 // 緑色LEDの端子番号
#define LED_KIIRO  6 // 黄色LEDの端子番号
#define LED_AKA    4 // 赤色LEDの端子番号

また、追加したLEDの接続端子の出力設定をsetupに追加します。

pinMode(LED_MIDORI, OUTPUT);   // 緑色LED接続端子設定
pinMode(LED_KIIRO,  OUTPUT);   // 緑色LED接続端子設定
pinMode(LED_AKA,    OUTPUT);   // 緑色LED接続端子設定

緑色LEDの制御(loop部分)

スイッチが押されてタイマーがスタートしたら、最初は緑色のLEDを点灯状態にしますので、緑色LEDを点灯する次の処理をスケッチを追加します。

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

タイマースタート直後の処理はこれでOKです。

残り時間に応じたLEDの制御(loop部分)

タイマーの時間計測が進み、残り時間が3分の2、3分の1のタイミングで、LEDの点灯状態を切り替える必要があります。

この処理は、タイマーをカウントしている部分で、残り時間に応じてLEDを制御すればよいわけです。

具体的には次の処理をスケッチに書くことになります。

  • もし、残り時間が3分の2だったら、緑色のLEDを消して、黄色のLEDを点灯する
  • もし、残り時間が3分の1だったら、黄色のLEDを消して、赤色のLEDを点灯する

タイマーをカウントしてる部分は以下のforのところです。

そこで、このforの処理の中で、残り時間に応じた処理をすることになります。

// TIMER_JIKAN分の回数を数える
for(uint8_t count=0;  count<TIMER_JIKAN; count++) {
  // 1秒に1回青色LEDを点滅する
  digitalWrite(BYOU_LED, HIGH);
  delay(BYOU_ON);
  digitalWrite(BYOU_LED, LOW);
  delay(BYOU_OFF);
}

この部分で変数countを使用してタイマーの時間計測をしているので、countの値をifで判断してLEDを制御すれば良さそうですよね。

この部分では、タイマーが時間計測を開始後に、変数countは0から始めて1秒に1ずつ増やしながら処理が進みます。つまり、変数count「経過時間」を意味しています。

でも、3色のLEDは「残り時間」に応じて制御する必要があります。

countの値がいくつになったら残り時間が3分の2か?というのはちょっとややこしそうですよね。

そこで、今回の記事では、タイマー時間が30秒のケースでスケッチを作成してみます。


最初は、もし残り時間が3分の2にだったら、緑色LEDを消して黄色LEDを点灯する」わけです。

残り時間が3分の2ということは、タイマーが30秒の場合、残り時間は「20秒」です。

経過時間に直すと「10秒」ですよね。

つまり、変数countが10になったら「緑色LEDを消して黄色LEDを点灯する」という処理をすればよいわけです。

つまり、残り時間が3分の2のときにLEDの点灯状態を変える部分は次のようになります。

// 残り時間が3分の2のとき、緑色をOFF、黄色をONにする
if( count == 10 ) {
  digitalWrite(LED_MIDORI, LOW);
  digitalWrite(LED_KIIRO,  HIGH);
}

同様に、残り時間が3分の1になったら黄色LEDを消して赤色LEDをつければよいので、次のようにスケッチを作成すればOKです。(残り時間が3分の1、ということはタイマー時間が30秒の場合、20秒経過した時点になりますので条件はcount == 20となります。)

// 残り時間が3分の1のとき、黄色をOFF、赤色をONにする
if( count == 20 ){
  digitalWrite(LED_KIIRO, LOW);
  digitalWrite(LED_AKA, HIGH);
}

これで残り時間を表現するLEDの制御が実現できます。

スケッチにまとめる

それではスケッチをまとめましょう。

ifの条件に数字を直接書いてしまうと、あとで修正する場所を探すのが面倒になります。

そこで、残り時間は#defineで定義しておきます。

定義文はタイマー時間設定の後に以下のように書いてみました。

// 残り時間を表現するLEDの制御時間
#define KIIRO_JIKAN 10  // 残り時間3分の2
#define AKA_JIKAN   20  // 残り時間3分の1

今までの内容をまとめると次のスケッチになります。

ずいぶん長くなってきましたね…

/*
  キッチンタイマー
  
  内容: スイッチ、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制御を追加
*/


// 秒を表現する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);
    }

    // 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 ) {
  }

}

【補足】式と評価

ifの説明の最後に「式と評価」について補足します。

本編とはあまり関係ありませんので、余力があればお読みいただければと思います。


ifの条件で「等しい」かどうかを判断するには==を使いました。

でも一般的な感覚では、「等しい」という意味の記号は=ですよね。

そこで、今回はifで等しい場合の条件式として==ではなく=を使った場合、どうなるのかを補足いたします。

例えば、変数 a が10に等しいかどうかを判断するifの条件部分は以下のようになります。

if( a == 10 )

これを、次のようにうっかり=で書いてしまった場合、どうなるのか?という問題です。

if( a = 10 )

Arduinoボードは、ifの条件式を評価して、結果として真になるか(0以外になるのか)、偽になるか(0になるのか)判定します。

上のスケッチのように=を使ってしまった場合、結局Arduinoボードは a = 10 をどのように評価するのかを確認すれば、評価結果がどうなるかわかります。

ということで、シリアルモニタにa = 10の結果を表示するスケッチで確認してみます。

void setup() {
  // シリアルモニタ初期化
  Serial.begin(9600);
  while(!Serial){
  }

  // a = 10の評価結果を表示
  uint8_t a = 0;
  Serial.println( a = 10 );
}

void loop() {

}

このスケッチを実行すると「a = 10」という式を評価した結果を表示してくれます。

シリアルモニタに表示された結果は次のようになりました。

10

?って感じですよね。

a = 10を評価すると、結果はaに代入された値になるんです。


Arduinoボードの処理の流れをまとめてみます。

Arduinoボードは次のifを処理するとき、最初に条件部分を評価します。

if( a = 10 )

a = 10を評価すると、評価結果はaに代入した値になりますので、Arduinoボードは次のように解釈します。

if( 10 )

つまり、if( a = 10)というスケッチは「常に真になる」ということになってしまうんです。これではaが10かどうかの判定はできませんね。


スケッチに慣れていてもうっかりやってしまうことがありますので、===の使い分けには注意しましょうね。

更新履歴

日付内容
2019.10.13新規投稿
2021.8.27新サイトデザイン対応
2022.2.16if文の例を追加
2022.2.21代入演算子の評価の説明を追加
2024.12.15説明内容一部変更
スケッチ一部変更
通知の設定
通知タイミング
guest
0 コメント
新しい準
古い順 一番投票が多い
本文中にフィードバック
全てのコメントを見る
目次