Arduinoでタクトスイッチを読む|プルアップ抵抗とINPUT_PULLUPのしくみ、チャタリング対策まで
チャタリングの原因と対策
接点は押した瞬間に跳ねる
タクトスイッチの中では、金属の接点どうしが触れたり離れたりしています。押した瞬間や離した瞬間、接点はばねのように細かく跳ね返り、ごく短いあいだに「つながる・離れる」を何度か繰り返します。これをチャタリング(バウンス)と呼びます。続く時間は数ミリ秒程度のことが多く、人の目や指では気づけませんが、高速に回る loop には何回もの押下として見えてしまいます。
ソフトウェアで待つ
対策のいちばん手軽な方法は、スケッチの中で「状態が変わってから、しばらく安定するのを待つ」ことです。値が変わったらその時刻を記録し、一定の時間(数十ミリ秒が目安)同じ値が続いたときに初めて「状態が変わった」と認めます。時刻の取得には、ボードが動き始めてからの経過ミリ秒を返す millis() を使います。
const int buttonPin = 2;
const unsigned long debounceMs = 30;
int stableState = HIGH; // 確定した状態
int lastReading = HIGH; // 前回読んだ値
unsigned long changedAt = 0;
bool ledOn = false;
void setup() {
pinMode(buttonPin, INPUT_PULLUP);
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
int reading = digitalRead(buttonPin);
if (reading != lastReading) {
changedAt = millis(); // 値が揺れたら時計を戻す
lastReading = reading;
}
if (millis() - changedAt > debounceMs && reading != stableState) {
stableState = reading;
if (stableState == LOW) { // 押されたと確定
ledOn = !ledOn;
digitalWrite(LED_BUILTIN, ledOn ? HIGH : LOW);
}
}
}
delay(50) を入れるだけでも症状は軽くなりますが、待っているあいだは他の処理が止まってしまいます。LEDの点滅と同時にボタンを読みたい場合などを考えると、millis() で時間を比べる方法に慣れておくと後で困りません。
部品で抑える方法もある
ボタンとGNDのあいだにコンデンサーを入れ、抵抗と組み合わせて電圧の変化をなだらかにする方法もあります。さらに、入力の揺れに強い「シュミットトリガ」という働きを持つ部品を組み合わせると、ハードウェアだけでチャタリングを抑えられます。部品が増えるため、入門のうちはソフトウェアでの対策で十分です。
| 方法 | 手軽さ | 特徴 |
|---|---|---|
| delayで待つ | とても手軽 | 待つあいだ他の処理が止まる |
| millisで比べる | 少し覚えることがある | 他の処理と両立しやすい |
| 抵抗とコンデンサー | 部品が増える | スケッチを変えずに揺れを減らせる |
長押しと複数のボタン
押している時間で動きを変える
1つのボタンで2つの役目を持たせたいときは、押している時間で区別する方法がよく使われます。押された瞬間の millis() を記録しておき、離された瞬間との差を計算すれば、押していた時間が分かります。例えば1秒未満なら「短押し」、1秒以上なら「長押し」として、別の処理に振り分けます。
長押しの判定は、離した瞬間に行う方法と、押し続けて一定時間が過ぎた時点で行う方法があります。離した瞬間に判定する方法は分かりやすい一方、長押しが成立したことに使う人が気づきにくいという欠点があります。一定時間が過ぎた時点でLEDを点けるなどの合図を出すと、使い勝手がよくなります。
ボタンを増やすときの考え方
ボタンを2つ、3つと増やすときは、それぞれ別のデジタル端子につなぎ、すべて INPUT_PULLUP にします。GND側はまとめて1本につないでかまいません。スケッチでは、前回の状態や時刻をボタンごとに持つ必要があるので、配列にまとめると見通しがよくなります。
端子の数が足りなくなるほどボタンを増やしたい場合は、ボタンを格子状に並べて少ない端子で読む「マトリクス」という方法があります。自作キーボードはこの仕組みでたくさんのキーを読んでいます。

よくある失敗と確かめ方
押していないのにずっと反応する
スイッチの向きを間違え、常につながっている2本の足を使っていると、押していなくても押した状態として読まれます。押しても押さなくても値が変わらないときは、まずスイッチを90度回して挿し直すか、対角の足を使っているか確かめます。
値が勝手に揺れる
pinMode を INPUT のままにしていて、プルアップもプルダウンもない状態になっていることが考えられます。外付けの抵抗を使う場合は、抵抗の片側が本当に5V(3.3V系なら3.3V)につながっているか、ブレッドボードの電源の列が途中で分かれていないかを確かめます。長いブレッドボードでは、電源の列が中央で切れている製品もあります。
押すと逆の動きになる
プルアップでは押したときがLOWです。プルダウンの作例のスケッチをそのまま使うと、動きが逆になります。どちらの回路を組んだかと、スケッチの条件が合っているかを照らし合わせます。
外部の電源や大きな負荷へ進むとき
ボタンで動かす対象を、モーターや多数のLEDなど電流の大きいものへ広げるときは、Arduinoの端子から直接電流を取らず、トランジスタやリレーのモジュールなど専用の回路を使います。家庭用コンセントにつながる機器を制御する配線は、この記事の範囲を超えます。必要な場合は、知識と資格を持つ専門家に任せてください。
まとめ
タクトスイッチは、4本の足のうち2本ずつが内部でつながり、押したときだけ2組がつながる部品です。入力端子は何もつながないと値が浮くため、抵抗で「押していないときの値」を決めておく必要があり、Arduinoでは INPUT_PULLUP を使えば抵抗なしでその回路を作れます。そのとき押した状態はLOWになること、1回の押下を正しく数えるには前回の状態との比較とチャタリング対策が必要なことを押さえておけば、ボタンを使った作品の多くに応用できます。
最初の一歩として、タクトスイッチを1つだけ用意し、対角の足をデジタル2番とGNDにつないで、INPUT_PULLUP のスケッチでシリアルモニタに0と1が切り替わるのを確かめてください。それができたら、押すたびにLEDが切り替わるスケッチに millis() のチャタリング対策を加え、1回押すごとに確実に1回だけ切り替わるところまで進めてみましょう。
よくある質問
タクトスイッチの4本の足はどれを使えばよいですか?
4本のうち2本ずつが内部で常につながっていて、押すとその2組がつながります。迷ったら対角の位置にある2本を使うと、どちらの向きに置いても押したときだけつながる配線になります。テスターの導通測定で確かめると確実です。
INPUT_PULLUPにすると、押したときの値が逆になるのはなぜですか?
内部の抵抗で入力端子を電源側へ引き上げておき、ボタンを押すとGNDへつながるためです。押していないときがHIGH、押したときがLOWになるので、スケッチでは「LOWなら押された」と読み替えます。
プルアップ抵抗の値は何Ωにすればよいですか?
外付けにする場合、電子工作の入門では10kΩ前後がよく使われます。小さすぎると押したときに流れる電流が増え、大きすぎると雑音の影響を受けやすくなります。内部プルアップを使えば抵抗を用意する必要はありません。
1回押しただけなのに何回も押されたと判定されます。
接点が押した瞬間に細かく跳ねる「チャタリング」が原因のことが多いです。状態が変わってから数十ミリ秒ほど安定するのを待ってから押されたと判断する処理を入れると落ち着きます。
3.3Vのボードでも同じ配線で使えますか?
考え方は同じですが、外付けのプルアップ抵抗は5Vではなく、そのボードの入出力電圧である3.3V側につなぎます。入力端子に扱える電圧以上をかけるとボードを傷めることがあるので、公式資料で電圧を確かめてください。










コメント
まだコメントはありません。最初の感想をどうぞ。