If you want to learn how to code with Raspberry Pi for physical computing, the most reliable path is using Python 3 with the gpiozero library on a Raspberry Pi 5. While older tutorials rely on legacy libraries that break on modern hardware, the Pi 5’s new RP1 southbridge architecture requires a modern approach to GPIO (General Purpose Input/Output) control. This guide walks you through a pushbutton-triggered LED circuit, providing the exact hardware specs, wiring diagrams, and production-ready Python code with built-in error handling.
Project Spec Sheet & Parts List
| Parameter | Specification |
|---|---|
| Difficulty Rating | Beginner (2/5) |
| Estimated Time | 45 minutes |
| Target Board Variant | Raspberry Pi 5 (4GB or 8GB variant) |
| Operating System | Raspberry Pi OS (64-bit, Bookworm or later) |
| Primary Language | Python 3.11+ |
Required Hardware
- Raspberry Pi 5 (4GB) (~$60 USD). The 8GB variant works identically but costs ~$80.
- Official 27W USB-C PD Power Supply (~$12 USD). Critical: The Pi 5 requires a 5V/5A Power Delivery supply to enable the full 1.6A downstream USB current. A standard 5V/3A phone charger will boot the board but throttle USB ports to 600mA.
- 330Ω Through-Hole Resistor (1/4W, 5% tolerance).
- 5mm Red LED (Standard 20mA forward voltage ~2.0V).
- 12mm Tactile Pushbutton (4-pin, momentary normally-open).
- Half-Size Solderless Breadboard and M-F (Male-to-Female) Jumper Wires.
Pin Mapping & Hardware Wiring
The Raspberry Pi uses two numbering systems: Physical Pin (1-40) and Broadcom (BCM) GPIO numbering. Python libraries like gpiozero default to BCM. Always double-check your physical board layout against the BCM map to avoid shorting 3.3V logic to 5V power rails.
| Component | Pi 5 Physical Pin | BCM GPIO | Function / Notes |
|---|---|---|---|
| LED Anode (+) | Pin 12 | GPIO 18 | Digital Out / PWM capable |
| LED Cathode (-) | Pin 14 | GND | Ground via 330Ω resistor |
| Button Leg 1 | Pin 16 | GPIO 23 | Digital In (Internal Pull-up) |
| Button Leg 2 | Pin 20 | GND | Ground reference |
Wiring Steps
- Prep the Board: Ensure the Pi 5 is completely powered down and unplugged from the wall. Never wire GPIO pins while the board is energized.
- Wire the LED: Insert the LED into the breadboard. Connect a jumper from Physical Pin 12 (GPIO 18) to the LED's anode (long leg). Connect the 330Ω resistor to the cathode (short leg), and route the other end of the resistor to Physical Pin 14 (GND).
- Wire the Button: Straddle the tactile button across the breadboard's center trench. Connect one side of the button to Physical Pin 16 (GPIO 23). Connect the opposite side to Physical Pin 20 (GND).
- Verify: Visually trace every wire back to the Pi header. A misplaced 5V wire into a GPIO pin will instantly destroy the Pi 5's RP1 chip.
The Code: Python GPIO Control
To code with Raspberry Pi 5 effectively, we use gpiozero. On the Pi 5, this library automatically routes commands through the lgpio backend, which interacts safely with the Linux kernel's character device interface rather than accessing raw memory.
sudo apt update && sudo apt install python3-gpiozero python3-rpi-lgpio
Create a file named gpio_control.py and paste the following complete, compilable code:
import sys
import logging
from gpiozero import LED, Button
from signal import pause
# Configure basic logging for terminal output
logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
# --- PIN DEFINITIONS (BCM Numbering) ---
LED_PIN = 18
BUTTON_PIN = 23
def main():
"""Initialize hardware and map button events to LED state."""
try:
# Initialize LED on GPIO 18
led = LED(LED_PIN)
# Initialize Button on GPIO 23
# pull_up=True uses the internal resistor to keep the pin HIGH until pressed to GND
# bounce_time=0.05 filters out mechanical switch chatter (50ms debounce)
button = Button(BUTTON_PIN, pull_up=True, bounce_time=0.05)
# Map hardware events to software functions
button.when_pressed = led.on
button.when_released = led.off
logging.info("System initialized. Press the tactile button to illuminate the LED.")
logging.info("Press Ctrl+C to exit safely.")
# Keep the script running in the background without blocking the CPU
pause()
except KeyboardInterrupt:
logging.info("Interrupt received. Cleaning up GPIO and exiting.")
sys.exit(0)
except Exception as e:
logging.error(f"Hardware initialization or runtime failure: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
Run the script from your terminal using python3 gpio_control.py. Press the button; the LED should illuminate instantly with no flicker, thanks to the 50ms software debounce.
Debugging: First Three Things to Check When It Fails
When learning how to code with Raspberry Pi, hardware-software integration errors are inevitable. If your script crashes, check these three specific failure modes in order.
1. The Pin Factory Error
Exact Error String: gpiozero.exc.BadPinFactory: Unable to load any default pin factory!
Ranked Causes:
- Missing Pi 5 Backend: You are on a Pi 5 but lack the
rpi-lgpiopackage. The Pi 5 cannot use the legacyRPi.GPIOmemory-mapped factory. Fix: Runsudo apt install python3-rpi-lgpio. - Virtual Environment Isolation: You installed the OS-level packages via
apt, but you are running your script inside a Pythonvenvthat doesn't have access to system site-packages. Fix: Recreate your venv withpython3 -m venv --system-site-packages myenv.
2. The Memory Access Error
Exact Error String: RuntimeError: No access to /dev/mem. Try running as root!
Ranked Causes:
- Using Legacy RPi.GPIO: You are trying to
import RPi.GPIOon a Pi 5. The Pi 5's RP1 chip routes GPIO through an external PCIe-connected southbridge; direct/dev/memregister access is physically blocked by the hardware architecture. Fix: Rewrite your code usinggpiozeroor the nativelgpiolibrary. - Permissions on Older Pis: If you are actually on a Pi 4 and see this, your user isn't in the
gpiogroup. Fix: Runsudo usermod -aG gpio $USERand reboot.
3. The Ghost Presses (Hardware Bounce)
Symptom: No terminal error, but pressing the button once toggles the LED on and off rapidly, or triggers your function 4-5 times.
Ranked Causes:
- Missing Debounce: Mechanical switches physically bounce their metal contacts for milliseconds before settling. Fix: Ensure
bounce_time=0.05(or higher) is set in yourButton()initialization. - Floating Pin: You wired the button but forgot to enable the internal pull-up resistor, leaving the pin susceptible to electromagnetic noise. Fix: Verify
pull_up=Trueis in your code.
Extending and Simplifying the Build
Once you understand the baseline of how to code with Raspberry Pi using physical pins, you can scale the project up or down based on your needs.
How to Simplify
If you are struggling with terminal execution, use the Thonny Python IDE (pre-installed on Raspberry Pi OS). Thonny allows you to step through the code line-by-line and inspect the state of the led and button objects in real-time. You can also use the built-in "Plotter" to visualize the button's boolean state changes as a waveform, which is invaluable for debugging hardware bounce.
How to Extend
To move from simple digital I/O to environmental monitoring, swap the pushbutton for an I2C BME280 Sensor (approx. $8). Connect the sensor's SDA to Physical Pin 3 (GPIO 2) and SCL to Physical Pin 5 (GPIO 3). Install the adafruit-circuitpython-bme280 library via pip. You can then modify the when_pressed callback to read the I2C bus and print temperature, humidity, and barometric pressure to an MQTT broker for home automation integration.
Frequently Asked Questions
What is the easiest programming language to learn how to code with Raspberry Pi?
Python is universally the easiest and most supported language for Raspberry Pi physical computing. The gpiozero library abstracts complex C-level hardware interactions into readable, object-oriented Python code. While Node.js (via onoff) and C++ are options, 95% of community tutorials, sensor drivers, and debugging forums assume you are using Python 3.
Can I use C++ instead of Python for Raspberry Pi GPIO control?
Yes, but it requires a steeper learning curve. On the Pi 5, you should use the libgpiod C/C++ bindings rather than legacy memory-mapping libraries like WiringPi (which is largely abandoned). C++ is preferred only when you need deterministic, microsecond-level timing for custom motor control or high-frequency signal generation, as Python's garbage collection can introduce unpredictable micro-delays.
Why does my Raspberry Pi 5 GPIO code fail when my Pi 4 code worked?
The Raspberry Pi 5 uses a custom RP1 southbridge chip connected via PCIe to handle all peripheral I/O, including GPIO, USB, and Ethernet. Older libraries like RPi.GPIO and WiringPi relied on directly manipulating the memory addresses of the Broadcom SoC's GPIO registers. Because the physical hardware architecture changed entirely on the Pi 5, those memory addresses no longer exist. You must use modern libraries that rely on the Linux kernel's standard libgpiod character device interface.
Do I need to solder headers to code with Raspberry Pi?
The Raspberry Pi 5 ships without the 40-pin GPIO header pre-soldered to accommodate low-profile use cases (like the official Active Cooler). If you bought a bare Pi 5 board, you must solder the 40-pin header yourself using a soldering iron set to 350°C with rosin-core flux, or purchase the "Raspberry Pi 5 with pre-soldered headers" variant from authorized resellers. Without the header, you cannot access physical GPIO pins for breadboard wiring.






