Push Button · Astro Tech Blog

Push Button

A push button (tactile switch) is a momentary-contact switch that connects two pins when pressed and breaks the connection when released. It is the simplest digital input for an Arduino. The button requires a pull-up or pull-down resistor to hold the input at a known logic level when the button is open. The internal pull-up resistor (INPUT_PULLUP) is typically used — this inverts the logic (LOW = pressed). Debouncing in software eliminates false triggers from mechanical bounce.

Push button pinout Push button pinout visualization

For this interfacing you need the following components:

  • Arduino board (Uno, Nano, Mega, etc.)
  • Tactile push button switch (4-pin or 2-pin)
  • Breadboard and jumper wires
  • USB cable to connect Arduino to your computer

Schematic

Pull-up configuration (internal pull-up, inverted logic)

Arduino           Push Button
-------           -----------
Digital Pin 2 ----+----+
                    |   |
                   / \  |
                  /   \ |
                 | BTN |
                  \   /
                   \ /
                    |
GND ---------------+

When the button is pressed, Pin 2 reads LOW. When released, it reads HIGH (via internal pull-up).

Pull-down configuration (external resistor, positive logic)

5V ----+

       /
       \ 10 kΩ
       /
       |
       +---- Digital Pin 2
       |
       |
       /
       \ Push Button
       /
       |
      GND

When the button is pressed, Pin 2 reads HIGH. When released, it reads LOW (via pulldown to GND).

Pin Map

Button PinDescriptionArduino Connection
Terminal 1One side of switchGND
Terminal 2Other side of switchDigital Pin 2 (with INPUT_PULLUP)

For 4-pin tactile switches, the pins are connected in pairs internally — any pin on one side bridges to any pin on the other side when pressed.

Install necessary Library

No library is required — the button is read with digitalRead(). For debouncing, the built-in millis() timing is sufficient. The Bounce2 library by Thomas O Fredericks provides a more robust debounce solution if needed:

arduino-cli lib install "Bounce2"

Code with complete explanation

This sketch demonstrates reading a push button with debouncing and detecting press, release, hold, and long-press events.

#define BUTTON_PIN  2
#define LED_PIN     13

unsigned long lastDebounceTime = 0;
unsigned long debounceDelay    = 50;  // ms

int lastButtonState = HIGH;   // Previous reading (HIGH = not pressed with pull-up)
int buttonState     = HIGH;   // Current debounced state

unsigned long pressStartTime = 0;
bool isPressed = false;

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

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

  // Debounce logic
  if (reading != lastButtonState)
  {
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay)
  {
    if (reading != buttonState)
    {
      buttonState = reading;

      if (buttonState == LOW)
      {
        // Button pressed (LOW with pull-up)
        Serial.println("Button PRESSED");
        digitalWrite(LED_PIN, HIGH);
        pressStartTime = millis();
        isPressed = true;
      }
      else
      {
        // Button released
        unsigned long holdDuration = millis() - pressStartTime;
        Serial.print("Button RELEASED after ");
        Serial.print(holdDuration);
        Serial.println(" ms");
        digitalWrite(LED_PIN, LOW);
        isPressed = false;
      }
    }
  }

  // Long-press detection while held
  if (isPressed && (millis() - pressStartTime > 2000))
  {
    Serial.println("Long press detected (> 2 seconds)");
    // Reset timer to avoid repeated messages
    pressStartTime = millis();
  }

  lastButtonState = reading;
}

Using the Bounce2 library

#include <Bounce2.h>

#define BUTTON_PIN 2

Bounce btn = Bounce();

void setup()
{
  Serial.begin(9600);
  btn.attach(BUTTON_PIN, INPUT_PULLUP);
  btn.interval(50);  // Debounce interval in ms
}

void loop()
{
  btn.update();

  if (btn.rose())
  {
    Serial.println("Button released");
  }

  if (btn.fell())
  {
    Serial.println("Button pressed");
  }

  if (btn.currentValue() == LOW)
  {
    // Button is currently held
  }
}

Code breakdown

  • pinMode(BUTTON_PIN, INPUT_PULLUP) — enables the ATmega’s internal 20–50 kΩ pull-up resistor. The pin reads HIGH when open, LOW when connected to GND.
  • Debounce algorithm: the reading must remain stable for debounceDelay ms before being accepted as the new state.
  • millis() — returns the number of milliseconds since the Arduino started. Used for non-blocking timing.
  • isPressed flag tracks whether the button is currently held to enable long-press detection.
  • digitalWrite(LED_PIN, HIGH) — turns on the built-in LED while the button is held.

Detecting multiple button states

StateConditionUse case
Pressedstate changed from HIGH → LOWTrigger an action once
Releasedstate changed from LOW → HIGHEnd of button interaction
Heldstate is LOW for > N msRepeat actions
Double-clicktwo presses within a short windowAlternate mode

Simple toggle (alternative)

bool ledState = false;

void loop()
{
  if (btn.fell())  // Button pressed
  {
    ledState = !ledState;
    digitalWrite(LED_PIN, ledState);
  }
}

Steps to perform this interfacing

  1. Connect the push button between Arduino digital pin 2 and GND (pull-up configuration).
  2. No library installation needed.
  3. Copy the code into the Arduino IDE.
  4. Select the correct board and port (Tools > Board and Tools > Port).
  5. Upload the sketch to the Arduino.
  6. Open the Serial Monitor (Tools > Serial Monitor, set baud rate to 9600).
  7. Press and release the button — observe the press/release events and hold duration printed.
  8. Hold the button for > 2 seconds — observe the “Long press” message.
  9. The built-in LED (pin 13) turns on while the button is held.

Caution

  • Internal pull-up range: The ATmega’s internal pull-up resistor is nominally 20 kΩ but varies from 20–50 kΩ between pins and across chips. This is adequate for a simple button but may cause unreliable readings with long wires (> 1 m) due to noise pickup. For long runs, use an external 10 kΩ pull-up resistor and disable INPUT_PULLUP.
  • No pull-up / pull-down: Without a pull-up or pull-down resistor, the input pin floats when the button is open, picking up ambient electrical noise. The pin will randomly read HIGH or LOW, causing false triggers. Always use INPUT_PULLUP or an external resistor.
  • Inverted logic with pull-up: When using INPUT_PULLUP, LOW = pressed, HIGH = released. This is the opposite of what beginners often expect. If you need positive logic (HIGH = pressed), use an external pull-down resistor to GND and connect the button between 5V and the pin.
  • Mechanical bounce: A tactile switch physically bounces for 5–20 ms after contact, producing multiple rapid transitions. Without debouncing, a single press can register as dozens of presses. The debounce delay of 50 ms in the example is conservative — you can reduce it to 10–15 ms for faster response, or increase it to 100 ms for a very bouncy switch (some cheap buttons are worse).
  • Schmitt trigger input: The ATmega’s digital inputs include a Schmitt trigger that provides hysteresis (≈ 300 mV). This helps with slow-rising signals but does not eliminate the need for debouncing on mechanical switches.
  • 4-pin tactile switch orientation: Many tactile switches have 4 pins arranged in two pairs. The two pins on each side of the switch are electrically connected internally. Pins on opposite sides are connected only when pressed. Use a multimeter in continuity mode to verify the pinout if unsure.