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].

Contact us now to discuss your project

Ready to order, contact us today for pricing or samples

Contact Us