The Decision Path: Choosing a C GPIO Library for Pi 5

If you are writing C code for Raspberry Pi GPIO in 2026, the hardware landscape has fundamentally shifted. The Raspberry Pi 5 uses the RP1 southbridge chip for peripheral routing, meaning older libraries that relied on direct memory mapping to the Broadcom SoC will silently fail or segfault. You must choose a library that interfaces with the Linux gpiochip character device.

Library Pi 5 Compatibility Architecture Verdict
wiringPi None (Abandoned) Direct Memory / sysfs Avoid. Deprecated since 2019.
bcm2835 Fails (RP1 blocker) Direct Memory Mapping Avoid. Will crash on Pi 5.
pigpio Partial (Daemon required) Socket / Daemon Use only if you need legacy PWM support and don't mind running pigpiod.
lgpio Native (Full Support) /dev/gpiochip ioctl Concrete Pick. The modern standard for Pi 4 and Pi 5.

The Default Recommendation: Use lgpio. It bypasses the need for a background daemon, talks directly to the Linux kernel's GPIO character device, and natively supports the RP1 chip's gpiochip4 interface on the Pi 5.

Hardware Spec Sheet and Pin Mapping

This build targets the Raspberry Pi 5 (8GB variant) running Raspberry Pi OS (Bookworm or later). The Pi 5's 40-pin header is physically identical to previous generations, but internally, the BCM GPIO numbers map to the RP1 chip.

Parts List

  • Board: Raspberry Pi 5 (8GB) with official 27W USB-C PD power supply.
  • LED: 5mm Diffused Red LED (2.0V forward voltage, 20mA max).
  • Resistor: 330Ω 1/4W Carbon Film (drops 3.3V to safe LED current: ~10mA).
  • Switch: 6x6mm Momentary Tactile Switch (4-pin DIP).
  • Wiring: 22 AWG solid core jumper wires.

Pin Mapping Table

Component Physical Pin BCM GPIO RP1 Function
LED Anode (via 330Ω) 11 17 Output
LED Cathode 9 GND Ground
Button Pin 1 13 27 Input (Internal Pull-Up)
Button Pin 2 14 GND Ground
Callout Tip: Never wire an LED directly to a Pi GPIO pin without a current-limiting resistor. The RP1 chip can source up to 8mA per pin safely, but a dead short will fry the RP1 silicon, and unlike older Pi models, the RP1 is a separate BGA chip that is nearly impossible to hot-air rework at home.

Complete Compilable C Code with Error Handling

Before compiling, install the lgpio development headers on your Pi:

sudo apt update
sudo apt install liblgpio-dev

Save the following code as gpio_monitor.c. This program claims GPIO 17 as an output and GPIO 27 as an input with an internal pull-up. It polls the button state and toggles the LED, including basic software debouncing.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <lgpio.h>

// Pin definitions mapped to BCM numbering
#define LED_PIN 17
#define BTN_PIN 27

// Pi 5 routes the 40-pin header through the RP1 chip, which registers as gpiochip4
// Pi 4 and older use gpiochip0
#define GPIO_CHIP 4 

int main() {
    // 1. Open the GPIO chip
    int h = lgGpiochipOpen(GPIO_CHIP);
    if (h < 0) {
        fprintf(stderr, "Error: lgGpiochipOpen failed on chip %d. Error code: %d\n", GPIO_CHIP, h);
        fprintf(stderr, "Are you on a Pi 5? If on Pi 4, change GPIO_CHIP to 0.\n");
        return 1;
    }

    // 2. Claim LED pin as Output (initial state: 0 / LOW)
    int err_led = lgGpioClaimOutput(h, 0, LED_PIN, 0);
    if (err_led < 0) {
        fprintf(stderr, "Failed to claim LED pin %d: %d\n", LED_PIN, err_led);
        lgGpiochipClose(h);
        return 1;
    }

    // 3. Claim Button pin as Input with Internal Pull-Up
    int err_btn = lgGpioClaimInput(h, LG_GPIO_PULL_UP, BTN_PIN);
    if (err_btn < 0) {
        fprintf(stderr, "Failed to claim Button pin %d: %d\n", BTN_PIN, err_btn);
        lgGpiochipClose(h);
        return 1;
    }

    printf("Monitoring button on GPIO %d. Press Ctrl+C to exit.\n", BTN_PIN);
    
    int last_state = 1; // Pull-up means default is HIGH (1)
    
    // 4. Main polling loop
    while (1) {
        int btn_state = lgGpioRead(h, BTN_PIN);
        
        // Detect falling edge (button pressed, pulled to GND)
        if (btn_state == 0 && last_state == 1) {
            printf("Button Pressed! Toggling LED.\n");
            int current_led = lgGpioRead(h, LED_PIN);
            lgGpioWrite(h, LED_PIN, !current_led);
            usleep(200000); // 200ms software debounce delay
        }
        
        last_state = btn_state;
        usleep(10000); // 10ms loop yield to prevent CPU thrashing
    }

    // 5. Cleanup (Note: Ctrl+C bypasses this, see signal handling for production)
    lgGpiochipClose(h);
    return 0;
}

Compile and Run:

gcc -o gpio_monitor gpio_monitor.c -llgpio
./gpio_monitor

Debugging: Fixing 'lgGpiochipOpen' and Permission Errors

When writing C code for Raspberry Pi GPIO, the most common point of failure is the chip initialization. If your code compiles but fails at runtime, you will likely see this exact error string:

Error: lgGpiochipOpen failed on chip 4. Error code: -1
or
lgGpiochipOpen: /dev/gpiochip4: No such file or directory

The First Three Things to Check

  1. Verify your board generation: Run ls /dev/gpiochip* in the terminal. If you only see gpiochip0, you are on a Pi 4 or older. Change #define GPIO_CHIP 4 to 0 in the C code and recompile.
  2. Check user group permissions: The /dev/gpiochip* devices are owned by the gpio group. If your user isn't in this group, the kernel blocks access. Fix it with: sudo usermod -aG gpio $USER, then log out and log back in.
  3. Confirm the library is linked: If you get undefined reference to lgGpiochipOpen during compilation, you forgot the -llgpio flag at the end of your gcc command.

Ranked Causes for Runtime Failures

Rank Cause Fix
1 Wrong GPIO Chip ID (Using 4 on a Pi 4) Change GPIO_CHIP macro to 0.
2 Missing gpio group permissions Add user to group and reboot/re-login.
3 Pin already claimed by another process Run sudo fuser -v /dev/gpiochip4 to find and kill the offending daemon (like a lingering Python script).
4 Outdated kernel missing RP1 overlays Run sudo apt full-upgrade to pull the latest Pi 5 device tree overlays.

Extending and Simplifying the Build

Once you have the baseline C code running, you will inevitably need to adapt it for production or more complex hardware.

How to Simplify: Drop the Polling Loop

Polling a GPIO pin in a while(1) loop wastes CPU cycles. The lgpio library supports asynchronous alerts. You can simplify the build by replacing the polling loop with a callback function using lgGpioSetAlertsFunc(). This hands the monitoring off to the kernel, dropping your C program's CPU usage to effectively zero while waiting for a button press.

How to Extend: Add Hardware PWM for Motor Control

If you decide to swap the LED for a servo motor or a DC motor driver (like the DRV8871), you need Pulse Width Modulation. Unlike pigpio which relies on software-timed PWM via a daemon, lgpio can claim the Pi 5's dedicated hardware PWM channels.

To extend the code for a servo on BCM GPIO 18 (Physical Pin 12):

// Claim hardware PWM (Chip 4, GPIO 18, 50Hz for servos)
lgPwm(h, 18, 50, 0.5, 0); // 50Hz, 50% duty cycle (approx 1.5ms pulse)

Always consult the official lgpio C API documentation for the exact parameter ordering, as PWM initialization requires specifying the frequency, duty cycle, and offset flags explicitly.

Summary Card: Writing C code for Raspberry Pi 5 requires acknowledging the RP1 architecture shift. By standardizing on lgpio, targeting gpiochip4, and handling character device permissions correctly, you bypass the legacy traps that break older tutorials. Keep your pin definitions macro-based, always check the return integers of lgGpioClaim* functions, and your embedded C projects will run natively and reliably.

For deeper kernel-level insights into how the character device maps to the RP1 silicon, refer to the Linux Kernel GPIO Character Device documentation.