Introducción al Rotary Encoder

Pero el modo determinante para saber si estamos utilizando un potenciómetro o un encoder, es que el potenciómetro tiene 3 pines, mientras que el encoder dispone de 5 pines de conexión.Muchas veces nos compramos un kit de Arduino y entre pequeños sensores, módulos y elementos de electrónica no nos aclaramos muy bien. Para hacer proyectos duraderos y robustos lo mejor es comprar estos módulos con toda la electrónica ya integrada para evitarnos cableado incómodo de mover y manipular.

Así que asumiendo que no nos gustan las resistencias sueltas… Vamos a utilizar hoy un encoder rotatorio. Un encoder rotatorio es un elemento incomprendido porque muchas veces no se nos ocurren cuáles pueden ser sus aplicaciones en proyectos.

Pero lo peor de todo quizás es que a veces lo podemos confundir con un potenciómetro.

CUIDADO: NO CONFUNDIR POTENCIÓMETRO CON ENCODER

Sí… los dos dan vueltas, pero la mejor manera de diferenciarlos es que el potenciómetro está limitado en el giro y el encoder no. En cuanto a su forma son muy parecidos.

El potenciómetro puede tener muchas formas como podemos observar en la imagen. Todos ellos son potenciometros y disponen de una ruleta para girar.

Pero el modo determinante para saber si estamos utilizando un potenciómetro o un encoder, es que el potenciómetro tiene 3 pines, mientras que el encoder dispone de 5 pines de conexión.

 

 

 

 


Si ya tenemos algunas pautas sobre programación con Arduino, sabremos que un potenciómetro hay que conectarlo a VCC ( Voltaje ) y GND (Tierra) y el pin es la lectura de la lectura de la resistencia del mismo.

Pero el encoder en cambio tiene los siguientes pines.

  • VCC –> Voltaje
  • GND –> Tierra
  • CLK –> CLOCK –> Señal de reloj
  • DT –> DATA –> Señal de datos
  • SW–> SWITCH –> Señal de presión.

Por lo que es más que evidente que este encoder es más complicado que un potenciómetro.

Mientras que un potenciómetro solo nos da una lectura proporcional al ángulo de giro, el encoder nos proporciona información de cuál es la dirección de giro, cuánto gira y cuándo ha sido presionado.

Las aplicaciones del encoder suelen utilizarse para la navegación en pantallas LCD o para la medición de velocidades de giro en un eje. Pero primero vamos a comprender qué hay dentro y como poder realizar una primera lectura.

La ciencia escondida del encoder

Quizás alguno no lo sepa, pero el scroll de los ratones de ordenador utilizan exactamente este mismo método para conocer cuánto ha sido girada la ruleta y cuándo ha sido presionada.

Si cogemos un ratón roto y miramos lo que tiene dentro podremos ver una pieza en la ruleta como esta.

 

Como podremos observar consta de una rueda ranurada en su eje y cuyo objetivo es bloquear el paso de un rayo de luz que se enciende a través de un led infrarrojo hasta un sensor de luz o detector de infrarrojos.

El objetivo de este método es saber cuándo se bloquea y cuando pasa la luz, activando alternativamente el sensor de luz. El aspecto constructivo de los encoders de ruleta suele parecerse al siguiente, pero la ciencia es la misma.

 

Las señales que tenemos que medir son las señales cuadradas resultantes de verificar que pasa señal de luz a través de las ranuras y cuando no.

Nuestro objetivo es conocer por una parte la dirección de giro, y por otra parte la velocidad de giro, que lo integraremos en la programación.

Para conseguir esto, necesitamos conocer más de una señal, por lo que los encoder buenos suelen contener dos sensores y dos leds para crear dos señales y establecer cuál es la diferencia relativa entre ambas para conocer más datos.

 


Los encoders funcionan con la lectura de dos señales, en las que se leen una respecto de otra para conocer su dirección y pasos de separación. Así es como deducimos su posición relativa, dirección y velocidad.

Conexión a un Arduino

Para conectarlo a Arduino, hemos de entender cuáles son las señales de interés. En este caso son 3.

  • DT –> Señal de datos en el que se detecta un cambio de movimiento.
  • CLOCK –> Señal de reloj en el que se realiza cada medición.
  • SW –>Esta señal solamente nos proporciona la presión sobre el botón

 

Programación con Arduino

Para programar un Encoder podemos acceder a la página oficial de Arduino en el que se documenta cada una de las opciones para programar estos Encoders, pero vamos a simplificar un poco el torrente de información.

En este primer ejemplo dejaremos de lado la presión sobre el botón ya que es la lectura más sencilla. Vamos a medir la señal de datos.

Programación Encoder básico

int pinA = 3;  // Connected to CLK on KY-040
 int pinB = 4;  // Connected to DT on KY-040
 int encoderPosCount = 0; 
 int pinALast;  
 int aVal;
 boolean bCW;

 void setup() { 
   pinMode (pinA,INPUT);
   pinMode (pinB,INPUT);
   /* Read Pin A
   Whatever state it's in will reflect the last position   
   */
   pinALast = digitalRead(pinA);   
   Serial.begin (9600);
 } 

 void loop() { 
   aVal = digitalRead(pinA);
   if (aVal != pinALast){ // Means the knob is rotating
     // if the knob is rotating, we need to determine direction
     // We do that by reading pin B.
     if (digitalRead(pinB) != aVal) {  // Means pin A Changed first - We're Rotating Clockwise
       encoderPosCount ++;
       bCW = true;
     } else {// Otherwise B changed first and we're moving CCW
       bCW = false;
       encoderPosCount--;
     }
     Serial.print ("Rotated: ");
     if (bCW){
       Serial.println ("clockwise");
     }else{
       Serial.println("counterclockwise");
     }
     Serial.print("Encoder Position: ");
     Serial.println(encoderPosCount);
     
   } 
   pinALast = aVal;
 } 

 

Programación Encoder Avanzado

/* Define digital pins used to read the encoder */
#define DT 4
#define CLK 3
#define SW 2
int counter;
byte DialPos;
byte Last_DialPos;

void setup()
{
  Serial.begin(9600);
  pinMode(DT, INPUT);   
  pinMode(CLK, INPUT); 
  pinMode(SW, INPUT); 
  digitalWrite(SW, HIGH);

  /* Reset the counter */
  counter = 0;
}

/* Main program */
void loop()
{
    /* Read the status of the dial */
    DialPos = (digitalRead(CLK) << 1) | digitalRead(DT);
 
    /* Is the dial being turned anti-clockwise? */
    if (DialPos == 3 && Last_DialPos == 1)
    {
      counter--;
    }
 
    /* Is the dial being turned clockwise? */
    if (DialPos == 3 && Last_DialPos == 2)
    {
      counter++;
    }
 
    /* Output the counter to the serial port */
    Serial.println(counter);
 
    /* Is the switch pressed? */
    if(!digitalRead(SW))
      Serial.println("Switch pressed!");
 
    /* Save the state of the encoder */
    Last_DialPos = DialPos;
  
}

Como podemos ver en esta programación, las tres señales son de entrada y vamos a medir en cada caso su estado.
Los estados que más nos interesan son los estados digitales de CLOCK y DT.

DialPos = (digitalRead(CLK) << 1) | digitalRead(DT);

Esta acción la conseguimos comparando la variable DialPos que es un producto simplificado de ambas lecturas de CLOCK y DT.

Programación con librerias

Librería Encoder

Pero para no tener que preocuparnos demasiado por lo que ocurre dentro y no invertir gran parte de nuestro tiempo. Vamos a utilizar una librería que nos facilite todo este proceso. La librería está disponible en el gestor de librerías o podemos descargarla desde el siguiente enlace .

En esencia, es tan fácil como incluir la librería, crear el objeto Encoder haciendo referencia a los pines de conexión y chequear en cada iteración del bucle la lectura del encoder con respecto a la anterior.

#include <Encoder.h>
Encoder myEnc(3, 4);


void setup() {
  Serial.begin(9600);
  Serial.println("Basic Encoder Test:");
}

long oldPosition  = -999;

void loop() {
  long newPosition = myEnc.read();
  if (newPosition != oldPosition) {
    oldPosition = newPosition;
    Serial.println(newPosition);
  }
}

Librería Encoder personalizada

Ofrecemos una minilibrería que podemos copiar muy rápido. Este modelo se basa en la creación de un modelo Encoder.h que contiene toda la programación y que sabemos que va a funcionar.

 

Y acto seguido integramos este modelo de encoder.

#pragma once

class Encoder {
public:
  long _counter = 0;
  Encoder (uint8_t pinA,uint8_t pinB, uint8_t pinSW, bool pullup = false, uint16_t debounceDelay = 50)
    : _pinA(pinA), _pinB(pinB), _pinSW(pinSW), _state(LOW), _lastState(LOW),
      _lastMillis(0), _debounceDelay(debounceDelay),
      _lastDebounceTime(0) {
    pinMode(_pinA, INPUT);
    pinMode(_pinB, INPUT);
    if (pullup == true) {
      pinMode(_pinSW, INPUT_PULLUP);
    } else {
      pinMode(_pinSW, INPUT);
    }
    digitalWrite(_pinSW, HIGH);
  }

  long readCount(){
    return _counter;
  }
  
  bool read(){
    byte DialPos = (digitalRead(_pinA) << 1) | digitalRead(_pinB); bool dir; 
    if (DialPos == 3 && _Last_DialPos == 1) { 
      _counter--; 
      dir = false; 
    } 
    if (DialPos == 3 && _Last_DialPos == 2) {
       _counter++; 
       dir = true; 
    } 
    _Last_DialPos = DialPos; return dir; 
  } // Debounces the button and returns the state if it was just changed. 
   
   bool check(bool triggerState = LOW) { 
      bool reading = digitalRead(_pinSW); // Checks if the buttons has changed state 
      if (reading != _lastState) {  
         _lastDebounceTime = millis(); 
      } // Checks if the buttons hasn't changed state for '_debounceDelay' milliseconds. 
      if ((millis() - _lastDebounceTime) > _debounceDelay) {
      // Checks if the buttons has changed state
      if (reading != _state) {
        _state = reading;
        return _state;
      }
    }
    _lastState = reading;
    // If this code is reached, it returns the normal state of the button.
    if (triggerState == HIGH) {
      return LOW;
    } else {
      return HIGH;
    }
  }

private:
  const uint8_t _pinA, _pinB, _pinSW;
  bool _state;
  bool _lastState;
  byte _Last_DialPos;
  uint32_t _lastMillis;
  uint16_t _debounceDelay;
  uint32_t _lastDebounceTime;
};

Main del programa

Y nuestro programa principal contendrá el siguiente código simplificado que llamará a la librería anteriormente creada.

#include "Encoder.h"
const bool pullup = false;

Encoder rotary(3,4,2);
void setup() {
  Serial.begin(9600);

}

void loop() {
  // put your main code here, to run repeatedly:
  rotary.read();
  Serial.println(rotary.readCount());
  if (rotary.check(HIGH)){
    Serial.println("Check");
    Serial.println(rotary.readCount());
  }
}

 

Seguiremos utilizando los Encoders posteriormente para realizar una navegación con pantallas LCD, pero aún queda mucho por aprender. 😉

Deja un comentario

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

catorce − tres =

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.