Introduction: Measure RPM - Optical Tachometer

Picture of Measure RPM - 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.

  • 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
  • Step 1: Part List :

    Picture of Part List :
    • 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

    Step 2: Build the Sensor

    Picture of Build the Sensor

    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 >>

    Step 3: Making the Sensor Board

    Picture of Making the Sensor Board

    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.

    Step 4: The 3-pin LCD

    Picture of The 3-pin LCD

    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

    Step 5: Make the Box

    Picture of Make the Box

    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.

    Step 6: Finishing Touch

    Picture of Finishing Touch

    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.

    Step 7: Program

    ShiftLCD lcd(8 ,10 , 9);    // DEFINE LCD PINS
    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
    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
         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
     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.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
           x = x/10;
         if(RPMlen!=prevRPM)                             // IF THE RPM FALLS TO A LOWER NUMBER WITH LESS DIGITS , THE LCD WILL GET CLEARED
           prevRPM = RPMlen;
           lcd.print("SENSOR MEASURING");
         lcd.setCursor(0, 1);
         lcd.print(rpm,DEC);                        //  PRINT RPM IN DECIMAL SYSTEM
         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.print("MAXIMUM RPM");
         lcd.setCursor(0, 1);
         lcd.print(maxRPM,DEC);                     // DISPLAY MAX RPM
         lcd.print("   RPM");
         lcd.print("IDLE STATE");
         lcd.setCursor(0, 1);
         lcd.print("READY TO MEASURE");
         prevtime = currtime;
       REV++;                                         // INCREASE REVOLUTIONS
       if (led == LOW)
         led = HIGH;                                 //  TOGGLE STATUS LED
         led = LOW;
       digitalWrite(ledPin, led);
    //////////////////////////////////////////////////////////////  END OF THE PROGRAM  ///////////////////////////////////////////////////////////////////////

    Step 8: 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;

    Step 9: 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.


    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.

    Step 10: 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 :)


    AliM155 (author)2017-12-04

    I'm new in arduino, this my question, coz I'm confused

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

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

    what the meaning of actualREV and REV?

    and for line 57


    reading what?

    say thanks before

    larryacoates (author)2016-12-12

    I must have missed something because I am baffled by something that you stated in step 8 - Explanation and calculation where you stated the following:

    "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"

    What confuses me is why there would be TWO rising edge interrupts for each revolution, if there is only one reflective area on the unit being measured. As the IR Led emitter hits the leading edge of the reflective surface, it will cause the IR Photo Diode detector to become activated and cause the voltage to rise to slightly less than the VCC voltage, which in turn will trigger the interrupt of Int0 to fire and invole the RPMCount ISR as the RISING edge is detected. That voltage level will remain at the active level until the IR LED emitter is no longer over the reflective surface and exciting the IR Photo Diode and the signal level will FALL to 0 but because the Int0 interrupt only reacts to the RISING edge, the RPMCount ISR will NOT be invoked. It would seem to me that the RMPCount would only be invoked two times per revolution if the Interrupt mode was set to activate on the condition of "Logic Change"

    Thus i believe that your RPM value is off by a factor of 2, because you divide the time in half to account for the "2 interrupts" that are not actually 2, but are rather only one.

    one thing i realized is that the rev/2 probably originated from measuring a 2 blade rotor. the only reason I can make this assumption is because my IR sensor is tripping the interrupt twice for each blade. for IR depending on the sensitivity set on your photodiode, reflective materials can be almost any surface. Much like how you can change the channel on your TV even when aiming at the wall in the other direction. Just remember to take into account the number of blades if your RPMs way higher than usual (like by a factor of 2 lol)

    ps - Also, you guys rock! I couldn't have finished this without you guys.

    kld555 (author)larryacoates2017-08-22

    Hello, absolutely correct! I kept wondering why he assumed it counts twice. and therefore he needs to divide by two. The only case would have been that he uses CHANGE rather than RISING or FALLING.

    I am trying to do the same for a motor with encoder and optocoupler, it seems that the optocoupler does mis count on low speeds! it basically counts more than once (interrupt is called 2~3 times) while the rotor blade is just leaving the optocoupler IR Led and receiver (i.e while going out)!

    Yet to try to figure this out! I already have a debounce for ~20 milliseconds and using a 65ohm resistor for the IR LED.

    NathanJ63 (author)larryacoates2017-01-11

    I agree dividing by two put reading off by a factor of 2. There will still be some error because we can assume the first time it interrupts was not a full revolution and should be subtracted off but the time would need to be updated to account for this offset as well and millis() can't be called within RMPcount because it is an interrupt itself.

    larryacoates (author)NathanJ632017-01-12

    Thanks for the feedback. Having been a professional software developer for 35+ year and an electronics engineer for 10+ years before that it just didn't add up. As for subtracting 1 from the count, it might just be better to set the inital count to -1 and then the first interrupt will set the counter to zero and then all calculations will be based upon all complete revolutions, irrespective to how much of the first revolution was completed when the interrupt occurred.

    AldiB1 (author)2017-08-08

    Ir sensor replaced with laser what can? Or have to change the sensor circuit again.
    thank you

    fhmazm (author)2017-07-15


    TurbineTed (author)2017-07-04


    I was wondering how the schema would look if use a serial I2C LCD-display??

    Thanks for this instructable top notch stuff I was going to used a prefabricated IR reflect sensor but will now fabricate one as you did.

    I also have some 7 segment displays lying around from an old cash register could be a possible alternative to an lcd the ones I have 10 digits a comma beside and an underscore below.

    zammykoo made it! (author)2016-09-07

    Just finished breadboarding this, thanks for the guide! I didn't have a mosfet to connect between the shift register and lcd but seems to be working fine. I left pin 5 on the sr floating but hopefully figure out what to do with it.

    I want to eventually convert this into a speedometer to measure MPH, anyone know where to find the code for that?

    Miztlinette (author)zammykoo2017-04-21

    You have to take the radius of the wheel and make a conversion: v = w x r, where w is the angular rate and r is the radius of your wheel. W needs to be in rad/s so you have to know that take the product of the rpm and the factor pi/30 (it means that 1 radian per 30 seconds). So you have: v = pi x w x r /30. The rate is on m/s

    JuriF1 (author)zammykoo2016-10-02

    do you know the maximum rpm it can measure?

    MuraliK37 (author)2017-02-25

    Hi can u suggest which type of ardunio uno I want to buy
    Because several types are present in online

    ramyac1 (author)MuraliK372017-03-05

    i want applications for this type of ardunio

    BennyM10 (author)2016-07-26

    You think it would work in a low lighting situation like under a car to measure rpm then convert to speed?

    PunitJ (author)BennyM102017-02-21

    It would work better. But only for a little while before it collects dust.

    MuraliK37 (author)2017-02-14

    How to programme this setep

    Indig0ardy (author)2017-01-20

    Is it possible to log the RPM data from this over time and, for example, make it into an excel graph?

    monusuthar2016 (author)2016-12-18

    I made a youtube video tutorial on it
    please watch

    matt_shungoh (author)2016-11-24

    Can create a function such if it exceeds a certain limit , the rpm data is sent via esp to a server

    kvnnam (author)2016-05-22

    This tutorial is really great! I had a question though and I'm new to Arduino so hopefully I'm not asking for the impossible. Could the tachometer record the number of revolutions over time? Like actually count the number of times that the white tag passed by? Kind of like a spin counter and then store that data? I guess if it can count the number of RPMs then it could also calculate how many revolutions actually occurred?

    BrianG62 (author)kvnnam2016-10-10

    Instead of removing the line to reset the variable REV, you could just create another variable to store total revolutions. Then you would be able to have current RPM and total revolutions.

    This would be my suggestion.

    electro18 (author)kvnnam2016-06-14

    Yes you can do that using the same code by deleting just one line :

    " REV = 0 ; " ( in loop() )

    which resets the value of REV to 0. The program will go on increasing the value of REV but then it won't display proper RPM.

    JuriF1 (author)2016-09-30

    hello, great project. i like it!
    what's the maximum rpm it can measure?

    IIsayiahA (author)2016-09-23

    Is the pot a double gang or single gang?

    doer_1 (author)2016-08-26

    Like your " 'ible" !! However...

    When replacing the MOSFET, with a 10 K pot, (brightness control?)

    what do you do with the pin 5 connection of the shift register?

    Does it get grounded ? Ommited? Left in Limbo???

    Very unclear to me... and not on schematic. Thx.

    zammykoo (author)doer_12016-09-08

    Hey did you ever figure this out? I left my pin 5 floating and my lcd still works. I posted a pic above.

    omkar711 (author)2016-09-03

    thank you! it was very helpful.

    JepherC (author)2016-08-28

    hi. im trying to make this project. but all i can see with my work is the status led at pin12. lcd not on also the photodiode and led. may i contact you thru fb?

    TariqM11 (author)2016-08-04

    could i use my laptop instead of Arduino !!?

    by taking my sensor reding through my audio jack and make a program via c++ !!? and make it read the pulses as RPM !?

    superenigma (author)2016-07-20

    Is it possible to do this with a breadboard?

    electro18 (author)superenigma2016-07-21

    Yes, the project can be laid out on a breadboard as well. Just follow the schematics that I've uploaded.

    alan6383 (author)2016-06-28

    is it possible to convert Rpm to mm/min if one revolution = 5mm travel

    MaxwellH2 (author)2016-05-26

    What is the lowest RPM this setup will measure accurately? Thanks

    electro18 (author)MaxwellH22016-06-14

    Haven't tried it yet but can't expect it to be too accurate. For RPM < 1, you'll need separate logic and a couple of lines to be added if you decide to display it on the LCD.

    rinksrides (author)2016-06-05

    should not buy parts from ebay, use digikey or better, Mouser dot com.

    shollis1 (author)2016-05-13

    Hi, can I rip out an ir led from an old remote?

    MikeD11 (author)shollis12016-05-16

    Well you need a sender and a receiver. The IR remote is a sender. So you'd have half. I just bought 10 each from eBay for $1.00 Of course there is that 30 day wait...

    MikeD11 (author)MikeD112016-05-16

    tankman93 (author)2016-04-27

    Hi there. Nice work on the detailed and updated content. I have a querry though. How accurate is this sensor? I am interested in using this for my final year project to measure RPM of a spinning drum outdoors.

    electro18 (author)tankman932016-05-06

    I cannot guarantee 100% accuracy but yeah, the precision depends on what resolution you set.

    kmunnery (author)2016-05-03

    hi I found this instructable by searching Google for an RPM controlled LED dimmer system will this setup work thanks.

    electro18 (author)kmunnery2016-05-03

    How do you wish to use an RPM value as a dimmer ?

    kmunnery (author)electro182016-05-04

    as the RPM's increase past 1k the LED's will turn on and gradually increase intensity till my motorcycle reaches redline which will be max intensity

    electro18 (author)kmunnery2016-05-04

    Well, then you need to add a "map" function which will map the values of engine RPM (1k to max RPM ) between 0 and 255.
    brightness = map(rpmCount,1000,maxRPM,0,255);

    kmunnery (author)electro182016-05-04

    ok makes some sense. probably more if I could program.
    where would I add the "map" function or could I pay u to make one or at least what my exact steps would be like where would I get the rpm data.

    electro18 (author)kmunnery2016-05-04

    Sure, catch me on Facebook. I'll give you detailed instructions there.

    kmunnery (author)electro182016-05-06

    ok what should I search on facebook to find you? electro18 ??

    electro18 (author)kmunnery2016-05-06

    You'll find a link to my facebook profile on the desktop version of my instructable profile.

    NileshH1 (author)2016-04-26

    Can you help me with the code like i want to take the feed back from the sensor assume it says 500 rpm can i set the rmp of motor to 250 or any specific value. How can i do that ?

    About This Instructable




    Bio: Hey there ! I'm Tanay , a hobbyist interested in making robots and sharing stuff. I hope that my instructables help you in solving your problems ... More »
    More by electro18:USB Chameleon Lamp ( Color replicating )Your Own Color Sensor using LEDsMeasure RPM - Optical Tachometer
    Add instructable to: