Arduinoでボタン入力

Arduinoでボタン入力

はじめに

前回はArduinoでLEDを光らせる、というアウトプットを行いました。
今回は逆にスイッチを使ってArduinoを制御する、というのに挑戦してみましょう。

スイッチの実装

用意するもの

Arduino本体 (今回はArduino Uno互換機)
Akizuki : http://akizukidenshi.com/catalog/g/gM-07385/

LED(今回は青色LED)
Akizuki : http://akizukidenshi.com/catalog/g/gI-01321/

タクタイルスイッチ
Akizuki : http://akizukidenshi.com/catalog/g/gP-03647/

抵抗(220Ω)
Akizuki : http://akizukidenshi.com/catalog/g/gR-25221/

抵抗(10kΩ)
Akizuki : http://akizukidenshi.com/catalog/g/gR-25103/

ブレッドボードとケーブル
Akizuki : http://akizukidenshi.com/catalog/g/gP-00315/
Akizuki : http://akizukidenshi.com/catalog/g/gC-05371/

Arduinoボード

今回は上のように接続します。
左側半分は前回のLチカと同様のものです。右半分が今回追加されたスイッチです。抵抗は10kΩ。

この10kΩの抵抗はプルダウン抵抗と呼ばれとても大切な抵抗です。この抵抗を入れないと適切に動かなくなるので、覚えておいてください。
プルダウン抵抗に関しては別記事を書きますのでそちらを参照してください。

プログラム側で説明しますが、7番ピンをINPUT(入力用)のピンにした場合、基本的にはそのピンに電流が流れないような電圧に調整されArduinoがその電圧を読み取ります。
今回はスイッチを押していない状態では、7番ピンはGND(0V)ピンにつながっているのでLOW(0V)値を取ります。
逆にスイッチを押した場合は7番ピンは5Vに繋がるのでHIGH値(5V)を取ります(スイッチを押している間5V – GND間は電流が流れ続けますね)。

このようにスイッチでI/Oピンを5VとGNDに切り替えるようにすることで、I/Oピンを操作することができるようになるというわけですね。

Arduino IDE

はじめに

全体の流れは「スイッチを押すとLEDがついたり消えたりする」です。

ここで、スイッチの方式には2種類あり、「押している開いただけON / 離すとOFF」というようなゲートスイッチと「押すたびに ON / OFFが入れ替わる」というようなトグルスイッチがあります。

ゲートスイッチ、トグルスイッチどちらも重要なスイッチングなので今回はそれぞれについてコーディングしていきましょう。

例によって必要に応じて、公式のリファレンスガイド ( https://www.arduino.cc/reference/en/ )や、日本語のリファレンスガイド ( http://www.musashinodenpa.com/arduino/ref/ )などを利用するのが良いかと思います。

コードの全体 (Gate その1)

//Gate Switch

int const LED = 13;
int button = 7;

void setup() {
  pinMode(LED, OUTPUT);
  pinMode(button, INPUT);
}

void loop() {
  if(digitalRead(button) == HIGH){
    digitalWrite(LED,HIGH);
  }else{
    digitalWrite(LED,LOW);
  }
}

細々とした内容は前回のLチカとほぼ同様です。
変更点だけを見ていきましょう。

変数の宣言

int const LED = 13;
int button = 7;

今回はタクタイルスイッチを7番ピンに繋げるので、7番にbuttonという変数を割り当てました。定数ではないのでconstはつけません。

setup()

void setup() {
  pinMode(LED, OUTPUT);
  pinMode(button, INPUT);
}

pinModeにpinMode(button, INPUT);を追加します。
OUTPUTはArduino側からLEDなどを操作するのに対して、INPUTはスイッチなどからArduinoを操作するのに使います。

loop()

void loop() {
  if(digitalRead(button) == HIGH){
    digitalWrite(LED,HIGH);
  }else{
    digitalWrite(LED,LOW);
  }
}

loop()関数内にはじめに出てくるif()関数ですが、これは引数の中の値が真(正しい)ならば、if文内の処理を行い、偽(間違っている)ならif文の処理を行わない、というものです。さらにelse文を加えると、if文が処理されない場合のみ(つまり偽の場合のみ)else内の処理が行われます。

つまり、digitalRead(button) == HIGH が正しいときにはdigitalWrite(LED,HIGH);が処理され、正しくないときにはdigitalWrite(LED,LOW);が処理されるというわけですね。

ついでながら if の日本語訳は「もし〜なら」、elseは「そうでなければ」という意味ですね。

digitalRead(button) == HIGH

ですが、digitalRead()digitalWrite()の逆で、いまそのI/Oピンに電圧がかかっているかどうか(5Vか0Vか)を読み取る命令です。この命令はINPUTピンに対して有効です。

== HIGHは HIGHに等しい(5Vがかかっている)という意味。
===は全く違うものなので注意してください。

これでボタンを押している間だけLEDが光るというゲートスイッチの実装ができました。

コードの修正 (Gate その2)

ここで、loop()内のコードを少しいじってみましょう。

//Gate Switch

int const LED = 13;
int button = 7;
int val; 

void setup() {
  pinMode(LED, OUTPUT);
  pinMode(button, INPUT);
}

void loop() {

  val = digitalRead(button);
  
  if(val == HIGH){
    digitalWrite(LED,HIGH);
  }else{
    digitalWrite(LED,LOW);
  }
}

int val という新しい変数を用意し、val = digitalRead(button);valに値を格納するようにします。
特に実際の動作に影響はありませんが、少しだけわかりやすくなったかと思います。

コードの全体 (Toggle その1)

//Gate Switch

int const LED = 13;
int button = 7;

int val;
boolean state;

void setup() {
  pinMode(LED, OUTPUT);
  pinMode(button, INPUT);
}

void loop() {

  val = digitalRead(button);

  if(val == HIGH){
    state = !state;
  }
  
  if(state == HIGH){
    digitalWrite(LED,HIGH);
  }else{
    digitalWrite(LED,LOW);
  }
}

基本はゲートスイッチから持ってきていますが、stateという変数が増えていると思います。トグルスイッチはスイッチが押されているかどうかに加えて、今のスイッチの状態(ONかOFFか)も記録しないといけません。それを格納する変数がstateです。

  if(val == HIGH){
    state = !state;
  }

そのため、スイッチが押されるたびにこのstateの情報を入れ替えることでトグルの動作を行なっているわけですね。
ここで、!は否定「ではない」をしめす論理演算子で 0と1しかとらない(boolean型)のstateの状態を入れ替えて代入する、という意味ですね。

  if(state == HIGH){
    digitalWrite(LED,HIGH);
  }else{
    digitalWrite(LED,LOW);
  }
}

後半のif文で、トグルがONの時にLEDは点いて、トグルがOFFのときにはLEDが消える、というわけです。

ここで、これで良いと思って実際にコンパイルと書き込みを行なっても、きちんと思ったような動作が起きないです。
なぜかというと、loop()が繰り返し実行されるからで、Arduinoの実行速度(クロック数)が人の動きよりもずっと早く、スイッチを押している間もこのloop()関数がひたすら周り続けているためです。
そのためボタンを押している間ひたすらにstateは1と0を切り替えてしまう、というわけですね。
そこで、ボタンを押したら必ず1回だけstateの状態を入れ替える、というようにしないといけません。

コードの修正 (Toggle その2)

//Gate Switch

int const LED = 13;
int button = 7;

int val;
int valOld;
boolean state;

void setup() {
  pinMode(LED, OUTPUT);
  pinMode(button, INPUT);
}

void loop() {

  val = digitalRead(button);

  if((val == HIGH)&&(valOld == LOW)){
    state = !state;
  }

  valOld = val;
  
  if(state == HIGH){
    digitalWrite(LED,HIGH);
  }else{
    digitalWrite(LED,LOW);
  }
}

ここで、新しい変数valOldが出てきました。
ボタンを押した一周目は val == HIGHvalOld == LOW なので (val == HIGH)&&(valOld == LOW)は真になります。
if文の処理ののちにvalOld = val となるので、ボタンを押した2周目以降はvalOld == HIGH なのでif文は実行されなくなります。

このようにすることでif文は意図されたタイミングで必ず1回だけおきるようになりますね。

これで完璧かと思いきやまだ思うように動いていないかと思います。
これはボタンのチャタリングとよばれる現象が原因です。チャタリングに関しては調べればいっぱい出てくるので、一度調べて見てください。こちらもそのうち記事にまとめようかと思います。

チャタリング対策はmsレベルのdelay()を加えることで解決します。

/Gate Switch

int const LED = 13;
int button = 7;

int val;
int valOld;
boolean state;

void setup() {
  pinMode(LED, OUTPUT);
  pinMode(button, INPUT);
}

void loop() {

  val = digitalRead(button);

  if((val == HIGH)&&(valOld == LOW)){
    state = !state;
  }

  valOld = val;
  
  if(state == HIGH){
    digitalWrite(LED,HIGH);
  }else{
    digitalWrite(LED,LOW);
  }

 delay(10);
}

ただし、delay()関数は極力使わない方が良いということは覚えておいてください。
そのため、スイッチのチャタリング対策はハード面・ソフト面において様々な方法で行われています。

まとめ

前回のLチカでOUTPUTの基礎、今回のボタン処理でINPUTの基礎を学ぶことができました。小さな組み合わせを複雑に行うことで、大きなプロジェクトを作ることができるので、こういった基礎を覚えるのは大切ですね。

Arduinoカテゴリの最新記事