Problem: Both the GPIO sysfs interface in Linux and the wiringPi GPIO management package are deprecated. The "new" character device driver (gpiod) based commands for managing GPIO over Linux behave differently than wiringPi's gpio
command. In particular; The gpiod-based gpioset
command releases GPIO lines to their default state upon the command's exit (especially bad if a connected device is not expecting to "see" a floating signal), and the gpiod-based gpioget
command automatically converts GPIO output lines to inputs rather than providing the option of determining what the line is being driven to (i.e. high or low) when the line is an output. This gpiod-based command behavior requires a different approach than with wiringPi's gpio
command for configuring, using, and querying the Raspberry Pi GPIO lines intended as outputs.
Solution: A simple and light GPIO service to initialize GPIO lines intended as outputs when the Raspberry Pi boots, and a script which uses gpiod in a manner emulating a subset of the wiringPi gpio
command's behavior with respect to GPIO output lines. This solution achieves my goal of booting a Raspberry Pi with specified pins being configured as outputs driven low, maintaining those pins as outputs while the Raspberry Pi is energized, and having changes to the logic-state of those outputs persist regardless of whether the user "pi" is logged on.
Disclaimer: This solution was implemented to solve the problem for my purposes. The solution may not be appropriate for your purposes. No warranty is implied. Use at your own risk.
Naming Convention: The instructions which follow include references to "ha" rather than "gpio" since the solution to the problem noted above was first implemented on a Raspberry Pi used for home automation (HA). For example; The script used for the GPIO initialization service is named "hainit" rather than the more general "gpioinit", and the file containing a Broadcom (BCM) GPIO-line to Raspberry Pi GPIO-pin cross-reference table is named "ha.table" rather than the more general "gpio.table".
/home/pi/etc/ha.table
ha.table
. Only the first two columns are required. In particular; Replace my BCM GPIO-line numbers (second column) with BCM GPIO-line numbers reflecting your application. Run the pinout
command on your Raspberry Pi to help determine which GPIO lines you are using as outputs (inputs are not represented in ha.table
). SigIDs (first column) must be unique counting numbers in the set {1..n}, where 'n' is the count of all GPIO-specific records (i.e. those which are not comment/header, or blank) in ha.table
. This SysID re/numbering is especially important if records were added or deleted, but the numbers do not need to be in any order (e.g. ascending or descending)./home/pi/bin/hainit
ha.table
, then update the set range on line 8 in hainit
accordingly.chmod 500 /home/pi/bin/hainit
/home/pi/bin/hactl
ha.table
, then update lines 13, 25, 26, and 44 in hactl
accordingly. (except for line 25; these updates are cosmetic)chmod 500 /home/pi/bin/hactl
/etc/systemd/system/home-automation.service
sudo systemctl daemon-reload
sudo systemctl enable home-automation.service
systemctl status home-automation.service
For each GPIO line listed in ha.table
; The GPIO service runs one gpiod gpioset
command (via /home/pi/bin/hactl
) in the background to keep driving the GPIO lines low when the Raspberry Pi is booted. To confirm these processes exist, issue the command:
ps -fC gpioset
If/when hactl
is later run by the user "pi", and if that run would invert a GPIO line's output (e.g. if hactl
is told to drive a currently-low line high), then the latest gpioset
background process for that line is replaced with a new gpioset
background process for that line. These processes survive the user "pi" logging out, or programs/scripts which run hactl
exiting, thereby providing output-drive persistence.
The hainit
script can easily be modified to change the start-up state (on/low or off/high) of each GPIO line listed in ha.table
, and the hactl
script can easily be modified to use positive (1 == on/true) rather than negative (1 == off/false) logic for those outputs.
Last updated: 2024-02-10 (after upgrading from Bullseye to Bookworm)