Buttons and switches

Wire a button with an internal pull-up, debounce it, and toggle an indicator once per press.

Small push-buttons

Ingredients

Follow a part’s name for an introduction and compatibility notes.

Many projects include physical buttons or switches of some kind. A button's contacts connect two points when pressed. To turn that into a dependable digital reading, the input needs a defined state both when the contacts are open and when they are closed.

Wire it with a pull-up

One normally open button
ConnectionDestination or purpose
D2One side of the switch.
GNDThe other side of the switch.

The sketch enables the Uno’s internal pull-up resistor. With the button released, it reads HIGH. Pressing connects D2 to ground, so it reads LOW. No external pull-up resistor is needed for this short breadboard circuit.

Four-legged tactile buttons usually have two pairs that are already joined internally. Check with a continuity meter, or consult the part drawing, and use contacts that connect only when pressed. Place the button across the breadboard trench if its lead arrangement supports that.

One press, one change

Download the complete Arduino sketch

const byte buttonPin = 2;
const byte ledPin = LED_BUILTIN;
const unsigned long debounceMs = 30;
int lastReading = HIGH;
int stableState = HIGH;
unsigned long changedAt = 0;
bool ledState = false;

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  Serial.begin(9600);
}

void loop() {
  const int reading = digitalRead(buttonPin);
  const unsigned long now = millis();
  if (reading != lastReading) changedAt = now;
  if (now - changedAt >= debounceMs && reading != stableState) {
    stableState = reading;
    if (stableState == LOW) {
      ledState = !ledState;
      digitalWrite(ledPin, ledState ? HIGH : LOW);
      Serial.println(ledState ? "On" : "Off");
    }
  }
  lastReading = reading;
}

Open Serial Monitor at 9600 baud. Each press toggles the built-in LED and prints one new state. Holding the button down should not keep toggling it. Releasing it prepares the next press.

Why debouncing matters

Real contacts can bounce between open and closed for a short time. The code remembers the last raw reading and restarts a timer whenever that reading changes. Only a state that remains unchanged for 30 ms becomes the stable state.

The action happens on the stable transition to LOW. It does not happen on every loop iteration or on release. This distinction is essential for counters, acknowledgement buttons, and menus. The timer uses unsigned arithmetic so its elapsed-time comparison survives millis() wrapping.

Troubleshooting

  • Always pressed: the two selected switch legs may already be connected, or both wires may share one breadboard strip.
  • Random input: check the ground connection and confirm INPUT_PULLUP is present.
  • Extra counts: increase the debounce interval modestly and inspect loose wiring. Very long wires may need additional filtering and protection.
  • Missed quick taps: a tap shorter than the debounce interval is intentionally ignored. Reduce the interval only after observing the switch behavior.

Put it to work

Replace the toggle with a press counter, or acknowledge an alert while retaining the underlying sensor state. Never make an acknowledgement button erase the fact that a leak or open door still exists. Continue with button-controlled servo movement. Arduino’s pull-up input example explains the same active-low wiring convention.