To connect a standard 4-pin tactile button to an Arduino, wire one switch terminal to a digital GPIO (like D2) and the diagonally opposite terminal to GND. You do not need an external resistor for most bench setups; simply enable the microcontroller's internal pull-up resistor in your code using INPUT_PULLUP. When the button is unpressed, the pin reads HIGH (5V); when pressed, it reads LOW (0V).
This guide walks through the exact physical terminals, traces the electrical path node-by-node, and shows you how to verify the circuit with a multimeter before you write a single line of code.
The Physical Button: Terminals, Symbols, and Specs
The most common switch in maker kits is the 6x6x5mm through-hole tactile push button. While it has four pins, it is internally wired as a simple SPST-NO (Single Pole, Single Throw, Normally Open) switch.
On the schematic symbol, an SPST-NO switch is drawn as a break in the wire line with a hinged actuator above it. The actuator represents the physical plastic plunger. When you press the plunger, the metal dome inside collapses, bridging the gap and allowing current to flow.
Physically, the four pins are arranged in a rectangle. Pins 1 and 2 are internally shorted together, and Pins 3 and 4 are internally shorted together. The switch mechanism bridges the 1-2 pair to the 3-4 pair only when pressed. To guarantee you are wiring across the actual switch mechanism and not just a dead short, always wire to diagonally opposite pins (e.g., Pin 1 and Pin 4).
Below is the engineering data for a standard Omron B3F-series tactile switch and the associated pull-up resistor values you need to know for circuit design.
| Parameter | Typical Value | Engineering Note |
|---|---|---|
| Contact Configuration | SPST-NO | Single Pole, Single Throw, Normally Open |
| Max Contact Rating | 50mA @ 12VDC | Logic signals only; use a MOSFET for loads >20mA |
| Contact Resistance | < 100mΩ | Measured across closed terminals (Pin 1 to Pin 4) |
| Mechanical Bounce | 1ms - 5ms | Metal leaf vibration; requires software debouncing |
| Internal Pull-Up (AVR) | 20kΩ - 50kΩ | Silicon resistor inside ATmega328P; enabled via code |
| External Pull-Up | 4.7kΩ - 10kΩ | Required for wire runs >12 inches or noisy environments |
For deeper reference on microcontroller pin configurations, consult the official Arduino digital pins documentation, and for switch mechanical tolerances, review the Omron B3F tactile switch datasheet.
Node-by-Node Wiring Trace: Source to GPIO
We will trace the INPUT_PULLUP circuit. This is the modern standard for Arduino wiring because it eliminates the need for a breadboard resistor, reducing component count and points of failure.
In this configuration, the microcontroller provides the voltage source internally, and the physical button acts as a switchable path to ground. Here is the exact textual trace of the electrical path:
- Node 1 (Internal Source): Inside the ATmega328P silicon, the 5V VCC rail connects to a ~30kΩ internal silicon resistor.
- Node 2 (GPIO Junction): The other end of that internal resistor connects to the physical Arduino Digital Pin 2 (D2). Because of the resistor, D2 is 'pulled up' to 5V when nothing else is connected.
- Node 3 (Breadboard Junction): A jumper wire carries this 5V-pulled signal from Arduino D2 to Breadboard Row 10.
- Node 4 (Switch Terminal A): Button Pin 1 is inserted into Breadboard Row 10, receiving the 5V logic HIGH signal.
- Node 5 (The Air Gap): Inside the button, the metal dome sits above the contact pad. The circuit is open. No current flows. The GPIO reads 5V (HIGH).
- Node 6 (Switch Terminal B): When you press the button, the dome collapses, connecting Pin 1 to Pin 4. Pin 4 is inserted into Breadboard Row 12.
- Node 7 (Ground Path): A second jumper wire connects Breadboard Row 12 to the Arduino GND pin.
Polarity and Ground Path Callout: Tactile switches are non-polarized; current can flow in either direction. However, the circuit logic relies on the ground path. When the switch closes, current flows from the internal 5V source, through the 30kΩ resistor, through the switch, and sinks into the Arduino's ground plane. The GPIO pin reads the voltage at the junction. Because the switch has near-zero resistance (<100mΩ) compared to the pull-up resistor (30,000Ω), almost all the voltage drops across the internal resistor, leaving the GPIO pin at ~0.0V (LOW).
| Arduino Pin | Breadboard Node | Button Pin | Function in Circuit |
|---|---|---|---|
| D2 | Row 10 | Pin 1 (Terminal A) | Digital Input (Reads HIGH when open, LOW when closed) |
| GND | Row 12 | Pin 4 (Terminal B) | Circuit Ground / LOW voltage sink |
| N/A | Row 10 | Pin 2 | Internally shorted to Pin 1 (Leave unconnected) |
| N/A | Row 12 | Pin 3 | Internally shorted to Pin 4 (Leave unconnected) |
Verifying the Circuit and Handling Bounce
Before uploading code, verify your physical wiring with a multimeter. This saves hours of debugging 'ghost' button presses caused by floating pins or miswired switch legs.
Step 1: Continuity Test (Power Off)
Disconnect the Arduino from USB. Set your multimeter to Continuity mode (the diode/sound wave icon). Place the probes on your chosen button pins (e.g., Pin 1 and Pin 4).
Expected Result: Silence when unpressed. A solid beep when the plunger is fully depressed. If it beeps constantly, you are probing two pins on the same side of the switch (the internal short). Move one probe to the opposite side.
Step 2: Voltage Test (Power On)
Plug the Arduino into USB. Set the multimeter to DC Voltage (20V range). Place the black probe on the Arduino GND pin and the red probe on the breadboard row connected to D2.
Expected Result:
• Unpressed: 4.8V to 5.1V (The internal pull-up is working).
• Pressed: 0.0V to 0.05V (The ground path is solid).
Failure mode: If the unpressed voltage reads something random like 1.4V or 2.7V, your GPIO pin is floating. You either forgot to set INPUT_PULLUP in the code, or your microcontroller pin is damaged.
Step 3: Debouncing in Code
When the metal dome inside the button makes contact, it physically bounces like a trampoline for 1 to 5 milliseconds before settling. To a microcontroller running at 16MHz, this looks like 50 rapid button presses. You must debounce the signal in software.
Here is a complete, copy-pasteable sketch using a non-blocking millis() timer to debounce the signal. This avoids the delay() function, keeping your main loop free to run motors or read sensors.
// Pin mapping
const int BUTTON_PIN = 2;
// Debounce variables
bool lastButtonState = HIGH;
bool currentButtonState = HIGH;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50; // 50ms is safe for most tactile switches
void setup() {
Serial.begin(115200);
// Enable the internal 20k-50k pull-up resistor
pinMode(BUTTON_PIN, INPUT_PULLUP);
}
void loop() {
// Read the raw physical state (LOW means pressed due to pull-up)
bool reading = digitalRead(BUTTON_PIN);
// If the switch changed state (due to noise or a real press), reset the timer
if (reading != lastButtonState) {
lastDebounceTime = millis();
}
// If the reading has been stable longer than the debounceDelay, accept it
if ((millis() - lastDebounceTime) > debounceDelay) {
if (reading != currentButtonState) {
currentButtonState = reading;
// Trigger action only on the exact moment of the press (LOW)
if (currentButtonState == LOW) {
Serial.println("Button Pressed (Debounced)");
}
}
}
// Save the raw reading for the next loop iteration
lastButtonState = reading;
}






