Hello Everyone,
I have read multiple posts dealing with using interrupts for the determination of RPM of a motor or propeller, with or without a Hall Sensor, IR sensor, etc. They appear to work well in some of the videos I've seen as well. I am merely trying to determine RPM by the fact that the interrupt is called at all, and there have been sketches already written to that effect. I chose micros() over millis() because of the expected interpretive speed of the micro().
Setup of the Project
- Using a 2004A LCD module with 4 wires-everything on this end is working perfectly.
- Arduino Uno
- Code posted below.
- Method:
a. I have the LCD connected properly. Output is accurate.
b. Using the digital pin 2 as my interrupt pin, I have a wire inserted there to catch my incoming
signal, and it is connected to a small breadboard on the positive side of a resistor going to an
LED. A 3 volt power supply is connected to another wire which I connect manually to the positive
side of the LED while the ground side of the power supply is connected to the ground of the LED.
c. The manual signal is generated by me tapping the positive battery wire on the resistor of the
LED which in turn is connected, as above, to the digital pin 2, my interrupt pin, which I am
expecting should "see" mt taps since there is voltage there.
Here's my question:
If I am using a manual means of trying to cause the interrupt to occur, should it be valid? The reason for the question is because I don't seem to be able to generate valid numbers by merely touching the positive lead to the LED. I expect I'm missing something.
Additionally, what is the fastest RPM I should expect the Arduino to be able to interpret?
//very little of this code is original by me--it is borrowed from opensource
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
#define I2C_ADDR 0x27 // <<- Add your address here.
#define Rs_pin 0
#define Rw_pin 1
#define En_pin 2
#define BACKLIGHT_PIN 3
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7
LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);
int rpm = 0;
volatile unsigned long time1 = 0;
volatile unsigned long time2 = 0;
volatile unsigned long time_last = 0;
unsigned long wkgTime = 0;
void setup()
{
Serial.begin(9600);
lcd.begin (20,4); // <<-- my LCD is a 20x4, change for your LCD if needed
// LCD Backlight ON
lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
lcd.setBacklight(HIGH);
lcd.home (); // go home on LCD
lcd.clear();
attachInterrupt(0, getTime, RISING);//Initialize the intterrupt pin (Arduino digital pin 2)
lcd.print("Good morning");
lcd.setCursor (7,2); // go to start of 2nd line
lcd.print("Dave: ");
delay (3000);
lcd.clear();
lcd.setCursor (7,0); // go to start of 1st line, 7th char
lcd.print("Big D");
lcd.setCursor (4,1); // go to start of 2st line, 4th char
lcd.print("RPM Detector");
}
void loop()
{
wkgTime = time1;
Serial.println("Should be printing beginning of loop and RPM.");
delay(300);
if (wkgTime> 0) {
rpm = 60* 1000000 / time1;
delay(500);
Serial.print("RPM: ");
Serial.println(rpm,DEC);
lcd.setCursor (0,3); // go to start of 4th line
lcd.print("RPM: ");
lcd.print(rpm);
}
}
void getTime() {
time1 = (micros() - time_last);
time_last = micros();
}
Thanks for all the help in getting this lined out.
houdinihar
Your code works fine when I test it at 3000 rpm or 30,000 rpm with the tone(2, 50) or tone(2,500)function on pin 2. You have declared rpm as in int, so the max rpm you will see is 32,767.
If I am using a manual means of trying to cause the interrupt to occur, should it be valid? The reason for the question is because I don't seem to be able to generate valid numbers by merely touching the positive lead to the LED. I expect I'm missing something.
I'm not clear about your wiring and what you are using for an interrupt signal. Can you please provide a sketch of what you are doing. It is not clear to me that the input to pin 2 is not floating if grounded through the led.
Are the grounds of the Arduino and the 3v power supply connected?
You are using a uno, and the 5v boards should read digital high at 3v but with your setup you may not be getting it there.
cattledog,
thanks for your reply, and your testing--i've seen some of your other posts in dealing with interrupts and I appreciate your expertise here.
i am not able to load the pictures tonight as i am out of town, but tomorrow I will post these for your viewing.
also in regards my choice of int vs long, for instance, my question is really directed toward the capability of the Arduino itself. Could it interpret a value of 300,000 RPM if present and using the correctly declared variable?
thanks again,
houdinihar
Could it interpret a value of 300,000 RPM if present and using the correctly declared variable?
Yes. That's only 5000 per second or 200 microseconds between pulses. That's pretty comfortable for a 16 Mhz processor.
At the high frequencies, it's possible that you will get more accurate readings by counting the number of pulses over a unit of time rather than taking the period of one pulse.
Alright cattledog,
Here is my simple setup.
As you will see, my grounds do not connect.
Another thing I was thinking about and then reading was concerning my manual method of testing. I may be introducing extra bounce in the circuit which may cause aberrant readings. Would I have to consider this in a real life use, and code for it in some way?
I would have figured the tone() function not to introduce this phenomenon because it is being generated by the Arduino and not a human per se, so the machine is actually making a precise controlled switch electronically and not depending on any manual input.
TY for examining this setup and giving me feedback.
houdinihar
I really don 't know if your setup will work.
First, you must connect the grounds.
Then you must confirm that pin2 will read ground when the positive lead from the battery is not connected? What do you see with a multimeter or a simple digitalRead() of the pin? I do not understand enough about led's to know if will provide a path to ground when not connected, but somehow I doubt it will.
You might do better to leave out the led and just use a resistor to ground which will be a pull down for digital 2 and touch the positive lead to digital 2 and the resistor. As I mentioned before, its uncertain if the 3v will be adequate to provide a high level to pin 2. Why are you using a battery to provide the input for the interrupt?
If you must do "manual testing" you may be better off using a push button switch instead of touching the lead to the pin. You could also consider using pinMode(2,INPUT_PULLUP) and grounding the pin for the test.
It's unclear what you are trying to do with your setup. Is it related to the application?
Another thing I was thinking about and then reading was concerning my manual method of testing. I may be introducing extra bounce in the circuit which may cause aberrant readings. Would I have to consider this in a real life use, and code for it in some way?
Yes, bounce will cause extra interrupts. Debouncing and interrupts is a whole other subject. There are both hardware and software methods for debounce. It will depend upon the actual application to figure out what is the best approach.
hello cattledog.
I've given up on this sucking aspect of my project. it's really irritating, BUT, when I switched to my Hall Sensor and modified the code I was able to use it instead. I would just rather have not had to use the Hall Sensor.
Anyway here is the successful code I used. I did not create any of it, but merely modified it for me.
I appreciate everyone's comments and assistance. Couldn't have done it without you all, especially cattledog.
houdinihar
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
#define I2C_ADDR 0x27 // <<- Add your address here.
#define Rs_pin 0
#define Rw_pin 1
#define En_pin 2
#define BACKLIGHT_PIN 3
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7
LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);
volatile double full_revolutions = 0;
volatile double rpm = 0;
volatile long timeold = 0;
void setup()
{
Serial.begin(115200);
attachInterrupt(0, magnet_detect, RISING);//Initialize the intterrupt pin (Arduino digital pin 2)
lcd.begin (20,4); // <<-- our LCD is a 20x4, change for your LCD if needed
// LCD Backlight ON
lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
lcd.setBacklight(HIGH);
lcd.home (); // go home on LCD
lcd.clear();
lcd.print("Good day,");
lcd.setCursor (0,2); // go to start of 1st char of 3nd line
lcd.print("Dave. ");
delay (2000);
lcd.clear();
lcd.setCursor (7,0); // go to 7th char of 1st line
lcd.print("Big D");
lcd.setCursor (6,1); // go to 6th char of 2st line
lcd.print("Hall RPM");
}
void loop()//Measure RPM
{
if (full_revolutions >= 10) {
rpm = 30*1000/(millis() - timeold)*full_revolutions;
rpm = rpm *2;
timeold = millis();
full_revolutions = 0;
lcd.setCursor (0,3); // go to start of 4th line
lcd.print("RPM");
lcd.setCursor (5,3); // go to 6th chart of 4th line
lcd.print(rpm);
}
}
void magnet_detect() //Called whenever a magnet/interrupt is detected by the arduino
{
full_revolutions++;
}
I would recommend changing some of your variable typing.
Because you are calculating rpm from 10 revolutions, I would change the variable typing of full_revolutions to a volatile byte.
//volatile double full_revolutions = 0;
volatile byte full_revolutions = 0;
timeold is not changed within an ISR and does not need to be declared volatile. It should be unsigned long to match the typing of millis().
Similarly, rpm does not need to be volatile, and an int or unsigned int is probably going to be large enough for the values you are going to see.