Introduction: Infrared Tachometer Using Arduino
Hi, this is just another tachometer as you can also find many tutorials out there. There are different approaches on measuring rpm, what I use here is based on analogRead method as written in arduino playground learning page (http://playground.arduino.cc/Learning/Tachometer) but with some modification on calculation method of rotational frequency. The code in arduino playground is very good and understandable but there are some spaces for improvement. Because it reads the number of "change in state" over specific time frame, it may lose accuracy when we try to make fast reading on slow revolution speed.
This is my first post in instructables, any feedback is welcome! :)
Step 1: Components
1. Arduino Duemilanove
2. IR LED BM1331
3. IR Phototransistor BPT1331
4. Resistor 10k Ohm
5. Resistor 150 Ohm
6. Cables, pin sockets, perfboard
7. Motor & Wheel Assy : This is not the main interest in this project, basically any arrangement that provides a wheel with adjustable speed will do. Here I used a styrofoam disk driven by a used walkman motor connected to an AC/DC adaptor through a potentiometer. Clamps, base, etc are just from the backyard. The only important thing is to make window on the wheel such that the IR LED & phototransistor can see each other sometimes while the wheel is turning.
Step 2: Assemble Parts and Make Connections
1. Put the IR LED and Receiver facing to each other in a position that make them can see each other occasionally through the window on the wheel. Better to make them not to far apart.
2. Create the shield (saying shield is somewhat fancy, actually only a circuit of limiting resistors *lol*), see the diagram.
3. Hook up with your arduino: connect the IR receiver to analog pin A0, see the diagram. Notice that the IR LED BM1331 and IR Phototransistor BPT1331 look alike, with two legs and clear head so make sure you are not mixing them. Connect the longer leg of LED to positive side of circuit (right after 150 resistor), but the longer leg of phototransistor should be connected to negative side of circuit (goes to the GND).
4. Hook up your motor (to drive the wheel), here i use external power supply to make it simple. Remember if you want to use a motor powered from the arduino board make sure you know exactly what you are doing, because you cannot draw a lot of current as for a motor through your board, you will overload her. Play safe, or use a motor shield.
Step 3: Write the Code
The code is pretty much similar to the one here: http://playground.arduino.cc/Learning/Tachometer, in fact i wrote based on that code. Only instead of counting how many times the sensors are saying hi to each other every certain period, I count how long until they can see the other again after the last meeting. Also, I find that using micros(), that returns microcontroller time in microseconds, gives a significantly more accurate result than if i used millis() on the same place.
Here is the code:
int sensorvalue; int state1 = HIGH; int state2; float rps; float rpm; long prevMillis = 0; long interval = 200; long currentTime; long prevTime = 1; long diffTime; int sensorthreshold = 30; // this value indicates the limit reading between dark and light, // it has to be tested as it may change acording to the // distance the leds are placed. // to see what number is good, check the sensorvalue variable value // as printed out in the serial monitor void setup() { Serial.begin(9600); pinMode(13,OUTPUT); // assign pin 13 led as indicator because we cannot se the IR light } void loop() { sensorvalue = analogRead(0); // read from pin 0 if(sensorvalue < sensorthreshold) state1 = HIGH; else state1 = LOW; digitalWrite(13,state1); // as iR light is invisible for us, the led on pin 13 // indicate the state of the circuit. if(state2!=state1){ //counts when the state change, thats from (dark to light) or //from (light to dark), remember that IR light is invisible to us. if (state2>state1){ currentTime = micros(); // Get the arduino time in microseconds diffTime = currentTime - prevTime; // calculate the time diff from the last meet-up rps = 1000000/diffTime; // calculate how many rev per second, good to know rpm = 60000000/diffTime; // calculate how many rev per minute unsigned long currentMillis = millis(); // print to serial at every interval - defined at the variables declaration if(currentMillis - prevMillis > interval){ // see if now already an interval long prevMillis = currentMillis; Serial.print(rps); Serial.print(" rps "); Serial.print(rpm); Serial.println(" rpm"); } prevTime = currentTime; } state2 = state1; } /* //only for testing to determine the sensorthreshold value delay(500); Serial.println(sensorvalue); */ }
Tricky thing was on defining the sensorthreshold value. I did this: comment the main routine, and uncomment the testing section as seen in the codes below. That way arduino will write to serial monitor and update the sensor value in your screen every 500 ms. Compile and upload your code to your board, then open the serial monitor. Turn and stop the wheel so that the sensors are blocked and look what number appears in the serial monitor (in my case around 37-40), then turn and stop where the sensors can see each other through the window and look what number in the screen now (in my case 14-19). Then set a number as sensorthreshold variable value that you now know what is a good number to distinguish between "see" or "do not see" (I set 30 in my case, because my "do not see" is 37-40 and "see" is 14-19, setting between 20-36 can do the same function also, but not really safe because the value may sometimes be 35 or 21 due to interference from environment lights, in the electrical connections, or other things that I honestly do not really understand about). So, for short, check the number when the IR sensors see each other, and check it when the sensors are blocked, then find a good number in between that you are sure it will distinguish between "see" and "do not see".
Below is the code modification for checking the good number for sensorthreshold variable:
... void loop() { sensorvalue = analogRead(0); // read from pin 0 /* if(sensorvalue < sensorthreshold) ... state2 = state1; } */ // only for testing to determine the sensorthreshold value delay(500); Serial.println(sensorvalue); }
Step 4: Serial Monitor Readings
The program writes the rev per second and rev per minute to the Arduino IDE serial monitor.
Of course this project is very rough and simple, but can be a part of further development. I will come again once I have something else. See you later...
10 Comments
8 years ago on Introduction
Nice work on your first instructable! Very detailed and thorough.
I hope you share more of your projects here!
Reply 8 years ago
Thank you, hopefully someone will find this useful :)
5 years ago
hello , its a great work that will help a lot of students like me . i have only one question how did you calculate the 100000 in rps and the 600000 in rpm , iam working on a licence degree project that its a little bit the same of that but in matlab so iam searching for how to calculate rps or rpm iam working with LM393 speed sensor ,it give us only the the 1 or 0 it high or low in your code , so your equation its seems logical to me to convert it to matlab but i do not know why you write 10000 and 60000 in rpm and rps . i really hope you answered me . thanks again for your help
Reply 5 years ago
Hi, i am not into this tech things for a while but i hope i can still give a good, understandable and correct answer.
So, basically my method is by capturing the current arduino internal time, like its stopwatch counting elapsed time of how long this arduino has been working since it powered up (in microsecs, 1/1000000s) using:
currentTime = micros();
Then I calculate time difference between 2 subsequent sensors "meet-up" (as i use 1 hole then 2 meet-ups mean 1 rev):
diffTime = currentTime - prevTime;
Next I calculate the rps, why i use 1000000 because i capture the arduino internal time in microsecs. So, suppose the previous sensors meet-up (when the hole allowed them to see each other) was at the time of arduino stopwatch read 200000 microsecs and then currently it reads 400000 when the sensors now meet again. That means it took 400k-200k = 200k microsecs for the hole to make 1 revolution. Therefore, in 1 sec, which is 1000000 microsecs, the number of rev is:
rps = 1000000/diffTime;
Because 1 rpm is 60xrps, then:
rpm = 60000000/diffTime;
(I could have written rpm = 60*rps, don't know why i didn't do that :))
As for the following lines, i use millis() to capture arduino's internal time in millisecs. Why didn't I use the micros()? First because this routine is only for printing purpose, doesn't need to be tight. Like if i want to print 5 data every sec then i use interval of 200millisecs. If i use interval of 1millisecs, i will get 1000 data per sec, which is too much. Second, i just wanted to try that function :)
Then why I use micros for rpm calculation? Because it is still possible for an electric motor to run more than 1000revs per sec (which means 60000 rpm or +) - not that motor I used in this instructable, but i just want to make my code universal. If a motor has 1000 rev/sec, the sensors meet every 1 millisecs (which millis() can still capture the time difference), but once it exceed 1000, millis() can no longer accomodate. Thus i use micros()
I know my answer is probably too long while you can already just get the point and continue working after reading first 5 lines, but yeah i just keep writing and writing.. Hope my answer helps. Cheers. PD.
Reply 5 years ago
thanks a lot for your quick reply , it helps me a lot thanks again :)
7 years ago
Is this the same way how scroll up/down in a mouse work?
8 years ago on Introduction
OK. Disregard my comments below. I had a good look at a blow-up of the photo and I see it is where you have inserted the two resistors into the circuit.
Reply 8 years ago on Introduction
sorry gray if my instructable is not really clear. however, glad you saw it..
8 years ago on Introduction
Nor is it listed in your Components.
8 years ago on Introduction
How does the 'IR Shield' fit into the scheme of things. I don't see it in your schematic and I cannot find one in the RobotShop.