C004 El uso del pulsador como un interruptor de acción permanente

Descripción
Conectaremos a arduino un pulsador y un LED, este se encenderá cuando presionemos el pulsador, y se mantendrá encendido cuando dejemos de presionar el pulsador.  Luego se apagará cuando lo volvamos a presionar el pulsador y se quedará apagado cuando dejemos de presionar.

Circuito
Este circuito es el mismo que usamos en  "El uso del pulsador", pero cambiando el código fuente, haremos que el pulsador se comporte como un interruptor. 


En realidad si que le vamos a hacer un pequeño cambio y el circuito quedará así:
El cambio ha sido suprimir la Resistencia Pull up R2, porque vamos utilizar una resistencia pull up internan que contiene la placa de arduino de 20K, con lo que ahorraremos cableado y un componente.  Para usar esta resistencia usaremos la palabra reservada input_pullup en el código fuente.

Montaje
Una las formas de montar el circuito en el protoboard puede ser la siguiente:

Básicamente podemos ver que es el mismo montaje que usamos para el pulsador, pero hemos retirado la resistencia pullup que iba desde la línea roja del positivo la entrada 10 de arduino y del pulsador.  En realidad también podemos retirar el cable rojo que viene desde el pin de 5V de arduino, ahorrando así otro cable, ya que la alimentación se genera interiormente a través de arduino.

Código
En el siguiente código haremos que cuando se pulse el pulsador el led se encienda y se quede encendido, y luego cuando se vuelva a pulsar se apague, y al soltar se quede apagado, pero veremos que cuando se monte el circuito y se cargue este código, no funcionará bien y tendremos un problema.



Tenemos definidas las variables LED que es el pin 6 y boton que es el pin 10. Hemos creado otra variable llamada "estado" de tipo booleano, que guarda la situación del LED, y se inicializa con un false.

En la función void setup() Configuramos el pin 6 mediante la variable LED como salida, usando pinMode y OUTPUT, configuramos el pin 10 mediante la variable boton como entrada conectada a una resistencia pull up interna de arduino, con pinMode y INPUT_PULLUP, y por esto, podemos retirar R2 como resistencia pull up.  Finalmente podemos establecer que el LED esté apagado al conectar arduino.

En la función void loop()
Creamos una variable boleana llamada "valor" que almacenará el estado del botón con un false o con un true, con un false si el pulsador está presionado y con un true si no lo está, ya que se le ha asignado la instrucción digitalRead que lee lo que hay almacenado en la variable "boton", es decir el estado de pin de entrada o pin 10.  Si es false, eso significa que el pulsador está presionado y se cumplirá la condición del if y se escribirá y guardará en la variable "estado" lo contrario de lo que contenía guardado, false si tenía guardado true y true si tenía guardado false, y finalmente cambiará el estado del LED, si estaba apagado, lo encenderá y viceversa, en función de lo que contenga en ese momento la variable "estado".



Como vemos en el vídeo, el programa responde, pero el LED, no siempre se apaga cuando se tiene que apagar, y tampoco se enciende siempre cuando se tiene que encender. Ya digimos que tendríamos un problema, pues este es el problema y se debe a que el tiempo que se tarda en pulsar y liberar el botón, Arduino es capaz de leer el pulsador y invertir el LED, una cuantas de miles de veces.  Si lee un número par, dejará el LED como estaba, y si lo lee un numero impar de veces, cambiará el estado del LED. En la práctica esta situación se vuelve aleatoria y el resultado es impredecible.  Otro problema es que en el mundo real, un interruptor o pulsador no cambia de estado de forma perfecta, sino que suele rebotar y causar varias conexiones y desconexiones muy rápidas antes de quedar en un valor estable. A estos rebotes se les llama bouncing y al procedimiento de eliminarlo se le llama debouncing,

El debouncing se puede hacer por hardware con ayuda de unas resistencias y un condensador o por software, que es mucho más sencillo y mucho más barato.  La solución pasa por frenar en seco a Arduino un pequeño tiempo determinado de entre 50 y 250 milisegundos, una vez que detecta que se ha pulsado el botón, de modo que nos de tiempo a liberar el botón.

La única diferencia con el código anterior es que hemos añadido un delay de 200 ms justo después de digitalRead, que es cuando lee, es decir cuando lee el pin se espera 200ms, y para entonces ya hemos levantado el dedo del pulsador, con lo que hemos eliminado el tema de los rebote.

Esto en parte resuelve el problema del bouncing, pero ¿que ocurre si se deja pulsado el pulsador?, pues que el LED parpadeara cada 200 milisegundos, puede que eso tenga alguna utilidad en otro proyecto, pero no ahora. 

Lo que queremos hacer es que no ocurra nada, es decir, si el LED esta apagado, al pulsar el botón y mantenerlo pulsado queremos que se encienda y se mantenga encendido, sin parpardear y luego cuando soltemos siga encendido, también queremos que cuando pulsemos el botón y lo mantengamos pulsado de nuevo se apague y se mantenga apagado sin parpardear y se mantenga apagado cuando soltamos. Para eso hay que mejorar un poco más el programa. Veamos que ocurre como está el código.



Como hemos visto en el vídeo, el circuito ha funcionado tal y como se había predicho, el efecto de rebote ha desaparecido, pero cuando se deja pulsado, el LED parpadea. También hemos visto en el vídeo, que se puede retirar la resistencia R2 que hacía las funciones del Pull-up, ya que en el código habíamos puesto el INPUT_PULLUP, y así usamos un resistencia interna que tiene arduino. 

Vamos a cambiar un poco más el código para quitar ese efecto de intermitencia. De momento declaramos una nueva variable booleana que almacena el estado anterior del LED, la llamaremos "estado_anterior" y la inicializamos como true.  En la función loop() tenemos la variable "estado",  que almacenará el estado del botón con un false o con un true, con un false si el pulsador está presionado y con un true si no lo está, ya que se le ha asignado la instrucción digitalRead que lee lo que hay almacenado en la variable "boton", es decir el estado de pin de entrada o pin 10, que al no estar pulsado tiene el valor true. Por tanto la variable "estado" y la variable "estado_anterior" tiene ambas el mismo valor true cuando comienza el código. En el momento que se presiona el pulsador, el valor de la variable "estado" cambia a false, con lo cual se cumple la condición del primer if, el valor de "estado" es distinto del valor de "estado_anterior" y por tanto, se ejecuta el código que hay en el interior del primer if.  Resulta que como el valor de "estado" es false, también se cumple la condición del segundo if, y por tanto se ejecuta el código de su interior y digitalWrite escribe en el pin de salida del LED, lo contrario a lo que había en dicho pin, es decir, si el LED estaba apagado, lo enciende y si estaba encendido lo apaga.  Para ello utilizamos el operador negación ! y la instrucción digitalRead, que además de leer los pines de entradas, también puede leer los pines de salida, como es el caso.  Luego se guarda el valor de la variable "estado" false, en la variable "estado_anterior", con lo que ahora ambas variable tiene el mismo valor false, y por tanto, no se cumple la condición del primer if y no se ejecuta el código de su interior. En el momento que soltamos el pulsador en la variable estado se guarda el valor true. con lo cual se cumple la condición del primer if, el valor de "estado" es distinto del valor de "estado_anterior" y por tanto, se ejecuta el código que hay en el interior del primer if, pero en este caso no se cumple la condión del segundo if y el LED se queda como estaba. y cambia el valor de la variable "estado_anterior" a true. 

En el momento que se vuelva a presionar el pulsador, ya sea para apagar el LED o encenderlo ser volverá a repetir el proceso descrito anteriormente. Con esto ya se habrá solucionado el problema y el pulsador se comportará como un interruptor normal y corriente, si extraños efectos.



Como podemos ver el pulsador actúa igual que un interruptor, sin problemas de rebote y sin efectos de intermitencia.