A 3-prong push button is a Single Pole, Double Throw (SPDT) momentary switch. The three prongs are COM (Common), NO (Normally Open), and NC (Normally Closed). For standard 'press-to-activate' logic, wire the COM prong to GND and the NO prong to your digital input pin, utilizing the microcontroller's internal pull-up resistor. This configuration pulls the pin LOW when pressed, avoiding the need for external resistors.

Anatomy of a 3-Prong Push Button (SPDT)

Unlike standard 2-pin or 4-pin tactile switches (which are Single Pole, Single Throw or SPST), a 3-prong switch gives you two distinct circuit paths. When the button is at rest, the internal wiper connects COM to NC. When you press the button, the wiper physically breaks the NC connection and bridges COM to NO.

Typical SPDT Momentary Switch Specifications
Parameter Typical Value (Subminiature Tactile) Notes
Max Voltage 12V DC to 24V DC Do not use for 120V AC mains switching.
Max Current 50mA to 100mA Well within the 20mA limit of an Arduino GPIO pin.
Contact Resistance ≤ 100mΩ Measured across COM and NO when actuated.
Mechanical Bounce 5ms to 50ms Requires software or hardware debouncing.
Insulation Resistance ≥ 100MΩ Measured between unconnected terminals.

According to the SparkFun Switch Basics Tutorial, understanding the difference between NO and NC is critical for safety and logic design. If you wire your circuit to the NC pin, the Arduino will read the button as 'pressed' when it is actually at rest, and 'released' when you push it.

Required Parts and Pin Mapping

This guide targets the Arduino Uno R3 (ATmega328P). The code and wiring are directly compatible with the Arduino Nano, Mega 2560, and Uno R4 Minima, provided you adjust the pin numbers if you use a different physical layout.

Parts List

  • Microcontroller: Arduino Uno R3 (ATmega328P)
  • Switch: SPDT Momentary Push Button (e.g., C&K KSA series or generic 3-pin tactile switch)
  • Resistor: 10kΩ (Optional — only needed if you disable the internal pull-up)
  • Capacitor: 0.1μF ceramic (Optional — for hardware debouncing)
  • Wiring: 22 AWG solid core jumper wires and a standard 830-point breadboard

Pin Mapping Table

Switch Prong Arduino Uno R3 Pin Function / State
COM (Common) GND Provides the LOW (0V) reference when the switch closes.
NO (Normally Open) Digital Pin 2 Reads HIGH (5V) at rest, LOW (0V) when pressed.
NC (Normally Closed) Not Connected Left floating (tape the tip to prevent accidental shorts).
Pro Tip: Always wrap a small piece of Kapton tape or heat shrink over the unused NC prong. On a crowded breadboard, a bare NC prong can easily short against adjacent power rails, causing erratic logic states or damaging your GPIO pin.

Step-by-Step Wiring and Debounced Code

Mechanical switches suffer from 'contact bounce'. When the metal wiper hits the NO terminal, it physically bounces microscopically for a few milliseconds, generating rapid HIGH/LOW spikes. The official Arduino Debounce documentation recommends handling this in software to prevent a single press from registering as multiple clicks.

Wiring Steps

  1. Insert the 3-prong SPDT switch into the breadboard, ensuring each prong is in its own separate row.
  2. Connect a jumper wire from the COM prong to any GND pin on the Arduino Uno R3.
  3. Connect a jumper wire from the NO prong to Digital Pin 2.
  4. Leave the NC prong unconnected.
  5. Connect the Arduino to your PC via USB and upload the code below.

Compilable Arduino Code (Zero External Dependencies)

This code uses the millis() function for non-blocking software debouncing. It requires no external libraries.

/*
 * Arduino SPDT 3-Prong Push Button with Debounce
 * Target Board: Arduino Uno R3 (ATmega328P)
 * Author: ElectricalFlux
 */

// Pin definitions
const int BUTTON_NO_PIN = 2; // Connect to NO prong (COM goes to GND)
const int LED_PIN = 13;      // Onboard LED for visual feedback

// Debounce variables
int buttonState = HIGH;         // Current debounced state
int lastButtonState = HIGH;     // Previous reading
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50; // 50ms debounce window

void setup() {
  Serial.begin(9600);
  
  // Enable internal pull-up resistor (approx 20k-50k ohms on ATmega328P)
  // This holds the pin HIGH until the button pulls it to GND
  pinMode(BUTTON_NO_PIN, INPUT_PULLUP);
  pinMode(LED_PIN, OUTPUT);
  
  digitalWrite(LED_PIN, LOW);
  Serial.println(F("System Ready. Waiting for button press..."));
}

void loop() {
  // Read the raw state of the switch
  int reading = digitalRead(BUTTON_NO_PIN);

  // If the switch state changed (due to noise or pressing)
  if (reading != lastButtonState) {
    // Reset the debouncing timer
    lastDebounceTime = millis();
  }

  // If the state has been stable for longer than the debounce delay
  if ((millis() - lastDebounceTime) > debounceDelay) {
    // If the button state has actually changed
    if (reading != buttonState) {
      buttonState = reading;

      // We only care about the LOW state (button pressed, COM connected to NO)
      if (buttonState == LOW) {
        int currentLedState = digitalRead(LED_PIN);
        digitalWrite(LED_PIN, !currentLedState); // Toggle LED
        Serial.println(F("[EVENT] Button Pressed - State Toggled"));
      }
    }
  }

  // Save the reading for the next loop iteration
  lastButtonState = reading;
}

Debugging: The First 3 Things to Check When It Fails

If your Serial Monitor is acting erratically or the LED isn't toggling, run through this diagnostic sequence before rewriting your code.

1. The Floating Pin (Random 0s and 1s in Serial Monitor)

Symptom: The Serial Monitor prints '[EVENT] Button Pressed' dozens of times a second without you touching the switch, or the LED flickers randomly.

Cause: You configured the pin as INPUT instead of INPUT_PULLUP. Without a pull-up resistor, the Digital Pin 2 acts as an antenna, picking up electromagnetic noise from your room.

Fix: Ensure line 18 in the code reads pinMode(BUTTON_NO_PIN, INPUT_PULLUP);. If you must use an external resistor, wire a 10kΩ resistor between Digital Pin 2 and the 5V pin.

2. The Inverted Logic (Triggers When Released, Not Pressed)

Symptom: The LED toggles exactly when you let go of the button, not when you push it down.

Cause: You wired the NC (Normally Closed) prong to Digital Pin 2 instead of the NO (Normally Open) prong.

Fix: Power down the board. Take a multimeter, set it to continuity mode (the diode/beep symbol), and place probes on COM and the suspected pin. If it beeps while the button is at rest, that is the NC pin. Move your jumper wire to the prong that only beeps when the button is actively held down.

3. Severe Contact Oxidation (Requires Hard Presses)

Symptom: The button only registers if you press it at a specific angle or with excessive force.

Cause: The internal metal wiper has developed an oxide layer, increasing contact resistance beyond the threshold the internal pull-up can reliably pull down.

Fix: Apply a single drop of DeoxIT D-Series contact cleaner into the switch housing and actuate it 50 times. If it is a cheap unsealed tactile switch, replace it with a sealed IP67-rated switch like the C&K PTS645 series.

How to Extend or Simplify the Build

Simplifying the Build

If you don't need the Normally Closed functionality, ditch the 3-prong SPDT switch entirely. Use a standard 2-pin SPST momentary tactile switch (the ubiquitous 6x6mm square buttons found in every starter kit). They only have two states, eliminating the risk of wiring to the wrong prong. Alternatively, use a KY-004 Key Switch Module. This is a 3-pin module (not a bare switch) with VCC, GND, and SIG pins, featuring an onboard pull-up resistor and a status LED.

Extending the Build (Hardware Interrupts)

Software debouncing using millis() is perfect for UI buttons, but if you are building a tachometer or counting high-speed encoder pulses, the 50ms delay will cause you to miss events. To extend this build for high-speed counting, move the NO prong to Digital Pin 2 (which supports interrupts on the Uno R3) and use attachInterrupt(). Pair this with a 0.1μF ceramic capacitor wired in parallel between COM and NO for hardware RC debouncing, eliminating the need for software delay loops.

Frequently Asked Questions

Can I use all three prongs of the push button at the same time?

Yes, but they share a single internal wiper. You can wire COM to GND, NO to a 'Start' input, and NC to a 'Stop' or 'Safe State' input. However, you cannot use the switch to independently control two separate high-current loads simultaneously, as the total current through the COM prong cannot exceed the switch's maximum rating (typically 50mA-100mA for subminiature tactile switches).

Why does my Arduino push button with 3 prongs trigger randomly when I don't touch it?

This is almost always a 'floating pin' issue caused by omitting a pull-up resistor. The ATmega328P's digital pins have high impedance when set to standard INPUT. By changing your code to INPUT_PULLUP, you engage an internal 20kΩ-50kΩ resistor that ties the pin to 5V, holding it firmly HIGH until the button physically bridges it to GND.

What is the difference between a 3-prong bare switch and a 3-pin button module?

A bare 3-prong switch is a raw mechanical component with COM, NO, and NC terminals. A '3-pin button module' (like the KY-004 or HW-219) is a small PCB that contains a standard 2-pin switch, an onboard 10kΩ pull-up resistor, a current-limiting resistor, and an LED. The module's pins are labeled VCC, GND, and SIG. If you are using a module, wire VCC to 5V, GND to GND, and SIG to your digital pin, and use standard INPUT (not INPUT_PULLUP) in your code.

Do I need an external resistor for a 3-prong SPDT switch?

No, not for 95% of hobbyist projects. The Arduino Uno R3 features built-in internal pull-up resistors on all digital I/O pins, activated via the INPUT_PULLUP command. You only need an external 10kΩ pull-up resistor if you are using a microcontroller that lacks internal pull-ups, or if you are running the wire over a long distance (over 1 meter) where the weak internal resistance might be susceptible to voltage drop and noise.