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

    • 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

    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

    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

    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

    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

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

    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.


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


    fhmazm made it!(author)2017-07-15


    TurbineTed made it!(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 made it!(author)2017-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 made it!(author)2016-10-02

    do you know the maximum rpm it can measure?

    MuraliK37 made it!(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 made it!(author)2017-03-05

    i want applications for this type of ardunio

    BennyM10 made it!(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 made it!(author)2017-02-21

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

    MuraliK37 made it!(author)2017-02-14

    How to programme this setep

    Indig0ardy made it!(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?

    larryacoates made it!(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:



    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.

    NathanJ63 made it!(author)2017-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 made it!(author)2017-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.

    monusuthar2016 made it!(author)2016-12-18

    I made a youtube video tutorial on it
    please watch

    matt_shungoh made it!(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 made it!(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 made it!(author)2016-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 made it!(author)2016-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 made it!(author)2016-09-30

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

    IIsayiahA made it!(author)2016-09-23

    Is the pot a double gang or single gang?

    doer_1 made it!(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 made it!(author)2016-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 made it!(author)2016-09-03

    thank you! it was very helpful.

    JepherC made it!(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 made it!(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 made it!(author)2016-07-20

    Is it possible to do this with a breadboard?

    electro18 made it!(author)2016-07-21

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

    alan6383 made it!(author)2016-06-28

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

    MaxwellH2 made it!(author)2016-05-26

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

    electro18 made it!(author)2016-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 made it!(author)2016-06-05

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

    shollis1 made it!(author)2016-05-13

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

    MikeD11 made it!(author)2016-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 made it!(author)2016-05-16

    tankman93 made it!(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 made it!(author)2016-05-06

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

    kmunnery made it!(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 made it!(author)2016-05-03

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

    kmunnery made it!(author)2016-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 made it!(author)2016-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 made it!(author)2016-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 made it!(author)2016-05-04

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

    kmunnery made it!(author)2016-05-06

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

    electro18 made it!(author)2016-05-06

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

    NileshH1 made it!(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 ?

    electro18 made it!(author)2016-05-04

    To control the speed of the motor you'll probably need a dedicated motor driver whose inputs shall be connected to the same micro controller. Feed a value to the MCU and a couple of logics to be added in the program which will go on reducing/increasing the speed of shaft as per the required RPM value.

    Qu%C3%BD+Ng%C3%A2nN made it!(author)2016-04-16

    I made it. Your tutorial is better than this site:

    electro18 made it!(author)2016-04-16

    Good to hear that ! Thanks :)

    CraigR55 made it!(author)2016-03-25

    I am unable to source the TN0604N. Will a BS170 BS170RLRAG MOSFET N-CHANNEL 60V 0.5A work as a substitute? Please excuse my lack of knowledge.

    Regards, CR

    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: