LINUX GPIO DAEMON (GPIOD)
GPIOD is a lightweight system daemon for Linux designed to monitor events on GPIO (General Purpose Input/Output).
It supports both the modern uapi interface and the legacy sysfs, providing flexible event handling and integration capabilities.
Key Features
- Flexible Monitoring: Trigger events on rising edge, falling edge, both, or periodic polling with a configurable interval.
- Dual Event Handling:
- Network: Broadcast events to all connected TCP clients.
- Hooks: Execute any user-defined scripts or programs when a GPIO triggers, with support for argument passing.
- Flexible Configuration: Simple text-based configuration file (using libconfuse) and the ability to store hooks in a separate directory (e.g.,
/etc/gpiod.d/). - State Management: Support for signals to reload configuration (SIGHUP) and gracefully terminate.
- Optimized for Embedded Systems: Cross-compilation support and build flags to disable uapi if needed.
Requirements
- libconfuse (https://github.com/martinh/libconfuse.git)
- bats-core (https://github.com/bats-core/bats-core) for tests
Explanatory note
Concept
gpiod supports both uapi and sysfs interface and currently (although this is not recommended) it can be used both at the same time. Triggers can be set to rising edge, falling edge, both or be polled via configured interval.
On being triggered an event is dispatched to all connected clients.
gpiod fully acts as a daemon, through it needs privileges to be launched as gpio’s are not considered user-space.
Installation
Compiling
For version that supports both sysfs and uapi simply invoke:
$ make
For sysfs only version provide flag:
$ make WITHOUT_GPIOD_UAPI=1
For UAPI only version provide flag:
$ make WITHOUT_GPIOD_SYSFS=1
Both can be provided without error, despite it’s pointless.
Cross-Compiling
$ make CROSS_COMPILE=${TARGET_CROSS}-
# for example:
$ make CROSS_COMPILE=arm-none-linux-gnueabi-
Installing
$ make DESTDIR= prefix= install
Both DESTDIR and prefix should be used when installing to an extern rootfs.
By default /etc/gpio.d and /etc/gpiod.conf are not created - you can find different examples in etc directory of repository.
Tests
Some sanity tests are provided in tests. bats (https://github.com/bats-core/bats-core) is additionally required to perform tests.
Running
Following options can be passed:
-v, --version prints version and exits
-V, --verbose be more verbose
-l, --log-level set log level[default=LOG_INFO]
-n don't fork off as daemon
-k, --kill kill old daemon instance in case if present
-H, --hup send daemon signal to reload configuration
-p, --pid path to pid file[default=/var/run/gpiod.pid]
-c, --config=FILE configuration file[default=/etc/gpiod/gpiod.conf]
-d, --tabdir=DIR tabs location directory[default=/etc/gpiod.d/]
-h, --help prints this message
Configuration file
# (optional) listen on specific address, can be overriden by cmd args [default=127.0.0.1]
listen = 127.0.0.1
# (optional) listen on port, can be overriden by cmd args [default=1500]
port = 1500
# (optional) poll interval in msec, can be overriden by cmd args [default = 50]
poll = 50
gpio {
# facility to use for this gpio sysfs or uapi [default = uapi]
facility = uapi
# (optional) calculated system gpio offset [nodefault]
system = 256
# (optional) gpiochip name [nodefault]
gpiochip = gpiochip0
# (optional) local offset in gpiochip, can be negative [nodefault]
offset = 0
# (optional) local mapping [default=system]
local = 0
# label for passing via libpack i.e. WD0, WD1, ..., WDN
label = WD0
# rising, falling, both, polled [default=both]
edge = both
}
See etc/gpiod.conf.example for more detailed explanation or directly etc/gpiod.conf.sysfs for sysfs usage or gpiod.conf.uapi for uapi.
Hook tabs
gpiod supports launching user defined executables based on configured trigger files placed under /etc/gpio.d or specified by –tabdir,-d argument.
Tab files should be in form of :
[LABEL] [MODIFIERS] [PATH] [ARGS]
WD0 EDGE_RISING /tmp/test.sh -n --version -v 23 -s test $@ $% $& $t
WD1 EDGE_RISING /tmp/test.sh $@
WD2 EDGE_BOTH /tmp/test.sh $%
WD3 EDGE_RISING /tmp/test.sh $&
WD4 EDGE_RISING /tmp/test.sh $t
WD5 EDGE_BOTH,NO_LOOP /tmp/sleep_test.sh 10
WD6 EDGE_BOTH,ONESHOT /tmp/test.sh $t
WD7 EDGE_RISING /tmp/sleep_test.sh 100
WD8 EDGE_FALLING /tmp/sleep_test.sh 100
Where LABEL is pin label defined in config file i.e. label =, MODIFIERS one of EDGE_RISING, EDGE_FALLING, EDGE_BOTH and (optional) NO_LOOP, ONESHOT flags.
NO_LOOP prevents launching before previous launch has finished and ONESHOT to launch hook only once, which can be rearmed by sendign HUP signal to daemon:
$ ./gpiod -H
# or
$ kill -HUP <pid>
see etc/gpio.d/testtab example.
Acknowledgements
Dedicated to the memory of Alexander Sergeevich Kolontaev.
You were a wonderful friend, teacher, and colleague. Your wisdom, guidance, and friendship have left a lasting impact. This work, like so much else, stands on the shoulders of your generosity and mentorship. You are deeply missed.
Copyright
© 2026 Nikita Shubin