Aquí traigo un pequeño code-kata en Shell Script para manejar un keypad conectado a una Raspberry Pi. Es un código fuente que configura, mediante el sistema de ficheros, el acceso a los PINs del GPIO. Este pequeño programa, se puede traducir a cualquier otro lenguaje de programación. Se puede traducir directamente, ya que el manejo del GPIO en la Raspberry Pi, mediante el sistema de ficheros, es compatible e independiente de cualquier lenguaje de programación.
Es decir, la forma más estándar de trabajar el uso a los GPIO de la Raspberry Pi es mediante el sistema de ficheros. Aunque no es la forma más eficiente, ya que si queremos alcanzar velocidades de acceso muy rápidas no podremos. Todo esto usando el sysfs lo podremos hacer incluso desde línea de comandos, usando los ficheros que hay en:
/sys/class/gpio/..
Si quieres ver un resumen de cómo manejar los GPIO de la Raspi mediante el sysfs me remito al post anterior que escribí. Vamos al grano en los siguientes apartados…
Un poco de teoría, la lógica de control de los keypads
En mi caso, ha llegado a parar a mis manos una botonera de 16 botones, como la de la imagen siguiente:
Esta, y muchas botoneras parecidas, vienen configuradas de forma interna en forma de matriz de conexiones. Así se reducen el número de pines con los que podremos consultar si hay algún botón presionado. Este keypad tiene 16 botones que pueden estar apretados, y para saber qué botón está apretado, se usan los pines de forma que 4 son columnas y 4 son filas.
El esquema de conexión es:
- pin1 = fila1
- pin2 = fila2
- pin3 = fila3
- pin4 = fila4
- pin5 = columna1
- pin6 = columna2
- pin7 = columna3
- pin8 = columna4
Entonces, la lógica de control que hay que aplicar para comprobar si un botón está apretado o no, debe ser una de las 2 siguientes:
- Ponemos filas como salida y columnas como entrada. Entonces hacemos un bucle infinito en el que vamos poniendos filas con bit a 1, comprobando si alguna columna recibe 1, entonces es un botón apretado. Luego ponemos dicha fila a 0 y seguimos con la siguiente.
- O viceversa, ponemos filas como entrada y columnas como salida. Entonces ponemos columnas a 1, comprobando si alguna fila recibe dicho 1, entonces tenemos un botón apretado en dicha columna, que según la fila que sea sabremos cuál es.
En el código fuente de abajo, he elegido la segunda lógica. Me explico, si tenemos columna1 a 1 y fila1 a 1, entonces se ha apretado el botón 1. Si por el contrario, cuando ponemos columna1 a 1, entonces nos llega el 1 a la fila 2 es que tenemos apretado el botón 4. Y así sucesivamente.
Es más fácil leer el código para comprenderlo.
El código fuente en Shell Script
Al final en Shell Script, jugando y jugando, haciendo el code-kata, que me ha quedado así:
#!/bin/bash
ROW1=27
ROW2=22
ROW3=10
ROW4=9
COL1=23
COL2=24
COL3=25
COL4=5
# Config input pins
if [ ! -d "/sys/class/gpio/gpio$ROW1" ]; then
echo "$ROW1" > /sys/class/gpio/export;
sleep 1;
fi
if [ ! $(cat "/sys/class/gpio/gpio$ROW1/direction" | grep in) ]; then
echo in > "/sys/class/gpio/gpio$ROW1/direction";
fi
if [ ! -d "/sys/class/gpio/gpio$ROW2" ]; then
echo "$ROW2" > /sys/class/gpio/export;
sleep 1;
fi
if [ ! $(cat "/sys/class/gpio/gpio$ROW2/direction" | grep in) ]; then
echo in > "/sys/class/gpio/gpio$ROW2/direction";
fi
if [ ! -d "/sys/class/gpio/gpio$ROW3" ]; then
echo "$ROW3" > /sys/class/gpio/export;
sleep 1;
fi
if [ ! $(cat "/sys/class/gpio/gpio$ROW3/direction" | grep in) ]; then
echo in > "/sys/class/gpio/gpio$ROW3/direction";
fi
if [ ! -d "/sys/class/gpio/gpio$ROW4" ]; then
echo "$ROW4" > /sys/class/gpio/export;
sleep 1;
fi
if [ ! $(cat "/sys/class/gpio/gpio$ROW4/direction" | grep in) ]; then
echo in > "/sys/class/gpio/gpio$ROW4/direction";
fi
echo "Input PINs configured.."
# Config output pins
if [ ! -d "/sys/class/gpio/gpio$COL1" ]; then
echo "$COL1" > /sys/class/gpio/export;
sleep 1;
fi
if [ ! $(cat "/sys/class/gpio/gpio$COL1/direction" | grep out) ]; then
echo out > "/sys/class/gpio/gpio$COL1/direction";
fi
if [ ! -d "/sys/class/gpio/gpio$COL2" ]; then
echo "$COL2" > /sys/class/gpio/export;
sleep 1;
fi
if [ ! $(cat "/sys/class/gpio/gpio$COL2/direction" | grep out) ]; then
echo out > "/sys/class/gpio/gpio$COL2/direction";
fi
if [ ! -d "/sys/class/gpio/gpio$COL3" ]; then
echo "$COL3" > /sys/class/gpio/export;
sleep 1;
fi
if [ ! $(cat "/sys/class/gpio/gpio$COL3/direction" | grep out) ]; then
echo out > "/sys/class/gpio/gpio$COL3/direction";
fi
if [ ! -d "/sys/class/gpio/gpio$COL4" ]; then
echo "$COL4" > /sys/class/gpio/export;
sleep 1;
fi
if [ ! $(cat "/sys/class/gpio/gpio$COL4/direction" | grep out) ]; then
echo out > "/sys/class/gpio/gpio$COL4/direction";
fi
echo "Output PINs configured.."
echo "0" > "/sys/class/gpio/gpio$COL1/value"
echo "0" > "/sys/class/gpio/gpio$COL2/value"
echo "0" > "/sys/class/gpio/gpio$COL3/value"
echo "0" > "/sys/class/gpio/gpio$COL4/value"
SLEEPTIME=0.1
while true
do
echo "1" > "/sys/class/gpio/gpio$COL1/value"; sleep $SLEEPTIME
if [ $(cat "/sys/class/gpio/gpio$ROW1/value" | grep 1) ]; then echo "Button 1"; fi
if [ $(cat "/sys/class/gpio/gpio$ROW2/value" | grep 1) ]; then echo "Button 4"; fi
if [ $(cat "/sys/class/gpio/gpio$ROW3/value" | grep 1) ]; then echo "Button 7"; fi
if [ $(cat "/sys/class/gpio/gpio$ROW4/value" | grep 1) ]; then echo "Button *"; fi
echo "0" > "/sys/class/gpio/gpio$COL1/value"; sleep $SLEEPTIME
echo "1" > "/sys/class/gpio/gpio$COL2/value"; sleep $SLEEPTIME
if [ $(cat "/sys/class/gpio/gpio$ROW1/value" | grep 1) ]; then echo "Button 2"; fi
if [ $(cat "/sys/class/gpio/gpio$ROW2/value" | grep 1) ]; then echo "Button 5"; fi
if [ $(cat "/sys/class/gpio/gpio$ROW3/value" | grep 1) ]; then echo "Button 8"; fi
if [ $(cat "/sys/class/gpio/gpio$ROW4/value" | grep 1) ]; then echo "Button 0"; fi
echo "0" > "/sys/class/gpio/gpio$COL2/value"; sleep $SLEEPTIME
echo "1" > "/sys/class/gpio/gpio$COL3/value"; sleep $SLEEPTIME
if [ $(cat "/sys/class/gpio/gpio$ROW1/value" | grep 1) ]; then echo "Button 3"; fi
if [ $(cat "/sys/class/gpio/gpio$ROW2/value" | grep 1) ]; then echo "Button 6"; fi
if [ $(cat "/sys/class/gpio/gpio$ROW3/value" | grep 1) ]; then echo "Button 9"; fi
if [ $(cat "/sys/class/gpio/gpio$ROW4/value" | grep 1) ]; then echo "Button #"; fi
echo "0" > "/sys/class/gpio/gpio$COL3/value"; sleep $SLEEPTIME
echo "1" > "/sys/class/gpio/gpio$COL4/value"; sleep $SLEEPTIME
if [ $(cat "/sys/class/gpio/gpio$ROW1/value" | grep 1) ]; then echo "Button A"; fi
if [ $(cat "/sys/class/gpio/gpio$ROW2/value" | grep 1) ]; then echo "Button B"; fi
if [ $(cat "/sys/class/gpio/gpio$ROW3/value" | grep 1) ]; then echo "Button C"; fi
if [ $(cat "/sys/class/gpio/gpio$ROW4/value" | grep 1) ]; then echo "Button D"; fi
echo "0" > "/sys/class/gpio/gpio$COL4/value"; sleep $SLEEPTIME
done
Por temas de consumo de CPU, si ponemos un SLEEPTIME más pequeño, se harán las comprobaciones más rápido, pero con mayor consumo de CPU.
Terminando
Si has llegado hasta aquí, para terminar, sólo queda remitirte a páginas oficiales y documentación:
- https://www.raspberrypi.org/
- https://elinux.org/RPi_GPIO_Code_Samples#sysfs.2C_part_of_the_raspbian_operating_system
Otro día más.. ¡Un saludo!