How to measure a RPM with interrupts?

Hi everyone,

I'm trying to get a RPM reading from a Hall effect sensor triggering an arduino input.

So the sensor is interfaced correctly and I'm getting a reading (for example when I'm trying with the line put under comments//, I'm getting a correct reading of period).

The problem is when I'm trying to convert from a period (in µS) to a RPM value, at this point, it will only give me wrong readings or -1???

So, since there is a division involved, is there something I should know about?

Below are the code parts you're interested in, to sum up : if I put the first line under comment and only use the second one (commented right now), I get the period reading and it works, if I try to get the RPM, it won't....

long int RPMN1
long int RPMN1count

attachInterrupt(0, N1, FALLING);

void N1()
{
  RPMN1=(60000000/(micros()-RPMN1count));
  //RPMN1=RPMN1count-micros();
  RPMN1count=micros();
}

Thanks for the help!

Marc

Simplify. Simplify.

Just save the micros value in the ISR and raise a flag to say there is a new value. Do the calculations elsewehere.

void N1()
{
  latestPulseMicros = micros();
  newPulse = true;
}
void loop() {
   if (newPulse == true) {
      revMicros = latestPulseMicros - prevPulseMicros;
      prevPulseMicros = latestPulseMicros;
      newPulse = false;
      // calculate RPM based on time between pulses
   }

}

...R

long int RPMN1
long int RPMN1count

Make the variable(s) volatile.

Thanks guys,

I will do the calculating outside the ISR then.
Also I’ll make the variables volatile also (understood everything thanks to your site!).

But about the division, anything I missed?

Marc

So, since there is a division involved, is there something I should know about?

I'm not sure what you mean, but nothing obvious hits me.

Hi Nick,

What I meant is that :

RPMN1=micros()-RPMN1count;

gives me correct period readings in µS

so in my mind that means that :

RPMN1=(60000000/(micros()-RPMN1count));

would give me a correct reading in RPM. But it does not... Gives me uncorrect values and in some cases -1...

Marc

Try putting a "UL" after the 60000000

Post your new complete code please.

Here is the sketch :

#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

volatile long RPMN1count=0;
volatile long RPMN1=0;
volatile boolean flag=0;


void setup()
{
  attachInterrupt(0, N1, FALLING);
  lcd.begin(20,4);
}

void loop()
{
  // put your main code here, to run repeatedly: 
  if(flag)
  {
    RPMN1=(60000000/RPMN1);
    flag=0;
    lcd.print(RPMN1);
  }
}

void N1()
{
  RPMN1=RPMN1count-micros();
  RPMN1count=micros();
  flag=1;
}

It may be being updated while you attempt to do the divide. You should make a protected copy.

http://www.gammon.com.au/interrupts

void loop()
{
// put your main code here, to run repeatedly:
if(flag)
{
long copyOfRPM;
noInterrupts (); // critical section
copyOfRPM = RPMN1;
interrupts ();
lcd.print(60000000UL / copyOfRPM);
flag=0;
}
}

Hi everyone,

So, tried to congregate everyone of your advices and came up with this :

volatile long RPMN1count=0;
volatile long RPMN1=0;
volatile boolean flag=0;
long RPMN1period;

void setup()
{
  Serial.begin(9600);
  attachInterrupt(1, N1, FALLING);

}

void loop()
{
    if(flag)
  {
    noInterrupts();
    RPMN1=(30000000UL/RPMN1period);   //Two signals per rotation.
    interrupts();
    Serial.println(RPMN1);
    flag=0;
  }
}

void N1()
{
  RPMN1period=micros()-RPMN1count;
  RPMN1count=micros();
  flag=1;
}

Problem is :
I tried it with a tone(pin, frequency) instruction and it worked all the way up to 18000Hz, it gave the right 540 000 RPM reading.

BUT... when I plug it to the real hall sensor on my machine, it reads ok up to around 200RPM and then it gives 37 constantly until I lower the RPM again... Go figure...

I guess that'd be a sensor problem or else but did anyone ever encountered this kind of problem? What's my best guess? Taking out the scope and trying to visualize the signal from the hall sensor?

Thanks for the help,

Marc

I think it will be easier to diagnose the problem if you look at the interval between pulses (in usecs) rather than looking at the RPM figure.

It sounds like you may be getting noise or "bounces" .

Why not define all the long values as unsigned long - they should never have negative values and using long will cause problems if the count rolls over.

If it was my project I would reduce my ISR to

void N1()
{
  RPMN1count=micros();
  flag=1;
}

and do the subtraction in the main code

and rather than do a calculation while the interrupts are off, I would just take a copy of the value in RPM1count.

I'm not suggesting either of these has a bearing on your 37 problem.

...R

Can you provide a data sheet for the Hall sensor that you are using and how it connected. There are different types of these sensors, and the pull up or pull down required varies.

Your scope will tell you if you are getting a clean signal from the sensor or not, but you already know that with good signals from tone your code works well.

How often do you really need to update a tachometer? I suppose that if you're using PWM to adjust the speed of the motor, but even then, it seems excessive to update the tachometer count every revolution.

how about instead...

unsigned long pulseCount=0;
const unsigned long updateInterval=250000; // 4 updates per second
int RPM=0;

void N1() {
 pulseCount++
}


void loop() {
 if (startTime == 0) {
  startTime=micros();
 }

 if (millis() - startTime > updateInterval) {
  RPM=((pulseCount/2)/((millis() - startTime)/60000000));
  startTime=0;
 }
 //display or otherwise use RPM value
}

Hi everyone,

Here is the Hall sensor I'm using (chinese crap but cheap! :smiley:

It is wired to a +12V and the NPN sink (am I right?) wire is wired to the Arduino pin. I'm just using the Internal Pullup, there is no external pullup (maybe I should use one?).

Marc

I've Googled pretty hard on your sensor, and can not find a proper data sheet. What I did find indicates that the maximum frequency of response is 150hz and 4mm sensing distance. I'm still not certain of the connections and pullup/down requirements. What are you trying to sense in your application, the mechanical configuration, and the rpm requirements. This sensor may not be appropriate.

Amico LJ12A3-4-Z/AX DC 3 Wire Inductive Type Proximity Sensor
Description

  • Metal shell, DC type, 4mm detecting distance, 3 wire system.
  • Power supply inverted connecting protection, short circuit protection, non-contact detecting device.When proximity switch is close to some target object, it will send out control signal.
  • Used widely in the machine tool industry, frequency counters and other automatic control system.
  • Wire Type: Cylindrical DC 3 Wire Type (Brown, Black, Blue);
  • Switch Appearance
  • Type: Cylinder Type, Metal Shell;
  • External Material: Plastic, Metal
    Features
  • Product Name: Inductive Proximity Switch; Model: LJ12A3-4-Z/AX;
  • Theory: Inductive Type Sensor
  • Output Type: NPN, NC;
  • Detecting Distance: 4mm/0.2'';
  • Supply Voltage: DC 6-36V
  • Current Output: 300 mA;
  • Response Frequency: 150Hz;
  • Column Sensor Dia.: 12mm/0.5''
  • Detect Object: Iron;
  • Total Size: 60 x 22mm/2.4'' x 0.9'' (L*Max.D);
  • Cable Length: 1.1M/43.3''
  • Net Weight: 47g;
  • Color: Silver Tone, Blue, Black;
  • Package Content: 1 x Inductive Proximity Switch

Hi cattledog,

It does seem like the exact same one that I'm using.

I'm sensing two small magnets at about 2-3mm distance.
Thing is : it seems to be working perfectly when I turn the propeller by hand. There is a small LED on the back that lights up when it passes the magnet so it does work perfectly!

Maybe I'll try to do the interrupts on the RISING and not the falling edge...

I'd be very concerned about the 150hz response frequency. It's a low cost inductive proximity sensor.

Since you have magnets attached in your application you should take a look at the Hall effect magnetic field sensors. The magnetic polarity is important.. If you require the screw probe type mount, take a look at Hall effect gear tooth sensors.

Ok then, I'll try to scope the signal and if necessary look for another one...

But the 150hz response is not really limiting as I'm intending to use it @100hz maximum (3000RPM with two edges per turn).

Also like I already said, it seems to be working as it is lighting up when I'm rotating it by hand...

I guess the signal ain't good enough @high speed then...

Thanks for the help,

Marc

hello

you can use a encoder 200 pulse and a D flip flop for detecting Left and Right and you should use INT ardunio
for counter

excuse me for my bad english