Pages: [1] 2 3   Go Down
Author Topic: Hall effect sensors  (Read 3371 times)
0 Members and 1 Guest are viewing this topic.
France
Offline Offline
Jr. Member
**
Karma: 0
Posts: 57
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I am writing some code to get RPM from a shaft using a magnet and reed switch. However I know that the speed will not be over 5000RPM. The reed switch seems fine from 0 to around 2500rpm but then it jumps up to 9000rpm plus and not very steady. It fluctuates from 9000 to 11000
My question is would a Hall sensor be better and more accurate? what type voltage etc.
Also I have found a  wire hall effect on a machine, how does that work a and how to wire it up.

For info here is the code I am using
Code:
//-----------------------------------------------

  volatile byte rpmcount;
  unsigned long time;
  unsigned int rpm;
  unsigned long timeold;

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

    rpmcount = 0;
    rpm = 0;
    timeold = 0;
    time = 0;
  
 }

 void loop()
 {
  if (rpmcount >= 20) {
     //Update RPM every 20 counts
    
     detachInterrupt(0);
    
     Serial.print("rpmcount");
     Serial.println(rpmcount);
    
     Serial.print("micros");
     Serial.println(micros());
    
     Serial.print("timeold ");
     Serial.println(timeold);
    
     time = micros() - timeold;
     Serial.print("Time ");
     Serial.println(time);

    RPM= (60000000/time) * 20
     Serial.print("RPM");
     Serial.println(RPM);

     timeold = micros();
     rpmcount = 0;
    
     attachInterrupt(0, rpm_fun, FALLING);
   }
 }

 void rpm_fun()
 {
   rpmcount++;
  
 }

//-----------------------------------------------
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 170
Posts: 12487
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The first step I would do is increase the Serial from 9600 to 115200 (or the highest baud rate supported) so it uses less time.

Furthermore you should not detach the interrupt, because then you will definitely miss pulses

rewrote your code a bit, it compiles but not tested, give it a try
Code:
//
//    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, FALLING);
}

void loop()
{
  unsigned long rounds = rpmcount;
  if (rounds - previous >= 20)
  {
    // DO THE MATH
    previous = rounds;

    time = micros();  // would millis not be fast enough?
    unsigned long duration = time - timeold;
    timeold = time;
    rpm = (rounds * 60000000UL / duration); // less rounding error if math in this order !! overflow might occur if rounds > ~70
    // 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++;
}

Succes!
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

France
Offline Offline
Jr. Member
**
Karma: 0
Posts: 57
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Rob
I have tried your code but I am still getting fluctuating output and way higher than reality. Monitor readings below.
Still thinking it could be the reed switch although using another electronic tacho using the reed switch it does not pose any problems, it gives steady accurate output.

Here is the output RPM should be no higher than 5000RPM
Code:
9689124, 123
129136, 18585
141108, 25512
161740, 3122
164620, 10357
162808, 17843
148456, 27651
164244, 6149
166992, 13234
190560, 17894
655320, 480
21667648, 69
130712, 20771
105464, 37122
116100, 7064
125380, 16112
98640, 32645
127852, 978
108672, 12194
141212, 17882
156216, 23846
110932, 5680
106732, 17147
108108, 28029
123488, 34255
140520, 8078
136992, 17046
125316, 28210
138660, 3175
151680, 10813
140284, 20246
153780, 26272
120684, 7832
109128, 19658
123776, 27027
144488, 1732
167332, 8667
421416, 6289
Thanks Tim
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 170
Posts: 12487
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

(oops, there was a serious bug in the code, causing overflows smiley

What is the range of RPM you expect 0..5000 per minute? 
Then, doing the math in millis could be accurate enough,

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

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

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

  rpmcount = 0;
  rpm = 0;
  timeold = 0;
  time = 0;
}

void loop()
{
  unsigned long rounds = rpmcount;
  if (rounds - previous >= 20)
  {
    // DO THE MATH
    time = millis();  // would millis not be fast enough?
    unsigned long duration = time - timeold;
    timeold = time;
    rpm = ((rounds-previous) * 60000UL / 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++;
}


another way to calculate the RPM is to measure for 1/10 of a second and then do the math
Code:
//
//    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, FALLING);
}

void loop()
{
  time = millis();
  if (time - timeold > 100)
  {
    unsigned long duration = time - timeold;
    timeold = time;
    unsigned long rounds = rpmcount;
    rpm = ((rounds - previous) * 60000UL / 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++;
}

There is still a bug in my code that has to do with assigning long variables, they are not atomic. So in practice the rpmcount can be updated just in the moment it is assigned to rounds. By disabling/enabling the IRQ your original code did not have this issue. So you might still add that to these sketches.


Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

France
Offline Offline
Jr. Member
**
Karma: 0
Posts: 57
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Rob
Will try both later but for now what is the UL in 60000UL ?
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 170
Posts: 12487
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

It tells the compiler to treat the number as an unsigned long.

You also have L (long) LL (long long 64 bit but slow) and ULL (...)
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

France
Offline Offline
Jr. Member
**
Karma: 0
Posts: 57
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
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
Code:
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
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 170
Posts: 12487
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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?
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

France
Offline Offline
Jr. Member
**
Karma: 0
Posts: 57
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

France
Offline Offline
Jr. Member
**
Karma: 0
Posts: 57
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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??
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

http://www.gammon.com.au/forum/?id=11504

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.
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 170
Posts: 12487
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Code:
//
//    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?
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 170
Posts: 12487
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Code:
//
//    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!
« Last Edit: October 20, 2012, 04:27:53 pm by robtillaart » Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Timing 2500 RPM (150 KHz) should be easy and accurate enough with the first one.

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.

Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 452
Posts: 18694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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).
Logged

Pages: [1] 2 3   Go Up
Jump to: