Migrating Raspberry Pi GPIO Scripts from sysfs to libgpiod on Bookworm
Legacy /sys/class/gpio GPIO is deprecated on Raspberry Pi OS Bookworm. Learn to detect chips, map BCM lines, and toggle GPIOs with gpiodetect, gpioinfo, gpioset, and gpioget.
10 Aug 2025, 01:36 UTC

The problem: your GPIO script stopped working after upgrading to Bookworm
If you upgraded a Raspberry Pi to Raspberry Pi OS Bookworm or later and a Python or shell script that toggles an LED or reads a button now fails with "permission denied" or simply does nothing, the cause is likely the GPIO interface, not your code. The legacy sysfs interface at /sys/class/gpio is deprecated and often disabled on newer kernels. The supported replacement is libgpiod, a character-device API that talks to /dev/gpiochip* directly.
What libgpiod changes
With sysfs, you exported a pin by writing its number to /sys/class/gpio/export, then read or wrote files in /sys/class/gpio/gpioN/. libgpiod removes the export/unexport step. You open a chip device such as /dev/gpiochip0 and request a line by its offset. The gpiod command-line package includes gpiodetect, gpioinfo, gpioset, and gpioget.
One constant remains: Raspberry Pi GPIO numbers are Broadcom (BCM) numbers, not physical header pin numbers. BCM 17 is physical pin 11 on the 40-pin header, for example. Always verify with gpioinfo before wiring hardware.
Worked example: detect, inspect, and toggle a GPIO line
Run these commands on the Raspberry Pi itself.
- Confirm libgpiod is present. Run gpiodetect as a normal user.
gpiodetect
Expected output includes a line such as gpiochip0 - 54 lines - bcm2835. If nothing appears, libgpiod or the kernel GPIO driver may be missing.
- List lines and consumers. Run gpioinfo gpiochip0.
gpioinfo gpiochip0
Look for the BCM offset you plan to use. The output shows line name, direction, active state, and any consumer such as i2c1 or uart1. A line already claimed by a device-tree overlay should not be toggled manually.
- Toggle an unused line. Assuming line 17 is free, set it high:
gpioset gpiochip0 17=1
Then read it back:
gpioget gpiochip0 17
Expected output is 1. Set it low with gpioset gpiochip0 17=0.
Permissions: Access to /dev/gpiochip devices may require membership in the gpio group or a udev rule. If gpioset fails with a permission error, add your user to the group with sudo usermod -aG gpio $USER, then log out and back in. Avoid using sudo for routine GPIO toggling unless the image requires it; root access makes accidental short circuits easier to cause.
Reclaiming a line reserved by an overlay
Device-tree overlays in config.txt can assign GPIO pins to UART, I2C, SPI, or PWM. A line that appears free in a minimal image may be claimed on a desktop image. Inspect the file before choosing a pin:
- Raspberry Pi 5: /boot/firmware/config.txt
- Raspberry Pi 4 and earlier: /boot/config.txt
Look for dtoverlay= or dtparam= entries. If you need a line currently assigned to a peripheral, back up the file first, edit it to remove or comment the overlay, then reboot. A typo in config.txt can prevent booting, so keep the backup available so you can restore it if the Pi fails to start.
Limitations
libgpiod is the supported userspace interface, but not every legacy library has been updated. Older Python GPIO packages that depend on sysfs will not work without modification. Also, gpioset changes state immediately and does not provide edge-detection or interrupt convenience; for event-driven input, use the libgpiod event API in C or a modern Python binding such as lgpio or gpiozero rather than polling gpioget in a loop.
Checklist before you deploy
- Run gpiodetect and confirm bcm2835 appears.
- Run gpioinfo gpiochip0 and confirm your target BCM line has no consumer.
- Check config.txt for overlays that claim the pin.
- Test with gpioset and gpioget, then observe with a multimeter or logic probe if available.
- Add your user to the gpio group if command-line tools lack permission.
Switching to libgpiod means replacing export/unexport habits with direct chip and line access. Once you verify the chip exists and the line is unclaimed, the new commands are shorter and more reliable than the deprecated sysfs paths.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.