Classroom Public page

Lab 8.1: Button Input

491 words

~90 min. Wire a pushbutton to a digital input pin; read it with digitalRead; toggle an LED when the button is pressed; add debouncing.


Goal: complete a full input-output loop in software; understand active-low logic and debouncing

Estimated time: 90 minutes

Prerequisites: lab 7.1 + 7.2 complete

Steps

Step 1: Wire the button (15 min)

Pushbutton on the breadboard, spanning the central gap (so its two sides are on different tie-strips). One side: connect to pin 2. Other side: connect to GND

That's it. With INPUT_PULLUP, you don't need an external resistor; the Arduino provides an internal one. The button shorts pin 2 to ground when pressed; pin 2 reads HIGH (5 V through pullup) when released and LOW (~0 V) when pressed

Wire an LED + resistor on pin 8 as in lab 7.2 (so you have something to control)

Step 2: Read the button (20 min)

Write a sketch:

void setup() {
  pinMode(2, INPUT_PULLUP);
  pinMode(8, OUTPUT);
  Serial.begin(9600);
}
void loop() {
  int state = digitalRead(2);
  Serial.println(state);
  delay(100);
}

Upload. Open Serial Monitor (Tools → Serial Monitor; baud 9600). You should see "1" when the button is released and "0" when pressed. The "0" is LOW (button pulls pin to ground); the "1" is HIGH (pullup keeps pin at 5 V when button is released)

Step 3: Drive LED from button (10 min)

Modify the sketch:

void loop() {
  int state = digitalRead(2);
  if (state == LOW) {
    digitalWrite(8, HIGH);  // pressed = LED on
  } else {
    digitalWrite(8, LOW);   // released = LED off
  }
}

Upload. Verify: hold the button = LED on. Release = LED off

Step 4: Implement a toggle (20 min)

A "toggle" means the LED state changes once per button PRESS (not on each loop iteration the button is held). Add state tracking:

int lastState = HIGH;
int ledState = LOW;
void loop() {
  int state = digitalRead(2);
  if (state == LOW && lastState == HIGH) {
    // edge: button just pressed
    ledState = !ledState;
    digitalWrite(8, ledState);
  }
  lastState = state;
}

Upload. Each press toggles the LED. The LED stays in its new state until the next press

Step 5: Add debouncing (25 min)

Without debouncing, one press registers as several (mechanical bounce). Add a 20 ms confirmation:

int lastState = HIGH;
int ledState = LOW;
unsigned long lastChange = 0;
void loop() {
  int state = digitalRead(2);
  if (state != lastState) {
    lastChange = millis();  // start timer on any change
  }
  if ((millis() - lastChange) > 20 && state != lastState) {
    // state has been stable for 20ms; accept
    lastState = state;
    if (state == LOW) {
      ledState = !ledState;
      digitalWrite(8, ledState);
    }
  }
}

(This is one common debouncing pattern; there are others. Read the Arduino "Debounce" example if you want more depth.)

Upload. Test by pressing slowly and rapidly. The LED should toggle exactly once per press, even on fast presses

Expected output

  • Working button-toggles-LED with debouncing
  • Serial Monitor showing the button state in real time
  • Notebook reflection on why debouncing matters

Common pitfalls

  • Missing pinMode(2, INPUT_PULLUP): without it, pin 2 floats and reads garbage
  • Wiring the button to +5V instead of GND: with pullup, the button SHORTS TO GROUND when pressed. Wiring to +5V means the button does nothing (already pulled high; pressing just confirms it)
  • No debouncing: a single press registers as 2-5 toggles. The lab works without debouncing; debouncing is the next-level polish

Stretch (optional)

  • Add a second button on pin 3 that controls a second LED on pin 9. Both buttons work independently
  • Make the LED blink (not just toggle) when the button is pressed: press = start blinking; press again = stop. This requires a state machine: idle / blinking
  • Replace Serial.println(state) with a count: print "button pressed N times" for each accepted press

Lab 8.1 v0.1. The first input + output Arduino lab. Pattern used in dozens of weeks ahead.