Thursday, March 2, 2017

Encoder and Arduino.Tutorial about the IR speed sensor module with the comparator LM393 (Encoder FC-03)

Versión española.
This tutorial shows how to use the FC-03 encoder or the encoder FZ0888 (infrared speed sensor module with the LM393 comparator) or opto-switch. I have implemented this encoder on the 4 wheels of the Andromina OFF ROAD robot to calculate the speed of rotation of these. I have chosen this encoder because it is the most economical in the market. This tutorial shows the two best solutions to connect the encoder FC-03 to Arduino UNO or MEGA without problems. The encoder is shown below:
Encoder FC-03
Photo of the robot motor assembly, the encoder and the toothed disk.
1-The encoder: This IR speed module sensor with the comparator LM393, we can calculate the speed of rotation of the wheels of our robot. If we place a ring gear that rotates attached to our wheel. It could also be used as an optical switch.
The basic operation of this sensor is as follows; If anything is passed between the sensor slot, it creates a digital pulse on the D0 pin. This pulse goes from 0V to 5V and is a digital TTL signal. Then with Arduino we can read this pulse.
Here are the different parts of the encoder:
Main parts of the encoder
Connecting pins of the speed module (encoder FC-03):
VCC: Module power supply from 3.3V to 12V.
GND: Ground.
D0: Digital signal of the output pulses.
A0: Analog signal of the output pulses. Output signal in real time. (Usually not used).

Main technical characteristics:
Dimensions: 32 x 14 x 7mm.
The sensor reading slot has a width of 5mm.
Two outputs, one Digital and one Analog.
LED power indicator.
LED indicator of the output pulses of pin D0.

2-Bad pulses: I have also had problems with this encoder, when reading the digital pulses generated by the comparator LM-393. Arduino ONE or MEGA read more pulses than the encoder actually generate. The boarts read 4 times more pulses than the encoder actually generate. I searched for information about this problem in the internet and found few information. From what I could learn, this sensor is very sensitive to the interference that can be introduced in the VCC and GND pins. If we feed the sensor from the Arduino itself to 3.3V or 5V, The voltage regulator of the Arduino itself can introduce stray currents into the sensor. Producing that this does not work properly.Using an oscilloscope connected between the pins D0 and GND and analyzed the pulses that are generated in the encoder. See the following pictures:
A0 pin digital pulse signal.
Signal of analog pulse of pin D0.
Last two pictures; If we look at the generated pulse it seems to be a correct square pulse, but if we greatly increase the onset of the pulse, we can see that the pulse is not square. As we can see in the following photo, the square digital signal TTL that generates the encoder FC-03 has rebounds at the beginning of the pulse.
Initial rebound of the signal seen with an oscilloscope.
The pulse also has rebounds at the end. Arduino is very sensitive and reads these rebounds as good pulses and really these rebounds are not correct. See the following photo with the rebounds at the end of the pulse:
Final rebound of the signal seen with an oscilloscope.
3-Two solutions: To solve this problem, I have designed two solutions. The first solution is to make a program, an "Arduino" sketch that does not read the rebounds and does not read false signals. The second solution is to place a capacitor to eliminate the initial and final rebounds.
The best solution that I propose to solve this problem is the following one.

4-Solution 1; The following figure shows the basic scheme I have made in this solution. The trick is to place a metallized (or ceramic) polyester capacitor between pin D0 and pin GND. I have tried with several different capacitor. The capacitor that has given me the best result is the capacitor (562J 250v). This capacitor almost does not deform the digital pulse generated by the comparator LM393. This solution is the best that we can implement.
Since I have tried it with different Arduino boards and it has never failed. I have tried Arduino ONE, MEGA and DUE.
Connection diagram of the encoder with a capacitor.
I have also tested with a capacitor of 100nF (104), which also gave me good results. This capacitor further distorts the digital pulse signal, being of greater capacity, but functions correctly. In the next two photos I show the capacitor soldered to the two pins of the speed module. In this position the condenser is perfectly integrated and does not disturb at all.
View of capacitor welded to two pins
Front view of module and soldered capacitor
Pulse start with rebounds eliminated


The end of pulse with rebounds eliminated
This solution is very good, because false signals are not sent to Arduino and the program does not have to waste time checking if the signal is good or bad. In this way, only interruption of the Arduino is activated when the signal is correct.

5-Design: This encoder is implemented in the OFF ROAD Andromina robot. To measure the speed of the robot, we can put only two encoders on two wheels or put 4 encoders on all four wheels.
View of the robot chassis Andromina OFF ROAD.
The basic schematic of the connection of the 4 encoders FC-03 to the Arduino MEGA for a 4x4 robot is shown below. This scheme can not be implemented in the Arduino UNO since it only has 2 pins with interrupts. The Arduino MEGA has 6 pins with interrupts, which are; 2, 3, 18, 19, 20 and 21.
Schematic of the robot with the connections of the 4 encoders.
In the following picture you can see all the parts that make up the driving set; The wheel, the toothed disk, the encoder, the robot motor and the motor support. I have designed a toothed disc that is attached to the wheel rim.I designed this disc with 20 teeth. With this number of teeth the encoder has an optimum pres- sure. I have also designed a new motor mount to be able to screw the encoder to the motor.
Vista de todas las piezas que conforman el motor del robot.
The following picture shows the assembly between the motor, the encoder and the wheel. There is a very compact and robust set.
View of how the encoder is mounted on the motor.
6-Tip: The following trick only worked for me with an original Arduino UNO board. I've tried it with an Arduino Mega board not original and it did not work.

For the FC-03 encoder to work correctly with Arduino UNO Original. The VCC pin of the encoder must be supplied with a voltage of 3.3V from the 3.3V pin of the Arduino. In this way the encoder gives a pulse signal without interference. No condenser needs to be attached. See the following connection diagram:
Connection diagram of the 3.3V encoder.
If the VCC pin of the FC-03 encoder is supplied with a voltage of 5 Volts, it generates eddy currents (bounces) in the digital pulse signal and Arduino does not read the encoder pulses correctly. Reading more pulses than they actually generate.
Below are the different signals coming out of the two pins of the module.
Analog signal of the output pulses of pin A0.
Digital signal of the output pulses of pin D0.
In the following graph you can see how the square digital pulse has been deformed a little at its beginning due to the placement of a capacitor.
Digital signal of the output pulses of pin D0 with a capacitor (104)
7-Solution 2; In this section I present the solution by "software". In the next "sketch" of Arduino the problem of rebounds in the digital pulses of the encoder is solved. With the "counter" function of the "sketch". This function is activated by means of an interruption in Arduino pin 2, when the pulse ramp is detected. To not count the rebounds the function first reads the ramp up, then checks that it has passed about 500 micro-seconds since the last reading of a pulse and then re-read the signal of pin 2 to verify that it is still reading the pulse incoming. If all this is true, the pulse is counted as good. In this way we avoid reading the rebounds that are generated at the beginning of the pulse and at the end.
This solution is correct if we do not want to put a capacitor. But we are sent to Arduino good signals and bad signals. Which causes more interruptions of the really necessaris.

/// Variables //////////////////////////////////////////////////////////////////////////////////////////////////////////////
int encoder_pin = 2;             //Pin 2, donde se conecta el encoder       
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(){
   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("Seconds ");
  Serial.print("RPM ");
  Serial.print("Pulses ");
  Serial.println("Velocity[Km/h]");}
////  Programa principal ///////////////////////////////////////////////////////////////////////
 void loop(){
   if (millis() - timeold >= 1000){  // Se actualiza cada segundo
      noInterrupts(); //Don't process interrupts during calculations // Desconectamos la interrupción 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
   }
  }
////Fin de programa principal //////////////////////////////////////////////////////////////////////////////////
///////////////////////////Función que cuenta los pulsos buenos ///////////////////////////////////////////
 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.
        else ; } 


21 comments:

  1. thank you so much for sharing, this is really helpful, im using NodeMCU and i've tried your solution to manipulate the sketch and it works well, but i think use the capacitor is a better choice, i just dont have any capacitor on my hand right now.

    but im a bit confuse with this line :

    static volatile unsigned long debounce = 0;

    i'd be very thankful if you dont mind to explain about why did you use "static volatile unsigned long" for debounce variable.

    ReplyDelete
    Replies
    1. i also use NodeMCU in my projec. I have tried the code above using both nodeMCU and Arduino Uno board. It seems like the Arduino Board give better result. Which part in the code should i modify to get better result in nodeMCU?
      Thankyou!

      Delete
  2. I have broken my brain for 5 days trying to find why this wasn't working. Thank you so much for the time you took to help others out by posting this helpful tutorial.

    ReplyDelete
    Replies
    1. Thanks Asturiasgk. I had the same problem that you. If you do an interesting project about encoders or robots. We could put a post in my blog about your project.

      Delete
  3. Hello , can you post the code for all 4 encoders connected to Arduino? I know that one works okay but I can't figure it out how to connect 4 encoders with just one interrupt on Arduino mega, everywhere I look is a tutorial with just one encoder connected, no one shows with all four connected . Thanks!

    ReplyDelete
    Replies
    1. Hello Robert. Arduino MEGA has 6 interrupts. The interrupt pins are 2, 3, 18, 19, 20 and 21. See this page; https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/

      Delete
  4. I would like to express my deepest thanks to you, I have been trying for so long to find a solution but with no avail. And now, it works perfectly!

    ReplyDelete
  5. thank you very much for your help.i want to know how can we translate this code in Mikroc,and use it with microcontroller

    ReplyDelete
  6. i try draw sketch in fritzing, but there is no encoder icon. can you help me how to put the encoder icon in fritzing?

    ReplyDelete
  7. can i delete " if (millis() - timeold >= 1000){ " in the void loop()? or "if( digitalRead (encoder_pin) && (micros()-debounce > 500) && digitalRead (encoder_pin) )" in void counter()?
    What is the significance of those line? is it affect directly to the reading of the sensor?
    Thanks

    ReplyDelete
  8. Sir
    i used this optical encoder fc-03 with dc motor to find the rpm of variable dc motor .But encoder not give proper constant value,its values change very fastly at every instant .Please give any suggestion to fix this problem? If you have arduino code for fc-03 with dc variable dc motor then also guide me.
    thanks

    ReplyDelete
  9. Plz share the arduino code to find the rpm of dc variable dc motor with optical encoder fc-03
    urgently required

    ReplyDelete
  10. Thank for sharosh your project with us.

    ReplyDelete
  11. Hello.
    I'm using similar module and the rebound issue was bothering me a while. In my case I've observed that rising edge on A0 is quite slow, so at the beginning I've changed the resistor on the phototransistors side from 10k to 1k, it helped a lot, but not enough. I thought that implementing "schmitt trigger" may be helpful, and I've noticed that A0 pin is attached to (+) input, and reference voltage to (-) input. I've switched inputs - A0 to (-) and reference to (+) and although now my D0 pulses are inverted in relation to A0 but they're clean and there are no rebounds.
    Additional 5-10k feedback resistor from output to reference cause that rise/fall are a little faster - but is not necessary.
    Regards

    ReplyDelete
  12. Hi. good tutorial. Do you have some references for this module, like datasheets or maybe a book which talks about it?
    Regards

    ReplyDelete
  13. hi good post but captions are wrong under images please check
    Signal of analog pulse of pin D0. and A0 pin digital pulse signal.
    thanks

    ReplyDelete
  14. Hi. I love your rover and its manouverability! Is there any way that you can share the STL file for the motor/gearbox brackets?
    Thanks,
    Dan

    ReplyDelete
    Replies
    1. Sorry, forgot to give my e-mail. It's gimondan@gmail.com

      Delete
  15. I love your explanation in chapter 6 and 7-Solution 2. I had similar problem with encoder that always give twice reading of pulses until i read your post here. Thank you very much, this is help me a lot.

    ReplyDelete

Google analytics