Tutorial: LM393 o FC-03 o FZ0888

Tutorial: LM393 o FC-03 o FZ0888

¿QUÉ ES UN OPTOINTERRUPTOR?

En este tutorial muestro como usar el encoder FC-03 o encoder FZ0888 (Módulo sensor de velocidad de infrarrojos con el comparador LM393) o optointerruptor. Un optointerruptor es un sensor con forma de “U” que permite detectar un objeto que atraviesa el dispositivo por la ranura interior.

 

Los optointerruptores son sensores sencillos. Uno de los extremos contiene un diodo emisor de infrarrojos, mientras que el otro contiene un fototransitor que recibe la señal. Cuando un objeto pasa por la ranura interrumpe el rayo de luz infrarroja, lo que es detectado por el fototransitor.

Los optointerruptores son muy empleados como encoder para detectar la velocidad de giro y posición del eje de un motor. Para ello se emplea un disco con ranuras acoplado al eje. También es posible emplear una lámina transparente sobre la que se imprimen franjas negras, algo que encontramos frecuentemente en impresoras.

No son componentes fáciles de encontrar porque cada vendedor le pone un nombre diferente y las traducciones de estas páginas son bastante malas. Probar buscando «photo interrupter» o «sensor velocidad arduino» o «sensor ranurado arduino»

También podemos encontrar optointerruptores en montajes de 2 o 4 canales. En general suelen ser más caros que comprarlos sueltos y no hay ninguna ventaja en tenerlos en un mismo dispositivo así que, en general, no son recomendables.

 

 

ESQUEMA ELÉCTRICO

Si usáis una placa comercial, que como hemos dicho en general es recomendable, el montaje de un optointerruptor a Arduino es realmente sencillo. Alimentamos el módulo a través de Vcc y GND conectándolos, respectivamente, a la salida de 5V y GND en Arduino. También es posible utilizar este sensor en una Microbit o incluso en una interfase de Fishertechnick, aunque en las LT o BT es necesario usar la salida digital.

 

En caso de decidirse por hacer todo el montaje por ustedes mismos, el esquema eléctrico tampoco es complicado. Simplemente necesitamos alimentar el módulo correctamente, respetando el esquema del componente. Como véis, el sentido de alimentación de ambas ramas suele estar invertido. Consultar el Datasheet de vuestro optoacoplador para verificar su patillaje.

Pines de conexión del módulo de velocidad (encoder FC-03):
VCC: Alimentación del módulo de 3,3V a 12V.
GND: Tierra.
D0: Señal digital de los pulsos de salida.
A0: Señal analógica de los pulsos de salida. Señal de salida en tiempo real. (normalmente no se usa).
Principales características técnicas:
Dimensiones: 32 x 14 x 7mm
La ranura de lectura del sensor tiene un ancho de 5mm.
Dos salidas, una Digital y otra Analógica.
LED indicador de alimentación.
LED indicador de los pulsos de salida del pin D0.

Pulsos malos:

Con este encoder he tenido también problemas a la hora de leer los pulsos digitales que genera el comparador LM-393. Arduino UNO o MEGA lee más pulsos de los que se generan realmente. Lee del orden de 4 veces más pulsos de los que se generan realmente. He buscado información sobre este problema en internet y he encontrado poca. Por lo que he podido aprender, es que este sensor es muy sensible a las interferencias que se puede introducir en los pines VCC y GND. Si alimentamos el sensor desde el propio Arduino a 3,3V o 5V, El regulador de tensión del propio Arduino pueden introducir corrientes parásitas en el sensor. Produciendo que este no funcione correctamente.
Usando un osciloscopio conectado entre los pin D0 y GND y analizado los pulsos que se generan en el encoder. Ver las fotos siguientes:

Señales cuadradas de salida del encoder.
Señal del pulso digital del pin A0.
Señal analógicas de salida del encoder.
Señal del pulso analógico del pin D0.

Si miramos el pulso generado parece que es un pulso cuadrado correcto, pero si se amplia mucho el inicio del pulso se puede ver que el pulso no es cuadrado. Tal y como puede verse en la foto siguiente, la señal digital cuadrada TTL que genera el encoder FC-03 tiene rebotes al inicio del pulso.

Rampa de subida de la señal con muchos rebotes
Rebote inicial de la señal visto con un osciloscopio.

También tiene rebotes al final del pulso. Arduino es muy sensible y lee estos rebotes como pulsos buenos y realmente no lo son. Ver foto siguiente con los rebotes al final del pulso:

Rampa de bajada de la señal con muchos rebotes
Rebote final de la señal visto con un osciloscopio.

3-Dos soluciones : Para solventar este problema, he diseñado dos soluciones. La primera solución es hacer un programa, un «sketch» de Arduino que no lea los rebotes y no lea falsas señales. La segunda solución es colocar un condensador para que elimine los rebotes iniciales y finales.
La solución mejor que propongo para solventar este problema es la siguiente.

4-Solución 1 En la figura siguiente se muestra el esquema básico que he realizado en esta solución. El truco consiste en colocar un condensador  de polièster metalizado (o cerámico) entre el pin D0 y el pin GND. Yo he probado con varios condensador diferente. El que me ha dado mejor resultado es el condensador (562J 250v). Este condensador casi no deforma el pulso digital generado por el comparador LM393.
Esta solución es la que mejor que se puede adoptar. Ya que la he ensayado con diferentes placas de Arduino y nunca ha fallado. Yo he probado con Arduino UNO, MEGA y DUE.
Esquema de conexión del encoder con un condensador.
También he probado con un condensador de 100nF (104), el cual también me ha dado buen resultado. Este condensador deforma más la señal del pulso digital, al ser de mayor capacidad, pero funciona correctamente. En las dos fotos siguientes muestro el condensador soldado a los dos pines del módulo de velocidad. En esta posición el condensador queda perfectamente integrado y no molesta para nada.
El módulo medidor de velocidad y el condensador
Vista del condensador soldado a los dos pines
Encoder LM393
Vista frontal de módulo y el condensador soldado
En la fotografía siguiente se aprecia la rampa de subida de un pulso del encoder con el condensador ya soldado. Podemos ver que los rebotes han desaparecido por completo.
Rampa de subida de la señal sin rebotes
Inicio del pulso con los rebotes eliminados
En la fotografía siguiente se aprecia la rampa de bajada de un pulso del encoder con el condensador ya soldado. Podemos ver que los rebotes han desaparecido, pero aún se aprecia unos pequeños escalones en la señal. Estas pequeñas oscilaciones no son captadas por el Arduino. El cual reconoce el pulso correctamente.
Rampa de bajadas de la señal sin rebotes
Final del pulso con los rebotes eliminados

Esta solución es muy buena, ya que no se envían al Arduino falsas señales y el programa no tiene que perder el tiempo verificando si la señal es buena o mala. De esta manera solo se activa la interrupción del Arduino cuando la señal es correcta.

EJEMPLOS DE CÓDIGO

Tenemos varias opciones para leer un optointerruptor con Arduino. Si estamos detectando la presencia de un objeto, simplemente leemos el estado de la entrada digital.

Cuando el sensor se dispara, ejecutamos las acciones necesarias, cómo incrementar un contador, o medir el tiempo entre disparos.

Ejemplo 1

const int sensorPin = 9;
 
void setup() {
  Serial.begin(9600);   //iniciar puerto serie
  pinMode(sensorPin , INPUT);  //definir pin como entrada
}
 
void loop(){
  int value = 0;
  value = digitalRead(sensorPin );  //lectura digital de pin
 
  if (value == LOW) {
      Serial.println("Optointerruptor activado");
  }
  delay(1000);
}

Sin embargo, en el caso de usar el optointerruptor como encoder lo normal es que empleemos las interrupciones de Arduino, lo que nos simplificará considerablemente el código. El punto negativo es que tendremos que hacer debounce a las entradas y que Arduino UNO y Nano solo tenemos dos interrupciones externas, lo cuál en algunos vehículos se nos quedará corto.

 

 

Ejemplo 2:

En éste ejemplo, usamos el Optointerruptor para encender 3 leds (conectados en 8,9,10) según la velocidad de rotación.

/// Variables //////////////////////////////////////////////////////////////////////////////////////////////////////////////
int encoder_pin = 2;             //Pin 2, donde se conecta el encoder

int led_r = 8;
int led_a = 9;
int led_v = 10;     

unsigned int rpm = 0;            // Revoluciones por minuto calculadas.
float velocity = 0;              //Velocidad en [Km/h]
volatile byte pulses = 0;        // Número de pulsos leidos por el Arduino en un segundo
unsigned long timeold = 0;       // Tiempo 
unsigned int pulsesperturn = 20; // Número de muescas que tiene el disco del encoder.
const int wheel_diameter = 64;   // Diámetro de la rueda pequeña[mm]
static volatile unsigned long debounce = 0; // Tiempo del rebote.

////  Configuración del Arduino /////////////////////////////////////////////////////////
void setup(){
  
  pinMode(led_v,OUTPUT);
  pinMode(led_a,OUTPUT);
  pinMode(led_r,OUTPUT);

  Serial.begin(9600);           // Configuración del puerto serie  
  pinMode(encoder_pin, INPUT);  // Configuración del pin nº2
  attachInterrupt(0, counter, RISING); // Configuración de la interrupción 0, donde esta conectado. 
  pulses = 0;
  rpm = 0;
  timeold = 0;
  Serial.print("Segundos ");
  Serial.print("RPM ");
  Serial.print("Pulsos ");
  Serial.println("Velocidad [Km/h]");}
  
 void loop(){
   if (millis() - timeold >= 1000){  // Se actualiza cada segundo
      noInterrupts(); // Desconectamos las interrupciones para que no actué en esta parte del programa.
      rpm = (60 * 1000 / pulsesperturn )/ (millis() - timeold)* pulses; // Calculamos las revoluciones por minuto
      velocity = rpm * 3.1416 * wheel_diameter * 60 / 1000000; // Cálculo de la velocidad en [Km/h] 
      timeold = millis(); // Almacenamos el tiempo actual.
      Serial.print(millis()/1000); Serial.print("       ");// Se envia al puerto serie el valor de tiempo, de las rpm y los pulsos.
      Serial.print(rpm,DEC); Serial.print("   ");
      Serial.print(pulses,DEC); Serial.print("     ");
      Serial.println(velocity,2); 
      pulses = 0;  // Inicializamos los pulsos.
      interrupts(); // Restart the interrupt processing // Reiniciamos la interrupción
   }
   if(rpm > 147){
    digitalWrite(led_v, LOW);
    digitalWrite(led_a,LOW);
    digitalWrite(led_r, HIGH);
   }
   else if( rpm > 110){
    digitalWrite(led_v, LOW);
    digitalWrite(led_a, HIGH);
    digitalWrite(led_r, LOW);
   }
   else{
    digitalWrite(led_v, HIGH);
    digitalWrite(led_a,LOW);
    digitalWrite(led_r, LOW);
   }
  }
  
void counter(){
  if(  digitalRead (encoder_pin) && (micros()-debounce > 500) && digitalRead (encoder_pin) ) { 
// Vuelve a comprobar que el encoder envia una señal buena y luego comprueba que el tiempo es superior a 1000 microsegundos y vuelve a comprobar que la señal es correcta.
        debounce = micros(); // Almacena el tiempo para comprobar que no contamos el rebote que hay en la señal.
        pulses++;}  // Suma el pulso bueno que entra.
}