Botonera DIY con Arduino: 3/5 Programación

Esta es la tercera entrada de la serie sobre el diseño, programación y fabricación de una botonera para simuladores de conducción. Veremos en esta entrada cómo programar la placa Arduino para que nos sirva como controladora para la botonera.

Puedes acceder a las entradas anterior y posterior clicando en los enlaces siguientes:


Pasos previos

Antes que nada, debemos preparar el entorno de desarrollo de Arduino, por lo que accederemos a su página web para descargar el software Arduino IDE (https://www.arduino.cc/en/software/). Tras la instalación, podremos programar la controladora en el IDE, aunque necesitaremos algunas librerías que nos van a facilitar el trabajo:

Una vez descargados ambos archivos, extraemos su contenido en la carpeta libraries de la ruta de instalación del Arduino IDE, normalmente:

C:\Program Files (x86)\Arduino\libraries

Al ser esta placa una placa clon de la placa Leonardo de Arduino, debemos tratar esta como una placa Leonardo dentro del Arduino IDE. Para ello, una vez instalado el software Arduino y conectada la placa via USB, vamos a Herramientas -> Placa y seleccionamos Arduino Leonardo. En Herramientas -> Puerto seleccionamos el puerto en el que se encuentra nuestra Pro Micro. Es recomendable desconectar las demás placas Arduino que podamos tener en el PC para no desconfigurarlas por error mientras trasteamos con esta.


El programa controlador

a modo de recordatorio, insertamos aquí la imagen del esquema eléctrico de la botonera.

Esquema eléctrico de la botonera.

Antes que nada, incluiremos las dos librerías que hemos descargado en el paso anterior y definiremos algunas variables que necesitaremos. En NUMROTARIES y NUMBUTTONS podríamos ajustarlo a la cantidad de entradas que vayamos a montar, pero si planeamos ampliar la botonera, como es mi caso, podemos dejarlo así para no tener que modificar el código más adelante.

#include <Keypad.h>
#include <Joystick.h>

#define ENABLE_PULLUPS
#define NUMROTARIES 4
#define NUMBUTTONS 25
#define NUMROWS 5
#define NUMCOLS 5

A continuación escribiremos la matriz de botones propiamente, de la siguiente forma:

byte buttons[NUMROWS][NUMCOLS] = {
  {0,1,2,3,4},
  {5,6,7,8,9},
  {10,11,12,13,14},
  {15,16,17,18,19},
  {20,21,22,23,24},
};

Como vemos, se trata de una matriz 5×5 que nos permitirá añadir hasta 25 botones de dos posiciones, suficiente para la botonera que hemos diseñado y sus futuras ampliaciones. En cada linea de la matriz debemos escribir cinco números correlativos, separados por comas, que se corresponden con las cinco columnas de la matriz.

Justo debajo definiremos los codificadores rotatorios:

struct rotariesdef {
  byte pin1;
  byte pin2;
  int ccwchar;
  int cwchar;
  volatile unsigned char state;
};

rotariesdef rotaries[NUMROTARIES] {
  {1,0,26,27,0},
  {2,3,28,29,0},
  {4,5,30,31,0},
  {6,7,32,33,0},
};

Aquí estamos creando la estructura del codificador y después asignándola a cada uno de los codificadores registrados en el controlador. Para cada uno de ellos le asignamos dos pines, dos botones virtuales que se corresponden con las dos direcciones de rotación y un último valor que indica el estado.

Cabe remarcar que no todos los pines tienen su número escrito en la placa, por lo que debemos guiarnos por este esquema inferior. Para entender el código, estamos diciendo que el primer codificador, indicado con el 1 en el esquema eléctrico de la botonera, lo vamos a conectar a los pines 1 y 0 de la placa, según el esquema de pines.

Esquema de pines del Arduino Pro Micro.

Seguidamente añadimos este código, que sirve para definir los estados de los codificadores:

#define DIR_CCW 0x10
#define DIR_CW 0x20
#define R_START 0x0

#define R_CW_FINAL 0x1
#define R_CW_BEGIN 0x2
#define R_CW_NEXT 0x3
#define R_CCW_BEGIN 0x4
#define R_CCW_FINAL 0x5
#define R_CCW_NEXT 0x6

const unsigned char ttable[7][4] = {
  // R_START
  {R_START,    R_CW_BEGIN,  R_CCW_BEGIN, R_START},
  // R_CW_FINAL
  {R_CW_NEXT,  R_START,     R_CW_FINAL,  R_START | DIR_CW},
  // R_CW_BEGIN
  {R_CW_NEXT,  R_CW_BEGIN,  R_START,     R_START},
  // R_CW_NEXT
  {R_CW_NEXT,  R_CW_BEGIN,  R_CW_FINAL,  R_START},
  // R_CCW_BEGIN
  {R_CCW_NEXT, R_START,     R_CCW_BEGIN, R_START},
  // R_CCW_FINAL
  {R_CCW_NEXT, R_CCW_FINAL, R_START,     R_START | DIR_CCW},
  // R_CCW_NEXT
  {R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START},
};

Terminadas las definiciones de los codificadores, vamos con los botones. Añadimos primero los pines a los que conectaremos cada fila y cada columna de la matriz, fijándonos en los números del esquema de pines que hemos visto anteriormente:

byte rowPins[NUMROWS] = {21,20,19,18,15};
byte colPins[NUMCOLS] = {14,16,10,9,8};

Iniciamos también una instancia de la clase Keypad y una de la clase Joystick:

Keypad buttbx = Keypad( makeKeymap(buttons), rowPins, colPins, NUMROWS, NUMCOLS); 

Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, 
  JOYSTICK_TYPE_JOYSTICK, 34, 0,
  false, false, false, false, false, false,
  false, false, false, false, false);

Todos los elementos false de la clase Joystick se corresponden con los ejes analógicos soportados por la clase. Como en esta botonera no hay ejes analógicos (acelerador, freno, dirección del volante…) los dejamos todos en false.

Seguidamente iniciamos la función setup() y la función loop(). Dentro de la última ejecutaremos las funciones que harán la comprobación del estado de todos los elementos de la botonera en forma de bucle y que definiremos más adelante.

void setup() {
  Joystick.begin();
  rotary_init();
}

void loop() { 
  CheckAllEncoders();
  CheckAllButtons();
}

Dentro de setup() hemos llamado a rotary_init(), que definimos a continuación. En esta función leemos los inputs que llegan a los pines de la placa cuando hacemos girar el codificador:

void rotary_init() {
  for (int i=0;i<NUMROTARIES;i++) {
    pinMode(rotaries[i].pin1, INPUT);
    pinMode(rotaries[i].pin2, INPUT);
    #ifdef ENABLE_PULLUPS
      digitalWrite(rotaries[i].pin1, HIGH);
      digitalWrite(rotaries[i].pin2, HIGH);
    #endif
  }
}

Dentro del loop, una de las funciones a las que llamamos es CheckAllEncoders(), que se encarga de comprobar el estado de todos los codificadores y que definimos a continuación:

void CheckAllEncoders(void) {
  for (int i=0;i<NUMROTARIES;i++) {
    unsigned char result = rotary_process(i);
    if (result == DIR_CCW) {
      Joystick.setButton(rotaries[i].ccwchar, 1); delay(50);
      Joystick.setButton(rotaries[i].ccwchar, 0);
    };
    if (result == DIR_CW) {
      Joystick.setButton(rotaries[i].cwchar, 1); delay(50);
      Joystick.setButton(rotaries[i].cwchar, 0);
    };
  }
}

También llamamos a CheckAllButtons():

void CheckAllButtons(void) {
  if (buttbx.getKeys()) {
    for (int i=0; i<LIST_MAX; i++) {   // Scan the whole key list.
      if ( buttbx.key[i].stateChanged ) {   // Only find keys that have changed state.
        switch (buttbx.key[i].kstate) {  // Report active key state : IDLE, PRESSED, HOLD, or RELEASED
          case PRESSED:
          case HOLD:
            Joystick.setButton(buttbx.key[i].kchar, 1);
            break;
          case RELEASED:
          case IDLE:
            Joystick.setButton(buttbx.key[i].kchar, 0);
            break;
        }
      }   
    }
  }
}

Esta función se encarga de enviar la señal de los botones tanto si están presionados, mantenidos, se han soltado o están en reposo.

Finalmente añadimos lo siguiente, para actualizar el valor del estado en la definición de los codificadores.

unsigned char rotary_process(int _i) {
  unsigned char pinstate = (digitalRead(rotaries[_i].pin2) << 1) | digitalRead(rotaries[_i].pin1);
  rotaries[_i].state = ttable[rotaries[_i].state & 0xf][pinstate];
  return (rotaries[_i].state & 0x30);
}

Subir el programa a la placa

Si todo ha ido bien, clicando en el botón Verificar en la barra superior del Arduino IDE nos devolverá un mensaje sin errores en el cuadro inferior de la ventana. Finalmente, solo queda clicar en el botón Subir para cargar el programa en nuestro Arduino Pro Micro.


En la próxima entrada veremos cómo cablear toda la botonera con todo lo que hemos aprendido hasta el momento. Puedes acceder a las entradas anterior y posterior clicando en los enlaces siguientes.

4 comentarios sobre «Botonera DIY con Arduino: 3/5 Programación»

  1. Daniel dice:

    Excelente tutorial

  2. Pepe Trueno dice:

    Buenas, tenéis previsto continuar con la serie? Me ha parecido muy interesante y me planteaba hacerlo

    1. SRS Ramon [Rhun14] dice:

      Sí, en cuanto tenga el tiempo que necesito, termino la serie de tutos. Pido disculpas por la demora y podría explicarlo de forma teórica porque al final solamente queda ensamblarlo todo, pero prefiero hacerlo bien.

Deja una respuesta

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