Migrar un proyecto viejo de RPi.GPIO a gpiozero o libgpiod v2



RPi.GPIO fue durante más de una década la opción por defecto para programar GPIO en Python en una Raspberry Pi, pero su ritmo de versiones se enlenteció bastante, y habla con el hardware GPIO mediante acceso directo a registros vía /dev/mem -- un enfoque que no encaja con el modelo de dispositivo de caracteres gpiochip al que se movió el kernel, y que directamente deja de funcionar en el SoC BCM2712 de la Pi 5 sin una capa de compatibilidad. Si tenés un script viejo del que todavía dependés, vale la pena migrarlo en vez de parchear el problema para siempre.

Por qué importa ahora

En una Pi 5, un simple pip install RPi.GPIO o falla al compilar o se instala pero no puede acceder a los pines GPIO, porque el BCM2712 movió el control de pines detrás del dispositivo de caracteres gpiochip del kernel en vez de exponer registros mapeados en memoria cruda como hacían el BCM2711 y anteriores. El shim comunitario rpi-lgpio (sudo apt install python3-rpi-lgpio) parchea en caliente la API de RPi.GPIO sobre el backend moderno lgpio y hace que un script viejo vuelva a andar en una Pi 5 sin tocar código -- útil como parche temporal, pero es una capa de traducción encima de otra capa de traducción. Los dos destinos reales son:

  • gpiozero -- una librería de más alto nivel con clases de dispositivo (LED, Button, PWMLED, DistanceSensor...) construida sobre fábricas de pines intercambiables. Ya usa lgpio por defecto en una Pi 5 y se encarga del antirrebote, el PWM y la limpieza por vos. Es el destino correcto para la mayoría del código de aplicación.
  • libgpiod v2 (el paquete Python gpiod) -- habla directo con el dispositivo de caracteres gpiochip del kernel. Sin supuestos específicos de la Pi, funciona igual en cualquier placa Linux que exponga un gpiochip, y tiene el menor overhead. Recurrí a esto cuando necesitás portabilidad entre placas, o cuando las abstracciones de gpiozero estorban para algo de bajo nivel.

Diferencias de API de un vistazo

OperaciónRPi.GPIOgpiozerolibgpiod v2 (gpiod)
ConfiguraciónGPIO.setmode(GPIO.BCM)
GPIO.setup(pin, GPIO.OUT)
led = LED(pin)gpiod.request_lines(chip, config={...})
Leer entradaGPIO.input(pin)button.is_pressedrequest.get_value(pin)
Escribir salidaGPIO.output(pin, GPIO.HIGH)led.on() / led.off()request.set_value(pin, Value.ACTIVE)
Resistencia pullpull_up_down=GPIO.PUD_UPButton(pin, pull_up=True)bias=Bias.PULL_UP en LineSettings
Callback de flancoGPIO.add_event_detect(...)button.when_pressed = fnrequest.read_edge_events()
Antirrebotebouncetime=msbounce_time=segundosmanual, con timestamps de eventos de flanco
LimpiezaGPIO.cleanup()automática (recolección de basura / contexto)gestor de contexto with ... as request:

Ejemplo resuelto: un LED que sigue a un botón

Este es el tipo de script que aparece en todos los tutoriales viejos de Raspberry Pi. Antes, con RPi.GPIO:

import RPi.GPIO as GPIO
import time

LED = 27
BUTTON = 17

GPIO.setmode(GPIO.BCM)
GPIO.setup(LED, GPIO.OUT)
GPIO.setup(BUTTON, GPIO.IN, pull_up_down=GPIO.PUD_UP)

try:
    while True:
        if GPIO.input(BUTTON) == GPIO.LOW:
            GPIO.output(LED, GPIO.HIGH)
        else:
            GPIO.output(LED, GPIO.LOW)
        time.sleep(0.01)
except KeyboardInterrupt:
    GPIO.cleanup()

Después, con gpiozero -- sin bucle de sondeo, sin limpieza manual, y se lee como lo que hace:

from gpiozero import LED, Button
from signal import pause

led = LED(27)
button = Button(17)

button.when_pressed = led.on
button.when_released = led.off

pause()

Instalá con pip install gpiozero lgpio. gpiozero elige automáticamente la fábrica de pines lgpio en una Pi 5; en Pis más viejas también funciona, pero podés forzar un backend específico con la variable de entorno GPIOZERO_PIN_FACTORY si alguna vez lo necesitás (por ejemplo GPIOZERO_PIN_FACTORY=pigpio para casos de GPIO remoto).

Lo mismo otra vez con libgpiod v2, para cuando querés sacarte de encima la abstracción específica de la Pi por completo:

import gpiod
from gpiod.line import Direction, Value, Bias

LED = 27
BUTTON = 17

with gpiod.request_lines(
    "/dev/gpiochip0",
    consumer="led-button",
    config={
        LED: gpiod.LineSettings(direction=Direction.OUTPUT),
        BUTTON: gpiod.LineSettings(direction=Direction.INPUT, bias=Bias.PULL_UP),
    },
) as request:
    try:
        while True:
            pressed = request.get_value(BUTTON) == Value.INACTIVE
            request.set_value(LED, Value.ACTIVE if pressed else Value.INACTIVE)
    except KeyboardInterrupt:
        pass

Instalá con sudo apt install python3-libgpiod o pip install gpiod (asegurate de usar la versión 2.x -- la API 1.x es una interfaz distinta). Notá que los números de pin acá se refieren a offsets en el gpiochip, no necesariamente la misma numeración que los pines BCM en cualquier placa; en una Pi coinciden, pero revisá con gpioinfo si estás migrando a otro hardware.

LED and button wiring used in the before/after scriptGPIO27 (OUT)330 ohmLEDGND3.3Vpull-up internoGPIO17pulsadorGND

Cuál elegir

Para casi todo el código de aplicación y de hobby -- cualquier cosa que lea sensores, maneje LEDs o motores, antirebote botones, genere PWM -- gpiozero es el mejor destino. Tiene mantenimiento activo, valores por defecto razonables, y el código resultante es más corto y legible que cualquiera de las otras dos opciones. Recurrí a libgpiod v2 directamente cuando estés escribiendo algo que tenga que correr sin modificar en placas que no son Raspberry Pi, cuando quieras el menor footprint de dependencias posible en una imagen mínima, o cuando necesites algo que gpiozero no modela bien, como leer una ráfaga de timestamps de flanco para un encoder rotativo de alta resolución.

De todas formas, el shim rpi-lgpio es un puente razonable si necesitás que un script viejo vuelva a andar hoy mismo, pero tratalo como algo temporal: está construido calcado a la API de RPi.GPIO justamente para que puedas migrar más adelante sin una fecha límite forzada, no para que nunca tengas que hacerlo.

Dejar un comentario

Los comentarios anónimos se publican tras ser revisados.