Magnetic Tachometer Code

Hello there,
Im new here, so please bare with me as I try to explain my conundrum.

Im trying to use a magnetic hall effect sensor to detect a couple of rotating magnets (as a tachometer). The sensor outputs 5v when there is no magnetic field, and about 2.5v when there is.

So basically everytime the sensor detects the magnet it outputs a value, and over a certain amount of time it gives a frequency. From there I need it to take frequency values over say a period of 60 seconds then output the average frequency to the serial.

Ive read online on many forums and read up on all sorts of tutorials, but nothing seems to address my topic specifically. Ive read that PulseIn is one way to determine that frequency, while others are using Analog In and calculating everything out.

Does anyone have suggestions on how I can go about doing this? Code would be greatly appreciated to get me pointed in the right direction. I have some code I have been working with, and it does change depending on the sensor, but I have no idea what the values Im reading represent, cause they arent frequency.

I also have been looking at using a dynamic array to store all the values in 60 seconds (with 250ms between each reading, so 240 readings in 60s). Then just calling all the data in that array and averaging them out. Even with that approach Im not sure how to start this.

Any help would be greatly appreciated. :slight_smile:

Thanks.

please bare with me

Well, feeling a bit daft without my clothes, but here goes.

I don't know what a 'dynamic array' is in this context, but to average over 60 seconds, why not just sum into a 'long', and use that?

I didnt know how else to phrase this...declaring an array with infinite size, and no values, but then as the values come in, the array becomes more defined. I guess Im just thinking of it a complicated way.

But then what do you mean by:

why not just sum into a 'long'

Do you mean just sum up all the array values and find the average? But then if I have 240 array values what would be the method to calculate average, so that I dont have to write a rediculous sized code?

Im thinking:

for current to max array size
  sum=array[current]+...+array[n size]
  ave=average (sum,n)

Something like that to shorten the code a bit. Dont think thats the right way of coding that though.

But what about the Frequency part of this?

But you know the size of array (if you really need one) in advance - you've already said it is 240 elements long.
What's with 'dynamic'?

Finding an average (aka 'arithmetic mean') - is there another sensible method in a memory-limited application, other than simply summing all the samples and dividing by the number of samples?

(getting cold now - can I get dressed?)

But then how would I take the arithmetic mean of 240 array values? :o

Could I use something like:

int loop() 
{
  int x[240];
  double avg = average(240, x);
  serial.println( "The mean is: " & avg);
  return 0;
}

Also found some code that will help me for determining RPM:

volatile byte rpmcount;

 unsigned int rpm;

 unsigned long timeold;

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

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

 void loop()
 {
   if (rpmcount >= 20) { 
     //Update RPM every 20 counts, increase this for better RPM resolution,
     //decrease for faster update
     rpm = 30*1000/(millis() - timeold)*rpmcount;
     timeold = millis();
     rpmcount = 0;
     Serial.println(rpm,DEC);
   }
 }

 void rpm_fun()
 {
   rpmcount++;
   //Each rotation, this interrupt function is run twice
 }

But then how would I read in the Hall Effect signal if its between 5v and 2.5v?

Yes you can put your clothes back on. ;D

Unless you're doing a rolling average (in which case you need to subtract the oldest sample before adding in the latest, so yes, you need an array) , I don't see the point of the array - why not just sum all the samples?

Unless you're doing a rolling average (in which case you need to subtract the oldest sample before adding in the latest, so yes, you need an array) , I don't see the point of the array - why not just sum all the samples?

But then how would I put 240 samples into variables? Arrays seem like the only way to do that...unless theres a way to do this on the fly without needing variables?

Why don't you just sum (add up) the sample values, and divide by the number of samples?
Why do you think you need an array?
If I just shout out a list of numbers, you should be able to give me the average of them as soon as I've read out the last, with just a simple pocket calculator - no need to even write them down.

Okay, I get what your saying, so I came up with this little piece of code, which should take care of that then.

int oldnumber =0
int newnumber =0

for(i=0,i<=240, i++)

  newnumber= sensor.getdata(); //ie say 4, then 6, then 2

  oldnumber+=newnumber

  // loop1, oldnumber=0, newnumber=4, oldnumber=4, then loop
  // loop2, oldnumber=4, newnumber=6, oldnumber=10, then loop 
  // loop3, oldnumber=10, newnumber=2, oldnumber=12

average = oldnumber/240;

But then Im still stuck for the Hall Effect Sensor part of it, Ive got a few examples, but I followed a few of them and I just get weird values on the serial, not actual RPM's, or even frequency. Any help on that?

Any help on that?

Post some code that actually compiles, along with some sample output, and an explanation of what the input was.

Post some code that actually compiles, along with some sample output, and an explanation of what the input was.

You do realize that if my code compiled and outputted data, I wouldnt need the help to begin with, right? ::slight_smile:

/*
Written by Tamir Emran
Oct 21, 2010
Detect a magnetic field with a Hall Effect sensor
*/

const int redled=6;
const int greenled=7;
const int ground=13;
const int power=10;
const int hallsensor=12;

int SensorState = 0;
int count=0;
float T, F, Ts, RPM;

void setup() //setup pin ins and outs
{
  Serial.begin(57600);
  Serial.println("Frequency counter started");
  Serial.println("");
  
 pinMode(redled,OUTPUT);
 //pinMode(yellowled,OUTPUT);
 pinMode(greenled,OUTPUT);
 pinMode(ground,OUTPUT);
 pinMode(power,OUTPUT);
 
 pinMode(hallsensor,INPUT); //sensor input
 digitalWrite(ground,LOW); //sensor ground
 digitalWrite(power,HIGH); //sensor power
 digitalWrite(greenled,HIGH); //power on indicator
 
}

void loop()
{
  //read the state of the pushbutton value
  SensorState = digitalRead(hallsensor);
  //check if button is pressed
  if(SensorState == LOW){
    digitalWrite(redled,HIGH); //turn LED on
    }
  else {
  digitalWrite(redled,LOW);  //turn LED off
  } 
  
  T = pulseIn(hallsensor, LOW,1000); //T=duration
  Ts=T/1000.00;
  F = 1/Ts; //F=frequency
  RPM = F/60;
  
   Serial.print("Duration in [ch956]s is: ");
   Serial.print(T,2);
   Serial.println("");
    Serial.print("Duration in ms is: ");
    Serial.print(Ts,2);
    Serial.println("");
    Serial.print("RPM is: ");
    Serial.print(RPM);
    Serial.println("");   
    

  delay(250);
 }

Thats my code from a week ago, so now that Ive learned a lot more, Im pretty sure the way I was caclulating RPM was wrong. Ive seen methods to try this with Interrupts instead of PusleIn, and others just do if statements. No idea what the best way to do it is.

Input is from a hall effect sensor, and output duration is really big numbers (usually between 2k and 120k), but Im sure thats wrong.

You haven't described the application - it could be the prop shaft of a tanker doing 15rpm, or a centrifuge doing thousands of rpm.

You are measuring the time it takes the hall sensor to go low, without any reference point, and assuming that RPM is a function of that time.

Suppose you are walking around a circle. At some point on that circle, there is a switch. Every time you go by that switch, you press it. The time between switch presses defines your RPM.

If I dump you randomly on the circle, and you start walking, you know nothing about the speed you are moving the next time you hit the switch.

If you timed the interval from when the sensor went HIGH until it went LOW, that would be an improvement, but still not enough. That would be equivalent to timing how long you held the button down on your walk around the circle. If you make an assumption that the LOW to HIGH to LOW transition is representative of the time it take to complete one revolution, then you can determine the speed. Not very accurately, since the interval is so short relative to the time of rotation.

You need to time the interval from the sensor going HIGH (or LOW) until it goes HIGH (or LOW) again.

The best way to do that is to have the sensor trigger an interrupt each time it goes HIGH (or LOW). In the interrupt service routine (the handler), just increment a counter. If the counter is a byte, and volatile, there is no need to turn off interrupt handling while you increment the counter.

Then, periodically, record the time, copy the counter, reset it, and compare the current time to the previous time. The counter will tell you the number of revolutions. The difference in the time can then be used to compute the speed.

You haven't described the application - it could be the prop shaft of a tanker doing 15rpm, or a centrifuge doing thousands of rpm.

Im measuring the RPM's in a high speed dynamometer that spins about 15-30k RPMs.

Thanks PaulS for the analysis of how the Tachometer should work. I had been confused as to how to do it because of all the varying results I found when I searched online. I had a feeling interrupts were the way to go, but I wasnt sure. I got some code written as a result (and it compiles fine), so when I get home I'll give it a shot with my board.

How can I show a plot of speed vs time on my screen, as seen here: http://www.arduino.cc/playground/Main/ReadingRPM
Does the arduino code have an option for a scope or plot, or do I have to write that code in Java or C++ seperate from the Arduino? Are there any free ones that I can just download to use instead?

Thanks! :slight_smile:

Does the arduino code have an option for a scope or plot

Given that the Arduino doesn't have a display...

Have a look at Processing - it is based on Java.

Have a look at Processing - it is based on Java.

Thanks for the suggestion. I got the software, and Ive already gotten a few working graphing examples going to plot my serial data.

I'll have to test the sensor unit now with a high speed motor to make sure that this works out. Ive just been doing it by hand with a magnet and the sensor this whole time just to make things easier.

Again thanks for all the help, I'll keep y'all updated in my progress. :wink: