第33回 if文

タイトル画像

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

目次

残り時間LEDの制御

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

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

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

if文

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

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

このような制御をするには「if文」という構文を使用します。

if文

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

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

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

例えば、変数countが10の時に何か処理をしたい場合は以下のように書きます。

uint8_t  count;

if( count == 10 )
 何らかの処理;

また、countが3未満の時に何らかの処理をする場合は以下のようになります。

uint8_t  count;

if( count < 3 )
 何らかの処理;

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

また、条件が成立した時に複数の処理をする場合は、以下のように中括弧で囲みます。中括弧は命令をまとめるときに使うんでしたよね。

uint8_t  count;

if( count == 10 ) {
 何らかの処理1;
 何らかの処理2;
}

if文の説明はこのぐらいなのですが、if文の動作についてもう少し詳しくみていきましょう。

if文の「条件が成立したら」という動作は、Arduino内部ではwhile文、for文の条件式と同様、「条件式を評価した結果、真であれば」という動作をしています。

先ほどの if( count == 10 ) は、Arduinoは最初に条件式を評価します。具体的にはcount == 10を評価します。この時、変数countの値が10であれば、この式を評価した結果は真ですので、Arduinoはcount == 10を「1」に置き換えます。置き換えた結果、「 if( 1 ) 」になりif文に書かれている処理を実行します。

この「条件式を評価する」ことや「真」などの意味を忘れてしまった場合、もう一度while文やfor文の説明を読み返して十分理解するようにしてください。というのは、if文はプログラマによっては条件式に見えないような書き方をしますので、今後ネットで他の人が書いたスケッチを読めるようにしておきましょう。

例えば以下のような書き方をする人もいます。

uint8_t  jyoutai;

if( jyoutai ) {
 何らかの処理;
}

このようにif文の条件式に変数だけ書く人もいます。この場合、変数jyoutaiが0であれば偽ですので「何らかの処理」は実行されません。jyoutaiが0以外であれば真ですので「何らかの処理」は実行されます。

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

uint8_t  jyoutai;

if( jyoutai > 0 ) {
 何らかの処理;
}

以上のことから、if文は以下のようにも説明できます。

if文補足

ちょっと難しいですが、このような考え方をしっかり身に付けるようにしてください。他のプログラミング言語でも同じような考え方、書き方が出てきますので、C/C++言語でしっかり習得すれば他のプログラミング言語の習得が容易になります。

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

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

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

最初に追加した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を点灯してスイッチが押されるまで待ちますが、スイッチが押されたら(タイマーがスタートしたら)緑色のLEDをつけるスケッチを追加します。

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

これでスタートスイッチを押すと緑色LEDが点灯し、青色LEDが点滅を開始してタイマーが動き始めます。あとは、タイマーをカウントしている部分で、残り時間に応じてLEDを制御すればよいわけです。

タイマーをカウントしてる部分は以下のfor文です。そこで、このfor文の中で残り時間に応じた処理をすることになります。

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

  // 1秒に1回青色LEDを点滅する
  digitalWrite(BYOU_LED, HIGH);
  delay(BYOU_ON);
  digitalWrite(BYOU_LED, LOW);
  delay(BYOU_OFF);
  
}

このfor文では、タイマーが開始すると、変数countが0から始めて1秒に1ずつ増やしながら処理が進みます。ということは、このcountの値を見ながらif文でLEDを制御すればよさそうです。

例えばタイマー時間が30秒のケースを考えましょう。スケッチでタイマー時間を設定している#defineを30にします。

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

残り時間が3分の2になったら、緑色LEDを消して黄色LEDを点灯するわけですが、これはcountが10になったらこの処理をすればよいですよね。countが10ということは、10秒経過、つまり残り時間が30秒の3分の2になったということです。

ということで、if文の条件式は

if( count == 10 )

と書けばよいことがわかります。あとはこの条件が成立したら緑色LEDを消して黄色LEDをつければよいので、この処理は

if( count == 10 ) {
  digitalWrite(LED_MIDORI, LOW);
  digitalWrite(LED_KIIRO,  HIGH);
}

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

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

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

補足 : 再度「式と評価」

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

if文の式で「等しい」かどうかを判断するには「==」を使いました。でも普段の感覚からすると、「等しい」という意味の記号は「=」ですよね。そこで、今回はif文で等しい場合の条件式として「==」ではなく「=」を使った場合、どうなるのかを補足いたします。

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

if( a == 10 )

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

if( a = 10 )

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

上のプログラムのように「=」を使ってしまった場合、結局Arduinoは a = 10 をどのように評価するのかを確認すれば結果がどうなるかわかります。

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

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

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

void loop() {

}

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

10

このように「=」の代入式は、左辺に代入された値が評価した結果になります。

ということで、最初の問題のif文をもう一度みてみましょう。

if( a = 10 )

「a = 10」を評価した結果は、「10」になります。つまり式を評価した結果は変数aの値によらず必ず10になります。if文は、式を評価した結果が0以外であれば真と判定しますので、このif文は「必ず真」になります。

実は、私もたまにうっかりif文の条件式を「=」で書いてしまうことがあります。その場合は、処理が変わるはずなのにどんなに条件を変えてもスケッチの動作が変わらないので、半日無駄にしたりします。

ということで、「==」と「=」の使い分けには注意しましょう。

スケッチをまとめる

それではスケッチをまとめましょう。if文の条件式に数字を直接書くと後で修正する場所を探すのが面倒になりますので、#defineで定義しておきます。定義文はタイマー時間設定の後に以下のように書くことにします。

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

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

あとは今までの内容をまとめればスケッチ完成です。

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

// 秒を表現する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 10
#define AKA_JIKAN   20

// アラーム音関連
#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);
    }

    // 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.13 新規投稿
2021.8.27 新サイトデザイン対応
2022.2.16 if文の例を追加
2022.2.21 代入演算子の評価の説明を追加

通知の設定
通知タイミング
guest
0 コメント
本文中にフィードバック
全てのコメントを見る
目次