Groveで広がるArduinoの世界-Senser Kit-④ Grove Button
電子工作で使われる「ボタン」と呼ばれるONとOFFの状態しか存在しないセンサは、日本ではタクト・スイッチと呼ばれます。家電の操作パネルやリモコンなどでも使われています。
回路図です。
ON/OFFする接点は一つですが、リード線は4本あります。たぶん、機構的に安定な状態を長期にわたって保つためだと思われます。
狭いところに実装するタクト・スイッチには、リード線が2本の製品もあります。
短辺がON/OFFされる接点です。長辺はそれぞれショートしています。回路図で書くと、次のようになります。見た目では短辺と長辺の差がわからないことがあります。その際は、テスタで確認します。
●接続
回路図のように、スイッチの一方は10kの抵抗でプルダウンされています。したがって、D4(ディジタルのポート4)端子の状態は常にLOWです。
SW1が押されると、Vccへつながるので、HIGHになります。R1の10kを通して電流が流れます。オームの法則から、
電流I = 5[V] / 10[kΩ] = 5[mA]
もったいないと考えるのであれば、10kを47kに変更すれば1mAで済みます。
D4側には電流は流れないとみなします。本当は流れますが、D4端子の入力インピーダンスは、100MΩぐらいらしいので、ほとんど電流は流れません。流れませんが、もし、マイコンの設定を間違ってプルアップやプルダウンを行うと過剰な電流が流れることもあるので、直列に470Ωから1kΩの抵抗を入れている回路を見かけます。
ボードの裏側です。D4をテスタで測ると、Vcc側は約10kでプルアップ、GND側は約10kでプルダウンされています。Arduino UNO単独ではそのような抵抗はないので、ほかの回路の影響を受けているのかもしれません。
Groveピン番号 | スイッチのピン |
---|---|
4 GND | GND |
3 Vcc | A端子 |
2 NC | NC |
1 SIG | B端子 |
●プログラム - ポーリング
THE BUTTONの解説ページに掲載されているスケッチです。動きません。
#define button 6 // connect button to D4 int button_state = 0; // variable for reading the pushbutton status void setup() { Serial.begin(9600); // initialize the pushbutton pin as an input: pinMode(button, INPUT); } void loop(){ // read the state of the pushbutton value: button_state = digitalRead(button); if (button_state == HIGH) { // turn LED on: Serial.println("Button pressed!"); } delay(50); }
D4は4番端子ですから、
#define button 4
に修正して動かします。今度はシリアルモニタに、Button pressed!が表示されます。
setup()内で、
pinMode(button, INPUT);
buttonを入力に指定しました。4番ピンの電圧を測ると0Vでした。なのでLOWですね。ボタンを押すと4.25Vになりました。HIGHですね。
setup()の前に、
int button_state = 0;
button_stateというフラグを定義し、初期値に0を代入しています。論理的にはLOWです。この領域に変数を宣言すると、グローバル変数の扱いになります。どの関数からも読み書きできます。普通の入門書では、見通しが悪いので避けるように書かれていますが、メモリの少ないArduinoでは、デバッグするときに問題が起こることは少ないので、よく使われます。
loop()の最初で、
button_state = digitalRead(button);
ボタンがHIGHかLOWかの状態を読み取って、フラグbutton_stateに代入しました。
次のif文で、HIGHと等しいかを比較します。
if (button_state == HIGH)
ボタンが押されるとHIGHになるので、if文はtrueなので、{ }内を実行します。
delay(50);
0.05秒間待ってloop()の最初に戻ります。
このプログラムはボタンしか見ていません。ボタンは数msの事象です。ON->OFF、OFF->ONは数msで遷移するので、このプログラムは、ボタンが押されたことを見逃すことはありません。
しかし、ほかに何もできません。このようにボタンの状態をループを回して監視する方法をポーリングと呼びます。もし、別のセンサの値を読み行くような処理がこのループの中で行われると、ボタンの押された瞬間を見逃すかもしれませんし、そうでないかもしれません。不安です。
●プログラム - 割り込み
Arduino UNOでは、割り込みを、2番ピン(int.0)と3番ピン(int.1)で利用できます。
割り込みは、その名称のとおり、通常のプログラムが動いているとき、ある条件がそろったとき、その割り込み処理プログラムが実行され、処理が終われば、元のプログラムに戻ります。
そのために用意することが二つあります。
- 割り込み処理をするプログラム
- I/Oピンが変化したことを初期設定する
上位のマイコンではソフトウェア割り込みがたくさん用意されていますが、このマイコンは、ハードウェア割り込みだけです。
ボタン・ボードの裏側で、SIG線をカッターで傷をつけて切り離します。Groveケーブルで、このボードのGroveコネクタと中央のD2と書かれたGroveコネクタを接続します。これで、割り込みの使える2番をボタンにつなげました。
#define button 2 // connect button to D4 volatile int button_state = 0; // variable for reading the pushbutton status void setup() { Serial.begin(9600); Serial.println("start"); // initialize the pushbutton pin as an input: pinMode(button, INPUT); attachInterrupt(digitalPinToInterrupt(button), push, RISING); } void loop(){ Serial.print("."); // main if (button_state == HIGH) { // turn LED on: Serial.println("Button pressed!"); delay(100); button_state= LOW; } delay(10); } void push() { button_state = HIGH; }
attachInterrupt(digitalPinToInterrupt(button), push, RISING);
が、割り込みの初期設定です。最初の引数はディジタルの2番ピンを指定しています。二つ目の引数は、割り込みがかかったときに仕事をする関数名です。2番ピンはプルダウンされているので、通常LOWです。ボタンを押すとHIGHに変わるので、立ち上がり=RISINGを指定しています。
実行中の様子です。ボタンを押さないと '.' がどんどん書かれていきます。
ボタンを押した瞬間に、push()関数に移動し、
button_state = HIGH;
と、フラグをHIGHに設定して終了です。元のプログラムに戻ります。
loop()のなかでは、
Serial.print(".");
を実行した後、button_stateの状態を調べます。最初に、button_state = 0でLOWにしています。HIGHになるのは、ボタンが押されて割り込みが実行されたときだけです。
Serial.println("Button pressed!");
delay(100);
button_state= LOW;
フラグbutton_stateがHIGHだったら上記を実行します。button_state= LOWは、次の割り込みの準備です。
●プログラム - 割り込み 応用
前回までに表示した光、温度、湿度センサから読み出した値を表示しながら、ボタンの状態もリアルタイムに表示します。
#include "Arduino_SensorKit.h" #define DHTPIN 3 #define light_sensor A3 #define button 2 void setup() { Environment.begin(); Oled.begin(); Oled.setFlipMode(true); // Sets the rotation of the screen Oled.clear(); Oled.setFont(u8x8_font_chroma48medium8_r); Oled.setCursor(0, 3); Oled.print("light:"); Oled.setCursor(0, 4); Oled.print("Temp:"); Oled.setCursor(0, 5); Oled.print("Humi:"); // initialize the pushbutton pin as an input: pinMode(button, INPUT); attachInterrupt(digitalPinToInterrupt(button), push, RISING); } void loop(){ int raw_light; raw_light= analogRead(light_sensor); // read the raw value from light_sensor pin (A3) int light = map(raw_light, 0, 1023, 0, 100); // map the value from 0, 1023 to 0, 100 Oled.noInverse(); Oled.draw2x2String(9,0,"off "); Oled.setCursor(6, 3); Oled.print(" "); Oled.setCursor(6, 3); Oled.print(light); Oled.setCursor(6, 4); Oled.print(" "); Oled.setCursor(6, 4); Oled.print(Environment.readTemperature(),1); Oled.setCursor(6, 5); Oled.print(" "); Oled.setCursor(6, 5); Oled.print(Environment.readHumidity(),0); Oled.refreshDisplay(); // Update the Display delay(500); } void push() { Oled.inverse(); Oled.draw2x2String(9,0,"on "); delay(100); Oled.noInverse(); }