PUBLIC
PRACTICEBUTTON INPUT10 MIN READ

ボタンで LED を操作する入力ピンとプルアップ、押すたびに切り替えるトグルとチャタリング対策

L チカの次は「入力」。ボタンの状態を GPIO で読み、押している間だけ LED を光らせ、次に押すたびに ON/OFF を切り替える(トグル)まで進めます。トグルで必ずぶつかるチャタリング対策も、ここで身につけます。

  • ESP32-S3
  • Raspberry Pi 5 8GB
  • M5Stack CoreS3
CONTENTSこの記事の目次

01 — WIRING

配線

3.3V (VCC)10kΩGPIO 入力ピンタクトスイッチGND・ボタン離す (OFF) → HIGH (3.3V)・ボタン押す (ON) → LOW (0V)
DIAGRAM

ボタンは GPIO と GND の間に接続。内部プルアップをコードで有効化するため外付け抵抗は不要です。対角線の 2 本足を使います。

ボードボタンLED
ESP32-S3GPIO5 と GNDGPIO4
Raspberry Pi 5GPIO27(物理 13)と GND(物理 14)GPIO17(物理 11)
M5Stack CoreS3画面下のタッチボタン BtnA。配線不要画面の色で代用

02 — HOLD

押している間だけ光らせる

CPPhold.ino(ESP32-S3)
const int BUTTON_PIN = 5;
const int LED_PIN = 4;

void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP);   // 内部プルアップを有効化
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  bool pressed = (digitalRead(BUTTON_PIN) == LOW);   // 押すと LOW
  digitalWrite(LED_PIN, pressed ? HIGH : LOW);
}
PYTHONhold.py(Raspberry Pi 5)
from gpiozero import LED, Button
from signal import pause

led = LED(17)
button = Button(27)          # 内部プルアップ有効。押すと GND

button.when_pressed = led.on
button.when_released = led.off
pause()                      # イベントを待ち続ける

「押すと LOW」が最初は逆に感じますが、プルアップ入力の標準的な形です。== LOW の結果を pressed という名前にしておくと、以降のコードが読みやすくなります。

03 — TOGGLE

押すたびに切り替える(トグル)

「押している間」ではなく「押した瞬間」を検出する必要があります。前回の状態を覚えておき、HIGH → LOW に変わった瞬間だけ反応します(エッジ検出)。

CPPtoggle.ino(ESP32-S3)
const int BUTTON_PIN = 5;
const int LED_PIN = 4;

bool ledOn = false;
int lastState = HIGH;

void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  int state = digitalRead(BUTTON_PIN);

  if (state == LOW && lastState == HIGH) {   // HIGH → LOW = 押された瞬間
    delay(30);                                // チャタリングが収まるのを待つ
    if (digitalRead(BUTTON_PIN) == LOW) {     // まだ押されていれば本物
      ledOn = !ledOn;
      digitalWrite(LED_PIN, ledOn);
    }
  }
  lastState = digitalRead(BUTTON_PIN);
}
PYTHONtoggle.py(Raspberry Pi 5)
from gpiozero import LED, Button
from signal import pause

led = LED(17)
button = Button(27, bounce_time=0.03)   # 30ms のチャタリング対策

button.when_pressed = led.toggle
pause()
CPPtoggle_cores3.ino(M5Stack CoreS3)
#include <M5Unified.h>

bool on = false;

void setup() {
  M5.begin();
}

void loop() {
  M5.update();
  if (M5.BtnA.wasPressed()) {              // チャタリング対策済み
    on = !on;
    M5.Display.fillScreen(on ? TFT_GREEN : TFT_BLACK);
  }
}

04 — CHATTERING

チャタリングで何が起きるか

delay(30) と読み直しを消して試すと、1 回押しただけなのに LED が変わらない(2 回反応して元に戻る)ことがあります。これがチャタリング。接点が数ミリ秒バウンドして、押した瞬間に HIGH/LOW が何度も入れ替わります。

  • ソフト対策:変化を検出したら 20〜50ms 待って読み直す(上のコード)
  • 大きなプログラムでは delay を使わず millis() で時間を管理する。またはライブラリ(Arduino の Bounce2、M5Unified の Button)に任せる
  • ハード対策:ボタンと並列に 0.1μF のコンデンサ(→ コンデンサの基本)

05 — NEXT

発展

  • 長押し検出:押されてから離されるまでの時間を millis() で測り、1 秒以上なら別の動作
  • 2 つのボタンで明るさを上下させる(→ PWM で明るさを変える)
  • 割り込み attachInterrupt(pin, 関数, FALLING) で、loop が忙しくても押下を取りこぼさない

06 — WATCH OUT

注意点

プルアップを忘れると不定

INPUT だけだと押していないときの値が決まりません。INPUT_PULLUP にします。

ESP32 の GPIO0・3・45・46 はボタンに使わない

起動時の状態で動作モードが決まるピンです。起動時に押されていると立ち上がらないことがあります。

delay の中は何もできない

30ms なら気になりませんが、長い delay は他の処理(LED の点滅、通信)を止めます。

07 — CHECK YOURSELF

理解チェック

Q1押していないときに 1、押すと 0 になる配線の名前は?

プルアップ(内部プルアップ)。押すと GND に落ちるので 0。

Q2トグルが「押しても変わらないことがある」。原因と対策は?

チャタリングで 2 回検出されて元に戻っている。30ms 待って読み直す。