Hall effect sensors

Hi Rob
Have tried out both of your above codes, the readings are still too high. I am wondering if it is still the reed switch although the electronic tacho uses this switch.
It seems to be ok up to about 2500RPM and then shoots up. I have tried out also on my workshop pillar dill which at max speed is 2800 and on this the arduino is ok, nice and steady between 2750 and 2850.
The shaft that I want to monitor is driven by a hydraulic motor and if I feather the oilflow to start with it starts and climes from o to 2500 ok but when the flow is full it shoots up but should be no more that 5000rpm max.

Here is the output from your first code

101, 7722
101, 8910
101, 8910
101, 7722
101, 8910
101, 7128
101, 10099
101, 10099
102, 10000
101, 10099
101, 12475
101, 10693
101, 10099
101, 8910
101, 10693
101, 8910
101, 10693
101, 8910
101, 8910
101, 7722
101, 8910
101, 10693
101, 10099
101, 12475
101, 10693
101, 12475
101, 10693
102, 12352
101, 11881
101, 11287
101, 11881
101, 10693
101, 10099
101, 10693
101, 10693
101, 12475
101, 11287
101, 12475
101, 10693
101, 13069

and the second

105, 11428
102, 11764
105, 11428
104, 11538
90, 13333
104, 11538
106, 11320
104, 11538
91, 13186
117, 10256
120, 10000
91, 13186
103, 11650
90, 13333
104, 11538
105, 11428
134, 8955
92, 13043
105, 11428
133, 9022
106, 11320
105, 11428
104, 11538
102, 11764
105, 11428
106, 11320
118, 10169
91, 13186
149, 8053
89, 13483
91, 13186
104, 11538
105, 11428

regards Tim

The first output seems to have stable timing, 101 milliseconds on average. (OK change the code to have >=100)

Can you tell how you connected the hardware?
Do you use a pullup/pulldown resistor?

Rob
tried uploading image but did not work.I have from +5v pin to reed switch, from reed switch to pin 2 with a T off to ground pin via a 10K ohm resistor.

Have run with different reed switch but only at slow speed. However at 115200 baud the RPM was a steady 740 I decided to change to 9600 I was supprised to see the RPM go up to a steady1560??

Calculating RPM is a variation on calculating frequency. On this page I have two different ways of doing that:

One counts pulses in an interval (eg. pulses per second) and the other calculates the time interval between two consecutive pulses (ie. the period) and works out the frequency. I was able to time up to 5 MHz with one sketch and 100 KHz with the other one. Timing 2500 RPM (150 KHz) should be easy and accurate enough with the first one.

HW sounds OK.

A look at the numbers of the first output shows that there are only a few numbers occurring for RPM and that they seem all to be multiples of 594 apart.
This is caused by the constant duration of 101 milliseconds and the constant in the formula of 60000

x * 60000 / 101 <==> x * 594 so that explains the step-size in the output.

This implies if you measure every second you wil get a stepsize of about 60. btw doing the math in micros will not change this substantially

If you set the interrupt on CHANGE you would get two IRQ's per rotation. That would improve the accuracy (in theory 2x if pulses are symmetrical)
combined with a timing of 1 second would give a stepsize of about 30.

//
//    FILE: rpm.pde
//  AUTHOR: 
//    DATE: 20-oct-2012
//
// PUPROSE:
//

volatile unsigned long rpmcount = 0;
unsigned long previous = 0;
unsigned int rpm = 0;

unsigned long time = 0;
unsigned long timeold = 0;

void setup()
{
  Serial.begin(115200);
  attachInterrupt(0, rpm_fun, CHANGE);
}

void loop()
{
  time = millis();
  if (time - timeold >= 1000)
  {
    unsigned long duration = time - timeold;
    timeold = time;
    unsigned long rounds = rpmcount;
    rpm = ((rounds - previous) * 30000UL / duration); 
    previous = rounds;
    // DO THE OUTPUT
    // comma separated allows you to copy the output into excel
    // and make a nice graph
    Serial.print(duration);
    Serial.print(", ");
    Serial.println(rpm);
  }
}

void rpm_fun()
{
  rpmcount++;
}

If the baudrate influences the RPM it might be that the wires influence each other.
You could check if you have the same problems if you take IRQ1 (pin 3)

================

What is strange is that the numbers are much higher than you expect [about 5000]. Is this correct?

Do you have a datasheet of the reed switch used?
What is its upper switch frequency?

Right Nick, that is exactly what the 2 sketches I posted do.

Problem is that MAX 5000RPM only gives less than 100 pulses per second so counting for a fix 1000 milliseconds and do the math creates steps of 60. Measuring shorter times makes the steps bigger.

There is a 3rd way of measuring, in which one uses a ring-buffer [array] of 60 longs and every second one subtracts the current rpmcount and the content of the corresponding buffer element. This is exact the rpm in the last minute. Then one overwrites that buffer element with the current value.

Please give it a try.

//
//    FILE: rpm3.pde
//  AUTHOR: 
//    DATE: 20-oct-2012
//
// PUPROSE:
//

volatile unsigned long rpmcount = 0;
unsigned long time = 0;
unsigned long timeold = 0;

unsigned long ringbuffer[60];
int idx = 0;

void setup()
{
  Serial.begin(115200);
  attachInterrupt(0, rpm_fun, FALLING);
  for (int i=0; i< 60; i++) ringbuffer[i] = 0; 
}

void loop()
{
  time = millis();
  if (time - timeold >= 1000)
  {
    timeold = time;
    unsigned long rounds = rpmcount;
    unsigned long rpm = rounds - ringbuffer[idx];
    ringbuffer[idx] = rounds;
    idx++;
    if (idx == 60) idx = 0;

    // DO THE OUTPUT
    Serial.print(time);
    Serial.print(", ");
    Serial.println(rpm);
  }
}

void rpm_fun()
{
  rpmcount++;
}

price is of course ~240 bytes of RAM!

  • update -
    This code shows the rpm of the last minute, which is not necessary the current rpm!

Oh I got that wrong didn't I? Oops.

2500 RPM is actually 2500 / 60 Rev per second, that is 41.6 Hz.

Well in that case measuring the period is quite achievable and to a high resolution. At low speeds measuring the period is more accurate because obviously at 41 Hz you potentially have a 1/41 error if the count is out by one (2.4%).

If you measure the period you get an instantaneous figure (well, after 1/41 of a second elapses) rather than having to wait a whole second. You could average a couple of them to smooth out variations.

Just to amplify, if you count revs then over one second (at 2500 RPM or 41 revs per second) if you get 41, the RPM is 2460. If the count goes up to 42 you then get 2520. So it must jump from 2460 to 2520. No intermediate value is possible.

Measuring the period, and taking the inverse, will be much more accurate because the period will be measured to an accuracy of 62.5 nS. And there are a lot of counts in 1/41 of a second (390243 of them).

will be much more accurate because the period will be measured to an accuracy of 62.5 nS.

62.5 ns is true if you use HW timers, if you use micros() the accuracy goes in steps of 4 micros().

Putting all the timing math in the IRQ gives something like this. Note I removed the counter in the IRQ routine to get max update frequency.
Drawback of this method seems to be that you cannot measure an RPM of zero. => additional code needed to check this.
In general low (<60) RPM's will result in a low (> 1s) update frequency ==> more pulses per rotation needed [for every measure method]

//
//    FILE: rpm4.pde
//  AUTHOR: 
//    DATE: 21-oct-2012
//
// PUPROSE: updates rpm after every pulse
//
volatile boolean newRpm = false;
volatile unsigned int rpm = 0;         // assuming not above 65535
volatile unsigned long time = 0;
volatile unsigned long timeold = 0;

void setup()
{
  Serial.begin(115200);
  attachInterrupt(0, rpm_fun, FALLING);
}

void loop()
{
  if (newRpm)
  {
    newRpm = false;
    Serial.print(millis());
    Serial.print(", ");
    Serial.println(rpm);
  }
}

// every pulse a new rpm value, 
void rpm_fun()  
{
  time = micros();
  unsigned long duration = time - timeold;
  rpm = 60000000UL/duration;
  timeold = time;
  newRpm = true;
}

robtillaart:
HW sounds OK.

If the baudrate influences the RPM it might be that the wires influence each other.
You could check if you have the same problems if you take IRQ1 (pin 3)

================

What is strange is that the numbers are much higher than you expect [about 5000]. Is this correct?

Do you have a datasheet of the reed switch used?
What is its upper switch frequency?

First thank you for all the replies as a newbie I really appreciate this forum.
Glad the HW ok

Re baud rate I changed to IRQ1 (pin 3), this work fine on 9600 but on 115200 the readings are all over the place??

Re reed switch I cant find data sheet but what I have done is change the switch for another and this is the result and is no more than 1% different to the electronic tacho.

rpm37
rpm4363
rpm4332
rpm4363
rpm4347
rpm4347
rpm4580  ^^^^^^^^ Here
rpm4332
rpm4363
rpm4332
rpm4363
rpm4363
rpm4562 ^^^^^^^^^ Here
rpm4347
rpm4347
rpm4347
rpm4363
rpm4347
rpm4347
rpm4562 ^^^^^^^^^^ Here
rpm4347
rpm4347
rpm4580  ^^^^^^^^^^^ Here
rpm4347
rpm4347
rpm4347
rpm4347
rpm4347
rpm4363

Just the few disparities marked. Also tried at lower speeds and slightly higher, the results were as good.

TimFr:
Re baud rate I changed to IRQ1 (pin 3), this work fine on 9600 but on 115200 the readings are all over the place??

Serial output uses interrupts, and at a higher baud rate it will use more of them. This will throw out readings that rely on interrupts.

Serial output uses interrupts, and at a higher baud rate it will use more of them.

But a string of the same length will generate the same amount of interrupts, independent of the baudrate? Or did I miss something in class?
Assuming the same amount of characters per second, BTW

I'm from Argentina, I am working with Hall sensors, at an advanced level, I care about your ideas. I am new to the forum arduino over time I will learn to use it well. Four years ago I started working with arduino

care with the baud rate when one wants to measure many rpm, speed serial port of the microprocessor removes arduino speed

Rob
Here are the results of your last code

19444, 3785
19460, 3783
19476, 3777
19491, 3778
19508, 3783
19523, 3780
19539, 3782
19555, 3782
19571, 3780
19587, 3779
19603, 3780
19618, 3782
19635, 3780
19650, 3776
19666, 3776
19682, 3782
19698, 3785
19714, 3787
19730, 3785
19745, 3781
19762, 3778
19777, 3777

Nicogongora
I can see what you mean, this morning I have had consistent results with 9600 but going to 115200 I get fluctuating results.

That are quite smooth readings esp compared to the previous ones. You can smooth these readings even more in the loop() e.g. by only adjusting for 25% of the new rpm value;

loop()
{
  if (newRpm)
  {
    newRpm = false;
    int smoothrpm = (3*smoothrpm + rpm)/4;  // weighted average
    Serial.print(millis());
    Serial.print(", ");
    Serial.print(rpm );
    Serial.print(", ");
    Serial.println(smoothrpm );
  }
}

I'll try that out but at the moment have to make a mount for the different reed switch, old was round threaded but new one is oblong.

robtillaart:
That are quite smooth readings esp compared to the previous ones. You can smooth these readings even more in the loop() e.g. by only adjusting for 25% of the new rpm value;

loop()

{
  if (newRpm)
  {
    newRpm = false;
    int smoothrpm = (3*smoothrpm + rpm)/4;  // weighted average
    Serial.print(millis());
    Serial.print(", ");
    Serial.print(rpm );
    Serial.print(", ");
    Serial.println(smoothrpm );
  }
}





int smoothrpm = (3*smoothrpm + rpm)/4;  // weighted average



I changed to


int smoothrpm = ((3*smoothrpm + rpm)/4)*4;  // weighted average

this is the result

rpm 4210   smooth rpm 4208
rpm 3647   smooth rpm 3644
rpm 3726   smooth rpm 3724
rpm 3692   smooth rpm 3692
rpm 3703   smooth rpm 3700
rpm 3715   smooth rpm 3712
rpm 3692   smooth rpm 3692
rpm 3692   smooth rpm 3692
rpm 3680   smooth rpm 3680
rpm 3680   smooth rpm 3680
rpm 3692   smooth rpm 3692
rpm 3669   smooth rpm 3668
rpm 3669   smooth rpm 3668
rpm 3692   smooth rpm 3692
rpm 3669   smooth rpm 3668
rpm 3669   smooth rpm 3668
rpm 3680   smooth rpm 3680
rpm 3669   smooth rpm 3668
rpm 3680   smooth rpm 3680
rpm 3680   smooth rpm 3680
rpm 3658   smooth rpm 3656
rpm 3669   smooth rpm 3668
rpm 3680   smooth rpm 3680
rpm 3680   smooth rpm 3680

Then tried this

int smoothrpm = ((9*smoothrpm + rpm)/10)*10;  // weighted average

gave this

rpm 3692   smooth rpm 3690
rpm 3692   smooth rpm 3690
rpm 3680   smooth rpm 3680
rpm 3692   smooth rpm 3690
rpm 3680   smooth rpm 3680
rpm 3680   smooth rpm 3680
rpm 3680   smooth rpm 3680
rpm 3680   smooth rpm 3680
rpm 3669   smooth rpm 3660
rpm 3680   smooth rpm 3680
rpm 3680   smooth rpm 3680
rpm 3680   smooth rpm 3680
rpm 3680   smooth rpm 3680
rpm 3680   smooth rpm 3680
rpm 3680   smooth rpm 3680
rpm 3680   smooth rpm 3680
rpm 3669   smooth rpm 3660
rpm 3680   smooth rpm 3680
rpm 3680   smooth rpm 3680
rpm 3669   smooth rpm 3660
rpm 3692   smooth rpm 3690
rpm 3669   smooth rpm 3660
rpm 3669   smooth rpm 3660
rpm 3870   smooth rpm 3870
rpm 3669   smooth rpm 3660
rpm 3680   smooth rpm 3680
rpm 3680   smooth rpm 3680
rpm 3658   smooth rpm 3650
rpm 3692   smooth rpm 3690

Can you explain the *4 and the *10 ? Looking at the code I expected other result (a growing value for smoothrpm).

robtillaart:

Serial output uses interrupts, and at a higher baud rate it will use more of them.

But a string of the same length will generate the same amount of interrupts, independent of the baudrate? Or did I miss something in class?
Assuming the same amount of characters per second, BTW

Er, yes you are right. I was wrong to say it will use more of them (assuming you are printing the same string, per second). If you are printing non-stop you would use more per second.

I would suggest though, that at a higher baud rate, to empty the serial buffer of the string you have put there would involve a lot of interrupts in quick succession. Once the serial interrupt starts, the higher-priority external interrupt has to wait. This is likely to throw out the timing it takes. Even at a lower baud rate this would be a problem, it's just less likely the interrupts would coincide.