Pages: [1]   Go Down
Author Topic: Problems with pulseIn()  (Read 1419 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Jr. Member
**
Karma: 0
Posts: 56
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Logged

UK
Offline Offline
Faraday Member
**
Karma: 100
Posts: 4153
Where is your SSCCE?!?!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Get 10% off all 4D Systems TFT screens this month: use discount code MAJENKO10

Poole, Dorset, UK
Offline Offline
Edison Member
*
Karma: 52
Posts: 2382
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You must post your code before anyone can help!.

Mark

PS This sounds like a job for interrupts.

M
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 504
Posts: 19095
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

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

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


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

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
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 504
Posts: 19095
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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


UK
Offline Offline
Faraday Member
**
Karma: 100
Posts: 4153
Where is your SSCCE?!?!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

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

Get 10% off all 4D Systems TFT screens this month: use discount code MAJENKO10

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 504
Posts: 19095
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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


UK
Offline Offline
Faraday Member
**
Karma: 100
Posts: 4153
Where is your SSCCE?!?!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Get 10% off all 4D Systems TFT screens this month: use discount code MAJENKO10

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 504
Posts: 19095
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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


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

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.

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

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 504
Posts: 19095
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

We are trying to get that rather useless option removed.
Logged


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

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
Logged

Pages: [1]   Go Up
Jump to: