第10回 論理演算子

前回の記事では、スイッチが押されたことを検知するアルゴリズムを考えました。これから、そのアルゴリズムをもとにスケッチを作成していきます。

第10回から第12回までの内容は、少し難しくなっています。
すべて理解できなくても、第13回以降の記事がわからなくなる、ということはありません。理解できるところまでチャレンジしてみましょう!
もし難しく感じたら、とりあえず第12回の記事まではスキップして、第13回の記事からお読みいただければと思います。

目次

日本語でスケッチを考える

これからスケッチを作成していきますが、最初は日本語まじりのスケッチで検討します。

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

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

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

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

の確認すればよかったんですよね。

ということは、loop関数ではまずは次のように書くことができます。

void loop() {

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

  }

ポイントとしては、3行目のようにifの条件を書ければ、最初の図の赤い矢印のタイミングだけ条件が成立します。

つまり、スイッチが押されたことを検知してLEDをピカッと50ms、1回だけ光らせることができるはずです。

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

スイッチ状態の記録

先ほど作成したプログラムでちょっと気になるところがあります。

それは「前回のスイッチ状態」です。

loop関数の中で「今回のスイッチ状態」はすぐにわかりますよね。digitalRead(A5)で現在のスイッチ状態を読み取ればOKです。

でも「前回のスイッチ状態」とは、前回loop関数を処理した時のスイッチ状態のことです。今回のloop関数の処理では、すでに前回のloop()関数の処理は終わっています。

そこで、「前回のスイッチ状態」は前回のloop関数を処理したときに記録しておく必要があります。


前回のスイッチ状態を記録しておきたいのですが、どうしたらよいか、詳しく検討してみましょう。

digitalRead(A5)で読み取ったスイッチ状態は、1(OFF)か0(ON)のどちらかです。

この10を記録、つまり数値をメモしておく機能って、今まで何度も出てきましたよね。

そうです!「変数」(メモ帳)を使ってdigitalRead(A5)で読み取った値を記録すればよさそうです。

digitalRead(A5)の戻り値は01ですので、uint8_t型(0〜255)の変数に代入することにします。

また、ifで前回と今回の値を扱いますので、両方とも以下のように変数宣言することにします。

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

ということで、スケッチは以下のように書いてみました。

void 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(A5);

と書いて、単に現在のスイッチ状態を変数konkaiに代入すれば実現できます。

この変数はifの条件部分で使いますので、ifより前に変数に代入しておくことが必要です。

void loop() {

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

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

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

}

これで今回のスイッチ状態の処理はできました!

前回のスイッチ状態

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

もう一度、スイッチ状態の図を確認してみましょう。

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

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

このタイミングの条件判断を行う場合、先程説明したように「今回のスイッチ状態」digitalRead(A5)で調べればOKです。(さらに調べた結果はkonkai変数に代入しています)

上の図では「今回の状態」はONになります。

次に、❷の赤色の点線枠の「今回の状態」というのは、❸のオレンジ色の点線枠にあるように、次回の条件判断を行う時の「前回の状態」になっています。

かなりややこしい話になってきましたね…泣

ここで、他のどのタイミングを見ても、今回の条件判断時の「今回の状態」は、次回の条件判断時の「前回の状態」になっていますよね。


ということはですよ、今回のifの判定が終わった後、次回loop関数を処理するときのために「今回の状態」を「前回の状態に保存しておく」ことが必要になります。

この処理は次のように17行目に追加しました。

void loop() {

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

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

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

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

}

さて、、、

かなりややこしくなってきました。

考えているとややこしくて「キーッ」ってなりそうですが、スケッチ完成までもう少しです!

もう一踏ん張りして先に進めましょう!

論理演算子

さて、スケッチはかなりの部分が出来上がりましたが、まだ一つ残っていますよね。

それは、zenkai == 1 かつ konkai == 0「かつ」の部分です。

電子工作では複数の条件を判断することが多くあります。例えば「気温が25度以上かつ湿度が75%以上のときは熱中症警戒と判定する」などです。

そこで、ifの条件部分で複数の条件を組み合わせて判断する方法を習得しましょう。


複数の条件を組み合わせる場合、次のように「かつ」「または」「ではない」の条件を組み合わせるための書き方が用意されています。

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

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


日本語の「かつ」「または」「ではない」は、C++言語では&|!の記号を使って上のように表記します。

&ではなく&&としているのは、&という別の意味を持つ記号があるためです。それと区別するために、条件を組み合わせるときの記号は&&というように&を2個続けて書くようになっています。(|も同様です)

また、プログラミングの世界では、例えば「かつ」「論理積」または「AND」と呼んでいます。

このような条件を組み合わせる記号のことを「論理演算子」と呼んでいます。

なんだか難しい呼び方でいろいろ出てきましたが、頭の中では「スケッチで条件の組み合わせ書く場合、『かつ』は&&などと覚えておけば十分です。


今回のスケッチではzenkai == 1 かつ konkai == 0という条件を書きたいので、「かつ」の記号&&を使用して次のように書けばOKです。(9行目)

void loop() {

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

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

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

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

}

これでスケッチが完成しました!

動作確認

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

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

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

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

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

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

void setup() {
  pinMode(LED_BLUE, OUTPUT);             // LEDのピンを出力に設定
  pinMode(SWITCH_HIDARI, INPUT_PULLUP);  // スイッチのピンを入力(プルアップ)に設定

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


void loop() {

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

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

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

実際にスケッチを動作させてみると、、、なんだかうまく動きません!!

ここまで一生懸命考えて作ったのに!!キーッ!!ってなって、落ち込んでしまいますね…

スケッチを作ってもうまく動かないことはよくあります。

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

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

ミニチャレンジ課題

課題

条件の組み合わせは電子工作のプログラミングでもよく使います。

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

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

ミニチャレンジ課題1

「屋外の明るさが500ルクス以下、かつ人感センサが人を検知したときに条件が成立する」ようにifの条件を作成してみてください。この条件が成立したときに室内の照明を点灯するようにリモコンを操作しようと思います。

屋外の明るさはuint16_t akarusa;で変数宣言してあります。この変数には明るさセンサで測定した明るさの数値がルクスで代入されています。
人感センサの状態はuint8_t jinkan;で変数宣言してあります。この変数には、人を検知した場合に1、人を検知していない場合に0が代入されます。

ミニチャレンジ課題2

屋外の気温を測定して、「気温が23度〜27度以外のときに条件が成立する」ようにifをの条件を作成してみてください。外気温が23度〜27度以外の場合、寒いか暑いかのどちらかなので、この条件が成立するときは窓を自動的に閉めるシステムを作ろうと思います。

外気温はint8_t kion;で変数宣言してあり、現在の気温はこの変数に代入されます。

解答例

いくつか書き方がありますので、次の例を参考にしてみてください。

ミニチャレンジ課題1

変数akarusaの値が500以下、かつ変数jinkanの値が1であれば条件整理です。

「変数akarusaの値が500以下」はスケッチでは次のように書けます。

akarusa <= 500

「変数jinkanの値が1」は次のように書けます。

jinkan == 1

これらの条件を「かつ」の記号で組み合わせれば良いので、ifは次のように書けばOKです。

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

ただ、このように書くと条件の区切りがわかりづらいので、次のように条件を括弧で囲むとわかりやすくなるかな、と思います。

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

ところで、数値の1は「真」(成立)を意味しますので、次のように簡略化して書くこともできます。

if( akarusa <= 500 && jinkan )

ただ、これはちょっと読みづらい気もしますね。

ミニチャレンジ課題2

条件の『23度〜27度』ではない」というのは、「かつ」や「または」を使って書き直すと、『23度以上、かつ27度以下』ではない」と意味になります。

また、「〜ではない」!です。

「23度以上、かつ27度以下」という条件を!を使って「ではない」にします。

この考え方で次のように作成してみました。

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

また、条件を「23度未満、または27度より高い」と考えることもできます。

この場合は以下のような書き方ができます。

if( kion <23 || kion > 27 )

複数の条件の「かつ」「または」の書き方はかなりややこしいですよね。

でも、電子工作でセンサを組み合わせて特定の条件で何かを制御する、ということは多いので、ぜひ今回の記事内容や課題を通して慣れてみてください。

更新履歴

日付内容
2021.8.29新規投稿
2025.2.7説明内容一部削除(簡略化のため)
通知の設定
通知タイミング
guest
0 コメント
新しい準
古い順 一番投票が多い
本文中にフィードバック
全てのコメントを見る
目次