Migrating Bash Scripts From sysfs GPIO To pinctrl/libgpiod On Raspberry Pi
Migrating GPIO Bash Scripts: From sysfs to Modern Solutions
As detailed in the Raspberry Pi whitepaper [RP-006553-WP], the transition from sysfs to modern GPIO control methods requires careful migration of existing scripts. This guide provides practical examples for migrating your GPIO control scripts.
The Old Way: sysfs GPIO Interface
The traditional sysfs approach used file operations to control GPIOs:
#!/bin/bash
# Export GPIO
export_gpio() {
if [ ! -e /sys/class/gpio/gpio$1 ]; then
echo "$1" > /sys/class/gpio/export
fi
}
# Set GPIO direction
set_gpio_direction() {
echo "$2" > /sys/class/gpio/gpio$1/direction
}
# Write GPIO value
write_gpio() {
echo "$2" > /sys/class/gpio/gpio$1/value
}
# Read GPIO value
read_gpio() {
cat /sys/class/gpio/gpio$1/value
}
# Example usage
export_gpio 18
set_gpio_direction 18 out
write_gpio 18 1
Migration Path 1: Using pinctrl
Basic Operations Migration:
#!/bin/bash
# Initialize GPIO (replaces export and direction setting)
init_gpio_output() {
sudo pinctrl set $1 op pn
}
init_gpio_input() {
sudo pinctrl set $1 ip pu # With pull-up
# or
sudo pinctrl set $1 ip pd # With pull-down
}
# Write GPIO (replaces write_gpio)
write_gpio() {
if [ "$2" -eq "1" ]; then
sudo pinctrl set $1 dh
else
sudo pinctrl set $1 dl
fi
}
# Read GPIO (replaces read_gpio)
read_gpio() {
sudo pinctrl lev $1
}
Example Migration: LED Control Script
Old sysfs version:
#!/bin/bash
# Setup
echo "18" > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio18/direction
# Cleanup function
cleanup() {
echo "18" > /sys/class/gpio/unexport
exit 0
}
trap cleanup SIGINT SIGTERM
# Blink LED
while true; do
echo "1" > /sys/class/gpio/gpio18/value
sleep 1
echo "0" > /sys/class/gpio/gpio18/value
sleep 1
done
New pinctrl version:
#!/bin/bash
# Setup
sudo pinctrl set 18 op pn
# Cleanup function
cleanup() {
sudo pinctrl set 18 ip pn # Set back to input, no pull
exit 0
}
trap cleanup SIGINT SIGTERM
# Blink LED
while true; do
sudo pinctrl set 18 dh
sleep 1
sudo pinctrl set 18 dl
sleep 1
done
Migration Path 2: Using libgpiod
Basic Operations Migration:
#!/bin/bash
# Write GPIO (replaces write_gpio)
write_gpio() {
gpioset gpiochip0 $1=$2
}
# Read GPIO (replaces read_gpio)
read_gpio() {
gpioget gpiochip0 $1
}
# Monitor GPIO (replaces polling in sysfs)
monitor_gpio() {
gpiomon gpiochip0 $1
}
Example Migration: LED Control Script
New libgpiod version:
#!/bin/bash
# No explicit setup needed with libgpiod
# Cleanup handled automatically by libgpiod
trap 'exit 0' SIGINT SIGTERM
# Blink LED
while true; do
gpioset --mode=time --sec 1 gpiochip0 18=1
gpioset --mode=time --sec 1 gpiochip0 18=0
done
Example Migration: Button Input Monitor
Old sysfs version:
#!/bin/bash
# Setup
echo "17" > /sys/class/gpio/export
echo "in" > /sys/class/gpio/gpio17/direction
echo "both" > /sys/class/gpio/gpio17/edge
# Cleanup
cleanup() {
echo "17" > /sys/class/gpio/unexport
exit 0
}
trap cleanup SIGINT SIGTERM
# Monitor button
while true; do
if [ $(cat /sys/class/gpio/gpio17/value) -eq 0 ]; then
echo "Button pressed!"
sleep 0.2
fi
sleep 0.1
done
New libgpiod version:
#!/bin/bash
# Monitor button events directly
gpiomon --num-events=0 --format="%e" gpiochip0 17 | while read edge; do
if [ "$edge" = "1" ]; then
echo "Button pressed!"
elif [ "$edge" = "0" ]; then
echo "Button released!"
fi
done
Complex Example: Multi-GPIO Control
Old sysfs version:
#!/bin/bash
# Setup multiple GPIOs
setup_gpios() {
for pin in "$@"; do
echo "$pin" > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio$pin/direction
done
}
# Cleanup multiple GPIOs
cleanup_gpios() {
for pin in "$@"; do
echo "$pin" > /sys/class/gpio/unexport
done
}
# Setup
PINS=(18 23 24 25)
setup_gpios "${PINS[@]}"
trap 'cleanup_gpios "${PINS[@]}"' SIGINT SIGTERM
# Control pattern
while true; do
for pin in "${PINS[@]}"; do
echo "1" > /sys/class/gpio/gpio$pin/value
sleep 0.5
echo "0" > /sys/class/gpio/gpio$pin/value
done
done
New libgpiod version:
#!/bin/bash
# Control pattern using libgpiod
PINS=(18 23 24 25)
# No setup needed
trap 'exit 0' SIGINT SIGTERM
while true; do
for pin in "${PINS[@]}"; do
gpioset --mode=time --sec 0.5 gpiochip0 $pin=1
gpioset gpiochip0 $pin=0
done
done
Migration Best Practices
1. Error Handling
# Add error checking for commands
if ! gpioset gpiochip0 18=1; then
echo "Failed to set GPIO 18"
exit 1
fi
2. Documentation
# Document GPIO usage and transitions
# Old: /sys/class/gpio/gpio18 - LED control
# New: gpiochip0 line 18 - LED control
3. Testing
- Test scripts with both old and new systems
- Verify timing requirements are met
- Check error conditions
4. Compatibility
#!/bin/bash
# Check for available tools
if command -v gpioset >/dev/null 2>&1; then
USE_LIBGPIOD=1
elif command -v pinctrl >/dev/null 2>&1; then
USE_PINCTRL=1
else
echo "No GPIO tools found"
exit 1
fi
Conclusion
When migrating from sysfs to either pinctrl or libgpiod:
1. pinctrl is best for:
- Development and debugging
- Direct hardware access needs
- Backward compatibility requirements
2. libgpiod is best for:
- Production applications
- Modern Linux integration
- Future-proof solutions
Choose the migration path that best fits your specific requirements, considering factors like:
- Production vs. development use
- Performance requirements
- System integration needs
- Long-term maintenance plans
For more detailed information about GPIO interfaces and migration strategies, refer to the Raspberry Pi whitepaper [RP-006553-WP].