¿cuantas veces nos ha pasado que nuestro proyecto necesita de uno o varios pulsadores? no hay problema, los pulsadores son sencillos de implementar y para leer su estado solo hay que hacer un digitalRead()…

…pero luego resulta que nuestro Arduino UNO hace cosas raras: si la acción a disparar por la pulsación es corta puede que nos la haga miles de veces por segundo o si es larga puede que no suceda nada cuando accionamos otros pulsadores porque Arduino está ocupado en otras cosas y no puede mirar el estado del pin al que hayamos conectado el pulsador.

A mi me pasó que tenía que medir las revoluciones por minuto de un anemómetro y no podía estar todo el tiempo esperando a que se activara el pin y cuando la velocidad de giro de aquel anemómetro era muy lenta el contador se volvía loco cada vez que se cerraba el contacto.

Solución: programar un pequeño código que detecte cuando un pin cambia de estado, bien sea de bajo a alto o viceversa, y además que lleve un contador dentro… y ha resultado ser uno de los códigos que más veces saco de mi “caja de herramientas”.

Mirad que sencillo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int estadoAnterior = 0;
int estadoActual;
int contador = 0;
int pin; // aquí el pin digital que se quiere leer

void setup()
{
  pinMode(pin, INPUT);
}

void loop()
{
  estadoActual = digitalRead(pin);
  if (estadoAnterior != estadoActual)  // ha habido un cambio de estado
  {
    contador++;                          // cuenta los cambios de estado
    int validarPar = contador % 2;       // solo queremos los cambios pares
    if (validarPar != 1)                 // si el cambio es par
    {
      //ponga aqui su codigo
    }
    estadoAnterior = estadoActual;
  }
}

Utilizamos 3 variables: estadoActual, estadoAnterior y contador. El código se limita a mirar en cada loop si las variables estadoActual y estadoAnterior son diferentes, si ello sucede es que el estado del pin ha cambiado desde la última vez que lo miró, incrementa el contador y a partir de aquí es donde se hace la magia: si el resto de la división entre el contador y 2 es diferente de 1 es que el cambio de estado se ha producido de “pulsado” a “no pulsado” y pasará a ejecutar lo que pongamos debajo de “ponga aqui su codigo”.
Si queremos que la acción se desarrolle cuando el puslador es accionado solo hay que cambiar la condición de la línea 18 y poner esta otra “if (validarPar == 1)”.

Además tenemos un contador que cuenta todos los cambios de estado con lo que podemos utilizar este código en encoders u otros dispositivos similares.

Podemos utilizarlo dentro de una función con las variables como locales y el código actuando dentro de una parte muy específica del proyecto, como por ejemplo, en el menú hecho con una LCD y unos botones.

Además podemos hacer que funcione también con pines analógicos, pero eso será en otro tutorial.

ACTUALIZACIÓN 17/05/2017

Recientemente he realizado un proyecto en el que tenía que mostrar unos datos en una pantalla lcd Nokia 5110, una pantalla de la que ya se ha hablado en esta web. Pues resulta que para navegar por la pantalla y sus menús utilizaba un rotary encoder que lleva incluido un pulsador y mi querido algoritmo de cambio de estado, que tantas veces me ha funcionado a la perfección, me falló.

Tras unas cuantas horas de depuración encontré el problema: al dividir el código en funciones que eran llamadas cuando se accionaba el pulsador y estas funciones llevar su propio cambio de estado no se realizaba la actualización en el valor de la variable estadoAnterior en el primer cambio de estado, y al volver a intentar detectar un nuevo cambio de estado antes de salir de la función llamada no funcionaba bien.

No me malentendáis, el algoritmo funciona bien cuando cuando dispara una acción que no vuelve a utilizar el mismo algoritmo. Pero en un caso tan enrevesado como el que tenía que utilizar fallaba.

Estas situaciones no hay que verlas como fracasos si no como oportunidades de mejorar, así que me estrujé los sesos
durante unas horas hasta dar con una solución que no sólo solucionaría mi problema si no que además me daría un código más eficiente para detectar el cambio de estado de un pulsador.

La solución que se me ocurrió fué encerrar el algoritmo en una función de tipo booleano que devolviera un true si el pulsador cambiaba de estado o un false si no se producía el cambio al compara su estado con el estado que tenía en el loop anterior…. y la verdad es que no fue difícil hacerlo.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
int estadoAnterior = 0;
int estadoActual;
int contador = 0;
int pin = 14; // aquí el pin digital que se quiere leer

void setup(){
  pinMode(pin, INPUT);

  Serial.begin(9600);
}

void loop(){

if (haCambiado(pin)){
  Serial.println(contador);
}

}

boolean haCambiado(int p){
  estadoActual = digitalRead(p);
  if (estadoAnterior != estadoActual)  // ha habido un cambio de estado
  {
    contador++;                          // cuenta los cambios de estado
    int validarPar = contador % 2;       // solo queremos los cambios pares
    if (validarPar != 1)                 // si el cambio es par
    {
      estadoAnterior = estadoActual;
      return true;
    }else{
      estadoAnterior = estadoActual;
      return false;
    }
   
  }else return false;
}

Las modificaciones no son significativas pero hacen que el resultado cambie por completo, además de que el código queda mucho más elegante.

Al final no resulta más que una función que modifica los valores de unas variables globales, lo cual debo decir que no me gusta demasiado.

También llegados a este punto podemos ver cual sería el siguiente paso lógico en la evolución de este algoritmo: convertirlo en una librería, lo cual simplificaría aún más su uso al permitir definir diferentes objetos cuando se trabaje con varios pulsadores, además permitiría hacer que las variables globales fueran internas de cada objeto eliminando la necesidad de declararlas por el programador. Y mirando un poco más lejos en esta evolución también sería sencillo medir el tiempo transcurrido entre dos cambios de estado y poder detectar así una pulsación larga.

Todo se andará.


Si este tutorial te ha sido de utilidad puedes considerar hacerme un donativo, por pequeño que sea estarás contribuyendo a que siga con esta labor.