gpiodevice
A GPIO counterpart to i2cdevice, generated from the Pimoroni Python Boilerplate.
What is gpiodevice?
gpiodevice is a middleware library intended to make some user-facing aspects of interfacing with Linux's GPIO character device ABI (via gpiod) simpler and friendlier.
gpiodevice is not intended to replace gpiod, but collects some common patterns into a reusable library for GPIO-based Python projects.
Installing
We'd recommend using this library with Raspberry Pi OS Bookworm or later. It requires Python >=3.9.
gpiodevice is usually installed as a dependency of another library. To install gpiodevice:
- Set up a virtual environment:
python3 -m venv --system-site-packages $HOME/.virtualenvs/pimoroni - Switch to the virtual environment:
source ~/.virtualenvs/pimoroni/bin/activate - Install the library:
pip install gpiodevice
Development:
git clone https://github.com/pimoroni/gpiodevice-python
cd gpiodevice-python
./install.sh --unstable
Finding A gpiochip
A pin's /dev/gpiochip* varies between boards and kernel versions. These functions return a gpiod.Chip.
By Pin Name
import gpiodevice
chip = gpiodevice.find_chip_by_pins("GPIO4")
chip = gpiodevice.find_chip_by_pins(("GPIO4", "GPIO17"))
chip = gpiodevice.find_chip_by_pins("GPIO4,GPIO17")
Returns the first gpiochip carrying all of the named pins. Pin names are those reported by the kernel.
A pin claimed by another consumer counts as a failure. Pass ignore_claimed=True to match on the name alone.
By Chip Label
import gpiodevice
chip = gpiodevice.find_chip_by_label("pinctrl-rp1")
chip = gpiodevice.find_chip_by_label(("pinctrl-rp1", "pinctrl-bcm2711"))
Returns the first gpiochip whose label matches. Labels are matched as regular expressions.
import gpiodevice
chip = gpiodevice.find_chip_by_label("pinctrl-rp1", pins={"my sensor": "GPIO4"})
Supply pins to also require that those pins are free.
By Platform
import gpiodevice
chip = gpiodevice.find_chip_by_platform()
Reads the board model and matches the chip labels known for it. Raspberry Pi, Radxa, NVIDIA Jetson and the Alienware m15 are supported.
import gpiodevice
name = gpiodevice.platform.get_name()
labels = gpiodevice.platform.get_gpiochip_labels()
get_name returns the detected board name. get_gpiochip_labels returns the labels tried for it. Both raise RuntimeError on an unrecognised board.
Requesting Pins
import gpiod
import gpiodevice
from gpiod.line import Direction, Value
settings = gpiod.LineSettings(direction=Direction.OUTPUT)
request, offset = gpiodevice.get_pin("GPIO4", "my led", settings)
request.set_value(offset, Value.ACTIVE)
get_pin requests one pin by name. It finds the chip and resolves the name to a line offset. It returns the gpiod.LineRequest and that offset.
The second argument labels the pin. It forms part of the consumer name reported by gpioinfo.
pin also accepts:
- An int line offset. The platform's chip is used.
- A
(request, offset)tuple. This is returned unchanged.
import gpiod
import gpiodevice
from gpiod.line import Direction
settings = gpiod.LineSettings(direction=Direction.OUTPUT)
pins = gpiodevice.get_pins_for_platform({
"Raspberry Pi 5": {"my led": ("GPIO4", settings)},
"Raspberry Pi 4": {"my led": ("GPIO4", settings)},
})
get_pins_for_platform takes a mapping of platform name prefix to pins. It returns a list of (request, offset) for the entry matching the detected board.
Edge Detection
A pin must be requested with edge detection for any of these to see events.
wait_for_edge
import gpiod
import gpiodevice
from gpiod.line import Bias, Edge
settings = gpiod.LineSettings(edge_detection=Edge.FALLING, bias=Bias.PULL_UP)
request, offset = gpiodevice.get_pin("GPIO4", "my button", settings)
event = gpiodevice.wait_for_edge(request, line=offset, timeout=5.0)
Blocks until an edge arrives. Returns the gpiod event, or None on timeout.
timeoutis in seconds or atimedelta.Nonewaits indefinitely.linefilters events to one offset. Omit it to take the first event on any line.raise_on_timeoutraisesTimeoutErrorinstead of returningNone.
watch_pin
import time
import gpiodevice
from gpiod.line import Bias, Edge
def handle_button(event):
print(f"edge on line {event.line_offset}")
watch = gpiodevice.watch_pin(
"GPIO4",
edge=Edge.FALLING,
bias=Bias.PULL_UP,
debounce=0.02,
callback=handle_button,
)
try:
while True:
time.sleep(1.0)
finally:
watch.close()
Requests one pin and returns a started Watch.
callbackis called with thegpiodevent on each edge.debounceis in seconds or atimedelta.
watch_pin owns the request it made. close() stops the thread and releases the line.
Watch
import gpiod
import gpiodevice
from gpiod.line import Bias, Edge
BUTTONS = {"A": "GPIO5", "B": "GPIO6"}
settings = gpiod.LineSettings(edge_detection=Edge.FALLING, bias=Bias.PULL_UP)
chip = gpiodevice.find_chip_by_pins(tuple(BUTTONS.values()))
offsets = {label: chip.line_offset_from_id(pin) for label, pin in BUTTONS.items()}
request = chip.request_lines(
consumer="buttons",
config={offset: settings for offset in offsets.values()}
)
def handler(label):
return lambda event: print(f"button {label}")
with gpiodevice.Watch(request, {offset: handler(label) for label, offset in offsets.items()}) as watch:
input("Press Ctrl+C to exit!\n")
Watches a request you made yourself. Edges are dispatched on a background thread.
handlers is a mapping of line offset to callable. Pass a single callable to use it for every line. Edges on lines with no handler are ignored.
As a context manager the watch starts on entry and closes on exit. Otherwise call start() and stop(). start() is idempotent.
close() stops the thread. It also releases the request, but only if the Watch owns it. A Watch you construct does not, unless you pass manage_request=True.
Errors
import gpiodevice
chip = gpiodevice.find_chip_by_pins("GPIO4", fatal=False)
if chip is None:
...
The find_* functions and check_pins_available raise SystemExit with a digest of everything they tried:
Woah there, suitable gpiochip not found!
✅ GPIO22: (line 22) found - /dev/gpiochip4 (pinctrl-rp1)!
⚠️ GPIO22: (line 22, GPIO22) currently claimed by some-other-app
✅ GPIO22: (line 22) found - /dev/gpiochip0 (pinctrl-rp1)!
⚠️ GPIO22: (line 22, GPIO22) currently claimed by some-other-app
❌ GPIO22: not found - /dev/gpiochip13 (gpio-brcmstb@107d508520)!
❌ GPIO22: not found - /dev/gpiochip10 (gpio-brcmstb@107d508500)!
Pass fatal=False to return None instead. Set GPIODEVICE_DEBUG in the environment to raise a RuntimeError with a traceback.
import gpiodevice
chip = gpiodevice.find_chip_by_platform()
free = gpiodevice.check_pins_available(chip, {"my led": "GPIO4"}, fatal=False)
check_pins_available reports whether a set of pins are free. It does not request them.
Changelog
0.1.0
- New: Watch, watch_pin and wait_for_edge edge/interrupt helpers
- New: Nvidia Jetson platform support
- Fix: find_chip_by_pins raised SystemError instead of returning None when fatal=False
- Docs: document the API in README.md
0.0.5
- Add support for int type in get_pin
0.0.4
- Gracefully handle a tuple being passed to get_pin
- Match all pinctrl- gpiodevices for RPi in get_gpiochip_labels
0.0.3
- Deprecate the
friendly_errorsflag in favour of a newfatalflag on methods - Catch use of
intpin numbers from unported code and raise a friendly error
0.0.2
- Add platform detection
- ROCK 5B support
- Bug fixes
0.0.1
- Initial Release
Release files for gpiodevice 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| gpiodevice-0.1.0.tar.gz | 19.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| gpiodevice-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 32.0 kB
Release files / gpiodevice-0.1.0.tar.gz
| Download URL | gpiodevice-0.1.0.tar.gz |
|---|---|
| Size | 19.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
823185e2230aba7a015f00b9f8045840f8f9253d2d58c76950065f446eb417b2
|
|
BLAKE2b-256 checksum How to use checksums |
8c5a919e85d28bd761a53bfaec4e54eac217b216895473993fd0ee249a1f1ccc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / gpiodevice-0.1.0-py3-none-any.whl
| Download URL | gpiodevice-0.1.0-py3-none-any.whl |
|---|---|
| Size | 12.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4d1716ebed3068dd233e9851612773cbdf7a194289b8ea0aaf712f2fe90b1adb
|
|
BLAKE2b-256 checksum How to use checksums |
6eb018437d9b784086b70ff9503c18e8b51d6905d382f3c695f9b3c70f85e91d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|