Measure RPM - Optical Tachometer

by electro18 in Circuits > Arduino

480859 Views, 914 Favorites, 0 Comments

Measure RPM - Optical Tachometer

Collages.jpg
IMG_4383.JPG
Measure RPM - DIY optical Tachometer

This Instructable will show you how to make a Portable Digital Optical Tachometer using an Arduino Uno.

Instead of a slotted sensor , it has a reflection based sensor. So :

1. You don't have to worry about the thickness of the rotor

2. The number of blades won't change your readings

3. It can also read the RPM of drum style rotors which slotted sensor can't

What is a tachometer ?

A tachometer is a device used to measure the RPM or Revolutions Per Minute of any rotating body. Tachometers can be contact based or non-contact ones. The non-contact or contact-less optical tachometers usually use laser or Infrared beam to monitor the rotation of any body. This is done by calculating time taken for one rotation.

FEATURES
  • It can measure RPM over 20k
  • Sensor range extends upto 7~8 cm
  • Displays Maximum RPM when left Idle
  • Automatically toggles modes from "Idle" to "reading"
  • Can be adjusted to match the ambient lighting conditions
  • It is comparatively cheap and easy to build
  • Can work without an LCD
  • Programmable and supports customization
  • Connect an SD card to the Arduino to keep logs
  • Downloads

    Part List :

    IMG_4315.JPG
    IMG_4339.JPG
    IMG_4342.JPG
    IMG_4332.JPG
    IMG_4371.JPG
    Electronics
    • Arduino
    • Resistors - 33k , 270 ohm , 10k potentiometer
    • LED - blue
    • IR LED and Photodiode
    • 16 x 2 LCD
    • 74HC595 shift Register
    • Ribbon cable ( 3 wire )
    • Perfboard and headers
    Tools and Hardware
    • Soldering Iron
    • Solder
    • Pins
    • Screws
    • Motors and DC fan

    Build the Sensor

    IMG_4351.CR2.jpg
    IMG_4318.CR2.jpg
    IMG_4320.JPG
    IMG_4321.CR2.jpg
    IMG_4323.JPG
    IMG_4326.CR2.jpg
    IMG_4328.JPG
    IMG_4330.JPG

    For the sensor you'll need an IR LED and a Photodiode.

    1. Start by sanding the LED and photodiode to make it flat ( do not sand it too much or you'll destroy it ).

    2. Then fold a strip of paper sheet as shown. Make two such Structures so that the LED and Photodiode fit tightly into it. Joint these together by glue and paint them black.

    3. Insert your LED and Photodiode in them in such a way that the positive ( longer ) lead of the LED is right above the shorter lead of the photodiode.

    4. Glue them into the cover using superglue and solder the positive ( longer ) lead of the LED to the shorter lead of the photodiode.

    5. Solder the 3 wire ribbon cable to the remaining leads

    In my case :

    1. Orange wire --> LED's positive pin and photodiode's shorter lead

    2. Yellow wire --> photodiode's longer lead

    3. Green Wire --> LED's ground pin

    You're ready to make the board >>


    Making the Sensor Board

    Tachonew.png
    IMG_4334.CR2.jpg
    IMG_4353.CR2.jpg
    IMG_4349.CR2.jpg
    IMG_4359.CR2.jpg

    Take a small piece of Perfboard and place the components according to the schematics.

    The resistor values may vary depending on what kind of photodiode are you using.

    The potentiometer helps in reducing or increasing the sensitivity of the sensor.

    Finally solder the sensor wires as shown and solder 3 headers.

    The headers ( in order ) are shown on the left side of the schematic.

    make a cuboidal paper tube whose length is equal to the sensor wires.

    The 3-pin LCD

    LCD V2.png
    IMG_4342.JPG
    IMG_4340.CR2.jpg
    IMG_4344.CR2.jpg

    This method uses a 8-bit shift register 74HC595 with a 16 x 2 LCD. Normally this LCD uses 6 pins but using a shift register reduces the pin requirement by 3.

    The full instruction guide and the library can be downloaded from THIS WEBSITE !

    ## Recent Update : The library provided on the website has a lot of errors and conflicts. I've uploaded my version of enhanced ShiftLCD library. I recommend you to use the one attached below.

    The only thing that I've changed is :

    instead of going for (2, 4, 3) configuration I've used (8, 10, 9)

    So be sure to change the pin mapping accordingly

    Downloads

    Make the Box

    IMG_4358.CR2.jpg
    IMG_4360.CR2.jpg
    IMG_4361.JPG
    IMG_4362.CR2.jpg
    IMG_4363.CR2.jpg

    You can use any type of case for this but I've used a piece of cardboard to make enclosure.

    Cut the cardboard as shown and cut appropriate sized slits for the USB port , power jack and the sensor board.

    Mount the Arduino on the platform using screws.

    Attach the sensor and push it through the hole.

    Connect the LCD to Arduino as shown.

    Close the box and paint.

    Finishing Touch

    IMG_4371.JPG
    IMG_4373.CR2.jpg
    IMG_4364.CR2.jpg
    IMG_4365.JPG
    IMG_4366.CR2.jpg
    IMG_4368.CR2.jpg

    Make a small ( 5mm ) hole to fix the status LED. Solder a 270 ohm resistor to the LED and insert it into pin 12 on Arduino.

    Fold the cardboard along the lines to complete the enclosure. Keep the folds in place by using pins.

    Cover the sensor with a cubical paper tube to give additional mechanical strength.

    Place the LCD module over the box.

    Your device is ready for calibration and programming.

    Program

    Code
    COPY THIS CODE INTO YOUR ARDUINO IDE
    #include<ShiftLCD.h>
    
    ShiftLCD lcd(8 ,10 , 9);    // DEFINE LCD PINS
    
    volatile byte REV;       //  VOLATILE DATA TYPE TO STORE REVOLUTIONS
     
    unsigned long int rpm, maxRPM;  //  DEFINE RPM AND MAXIMUM RPM
     
    unsigned long time;         //  DEFINE TIME TAKEN TO COVER ONE REVOLUTION
     
    int ledPin = 12;           //   STATUS LED
     
    int led = 0,RPMlen , prevRPM;  //  INTEGERS TO STORE LED VALUE AND CURRENT RPM AND PREVIOUS RPM
     
    int flag = 0;             //  A VARIABLE TO DETERMINE WHETHER THE LCD NEEDS TO BE CLEARED OR NOT
    
    long prevtime = 0;       //  STORE IDLE TIME TO TOGGLE MENU
        
     void setup()
     {
         Serial.begin(9600);   // GET VALUES USING SERIAL MONITOR
         
         lcd.begin(16, 2);     // INITIATE LCD
         
         attachInterrupt(0, RPMCount, RISING);     //  ADD A HIGH PRIORITY ACTION ( AN INTERRUPT)  WHEN THE SENSOR GOES FROM LOW TO HIGH
         
         REV = 0;      //  START ALL THE VARIABLES FROM 0
         
         rpm = 0;
         
         time = 0;
         
         pinMode(ledPin, OUTPUT);
         
         pinMode(3, OUTPUT);           
         
         pinMode(4, OUTPUT);
         
         digitalWrite(3, HIGH);             //  VCC PIN FOR SENSOR
         
         digitalWrite(4, LOW);              // GND PIN FOR SENSOR
         
         lcd.print("TACHOMETER");           //   STARTUP TEXT
         lcd.setCursor(0, 1);
         lcd.print("- ELECTRO18");          //  THAT'S ME
         delay(2000);
         lcd.clear();
         
     }
     
     void loop()
     {
      long currtime = millis();                 // GET CURRENT TIME
      
      long idletime = currtime - prevtime;        //  CALCULATE IDLE TIME
        
        if(REV >= 5 )                  //  IT WILL UPDATE AFETR EVERY 5 READINGS
       {
         
                 
         if(flag==0)                     //  CLEAR THE LCD TO AVOID ANY GARBAGE TEXT
         {
           lcd.clear();
           lcd.print("SENSOR MEASURING");
           flag=1;                          //   AFTER FLAG = 1 , THE LOOP WILL NOT EXECUTE AGAIN
         }
         
         rpm = 30*1000/(millis() - time)*REV;       //  CALCULATE  RPM USING REVOLUTIONS AND ELAPSED TIME
         
         if(rpm > maxRPM)
         maxRPM = rpm;                             //  GET THE MAX RPM THROUGHOUT THE RUN
        
         time = millis();                            
         
         REV = 0;
         
         int x= rpm;                                //  CALCULATE NUMBER OF DIGITS IN RPM
         while(x!=0)
         {
           x = x/10;
           RPMlen++;
         }       
              
         
         
         if(RPMlen!=prevRPM)                             // IF THE RPM FALLS TO A LOWER NUMBER WITH LESS DIGITS , THE LCD WILL GET CLEARED
         {
           lcd.clear();
           prevRPM = RPMlen;
           flag=0;
           lcd.print("SENSOR MEASURING");
         }
         
         lcd.setCursor(0, 1);
         lcd.print(rpm,DEC);                        //  PRINT RPM IN DECIMAL SYSTEM
         
         lcd.setCursor(6,1);
         lcd.print("RPM");
         delay(500);
         
         prevtime = currtime;                        // RESET IDLETIME
        
       }
       
       if(idletime > 5000 )                      //  IF THERE ARE NO READING FOR 5 SEC , THE SCREEN WILL SHOW MAX RPM
       {
         
         if(flag==1)                            // CLEAR THE LCD
         {
           lcd.clear();
           flag=0;
         }
         
         lcd.clear();
         lcd.print("MAXIMUM RPM");
         lcd.setCursor(0, 1);
         lcd.print(maxRPM,DEC);                     // DISPLAY MAX RPM
         lcd.print("   RPM");
         delay(2000);
         lcd.clear();
         lcd.print("IDLE STATE");
         lcd.setCursor(0, 1);
         lcd.print("READY TO MEASURE");
         delay(2000);
         prevtime = currtime;
       }
         
     }
     
     void RPMCount()                                // EVERYTIME WHEN THE SENSOR GOES FROM LOW TO HIGH , THIS FUNCTION WILL BE INVOKED 
     {
       REV++;                                         // INCREASE REVOLUTIONS
       
       if (led == LOW)
       {
         
         led = HIGH;                                 //  TOGGLE STATUS LED
       } 
       
       else
       {
         led = LOW;
       }
       digitalWrite(ledPin, led);
     }
    //////////////////////////////////////////////////////////////  END OF THE PROGRAM  ///////////////////////////////////////////////////////////////////////
    

    Explanation and Calculation

    This program basically monitors the IR sensor's value constantly and with the highest priority using Interrupts.

    The Arduino Uno has 3 interrupts and the Interrupt 0 is pin 2 on the arduino.

    attachInterrupt(0, RPMCount, RISING);
    

    This line attaches an interrupt to pin 2 on arduino in "RISING" mode. This means that whenever the sensor goes from LOW to HIGH , the function RPMCount(); is invoked.

    This means that in one revolution , the function will be called twice ( REV++ ). Therefore actualREV = REV/ 2.

    rpm = 30*1000/(millis() - time)*REV;
    

    To calculate the actual RPM, we need the time taken for one revolution. And (millis() - time) is the time taken for one full revolutions.

    In this case , let t be the time taken for one full revolution , so the total number of revolutions RPM in 60sec ( 60*1000 millisecond ) is :

    rpm = 60*1000 / t * actualREV => rpm = 60*1000 / (millis() - time ) * REV/2

    OR rpm = 30*1000 / (millis() - time) * REV;

    Testing and Troubleshooting

    Testing :

    1. Take a DC fan and stick a white tape to one of it's blades. Place the sensor 2~7 cm from the blades

    2. The readings will appear on the LCD

    3. If the sensor gets no readings for 5 sec then it will automatically display the idle screen

    4. The Idle screen will display the maximum RPM reached in that particular run.

    TROUBLESHOOTING :

    1. If the status LED is not blinking, try to adjust the potentiometer until the sensor is able to get readings

    2. Ambient light may sometimes interfere with the sensor. Decreasing the sensitivity would eliminate the chance of getting false readings.

    3. Check the polarity of the photodiode properly.

    4. If everything fails , check your sensor manually by using :

    Serial.println( digitalRead(2) ) ;

    if your sensor doesn't show " 1 " when any object is placed in front of it then try increasing the value of 33k resistor.

    Conclusion

    Though there are many optical tachometers available in the market, this device is comparatively cheap and works quite well. I've tested it above 20000 RPM and it works every time ! Being open source and programmable , there arise infinite possibilities of customizing this project.

    Feel free to ask anything about this project. Suggestions , queries , corrections and "grammatical errors" are welcome !

    Happy Tinkering :)