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.
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 |
- De-energize: Unplug the USB-C power cable from the Pi.
- Prep Wires: Strip 1/4 inch of insulation from both ends of two 22 AWG wires.
- Solder: Solder one wire to each leg of the momentary pushbutton. Polarity does not matter for a standard SPST switch.
- 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.
- 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.
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:
- Ghost Process: You ran the Python script manually in the terminal, didn't kill it (Ctrl+C), and now the
systemdservice is trying to claim the same pin. Fix: Runsudo killall python3and restart the service. - I2C Bus Collision: The
i2c_devkernel module has claimed GPIO5. Fix: Runsudo raspi-config, go to Interface Options > I2C, and disable it. Reboot.
Error: 'gpiozero.exc.BadPinFactory: Unable to load any default pin factory'
Ranked Causes:
- Missing Dependencies: You are in a virtual environment that lacks the
RPi.GPIOorlgpiobackend. Fix: Install the backend viapip install rpi-lgpio(required for Bookworm OS). - OS Incompatibility: You are running Raspberry Pi OS Bookworm, which deprecated the legacy
RPi.GPIOlibrary in favor oflibgpiod. Fix: Ensuregpiozerois updated to v2.0+ andrpi-lgpiois 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.






