Problems with pulseIn()

Hello,
I have made a basic anemometer ( wind speed ), mostly from parts salvaged from CD ROMs!
There are 3 cups which rotate in the wind. To keep the inertia down, I used half ping-pong balls.
Seems to work OK.
To get the speed of the wind, I attached an optical disk from a mouse and used a basic photo-interrupter.
The rotating cups therefore generate a stream of electrical pulses which I clean up with a comparator.

Initially I then used a mono-flop to produce fixed pulse-width pulses such that the faster the thing went
the more of these fixed-width pulses there would be in a second. These pulses drove a moving-coil meter
which integrated them. I calibrated the thing by mounting it on the roof of the car!

Now, I want to move on. I have a '328 ( with bootloader) in a standalone arragement ( I program it and
get data via a MAX232 ). But how to deal with the output from the 393 comparator? I tried various
schemes, none of which worked ) Finally I got some readings from the pulseIn() command. In general,
the HIGH period is slightly shorter than the LOW period, but it does vary a lot, which puzzles me.
Sometimes the LOW is shorter than the HIGH. If the wind is moderate I get a TOTAL period of, maybe,
10 to 20 msec; sort of the figure I would expect. Occasionally, I get readings of around 2-5 usec which is
baloney. It would take Hurricane Sandy to do that.

All I can think is that the wind had dropped to zero and the thing has stopped ( it does that a lot ). But,
surely the reading should be zero due to the 'timeout' feature? Is there a better way of doing this?

bruce

Personally I would attach the signal to one of the interrupt pins. Then, in the interrupt routine you count the number of pulses seen - just a simple "pulses++" and nothing else.

Then, in the main loop, you can monitor millis() and find when a second has passed. At that point you record the current number of pulses and zero it. You then have the number of pulses per second. You don't care about the length of the pulses, just the number per second.

From that you can then calculate the number of rotations per second (or minute, or whatever), and thus the wind speed.

Of course, you don't have to do it for a second, you can do it for any amount of time - as long as you know how long it is since you last zeroed the number of pulses.

You must post your code before anyone can help!.

Mark

PS This sounds like a job for interrupts.

M

Or timers. Basically you want to know the frequency of the pulses, right? The wind speed follows from that.

On that page are some examples of using timers to count things.

Thanks guys.
I was going to use interrupts, but thought there was a problem since it wont allow incrementing millis()
( I didnt really follow that, but I'd been at it for ages and was losing the will to live )

The code is:
/* I have standalone '328 with RS232 as test rig for program to read the anemometer

The O/P from the anemometer is a manky square wave: freq from probably
50Hz to 500Hz but I need to check on a scope first.
The O/P from anemometer opto-slot is squared with a 393
Use Analogue A0 configured as digital input
Use pulseIn() */

unsigned long/ t1,t2;
int T = 0;

void setup()
{
Serial.begin (9600);
pinMode(A0, INPUT);
}

void loop()
{
delay(100);
t1 = pulseIn( A0, HIGH );
delay(1000);
t2 = pulseIn( A0 ,LOW );

Serial.print (" Time = " ); Serial.print ( T ); Serial.print (" mins " );
Serial.print (" t1 = "); Serial.print (t1); Serial.print ( ", usec");
Serial.print (" t2 = "); Serial.print (t2); Serial.print ( ", usec");

Serial.println ("");
delay(600000UL);
T=T+10;

}

What I'm doing here is to look for a HIGH, and then time it. Wait for a second ( that could be a mistake!)
and then count the length of the LOW. The 1 sec delay is only to settle things down. I'm making the assumption that
the speed doesnt change significantly in 1 sec.

The results I got:

TIME (min) HIGH LOW (all in usec)
0 - -
10 6018 8647
20 3471 5953
30 2731 10489
40 3458 5496
50 4088 7469
60 5093 25764
70 3158 6864
80 4652 4252
90 7 8326
100 3723 5627
110 3204 3976
120 4010 8739
130 5338 4981
190 1 10207

Then I get some funny ones

230 5211 1 usec
330 4 2
500 1 4

Clearly this is telling me something.....
Can anyone see the deliberate mistake?
( I need to read Nick's stuff. )

bruce

poriet:
I was going to use interrupts, but thought there was a problem since it wont allow incrementing millis()

Yes but in each interrupt millis() has a chance to increment since the last one. As does micros().

Please edit your post, select the code, and put it between [code] ... [/code] tags.

You can do that by hitting the # button above the posting area.

You should do as little as possible in an interrupt. Here is what I was talking about in (untested) code:

volatile unsigned long pulses = 0;

void setup()
{
  Serial.begin(9600);
  pinMode(2,INPUT);
  attachInterrupt(0,"pulseCounter",RISING);
}

void pulseCounter()
{
  pulses++;
}

void loop()
{
  static unsigned long tick = 0;
  unsigned long mil = millis();
  float pps;
  float interval;

  if(tick <= mil)
  {
    interval = (mil-tick) / 1000;
    tick = mil + 1000;

    pps = interval / (float)pulses;
    pulses = 0;

    Serial.print("Pulses per second: ");
    Serial.println(pps,4);
  }
}

The interrupt purely counts the pulses, and nothing else. No calculating, no timing, nothing - just count the pulses.

The main loop deals with the timing, and the calculation of the number of pulses per second.

You could do the interrupt/pulse counting using pure hardware if you wanted more accuracy and lower latency, by feeding the pulses in to the input pin of a timer, then using that to count the pulses - the rest is the same, except you're reading and resetting a timer's count value instead of a pulses variable.

majenko:
You should do as little as possible in an interrupt.

Whilst I agree with that in principle, doing sufficient to make your application work is OK. For example, recording micros() value in a variable.

A quick test shows that a call to micros() takes 3.5625 uS. A call to millis() takes 1.9375 uS.

micros() does more as you can see. It reads the current count in Timer 0, plus the accumulated number of milliseconds and adds them together.

If you are reading a square wave of up to 500 Hz, I think you can afford the time taken by either of those calls.

You can, but is there any need? If the frequency is the number of pulses in a time period, what do you need to do other than count those pulses?

Yes in his case I agree. Counting like you suggest, and looking at the counts every second or so should do it.

Hello,
Thanks for the replies. I will try using Majenko's code
and let you know what happens. BTW, the scope trace was very jittery and that
may have affected the result

Nick, I've tried editing the post the way you suggest, but I'm doing something wrong.
If I 'copy/paste' I get what you see below; if I try 'copy to forum/paste' I get a flood
of formatting text.

   /* Nov 2012
   
   I have standalone '328 with RS232 as test rig for program to read the anemometer

    The O/P from the anemometer is a manky square wave freq from probably
    50Hz to 500Hz but I need to check on a scope first. 
    The O/Pfrom anemometer opto-slot is squared with a 393
    Use Analogue A0 configured as digital input
    Use pulseIn()  */
    
    
       unsigned long t1,t2;
       int T = 0;
      
       
       void setup()
     { 
        Serial.begin (9600);
        pinMode(A0, INPUT);
     }
       
     void loop()
     {
       delay(100);
       t1 = pulseIn( A0, HIGH );
       delay(1000);
       t2 = pulseIn( A0 ,LOW );
       
       
      Serial.print (" Time = " ); Serial.print ( T ); Serial.print (" mins " );
      Serial.print  (" t1 = "); Serial.print (t1); Serial.print ( ",  usec");
      Serial.print  (" t2 = "); Serial.print (t2); Serial.print ( ",  usec");

      Serial.println ("");
      delay(600000UL);
      T=T+10; 

     }

bruce

Yes, don't use "copy for forum" no matter how tempting it looks. :slight_smile:

We are trying to get that rather useless option removed.

Hello Nick,
I think I have it now ( well, the copy bit, anyway : as for the pulseIn() command,
or the interrupt, I still need to study that.)
However, as you say, the 'copy to forum' is misleading.
I chose the 'copy' option and got what I thought was just ordinary text on my
answer. But, when I checked the forum again today, I see the thing has printed the code as you requested.
So, I did the right thing, but only by accident, and never even knew!

Thanks to all for assistance so far. I've been regrettably side-tracked by another project
( ever heard that one before? ), but I'll get back to the datalogger pretty soon and will hope to let you know if it works OK.
Next step, of course, is writing to SD card!

Bruce