Jungle drums say that the long-deprecated sysfs
access to GPIO is definitely ending soon. The drums also say that the "character-based interface" (??) aka libgpiod
is the only game in town for interfacing with GPIO once the ver 6.6 kernel is released. bookworm
is the current distribution, but as is typical some are holding out - sticking with their bullseye
systems (aka old-stable) - until most of the kinks are worked out of bookworm
. I am one of the hold-outs, but I wanted to test the latest version of libgpiod
.
In the research for this recipe, two (2) limits or qualifications on libgpiod
usage have been found:
-
libgpiod
is not intended for use in specific or specialized usage of GPIO pinsFor example,
libgpiod
is not designed or intended to support (for example) an I2C interface, or an SPI interface. These specialized/specific interfaces are generally realized in custom drivers, or in the hardware itself, and "pinned out" to GPIO pins. Refer to kernel documentation for I2C, PWM, 1-wire, SPI, etc. for these specific interfaces. This "limitation" oflibgpiod
will be obvious to some, but not-so-obvious to others; e.g. those that started out using a GPIO library such as WiringPi. -
libgpiod
is not intended for use in "production systems"This one was a bit of a surprise; learned from reading this passage which is part of the Linux Kernel documentation:
Do not under any circumstances abuse the GPIO userspace ABI to cut corners in any product development projects. If you use it for prototyping, then do not productify the prototype: rewrite it using proper kernel drivers. Do not under any circumstances deploy any uniform products using GPIO from userspace.
I would characterize this limitation as a recommendation, but as it comes from the Linux Kernel development organization/team (?), deserves our consideration.
Some reviews of libgpiod
have been quite negative. I tried it myself back in 1Q 2022, and found it confusing. But those were early days. Approximately two years of libgpiod
development have taken place since then, and a hard deadline is just around the corner. It is said that the release of Linux 6.6 marks the end of the old sysfs
interface (i.e. it will no longer work). Surely things have improved?
An early criticism of libgpiod was its lack of persistence; i.e. switch a GPIO line to HIGH (e.g. to drive an LED), and it appeared that nothing happened. The man gpioset
document explained it like this:
Note: the state of a GPIO line controlled over the character device reverts to default when the last process referencing the file descriptor representing the device file exits. This means that it's wrong to run gpioset, have it exit and expect the line to continue being driven high or low. It may happen if given pin is floating but it must be interpreted as undefined behavior.
Apparently what is meant by this indistinct explanation is that libgpiod
is designed to leave the persistence issue up to the platform-dependent GPIO driver. Apparently this took some time to get resolved due to insufficient communication between the kernel-based libgpiod team and the Raspberry Pi-based driver team. It was eventually addressed in 1Q 2023 by the RPi team in a change to their pinctrl
code.
Another issue that has dogged libgpiod is the fact that in 2024, the Debian/RPi current ('bookworm') version of the library is nearly 3 years old. This is likely a case of unfortunate timing as the libgpiod ver 2
release just missed the cutoff for 'bookworm'. That's all I can say - I'm not familiar with Debian/RPi policies re upgrades.
As it turns out, the current version of libgpiod
on bookworm
is ver 1.6.3, and the current version of libgpiod
on bullseye
is ver 1.6.2. However, the Linux kernel git
repository has ver 2.1. We don't know when (or if) the RPi organization will release ver 2.1 for RPi OS, but given the depth of changes in ver 2.1 it seems that any worthwhile testing should be done against the latest version (ver 2.1).
I've built the ver 2.1 libgpiod
- and documented the steps I followed - in this recipe. N.B.: I am not a developer, so I'd appreciate hearing from anyone with better ideas or shortcuts for this recipe. Otherwise, you can follow my build recipe if you want to build this yourself, and run your own tests.
One other note: The ver 2.1 libgpiod tarball contains a lengthy script for testing the results of the build; it's located at ~/libgpiod-2.1/tools/gpio-tools-test.bash
. I did not run this test b/c I couldn't figure out exactly how to run it!
It should be said first that the testing here is not on libgpiod
per se, but on the "tools" included in the distribution. These tools were written by the libgpiod
dev team, and In that sense the "tools" are used here as proxies for libgpiod ver 2.1
testing :
tool | ||
---|---|---|
gpioget | gpiomon | gpioinfo |
gpioset | gpionotify | gpiodetect |
The circuit used for this part of the test is shown below, and as you can see it is simple. R1 & R2 are sized to current limit to something in the range of 10mA when the GPIO line is HIGH/1 (3V3), but bright enough to see clearly. We'll do this testing on an RPi 3A+ using the command line tools from the terminal/pty
.
$ gpiodetect --version
gpiodetect (libgpiod) v2.1
Copyright (C) 2017-2023 Bartosz Golaszewski
License: GPL-2.0-or-later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ gpiodetect
gpiochip0 [pinctrl-bcm2835] (54 lines)
gpiochip1 [raspberrypi-exp-gpio] (8 lines)
$ gpioinfo --version
gpioinfo (libgpiod) v2.1
# ... blah, blah, blah
$ gpioinfo
gpiochip0 - 54 lines:
line 0: "ID_SDA" input
line 1: "ID_SCL" input
line 2: "SDA1" input
line 3: "SCL1" input
line 4: "GPIO_GCLK" input
line 5: "GPIO5" input
line 6: "GPIO6" input
line 7: "SPI_CE1_N" input
line 8: "SPI_CE0_N" input
line 9: "SPI_MISO" input
line 10: "SPI_MOSI" input
line 11: "SPI_SCLK" input
line 12: "GPIO12" input
line 13: "GPIO13" input
line 14: "TXD0" input
line 15: "RXD0" input
line 16: "GPIO16" input
line 17: "GPIO17" input
line 18: "GPIO18" input
line 19: "GPIO19" input
line 20: "GPIO20" input
line 21: "GPIO21" input
line 22: "GPIO22" input
line 23: "GPIO23" input
line 24: "GPIO24" input
line 25: "GPIO25" input
line 26: "GPIO26" input
line 27: "GPIO27" input
line 28: "SDA0" input active-low consumer="hpd"
line 29: "SCL0" output consumer="ACT"
line 30: "NC" input
line 31: "LAN_RUN" input
line 32: "CAM_GPIO1" input
line 33: "NC" input
line 34: "NC" input
line 35: "PWR_LOW_N" input
line 36: "NC" input
line 37: "NC" input
line 38: "USB_LIMIT" input
line 39: "NC" input
line 40: "PWM0_OUT" input
line 41: "CAM_GPIO0" input
line 42: "NC" input
line 43: "NC" input
line 44: "ETH_CLK" input
line 45: "PWM1_OUT" input
line 46: "HDMI_HPD_N" input
line 47: "STATUS_LED" output
line 48: "SD_CLK_R" input
line 49: "SD_CMD_R" input
line 50: "SD_DATA0_R" input
line 51: "SD_DATA1_R" input
line 52: "SD_DATA2_R" input
line 53: "SD_DATA3_R" input
gpiochip1 - 8 lines:
line 0: "BT_ON" output
line 1: "WL_ON" output
line 2: "PWR_LED_R" output consumer="PWR"
line 3: "LAN_RUN" input
line 4: "NC" input
line 5: "CAM_GPIO0" output consumer="cam1_regulator"
line 6: "CAM_GPIO1" output
line 7: "NC" input
We can see that our chosen GPIO lines GPIO24, GPIO25
(from the schematic above) is identified as follows:
line 24: "GPIO24" input
line 25: "GPIO25" input
$ gpioget 24
gpioget: cannot find line '24'
$ gpioget 0 24
gpioget: cannot find line '0'
gpioget: cannot find line '24'
$ gpioget gpiochip0 24
gpioget: cannot find line 'gpiochip0'
gpioget: cannot find line '24'
$ gpioget -c 0 24
"24"=inactive # BINGO!!
$ gpioget GPIO24
"GPIO24"=inactive # BINGO!!
Interesting... It seems the gpioget
man page is misleading. The "SYNOPSIS" in the man
page is:
gpioget [OPTIONS] <line>...
But <line>
isn't well-defined (at least not in the man
). Theman
says:
Lines are specified by name, or optionally by offset if the chip option is provided
That only helps if you happen to know that the name is GPIO24
. You might guess that from the output of gpioinfo
, but then again you might not. At any rate, we now know that GPIO24
is a line name, and if you wish to refer to it as 24
, the -c
option is required to specify the chip number - which may be 0
, or gpiochip0
or /dev/gpiochip0
.
Another thing that strikes me as oddly dysfunctional about gpioget
is that it is not strictly a get; i.e. it is not a read-only function; it actually sometimes behaves as if it were gpioset
, and changes the state of the GPIO line under some circumstances. We'll see that in a moment.
$ gpioset GPIO24=1 GPIO25=1
# both LEDs illuminated, but bash prompt does not return...???
# ^C restores the bash prompt, & LEDs remain illuminated (persistence - good!; loss of prompt - BAD!)
# Loss of the bash prompt would seem to mean that it
# will be very awkward to use gpioset in a script!!
^C
$ gpioset GPIO24=0
# the 24 LED Green is extinguished, and as before, the bash prompt does not return
^C
$ gpioset GPIO24=1 # LED illuminated, ^C to return prompt
^C
# now try gpioget on GPIO24:
$ gpioget GPIO24 # LED Extinguishes; i.e. gpioget changed state of GPIO24
"GPIO24"=inactive # Here, we see gpioget acting more like "gpioset",
# which strikes me as unconventional, but ... ???
$ gpioset -z GPIO24=1 # The -z (-daemonize) option illuminates LED (persistently), and bash prompt returns
$ gpioset GPIO24=0
gpioset: unable to request lines on chip '/dev/gpiochip0': Device or resource busy
$ gpioget GPIO24
gpioget: unable to request lines: Device or resource busy
# Hmm... seems a "ps hunt and kill drill" is required
$ ps -aux | grep 'gpioset'
pi 7386 0.0 0.0 2760 148 ? Ss 08:50 0:00 gpioset -z GPIO24 1
$ kill 7386 # LED remains illuminated. :)
$ gpioget GPIO24
"GPIO24"=inactive # LED extinguishes
# Let's try this:
$ gpioset GPIO24=1 & # put job in background
[1] 7439 # get the PID for "free"
$ $ gpioget GPIO24
gpioget: unable to request lines: Device or resource busy
$ kill 7439 # no 'ps -aux | grep' required
$ gpioget GPIO24
"GPIO24"=inactive
[1]+ Terminated gpioset GPIO24=1
# Wonder what the '-z' option does that background doesn't?
# The '-t' / '--toggle' option is nice for a repetitive sequence
#
$ gpioset -t 100ms,500ms,200ms,500ms,300ms,500ms,400ms,500ms,500ms,500ms GPIO24=1 &
[1] 7471 # background (&) enables return prompt
$ gpioget GPIO24
gpioget: unable to request lines: Device or resource busy
$ kill 7471
$ gpioget GPIO24
"GPIO24"=inactive
[1]+ Terminated gpioset -t # gpioset -t for "toggle" 100ms,500ms,200ms,500ms,300ms,500ms,400ms,500ms,500ms,500ms GPIO24=1
# moving on to the "--hold-period" option...
# the '-p' option is a mystery to me... I cannot explain it
#
$ gpioset -p 10s GPIO24=1
^C # waited 20 secs, LED remained illuminated
$ gpioget GPIO24
"GPIO24"=inactive # LED extinguished
# man says "the minimum time period to hold lines at the requested values"; yes, it says 'minimum'
$ gpioset -p 10s GPIO24=1
^C # I did not wait,
$ gpioget GPIO24 # I issued commands immediately (< 2 secs)
"GPIO24"=inactive # LED extinguished as soon as 'gpioget' issued ???
# moving on to the "--interactive" option
# note: this option only exists if the compiler flag is set (see building resipe)
# the '-i' / '--interactive' option is interesting
#
$ $ gpioset -i GPIO24=1 # LED illuminated
gpioset> toggle GPIO24 # LED extinguished
gpioset> toggle GPIO24 # LED illuminated
gpioset> get GPIO24
"GPIO24"=active
gpioset> set GPIO24=0 # LED extinguished
gpioset> set GPIO24=1 # LED illuminated
gpioset> sleep 30s # 'gpioset>' prompt disappears for 30 sec, LED illuminated
gpioset> get GPIO24
"GPIO24"=active # note: gpioget did not change state of GPIO24 in this case
gpioset> set GPIO24=0 # LED extinguished
gpioset> get GPIO24
"GPIO24"=inactive
gpioset> exit
# Wonder why the entire 'gpioset' app isn't run this way??
Given the failure of gpioset
to return a prompt at the command line, it seems likely there will be issues using it in a script:
#!/usr/bin/env bash
echo "Hello, starting gpioset to illuminate LED in 5 secs"
sleep 5
gpioset GPIO24=1
echo "Started gpioset, sleeping for 5 sec"
sleep 5
echo "Slept, starting gpioset to extinguish LED"
gpioset GPIO24=0
exit
As expected, the script fails after executing the first gpioset
. This is an issue.
As mentioned above, the testing in this step did not actually test libgpiod ver 2.1
- it tested four (4) of the "tools" (command line apps) written by the authors of libgpiod as proxies for libgpiod. The ''tools'' tested above are:
- gpiodetect
- gpioinfo
- gpioget
- gpioset
gpiodetect
and gpioinfo
seemed entirely serviceable (to me). The only suggestion I might make is to add a header line to gpioinfo
to emphasize which column contains the line name and which contains the offset.
gpioget
- as mentioned in a couple of places in the test results still strikes me as out of kilter due to its usage in changing the state of a GPIO line. That behavior just doesn't seem copacetic to me, but YMMV.
gpioset
- I don't understand the need to not return the command line prompt following execution of (for example) gpioset GPIO24=1
. If the libgpiod ver 2.1
designers delegated line persistence to the GPIO driver, so why not do the same in this case? As shown above, this limits its use in scripting.
Similarly, I do not understand the -z --daemonize
option as it seems to have the same effect as running gpioset ... &
(i.e. in the background). And if one adds a --daemonize
option, it seems to me one should also add an option to kill the daemon without having to resort to ps
and kill
.
I was unable to follow the point of the -p --hold-period
option; did not grok this option.
I felt the -i --interactive
and the -t --toggle
options were both useful. In particular the --interactive
option suggests interesting usages in scripting applications when gpioset
is running in the background/daemonized
.
I found the libgpiod ver 2.1
tools awkward to use in some cases, even un-usable in the case of using gpioset
in a script. There is probably a way to overcome this limitation using the --interactive
option (or the --toggle
option), but this unnecessarily complicates an otherwise simple task.
On second thought, there seems to be no point in taking this any further because:
- Despite my efforts, the author has made it abundantly clear that he has no intention of changing anything.
- The author (Kent Gibson) is an overbearing, arrogant asshole who deals in false claims for self-promotion. (I wonder if he's been influenced by Linus Torvalds?). ICYI, the false claims have to do with "Production Environments".
- I've lost interest in
libgpiod
& "Kent's tools" due to the illogical interface and poor documentation. Until something better comes along, I'll stick withpinctrl
for all of my GPIO programming.