第10回 論理演算子

前回の記事では、スイッチが押されたことを検知するアルゴリズムを考えました。今回はそのアルゴリズムをもとに、実際にプログラミングをしていきます。

第10回から第12回までの内容は、少し難しくなります。第13回以降は別の内容になりますので、すべて理解できなくても大丈夫です。ご自身で理解できるところまでチャレンジしてみましょう!

目次

日本語でプログラムを考える

これからプログラムを作成していきますが、最初は日本語まじりのプログラムで検討します。

それでは前回考えたアルゴリズムを図でもう一度確認してみましょう。

スイッチ状態タイミング1

スイッチが押されたタイミングを検知するには、この図の赤い矢印のタイミングの

「前回のスイッチ状態がOFF」かつ「今回のスイッチ状態がON」

かどうかの確認すればよかったんですよね。ということは、loop関数ではまずは以下のように書くことができます。

loop(){

  if(「前回のスイッチ状態がOFF」かつ「今回のスイッチ状態がON」) {
    // スイッチが押されたタイミングが検知できたので、LEDを50ms点灯する
    digitalWrite(12, HIGH);
    delay(50);
    digitalWrite(12, LOW);
  }

}

3行目のようにif文の条件を書けば、上の図の赤い矢印のタイミングだけ条件が成立します。つまり、スイッチが押されたタイミングのみを検知してLEDをピカッと50ms光らせることができるはずです。

それでは次に、 if( 「前回のスイッチ状態がOFF」でかつ「今回のスイッチ状態がON」)の部分をどうすればよいか、考えていきまょう。

スイッチ状態の記録

先ほど作成したプログラムでちょっと気になるところがあります。それは「前回のスイッチ状態」です。

loop()関数の中で「今回のスイッチ状態」はすぐにわかりますよね。digitalRead(23)で現在のスイッチ状態を読み取ることができます。

でも「前回のスイッチ状態」とは、前回loop()関数を処理した時のスイッチ状態のことです。今回のloop()関数の処理では、すでに前回のloop()関数の処理は終わっていますので、「前回のスイッチ状態」は前回のloop()関数を処理したときに記録しておく必要があります。

そこで、前回のスイッチ状態を記録しておきたいのですが、どうしたらよいでしょうか。そのためにdigitalRead(23)で読み取った値を、基礎編パート1で説明した「変数」(メモ帳)を使ってスイッチ状態を記録することにします。

digitalRead(23)は、スイッチ状態がOFFのとき1、ONのとき0ですので、uint8_t型の変数に代入することにします(uint8_t型変数は0〜255までの数字を代入できます)。また、if文で前回と今回の値を扱いますので、両方とも以下のように変数宣言することにします。

記録する内容 変数名
前回のスイッチ状態 zenkai (uint8_t型)
今回のスイッチ状態 konkai (uint8_t型)

ということで、プログラムは以下のように書いてみました。

loop(){

  // 前回と今回のスイッチ状態を記録する変数
 uint8_t zenkai, konkai;

  if( zenkai == 1 かつ konkai == 0 ) {
    // スイッチが押されたタイミングが検知できたので、LEDを50ms点灯する
    digitalWrite(12, HIGH);
    delay(50);
    digitalWrite(12, LOW);
  }

}

4行目で変数の宣言をして、6行目のif文を日本語から変数名に書き換えました。ところで、if文の条件を変数に置き換えてみたものの、この変数に実際のスイッチ状態を代入する必要があります。これから今回のスイッチ状態「konkai」、前回のスイッチ状態「zenkai」の変数に何をどのように代入すればよいか検討しましょう。

今回のスイッチ状態

最初に今回のスイッチ状態の変数konkaiについて考えます。この変数には今回のスイッチ状態を代入すればよいわけですから、

konkai = digitalRead(23);

と書いて、単に現在のスイッチ状態を変数konkaiに代入すれば実現できます。if文で今回のスイッチ状態の比較を行いますので、この命令は以下のようにif文の直前、7行目に書くことにします。

loop(){

  // 前回と今回のスイッチ状態を記録する変数
 uint8_t zenkai, konkai;

  // 今回のスイッチ状態を調べて、変数「konkai」に代入する
  konkai = digitalRead(23);

  if( zenkai == 1 かつ konkai == 0 ) {
    // スイッチが押されたタイミングが検知できたので、LEDを50ms点灯する
    digitalWrite(12, HIGH);
    delay(50);
    digitalWrite(12, LOW);
  }

}

前回のスイッチ状態

では前回のスイッチ状態の変数zenkaiはどうしたらよいでしょうか。

もう一度、スイッチ状態の図を確認してみましょう。図は説明しやすいようにスイッチ状態を調べるタイミングを間引いています。

スイッチ状態タイミング2

この図で、(1)の点線枠の部分のタイミングで条件判断するケースを考えてみます。

このタイミングの条件判断を行う場合、先程説明したように「今回のスイッチ状態」はdigitalRead(23)で調べればOKです。図ではスイッチがONになっていますので、「今回のスイッチ状態」はONになります。

一方、(2)の点線枠の「今回の状態」というのは、(3)の点線枠にあるように、次回の条件判断を行う時の「前回の状態」になっています。

かなりややこしい話なので、じっくり図を確認してみてください。どのタイミングでも、今回の条件判断時の「今回のスイッチ状態」は、次回の条件判断時の「前回のスイッチ状態」になっていますよね。

ということは、今回のif文の判定が終わった後、次回のloop()関数を処理するときのために「今回のスイッチ状態を前回のスイッチ状態に保存しておく」ということになります。この命令を以下のように17行目に追加しました。

loop(){

  // 前回と今回のスイッチ状態を記録する変数
 uint8_t zenkai, konkai;

  // 今回のスイッチ状態を調べて、変数「konkai」に代入する
  konkai = digitalRead(23);

  if( zenkai == 1 かつ konkai == 0 ) {
    // スイッチが押されたタイミングが検知できたので、LEDを50ms点灯する
    digitalWrite(12, HIGH);
    delay(50);
    digitalWrite(12, LOW);
  }

  // 今回のスイッチ状態を、次回用に前回のスイッチ状態に保存しておく
  zenkai = konkai;

}

さて、、、

なんだかかなりややこしくなってきました。プログラミングに慣れていないとなかなか理解しづらいので、上のタイミングの図とプログラムを比較しながらじっくり確認してみてください。

論理演算子

さて、かなりプログラムができてきましたが、まだ一つ残っていますよね。それは、zenkai == 1 かつ konkai == 0の「かつ」の部分です。

電子工作に限らず、プログラミングでは複数の条件を判断することが多くあります。そこで、if文で複数の条件を組み合わせて判断する文法を習得しましょう。

if文で複数の条件を組み合わせる場合、以下のように「かつ」「または」「ではない」の3種類が用意されています。

意味 C/C++言語の
論理演算子
論理学の呼び方 書き方の例
かつ && 「論理積」
または「AND」
zenkai == 1 && konkai == 0
(意味は「zenkaiが1」かつ「konkaiが0」)
または || 「論理和」
または「OR」
zenkai == 1 || konkai == 0
(意味は「zenkaiが1」または「konkaiが0」)
ではない ! 「論理否定」
または「NOT」
!(zenkai == 1)
(意味は「zenkaiが1ではない」)

またいろいろと新しい言葉が出てきました… 少しずつ読み解いていきましょう。

今までの検討で「前回のスイッチ状態がOFF」かどうかなど、条件が成り立つかどうかという話をしてきました。このように条件が成立するかしないかなど、2つの状態を扱う学問を論理学といいます。

論理学では、例えば「猫が好きな人」と「犬が好きな人」などのように「好き」か「好きではない」といように2つの状態を考え、「猫が好き、かつ犬が好きな人」などのように「かつ」や「または」を使っていろいろなことを調べていくわけです。

算数で使う四則演算は、+(プラス記号)や-(マイナス記号)を使うわけですが、論理学では「かつ」や「または」を表現するための記号を使用します。論理学での「かつ」や「または」は、論理学の計算(演算)になりますので、これらの記号は「論理学で使う計算の記号」ということで「論理演算子」と呼ばれています。

C/C++言語では、「かつ」や「または」を表現するための論理演算子が用意されていて、先程の表の記号になります。「かつ」は「&」記号を2つ書いた記号になります。「または」は「|」記号を2つ書いた記号で、この文字はキーボードの右上の方にあり、おそらくほとんどの場合「Back Space」キーの隣にあると思います。「ではない」は「!」記号になります。

今回のプログラムではzenkai == 1 かつ konkai == 0という条件をプログラミングしたいので、「かつ」の論理演算子を使用して以下のように書けばOKです。

loop(){

  // 前回と今回のスイッチ状態を記録する変数
 uint8_t zenkai, konkai;

  // 今回のスイッチ状態を調べて、変数「konkai」に代入する
  konkai = digitalRead(23);

  if( zenkai == 1 && konkai == 0 ) {
    // スイッチが押されたタイミングが検知できたので、LEDを50ms点灯する
    digitalWrite(12, HIGH);
    delay(50);
    digitalWrite(12, LOW);
  }

  // 今回のスイッチ状態を、次回用に前回のスイッチ状態に保存しておく
  zenkai = konkai;

}

このプログラムの9行目、「かつ」の部分を「&&」に書き換えました。

これでプログラムが完成しました。

動作確認

今回作成したプログラムをまとめます。#defineを使用してなるべくわかりやすく書き直してみました。

/*
 *  スイッチ制御プログラム改良版
 *  内容: スイッチを押すとピカッとLEDを光らせる
 */

// 青色LEDのピン接続番号
#define LED_BLUE  12

// 左側スイッチのピン接続番号
#define SWITCH_HIDARI  23

// スイッチの状態
#define OFF 1
#define ON  0

// 点灯時間(単位:ms)
#define TENTOU_JIKAN 50

void setup() {
  // LEDのピンを出力に設定
  pinMode(LED_BLUE, OUTPUT);

  // スイッチのピンを入力に設定
  pinMode(SWITCH_HIDARI, INPUT_PULLUP);

  // 最初はLEDをOFFに設定しておく
  digitalWrite(LED_BLUE, LOW);
}


void loop() {

  // スイッチ状態を記録する変数
  uint8_t zenkai, konkai;
  
  // 今回のスイッチ状態を読み取る
  konkai = digitalRead(SWITCH_HIDARI);

  // スイッチ状態が、前回OFF、今回ONの時、スイッチが押されたと判断する
  if( (zenkai == OFF) && (konkai == ON) ) {
    // LEDを点灯
    digitalWrite(LED_BLUE, HIGH);
    // 一定時間待つ
    delay(TENTOU_JIKAN);
    // LEDを消す
    digitalWrite(LED_BLUE, LOW);
  }

  // 今回の値を前回の値として保存
  zenkai = konkai;
  
}

Arduino IDEで新規スケッチを作成し、上のプログラムをコピペしてArduino Microに送ります。Arduino Microが動き始めたら、スイッチを押してみると、、、なんだかうまく動きません。

ここまで一生懸命考えて作ったのに!キーッ!てなって落ち込みますが、プログラミングをしているとなかなかうまく動かないことがよくあります。

このようなときは「なんでうまく動かないんだろう…」と後ろ向きに考えるより、「新しいことにチャレンジする機会ができた!」と思って前向きに明るく行きましょう!

ということで、次回の記事ではうまく動かない原因を見つけて、新しいことを習得します。

ミニチャレンジ課題

論理演算子は電子工作のプログラミングでもよく使います。例えば室内の照明を自動制御するシステムを作るときに「外の明るさが〇ルクス以下、かつ人感センサで人がいることを検知した場合に照明をつける」など、「かつ」や「または」という条件はよく使います。

そこで、論理演算子の使い方に慣れるため、ミニチャレンジ課題に挑戦してみましょう。

ミニチャレンジ課題1

外の明るさが500ルクス以下、かつ人感センサが人を検知した場合に条件が成立するようにif文を作成してください。この条件が成立したときに室内の照明を点灯するようにリモコンを操作しようと思います。
外の明るさはuint16_t akarusa;で変数宣言してあります。この変数には明るさセンサで測定した明るさの数値がルクスで代入されています。
人感センサの状態はuint8_t jinkan;で変数宣言してあります。この変数には、人を検知した場合に1、人を検知していない場合に0が代入されます。

ミニチャレンジ課題2

外の気温を測定して、気温が23度〜27度以外のときに条件が成立するようにif文を作成してください。外気温が23度〜27度以外の場合、寒いか暑いかのどちらかなので、窓を自動的に閉めるシステムを作ろうと思っています。
外気温はint8_t kion;で変数宣言してあり、現在の気温はこの変数に代入されます。

実際の動作を確認できないため、今回は解答例を以下に示します。解答を見る前にじっくり考えてみてください。

ミニチャレンジ課題1解答例

以下のような書き方が一般的になると思います。

if( akarusa <= 500 && jinkan == 1 )

また人感センサの変数は人を検知した場合に1が代入されています。1は真を意味しますので、以下のように書くこともできます。

if( akarusa <= 500 && jinkan )

なお、「<=」と「&&」の関係がわかりづらい場合は、以下のようにカッコで囲んでもOKです。ただ、余計な記述を嫌う人もいますが、ご自身がわかりやすい書き方が一番です。

if( (akarusa <= 500) && (jinkan == 1) )
ミニチャレンジ課題2解答例

条件は「『23度以上、かつ27度以下』ではない」と意味になります。「〜ではない」は「!」です。「23度以上、かつ27度以下」という条件を丸ごと「!」で「ではない」にします。言葉で説明してもよくわかりませんよね。実際のif文を確認しましょう。

if( !(kion >= 23 && kion <= 27) )

また、条件を「23度未満、または27度より高い」と考えることもできます。この場合は以下のような書き方が一般的になります。

if( kion < 23 || kion > 27 )

複数の条件の「かつ」「または」の書き方はかなりややこしいですが、電子工作でいろいろなスイッチやセンサを組み合わせて特定の条件で何かを制御する、ということは多いので、ぜひ論理演算子に慣れてみてください。

更新履歴

日付 内容
2021.8.29 新規投稿
通知の設定
通知タイミング
guest
0 コメント
本文中にフィードバック
全てのコメントを見る
目次
閉じる