Raspberry Pi: manejando un keypad de 4×4 mediante Shell Script

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:

Keypad Raspberry Pi 16 botones de 4×4, 8 pines de conexión.

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!

Compartir..

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *