はじめに
前回は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 == HIGH
でvalOld == 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の基礎を学ぶことができました。小さな組み合わせを複雑に行うことで、大きなプロジェクトを作ることができるので、こういった基礎を覚えるのは大切ですね。