The Direct Answer: Safe Shutdown via GPIO5

Yanking the USB-C cable while the Raspberry Pi is writing to the SD card is the fastest way to corrupt your filesystem. Because the Raspberry Pi 4 and 3 lack a native hard power switch, the most reliable DIY power switch for Raspberry Pi builds uses a momentary pushbutton wired between GPIO5 (Pin 29)GND (Pin 30), paired with a Python gpiozero script running as a systemd service. This triggers a safe OS shutdown when held for two seconds.

However, a software shutdown only puts the Pi into a 'halted' state, still drawing ~1.2W of quiescent current. To physically cut power and restore it, you must either add an inline USB-C toggle switch (used only after the safe shutdown completes) or build a P-Channel MOSFET hardware latch. Below is the complete bench-tested guide for the Raspberry Pi 4 Model B (4GB), including the exact code, wiring, and debugging paths for when the GPIO throws errors.

Safety & Hardware Note: Never wire a physical toggle switch directly to the 5V and GND GPIO header pins to act as a hard power switch while the OS is running. Always initiate a software shutdown first to flush the filesystem cache.

Parts List & Spec Sheet

This build targets the Raspberry Pi 4 Model B (4GB or 8GB) running Raspberry Pi OS (Bookworm or Bullseye). The Pi 5 includes a native physical power button on the PCB, making this specific GPIO5 workaround unnecessary unless you are routing a remote case button to the Pi 5's dedicated J2 power header.

Component Exact Variant / Specification Notes
Microcontroller Raspberry Pi 4 Model B (4GB) Target board for this code and pinout.
Switch 12mm Momentary Pushbutton (SPST, NO) Normally Open. Panel mount preferred.
Wiring 22 AWG Solid Core Hookup Wire Keep runs under 6 inches to avoid EMI bounce.
Resistor (Optional) 10kΩ Carbon Film Only needed if wire run exceeds 6 inches.
Storage SanDisk Extreme 32GB microSD (A1 rated) A1 app performance class reduces corruption risk.

Difficulty Rating: 2/5 (Soldering and basic Linux CLI required)
Time to Complete: 45 minutes

Pin Mapping & Wiring Steps

We use GPIO5 (Pin 29) because it features a hardwired 1.8kΩ pull-up resistor on the Raspberry Pi PCB, originally intended for I2C. This means you do not need an external pull-up resistor for short wire runs, and it natively supports waking the Pi from a halted state when shorted to ground.

Button Pin Raspberry Pi Header BCM GPIO Function
Leg 1 Pin 29 GPIO5 Signal (Active Low)
Leg 2 Pin 30 GND Ground Reference
  1. De-energize: Unplug the USB-C power cable from the Pi.
  2. Prep Wires: Strip 1/4 inch of insulation from both ends of two 22 AWG wires.
  3. Solder: Solder one wire to each leg of the momentary pushbutton. Polarity does not matter for a standard SPST switch.
  4. Connect to Header: Connect the first wire to Pin 29 (GPIO5) and the second to Pin 30 (GND) on the 40-pin header. If using a breadboard or terminal block, ensure a solid mechanical connection to prevent switch bounce.
  5. Verify: Use a multimeter in continuity mode. Place probes on Pin 29 and Pin 30. The meter should read 'OL' (open). Press the button; it should beep (read < 1 ohm).

The Python Shutdown Script & Systemd Service

To make this a true power switch for Raspberry Pi projects, the script must run in the background on boot. We use gpiozero for hardware abstraction and systemd for process management.

1. The Python Script

Create the file at /usr/local/bin/pi_safe_shutdown.py. Ensure you make it executable (sudo chmod +x /usr/local/bin/pi_safe_shutdown.py).

#!/usr/bin/env python3
"""
Safe Power Switch Script for Raspberry Pi 4 Model B
Target: GPIO5 (Pin 29)
"""
import sys
import os
import logging
from gpiozero import Button
from signal import pause

# Configure logging to track failures in journalctl
logging.basicConfig(
    filename='/var/log/pi_power_switch.log',
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

# Pin Definitions
GPIO_PIN = 5
HOLD_TIME = 2.0  # Seconds to hold to prevent accidental bumps

def safe_shutdown():
    logging.info('Shutdown button held. Initiating safe OS shutdown...')
    try:
        # Broadcast to any active SSH sessions
        os.system("wall 'System shutting down via GPIO switch in 5 seconds...'")
        os.system('sleep 5 && sudo shutdown -h now')
    except Exception as e:
        logging.error(f'Shutdown command failed: {e}')
        sys.exit(1)

if __name__ == '__main__':
    try:
        # pull_up=True relies on the Pi's internal 1.8k resistor on GPIO5
        # bounce_time=0.05 filters out mechanical switch contact bounce
        btn = Button(GPIO_PIN, hold_time=HOLD_TIME, pull_up=True, bounce_time=0.05)
        btn.when_held = safe_shutdown
        logging.info(f'Power switch listener active on GPIO {GPIO_PIN}')
        pause()  # Keeps the script running efficiently
    except Exception as e:
        logging.critical(f'Failed to initialize GPIO hardware: {e}')
        sys.exit(1)

2. The Systemd Service

Create /etc/systemd/system/pi_power_switch.service:

[Unit]
Description=Raspberry Pi Safe Power Switch GPIO Listener
After=multi-user.target

[Service]
Type=simple
ExecStart=/usr/bin/python3 /usr/local/bin/pi_safe_shutdown.py
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Enable and start the service:

sudo systemctl daemon-reload
sudo systemctl enable pi_power_switch.service
sudo systemctl start pi_power_switch.service

Debugging: Exact Errors & The First 3 Things to Check

When working with GPIO interrupts, the Linux kernel and Python libraries can clash. If your switch fails to trigger or the service crashes, check these exact error strings.

The First 3 Things to Check When It Fails:
1. Is I2C enabled? GPIO5 is the I2C SCL line. If you enabled the I2C interface in raspi-config, the kernel driver will lock the pin. Disable I2C if you aren't using it.
2. Is the service running as root? Accessing /dev/mem or /dev/gpiomem requires root privileges. Ensure the systemd service is not restricted by a non-root User directive.
3. Is the pin physically floating? If your wires are longer than 6 inches, parasitic capacitance can cause ghost triggers. Solder a 10kΩ pull-up resistor between Pin 29 and Pin 1 (3.3V).

Error: 'RuntimeError: Conflicting edge detection already enabled for this GPIO channel'

Ranked Causes:

  1. Ghost Process: You ran the Python script manually in the terminal, didn't kill it (Ctrl+C), and now the systemd service is trying to claim the same pin. Fix: Run sudo killall python3 and restart the service.
  2. I2C Bus Collision: The i2c_dev kernel module has claimed GPIO5. Fix: Run sudo raspi-config, go to Interface Options > I2C, and disable it. Reboot.

Error: 'gpiozero.exc.BadPinFactory: Unable to load any default pin factory'

Ranked Causes:

  1. Missing Dependencies: You are in a virtual environment that lacks the RPi.GPIO or lgpio backend. Fix: Install the backend via pip install rpi-lgpio (required for Bookworm OS).
  2. OS Incompatibility: You are running Raspberry Pi OS Bookworm, which deprecated the legacy RPi.GPIO library in favor of libgpiod. Fix: Ensure gpiozero is updated to v2.0+ and rpi-lgpio is installed.

Extending the Build: Cutting Standby Power

The script above safely halts the OS, but the Pi's power management IC (PMIC) remains active, drawing ~1.2W. If your project is battery-powered, you need to physically sever the 5V rail.

The Simplification (Inline USB-C Switch):
The easiest method is to wire a heavy-duty SPST toggle switch (rated for at least 3A) into the 5V and GND lines of a USB-C breakout board. Rule of operation: You may only flip this switch to 'OFF' after the Pi's green activity LED stops blinking and goes dark. To turn it back on, flip the switch to 'ON'.

The Extension (MOSFET Hardware Latch):
For a true 'one-button' smart power experience (press to boot, press to shut down and cut power), you must build a latching circuit. This involves a P-Channel MOSFET (e.g., IRF9540) on the 5V high-side, driven by an N-Channel MOSFET (e.g., 2N7000). The Pi holds the N-Channel gate HIGH via GPIO14 (TXD) while running. When the safe shutdown script executes, it drops GPIO14 LOW before halting, turning off the P-Channel and physically removing power. Pressing the momentary button bypasses the P-Channel momentarily to boot the Pi, allowing the boot script to latch the gate HIGH again. For a reliable off-the-shelf alternative to rolling your own MOSFET latch, look into the Pi pinout documentation and compatible HATs like the PiJuice or X735 power management boards.

FAQ: Power Switch for Raspberry Pi

Can I just wire a toggle switch to the 5V USB-C pins?

You can, but it is highly discouraged as your primary shutdown method. The Raspberry Pi OS uses a journaled file system (ext4), but sudden power loss during a write operation can still corrupt the superblock or leave the journal in a dirty state, requiring an fsck repair on the next boot. Always use a GPIO software shutdown script first, then cut the physical power.

Does the Raspberry Pi 5 need a custom GPIO power switch?

No. The Raspberry Pi 5 features a dedicated, physical power button located near the USB-C port on the PCB. It handles safe shutdown and hard power-cut natively via the new Renesas DA9098 PMIC. If you need to route a button to the outside of a custom case, you wire a momentary switch to the dedicated J2 power button header on the Pi 5 board, not to GPIO5.

How do I wake the Raspberry Pi back up after a GPIO shutdown?

When the Pi 4 enters a halted state via software shutdown, the 3.3V standby rail remains active. Shorting GPIO5 (Pin 29) to GND (Pin 30) again will signal the PMIC to wake the board and begin the boot sequence. This is exactly why we chose GPIO5 for this build—it is hardware-wired at the silicon level to support wake-from-halt.

Why does my SD card corrupt when I use a hard power switch?

MicroSD cards have internal wear-leveling controllers. When the Pi sends a write command, the data is buffered in the Pi's RAM and then flushed to the SD card's NAND flash in pages. If a hard power switch cuts the 5V rail while the SD card controller is in the middle of a page program or block erase cycle, the internal mapping table can become scrambled. Using an A1-rated SD card and ensuring you wait for the green LED to stop flashing before cutting power mitigates this risk.

Can I use a remote control instead of a physical button?

Yes. If your project is enclosed, you can replace the momentary pushbutton with an IR receiver (like the TSOP38238) wired to GPIO5, or use a Bluetooth Low Energy (BLE) beacon to trigger the shutdown script via a background Python daemon. The underlying systemd service and OS shutdown mechanics remain identical.