To switch on a Raspberry Pi safely without corrupting the SD card, wire a normally-open (NO) momentary pushbutton to GPIO 3 (Physical Pin 5) and Ground (Physical Pin 6), then enable the gpio-shutdown overlay in your boot configuration. This configures the board's Power Management IC (PMIC) to trigger a safe OS shutdown on press, and subsequently wake the Pi from a halted state on the next press.
The Raspberry Pi 4 Model B lacks a native physical power switch. Yanking the USB-C power cable while the OS is writing to the SD card's write-cache is the number one cause of file system corruption and dead cards in embedded projects. Below is the definitive, decision-forward guide to adding a physical switch that respects the Linux filesystem.
The "No Switch" Problem: Hardware Cut vs. Soft Shutdown
Before soldering wires, you must decide how you want to manage power. Makers generally fall into two camps: those who want a graceful OS shutdown (soft switch) and those who need a total physical power disconnect (hard switch). Use the decision tree below to pick your hardware path.
| Scenario | Requirement | Recommended Hardware |
|---|---|---|
| Retro-gaming console, kiosk, or desktop enclosure | Safe OS shutdown, prevent SD corruption, ability to wake up without unplugging | GPIO 3 Soft Switch (Momentary Button + Overlay) |
| Battery-powered rover, remote weather station | Zero parasitic draw when off, total galvanic isolation | Inline Hardware Latch (e.g., Pololu RC Switch or 5V 3A Rocker) |
| Raspberry Pi 5 in a custom 3D-printed case | External button to reach the onboard switch pads | Pi 5 PWR Pads (Solder/wire to dedicated PCB pads) |
Parts List & Pin Mapping (Target: Raspberry Pi 4 Model B)
This build targets the Raspberry Pi 4 Model B (4GB or 8GB variant). While the Pi 5 includes a dedicated onboard power button, the Pi 4 remains the workhorse for embedded enclosures where the board is buried and the onboard pins are inaccessible. (Note: This exact GPIO 3 method also applies to the Pi 3B+ and Pi Zero 2 W).
Bill of Materials
- Microcontroller: Raspberry Pi 4 Model B (4GB) — ~$55.00
- Switch: 12mm Momentary Pushbutton, Normally Open (NO), with optional built-in LED — ~$2.50
- Resistor: 220Ω or 330Ω through-hole (if using switch LED) — ~$0.10
- Wiring: 24 AWG Female-to-Female jumper wires (20cm length) — ~$3.00
- Tools: Multimeter (for continuity testing), wire strippers.
Pin Mapping Table
GPIO 3 (Pin 5) is hardcoded in the Raspberry Pi firmware to support wake-from-halt because it is tied to the I2C1 SDA line, which the PMIC monitors even when the main SoC is powered down. Do not use a random GPIO pin for this, or the wake function will fail.
| Component Pin | Raspberry Pi GPIO | Physical Pin # | Function |
|---|---|---|---|
| Button Leg 1 | GPIO 3 (I2C1 SDA) | Pin 5 | Shutdown Trigger / Wake Signal |
| Button Leg 2 | GND | Pin 6 | Circuit Ground Reference |
| LED Anode (+) | GPIO 14 (UART TXD) | Pin 8 | Status LED (Optional) |
| LED Cathode (-) | GND (via 220Ω Resistor) | Pin 9 | LED Ground Return |
Wiring the Momentary Switch
- Prep the Wires: Strip 5mm of insulation from both ends of two 24 AWG jumper wires.
- Connect the Switch: Attach one wire to Pin 5 (GPIO 3) and the other to Pin 6 (GND). Connect the opposite ends to the two legs of your momentary pushbutton. Because it is a simple continuity circuit, polarity on the switch legs does not matter.
- Wire the Status LED (Optional): Connect Pin 8 to the LED Anode. Connect the LED Cathode to one leg of the 220Ω resistor, and the other resistor leg to Pin 9 (GND).
- Verify with Multimeter: Set your multimeter to continuity mode (the diode/beep symbol). Place probes on the GPIO 3 and GND pins at the board header. Press the button. The meter should beep only while the button is held down. If it beeps continuously, your switch is Normally Closed (NC) and must be replaced.
The Code: Device Tree Overlay & Python Fallback
The most common mistake makers make is writing a Python script to listen for a button press and execute sudo poweroff. Do not do this. A Python script cannot wake the Pi from a halted state because the OS is dead. Wake-from-halt requires configuring the hardware PMIC via a Device Tree Overlay.
Step 1: The Mandatory Overlay (Wake & Shutdown)
Open your boot configuration file. Note: In Raspberry Pi OS Bookworm and newer, the path moved from /boot/config.txt to /boot/firmware/config.txt.
sudo nano /boot/firmware/config.txt
Add the following line at the very bottom of the file:
# Enable GPIO 3 soft shutdown and wake-from-halt
# gpiopull=up uses the internal pull-up resistor, so no external resistor is needed
dtoverlay=gpio-shutdown,gpio_pin=3,active_low=1,gpio_pull=up
Save and reboot. Your button will now safely shut down the Pi and turn it back on.
Step 2: Python Companion for External USB Drive Unmounting
If your Pi runs a NAS or retro-gaming rig with external USB drives, the standard gpio-shutdown overlay might cut power before slow USB drives finish spinning down. Below is a complete, compilable Python script using gpiozero that intercepts the shutdown, forces a filesystem sync, and safely unmounts external drives before triggering the poweroff.
#!/usr/bin/env python3
"""
Safe Unmount & Shutdown Manager for Raspberry Pi 4
Target Board: Raspberry Pi 4 Model B
Requires: sudo apt install python3-gpiozero
"""
import sys
import logging
import subprocess
from gpiozero import Button
from signal import pause
# Configure logging to file for debugging
logging.basicConfig(
filename='/var/log/pi_power_manager.log',
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
# GPIO 3 (Physical Pin 5)
SHUTDOWN_PIN = 3
shutdown_btn = Button(SHUTDOWN_PIN, pull_up=True, bounce_time=0.05)
def get_mounted_usb_drives():
"""Finds mounted partitions in /media or /mnt to unmount safely."""
try:
result = subprocess.run(
['findmnt', '-rn', '-o', 'TARGET', '-t', 'vfat,ext4,ntfs'],
capture_output=True, text=True, check=True
)
return [line for line in result.stdout.splitlines() if line.startswith('/media') or line.startswith('/mnt')]
except subprocess.CalledProcessError:
return []
def safe_shutdown_sequence():
logging.info("Shutdown sequence initiated via GPIO 3.")
try:
# 1. Sync filesystems to flush write caches
subprocess.run(['sync'], check=True)
logging.info("Filesystems synced.")
# 2. Unmount external USB drives
drives = get_mounted_usb_drives()
for drive in drives:
logging.info(f"Unmounting {drive}...")
subprocess.run(['sudo', 'umount', '-f', drive], check=True)
# 3. Trigger system poweroff
subprocess.run(['sudo', 'systemctl', 'poweroff'], check=True)
except subprocess.CalledProcessError as e:
logging.error(f"Shutdown command failed: {e}")
sys.exit(1)
except Exception as e:
logging.error(f"Unexpected error during shutdown: {e}")
sys.exit(1)
# Bind the function to the button press
shutdown_btn.when_pressed = safe_shutdown_sequence
if __name__ == "__main__":
logging.info("Smart Power Manager active. Waiting for button press...")
try:
pause() # Keeps the script running efficiently without CPU polling
except KeyboardInterrupt:
logging.info("Service interrupted manually. Exiting cleanly.")
sys.exit(0)
Source reference for GPIO pin mapping and button API: gpiozero Button Documentation.
Troubleshooting: Boot and Wake Failures
If your button does nothing, or the Pi behaves erratically, check these exact failure modes. These are the first three things to check when a GPIO switch fails.
Error 1: Button triggers shutdown, but will NOT wake the Pi
- Cause A (Most Likely): I2C is enabled in
raspi-config. GPIO 3 is the I2C1 SDA pin. If the I2C interface is enabled, the OS overrides the PMIC's wake listener.
Fix: Runsudo raspi-config-> Interface Options -> I2C -> Disable. Reboot. - Cause B: You used a pin other than GPIO 3. Only GPIO 3 supports hardware wake-from-halt on the Pi 4.
Fix: Rewire to Pin 5.
Error 2: Python script throws RuntimeError: No access to /dev/mem. Try running as root!
- Cause: The script is trying to access the GPIO memory registers without elevated privileges, or your user is not in the
gpiogroup (common on older OS versions).
Fix: Run the script withsudo python3 power_manager.py, or add your user to the gpio group viasudo usermod -aG gpio $USER.
Error 3: Pi boots up, but immediately shuts down after 2 seconds
- Cause: Your pushbutton is Normally Closed (NC), or your wiring is shorting Pin 5 to Pin 6 permanently. The Pi boots, the OS loads the overlay, reads a "pressed" state, and immediately shuts down.
Fix: Test the switch with a multimeter. Replace with a Normally Open (NO) switch.
Extending the Build: Pi 5 Upgrades and Hardware Latches
Once your soft-switch is working, you can adapt the build to your specific enclosure needs.
How to Simplify
If you do not have external USB drives and don't need custom logging, delete the Python script entirely. The dtoverlay=gpio-shutdown line in config.txt is handled entirely by the kernel and the PMIC. It uses zero CPU cycles and is immune to Python script crashes. For basic kiosks, the overlay alone is the gold standard.
How to Extend
To add visual feedback, wire a 16x2 I2C LCD to the GPIO header (using Pins 1, 2, 3, and 5). You can modify the Python script above to print "Shutting down..." to the LCD before calling systemctl poweroff, giving you visual confirmation that the OS is safely parking the read/write heads before the power LED goes dark.
Upgrading to the Raspberry Pi 5
If you migrate this project to a Raspberry Pi 5 (8GB), the architecture changes. The Pi 5 features a dedicated physical power button directly on the PCB, managed by the new Renesas DA9098 PMIC. To wire an external case switch to a Pi 5, do not use GPIO 3. Instead, locate the two small pads labeled PWR near the USB-C port and solder your momentary switch directly to those pads. For full hardware specifications on the Pi 5 power management, refer to the official Raspberry Pi hardware documentation.






