Bench NotesUno handbook

Pull-ups and Debouncing

Use INPUT_PULLUP for a one-wire button, then debounce it with millis() so each press counts once.

IntermediatePushbutton

Overview

INPUT_PULLUP enables a resistor inside the chip that holds the pin HIGH. Wire the button from the pin to GND and you need no extra parts. The catch is contact bounce: a mechanical switch chatters for a few milliseconds when pressed, which a fast loop sees as several presses.

You will need

  • Arduino Uno R3
  • Momentary pushbutton
  • Jumper wires

Wiring

Uno pinConnects toNotes
2Button, one sideINPUT_PULLUP: reads LOW when pressed (active-low)
GNDButton, other side
Wiring for Pull-ups and DebouncingArduino Uno D2 (INPUT_PULLUP) to Pushbutton side A; Arduino Uno GND to Pushbutton side B.Connections (each line is one wire)Arduino UnoPushbuttonD2 (INPUT_PULLUP)side AGNDside B
Original wiring diagram. Each line is one jumper wire; boxes on a line are series resistors.

Diagram

Contact bounceVoltage on an INPUT_PULLUP pin when a button is pressed. It sits HIGH, then chatters between HIGH and LOW for a few milliseconds before settling LOW. A debounce delay ignores the chatter.bounce: ~1–10 msHIGH (released, pull-up)LOW (pressed)debounce: accept a new state only after it is stable for 30 ms
What the pin actually sees during a press.

The sketch

debounced_toggle.ino
const int BUTTON_PIN = 2;
const unsigned long DEBOUNCE_MS = 30;

int stableState = HIGH;      // debounced state (HIGH = released)
int lastReading = HIGH;      // raw reading from the previous loop
unsigned long lastChangeMs = 0;
bool ledOn = false;

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

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

  if (reading != lastReading) {      // raw input changed: restart the timer
    lastChangeMs = millis();
    lastReading = reading;
  }

  if (millis() - lastChangeMs >= DEBOUNCE_MS && reading != stableState) {
    stableState = reading;           // stable long enough: accept it
    if (stableState == LOW) {        // a new press
      ledOn = !ledOn;
      digitalWrite(LED_BUILTIN, ledOn ? HIGH : LOW);
    }
  }
}

How the code works

The sketch accepts a new state only after the raw reading has stayed the same for 30 ms, and acts only on the change from released to pressed. Unlike a delay()-based debounce, the loop never stops, so it can do other work at the same time. The workshop button projects use the simpler blocking approach, which is fine when the button is the only thing the sketch watches.

What you should observe

  • Each press toggles the L LED exactly once.
  • Set DEBOUNCE_MS to 0 and some presses will toggle twice (the LED appears not to change).