Objetivos
Material requerido.
Arduino UNO | |
Rotary Encoder |
Empezando con los Encoders
No estoy muy seguro de como traducir Rotary Encoder del inglés. Tal vez ¿Codificador giratorio? De cualquier modo estos Encoders son cada vez más populares porque son bastante fácil usar y nos dan bastantes ventajas con relación a otros componentes como los potenciometros, y por alguna razón que se me escapa no se suelen usar mucho en proyectos DIY. A ver si podemos hacer un pequeño aporte al asunto y cambiar eso.
Un Rotary Encoder es un dispositivo electro mecánico que convierte el movimiento de giro de un eje en una señal (Que puede ser analógica o digital dependiendo del tipo) y genera una señal proporcional al giro de dicho eje. Se parece mucho a un potenciómetro, pero a diferencia de este, puede girar indefinidamente tanto en sentido del reloj como al contrario.
En principio un Rotary encoder puede ser de varios tipos básicos como absolutos e incrementales. Los primeros proporcionan información absoluta con respecto a la marca de base del encoder, mientras que los segundos simplemente indican el giro con respecto a la posición anterior sin preocuparse de la referencia.
Naturalmente los segundo son más baratos y nos vamos a centrar en ellos porque suelen ser más fáciles de conseguir (Y más accesibles)
Un típico Rotary Encoder digital incremental, como el que nos ocupa, es un eje que gira sin límite y provoca, con unos pequeños micro interruptores una señal digital por cada muesca que gira(Un tren de pulsos cuadrados) similar al diagrama que os pongo aquí abajo:
Dependiendo de la separación entre muescas, se producen más o menos pulsos por cada vuelta (Típicamente 20 como el que os mostramos) y además producen un ruidito de click muy agradable al girar además de producir un tacto de lo más simpático en los dedos, y encima dispone de un botón que podemos pulsar cuando queramos producir una señal (De fijado por ejemplo)
Un modelo típico como el que tenemos en la tienda necesita alimentación y GND, y dispone de 3 salidas: Output A, Output B y Boton.
¿Por qué dos señales de salida en lugar de una? ¿No bastaría con una sola? Pues me temo que no, porque en ese caso no podríamos saber en dirección hemos girado el Encoder.
Al disponer de dos salidas mecánicas similares, obtenemos dos pulsos diferentes que debidamente estudiados nos indican el sentido de giro además del número de pulsos que se ha girado, porque la señal de una salida estará necesariamente desfasada con respecto a la otra ya que primero pasamos por una muesca y luego por la otra. ¿Inteligente no? Fíjate en este gráfico en detalle que he pillado por ahí:
Comprobando la secuencia de las dos señales podemos decidir en qué sentido estamos girando el Encoder, y contando los pulsos de cualquiera de las dos señales podemos calcular el número de clics girados una vez que conocemos el numero de clicks por vuelta completa.
Pero es importante que sepas que necesitas conectar AMBAS señales de salida a tu Arduino y no creer que basta con una (Porque no sabrás en qué sentido gira el Encoder). Una vez puestas las bases vamos a ver las conexiones.
Montando y programando
La conexión de los elementos es trivial:
Se trata simplemente de conectar las dos señales del Encoder a los pines 6 y 7 por ejemplo y el botón al 8 para poder leer el pulsador cuando sea preciso.
En principio contar los pulsos que nos entrega el Encoder parece bastante fácil, pero aquí el truco esta en comprender como saber si está girando a la derecha o la izquierda y que es el desfase de las señales es la clave. Volvamos a la imagen que os ponía arriba:
Supón que usamos dos variables llamadas aState y aLastState que la primera lee la situación actual del pulso en el output A y la siguiente registra el ultimo valor que leímos. Si son diferentes es que se ha girado el encoder.
Si primero leemos el valor de aState para la salida A y la encontramos en HIGH, y si ahora leemos el Output B y este está en LOW, significa que hemos girado en el sentido del reloj, porque A se ha levantado mientras B sigue en LOW. Más adelante A y B tomaran el mismo valor cuando la rueda alcance el switch B, pero por ahora solo nos interesa fijarnos en el primer momento en que ambos son diferentes.
Si por el contrario cuando la salida en A y B son diferentes pero la salida B se ha activado antes es que giramos a contra reloj. Esta parte es fácil de entender. Pero ahora viene cuando la matan… ¿Cómo pasamos esto a código? Pues como siempre sin dificultad y mucha elegancia. Aquí tenemos el programa completo:
y pasemos a verlo en un poco detalle.
Empecemos definiendo algunas variables para controlar las dos salidas del encoder más el pulsador:
#define outputA 6 #define outputB 7 #define boton 8
Además necesitamos las variables que ya acabamos de mencionar:
int counter = 0; int aState; int aLastState;
Y vamos con el setup():
void setup() { pinMode (outputA,INPUT); pinMode (outputB,INPUT); pinMode (boton, INPUT_PULLUP); Serial.begin (9600); aLastState = digitalRead(outputA); //Leemos el valor incial }
Definimos output A y B como entradas tranquilamente y como el pulsador es un botón con todos sus problemas pedimos a Arduino que monte una resistencia de pullup para leerlo (Nos hemos hartado a hablar de esto). Activamos la puerta serie para sacar las lecturas del contador y leemos la situación de A cuando iniciamos.
aState = digitalRead(outputA); // Leemos el ouputA if (aState != aLastState) { // Han girado el encoder }
Si la lectura actual y la guardada son diferentes es que ha llegado un pulso y ahora tenemos que comprobar si ha girado a favor o en contra del reloj:
if (digitalRead(outputB) != aState) counter ++; else counter --;
Solo llegamos a estas instrucciones cuando Ouput A esta en HIGH pero la última vez estaba en LOW. Por eso comparamos la lectura de la salida de B con la lectura de A. Si son diferentes B = LOW o sea aún no ha llegado el giro por lo que estamos girando a favor del reloj e incrementamos el contador.
En caso contrario es que giramos al revés, disminuimos el contador y listo. El código montado queda así:
aState = digitalRead(outputA); if (aState != aLastState) { if (digitalRead(outputB) != aState) counter ++; else counter --; Serial.print("Position: "); Serial.println(counter); } aLastState = aState; // Guardamos el ultimo valor
E imprimimos al final el valor del Contador. Solo nos queda recoger el pulsador y por ejemplo poner a cero el contador cuando se active
bool B = digitalRead(boton); if ( !B ) { Serial.println("Boton pulsado: Contador a 0"); Counter = 0 ; delay(200); }
Como hemos puesto un pullup en la entrada del botón este leerá un LOW cuando se pulse y por eso la condición es que se dispare cuando sea LOW.
Lo del delay viene porque recordad que lo interruptores no son las maquinas ideales que nos gusta imaginar sino que sufren rebotes que conviene eliminar para evitar errores y una forma sencilla es meter un retraso antes de leer de nuevo el botón (Lo suyo sería un filtro RC resistencia condensador que igualase la caída de tensión al rebotar el botón, pero hoy no queremos nota)
El resultado puede ser algo como esto cuando giras el Encoder en ambos sentidos. El contador se va incrementando o disminuyendo de forma consistente:
Y cuando pulsas el botón central el contador se va a 0:
Resumen de la sesión