Simple Code for an 8 pulse anemometer

I am a novice with Arduino.

I am trying to write a simple code for an 8 pulse anemometer.

I have searched older posts which touch on this and have arrived at the following code. While I believe it works, I am sure there are improvements to be done as I did not understand all the critiques in the older posts.
According to the anemometer specs, 2.5/8 mph per hz.

float  windspeed;   
volatile unsigned long start, duration;


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

  attachInterrupt( 0, wspeed,  RISING );
  start=millis();
 
}

void wspeed()
{
  duration = millis() - start;
  start    = millis();
 
}

void loop()

{
   
Serial.print ("windspeed : ");

  windspeed = ( 312.5/ (float) duration ); // .3125 mph per hz
  if (duration > 1000)
    {
    windspeed =0;
    }

Serial.print (windspeed);
Serial.println (" MPH");


}

Can you post a link to the anemometer ?

You'll probably need some sort of guard against "bouncing" (main pulse accompanied by multiple sub pulses) unless the anemometer has some debouncing built in.

Here is the link to the anemometer

Are you getting any pulses? Lets add a few lines to your code to see:

float  windspeed;   
volatile unsigned long start, duration;
void setup()
{
  Serial.begin(9600);
  pinMode(2,INPUT);
  pinMode(LED_BUILTIN,OUTPUT);

  attachInterrupt( 0, wspeed,  RISING );
  start=millis();
}
void wspeed()
{
  duration = millis() - start;
  start    = millis();
}

void loop()
{
digitalWrite(LED_BUILTIN,digitalRead(2));
   
Serial.print ("windspeed : ");
  windspeed = ( 312.5/ (float) duration ); // .3125 mph per hz
  if (duration > 1000)
    {
    windspeed =0;
    }
Serial.print (windspeed);
Serial.println (" MPH");
}

See if the onboard LED flashes when you turn the rotor slowly.

You could also try it using interrupts:

//#define TESTPULSES      1               //uncomment to use test pulses on pin 7 (jumper to pin 2)

const byte pinAnamometer = 2;
#ifdef TESTPULSES
const byte pinTestOutput = 7;
#endif

#define UPDATE_PERIOD   500             //1/2-second updates
#define PULSE_TOUT      1000ul          //1-second 

void ISR_anamomPulse( void );

unsigned long
    Period,
    pulseTimeout;
bool
    bFirstPulse,
    bGotPulse;
    
void setup( void )
{
    Serial.begin( 9600 );
    pinMode( pinAnamometer, INPUT_PULLUP );
#ifdef TESTPULSES    
    pinMode( pinTestOutput, OUTPUT );
#endif    

    attachInterrupt( digitalPinToInterrupt(pinAnamometer), ISR_anamomPulse, RISING );
    bFirstPulse = true;    
    bGotPulse = false;
        
}//setup

void loop( void )
{   
#ifdef TESTPULSES    
    static bool
        bTestState = false;
    static unsigned long
        testPulse = 0;
    unsigned long
        timeNow;
        
    timeNow = millis();
    if( (timeNow - testPulse) > 10 )
    {
        testPulse = timeNow;
        bTestState ^= true;
        digitalWrite( pinTestOutput, (bTestState)?HIGH:LOW );
        
    }//if
#endif  
   
    CheckPulseTimeout(); 
    CheckSpeed();  
        
}//loop

void CheckSpeed( void )
{
    char
        szSpd[20];
    float
        fP,
        fWindSpeed;
    static unsigned long
        timeUpdate = 0;
    unsigned long
        timeNow;
        
    timeNow = millis();
    if( timeNow - timeUpdate < UPDATE_PERIOD )
        return;
    timeUpdate = timeNow;

    if( bFirstPulse )
        fWindSpeed = 0.0;
    else
    {
        if( bGotPulse )
        {
            noInterrupts();
            bGotPulse = false;
            interrupts();
            fP = (float)Period/1000000.0;
            fWindSpeed = 0.31/fP;
            
        }//if
                
    }//else

    Serial.print( "Windspeed : " );Serial.println( fWindSpeed,2 );
        
}//CheckSpeed

void CheckPulseTimeout( void )
{
    if( !bFirstPulse )
    {
        if( millis() - pulseTimeout > PULSE_TOUT )
        {
            noInterrupts();
            bFirstPulse = true;
            interrupts();            
            
        }//if
        
    }//if
    
}//CheckPulseTimeout

void ISR_anamomPulse( void )
{
    unsigned long
        pulseNow;
    static unsigned long
        lastPulse;

    pulseNow = micros();
    pulseTimeout = millis();
    if( bFirstPulse )
    {
        lastPulse = pulseNow;
        bFirstPulse = false;
    }//if    
    else
    {
        Period = pulseNow - lastPulse;
        lastPulse = pulseNow;
        bGotPulse = true;
        
    }//else
    
}//ISR_anamomPulse

Outsider.

Yes I am getting pulses. As near as I can tell, it works. I'm not sure of it's accuracy yet.

By adding your two lines, The LED also blinks. I was just wondering if there was anything wrong with the code or if it could be improved.

Blackfin: Wow. That is a lot of code. And it works! It is above my skill level and I don't understand most of it.
Can you tell me about pin 7?

I only have 5vcc, ground and signal (attached to pin 2)

dillingerkt:
Blackfin: Wow. That is a lot of code. And it works! It is above my skill level and I don't understand most of it.
Can you tell me about pin 7?

I only have 5vcc, ground and signal (attached to pin 2)

It's not as much as it looks. Sorry I didn't have time to comment it (sort of snuck it in under the radar here at work...)

Since i don't have an anamometer here I used pin 7 to generate pulses that I fed into pin 2 to check the pulse timing and wind speed calcs.

LMK if you'd like it commented out; I'll can do that later this evening if you like.

Blackfin,

That would be great if its not too much work. I am interested to know what each line does.

Thanks!

dillingerkt:
Blackfin,

That would be great if its not too much work. I am interested to know what each line does.

Thanks!

Here you go. I made a small change from the first one (made sure to grab the Period value during the time when I have interrupts disabled to prevent it from changing while its multi-bytes are being read...)

This also has the test pulse code enabled.

#define TESTPULSES      1         //uncomment to use test pulses on pin 7 (jumper to pin 2)

const byte pinAnamometer = 2;       //anamometer signal input
#ifdef TESTPULSES
const byte pinTestOutput = 7;       //if TESTPULSES is defined, this is a test pin the produces
                                    //a test pulse that can be fed into pin 2 to simulate an anamometer
#endif

#define UPDATE_PERIOD   500         //send serial messages to the monitor every 500mS
#define PULSE_TOUT      1000ul      //time between pulses where we say wind speed is basically 0.0mph

void ISR_anamomPulse( void );       //prototype of the function used for the anamometer pin rising-edge interrupt

unsigned long
    Period,                         //holds the time between rising edges in microseconds
    pulseTimeout;                   //holds the time of the last pulse (mS) use to determine if there's little/no wind
bool
    bFirstPulse,                    //indicates to ISR that this is the first pulse
    bGotPulse;                      //indication from ISR that a valid Period reading is ready
    
void setup( void )
{
    Serial.begin( 9600 );
    pinMode( pinAnamometer, INPUT_PULLUP );
#ifdef TESTPULSES    
    pinMode( pinTestOutput, OUTPUT );
#endif    

    //set up to interrupt on rising edges of pin 2 (anamometer input) and to call function ISR_anamomPulse
    //on each interrupt
    attachInterrupt( digitalPinToInterrupt(pinAnamometer), ISR_anamomPulse, RISING );
    
    //ready the ISR to receive its first pulse and indicate no period is ready yet
    bFirstPulse = true;    
    bGotPulse = false;
        
}//setup

void loop( void )
{   
    //if defined, this sets up and times the test pulses produced on pin 7
#ifdef TESTPULSES    
    static bool
        bTestState = false;
    static unsigned long
        testPulse = 0;
    unsigned long
        timeNow;

    //change the value (e.g. 10) to lengthen the pulses
    timeNow = millis();
    if( (timeNow - testPulse) > 10 )
    {
        //when the time has passed, set the current time as the last time
        testPulse = timeNow;
        //toggle the internal flag representing the pin state (high or low, true or false, 1 or 0 etc)
        bTestState ^= true;
        //then set the pin to that state
        digitalWrite( pinTestOutput, (bTestState)?HIGH:LOW );
        
    }//if
#endif  

    //check to see if too much time has elapsed since the last anamometer rising edge   
    CheckPulseTimeout(); 
    //see if it's time to send a windspeed message
    CheckSpeed();  
        
}//loop

void CheckSpeed( void )
{
    char
        szSpd[20];
    float
        fP,
        fWindSpeed;
    static unsigned long
        timeUpdate = 0;
    unsigned long
        timeNow;

    //we send a message every UPDATE_PERIOD mS
    timeNow = millis();
    if( timeNow - timeUpdate < UPDATE_PERIOD )
        return;
    timeUpdate = timeNow;

    //if we haven't even gotten a single pulse yet (or it's been long enough since the last
    //one that we re-set this flag due to timeout) just print a windspeed of 0.0
    if( bFirstPulse )
        fWindSpeed = 0.0;
    else
    {
        //check the ISR has been able to measure a period yet
        if( bGotPulse )
        {
            //yes...stop interrupts for a moment while the flag is cleared and we get the 
            //current value of the period, then re-enable them
            noInterrupts();
            bGotPulse = false;        
            //period is measured internally in microseconds; divide by a million to get
            //a value in seconds suitable for use in the Hz calculation   
            fP = (float)Period/1000000.0;
            interrupts();

            //calculates the windspeed
            fWindSpeed = 0.31/fP;
            
        }//if
                
    }//else

    //and send it out to the serial monitor
    Serial.print( "Windspeed : " );Serial.println( fWindSpeed,2 );
        
}//CheckSpeed

void CheckPulseTimeout( void )
{
    //don't check for a timeout if we've not even gotten a pulse since power-on/timeout
    if( !bFirstPulse )
    {
        //check if enough mS has elapsed since the last rising edge
        if( millis() - pulseTimeout > PULSE_TOUT )
        {
            //if so, halt interrupts, set the flag, and then re-enable ints
            noInterrupts();
            bFirstPulse = true;
            interrupts();            
            
        }//if
        
    }//if
    
}//CheckPulseTimeout

void ISR_anamomPulse( void )
{
    unsigned long
        pulseNow;
    static unsigned long
        lastPulse;

    //get the time of this pulse (not exact using this method but close enough)
    pulseNow = micros();
    //and update the time of the timeout value for each rising edge
    pulseTimeout = millis();

    //if this is the first edge since power-on or timeout, we need to save its time as the "last" time
    //so we can compute the period on the following edge
    if( bFirstPulse )
    {
        lastPulse = pulseNow;
        //got a pulse, so clear this flag now
        bFirstPulse = false;
        
    }//if    
    else
    {
        //not the first edge; calculate the period between rising edges as the time of this
        //pulse (pulseNow) minus the time of the last pusle (lastPulse)
        Period = pulseNow - lastPulse;
        //now we save the current time as the last time so we can recalc the period on the 
        //next rising edge
        lastPulse = pulseNow;
        //we've calculated a Period so indicate to CheckSpeed() that there's a good value
        bGotPulse = true;
        
    }//else
    
}//ISR_anamomPulse

Thanks for explaining everything. I am still working my way through it. Very Impressive.

"This also has the test pulse code enabled." - I didn't get it at first - that you had to create your own test pulses. But that explains some of the code which confused me. So, if I uncomment the "define TESTPULSES", most of the code is not used?

I have another question for you.
My intention is to use the anemometer with a watt meter and a time/date stamp to record data from my wind turbine on a SD Card.

The watt meter, SD card, and time/date all run in the loop. Since the fWindSpeed reading occurs outside the loop, I get an error that it is not defined when I merge your sketch with mine.

Is there a way to send the fWindSpeed reading into the loop so that I can write to the SD card?

Here is the whole code

this is link to pzem library: GitHub - olehs/PZEM004T: Arduino communication library for Peacefair PZEM-004T Energy monitor

#include <Wire.h>
#include <LiquidCrystal.h>
#include <SD.h>
#include <SPI.h>
#include <SoftwareSerial.h> 
#include <PZEM004T.h>
#include <DS3231.h>

LiquidCrystal lcd(8,7,3,4,5,6);
PZEM004T pzem(10,11);  // TX,RX connections for watt meter
IPAddress ip(192,168,1,1);
DS3231  rtc(SDA, SCL); // connections for time and date stamp

File myFile;
int pinCS = 53; //assign pin for sd card reader
int maxspeed = 0;
  // insert Blackfin code 

//#define TESTPULSES      1         //uncomment to use test pulses on pin 7 (jumper to pin 2)

const byte pinAnamometer = 2;       //anamometer signal input
#ifdef TESTPULSES
const byte pinTestOutput = 7;       //if TESTPULSES is defined, this is a test pin the produces
                                    //a test pulse that can be fed into pin 2 to simulate an anamometer
#endif

#define UPDATE_PERIOD   500         //send serial messages to the monitor every 500mS
#define PULSE_TOUT      1000ul      //time between pulses where we say wind speed is basically 0.0mph

void ISR_anamomPulse( void );       //prototype of the function used for the anamometer pin rising-edge interrupt

unsigned long
    Period,                         //holds the time between rising edges in microseconds
    pulseTimeout;                   //holds the time of the last pulse (mS) use to determine if there's little/no wind
bool
    bFirstPulse,                    //indicates to ISR that this is the first pulse
    bGotPulse;                      //indication from ISR that a valid Period reading is ready


void setup() 
{
 // insert Blackfin setup code
    Serial.begin( 9600 );
    pinMode( pinAnamometer, INPUT_PULLUP );
#ifdef TESTPULSES    
    pinMode( pinTestOutput, OUTPUT );
#endif    

    //set up to interrupt on rising edges of pin 2 (anamometer input) and to call function ISR_anamomPulse
    //on each interrupt
    attachInterrupt( digitalPinToInterrupt(pinAnamometer), ISR_anamomPulse, RISING );
    
    //ready the ISR to receive its first pulse and indicate no period is ready yet
    bFirstPulse = true;    
    bGotPulse = false;
//end Blackfin setup

   
   pinMode(pinCS, OUTPUT);
   pzem.setAddress(ip);
   rtc.begin(); // Initialize the rtc object


  
   lcd.begin(16, 4);
   lcd.print("Initializing");
   delay(1000);
   lcd.clear();

 
   if (SD.begin())
   {
    Serial.println("SD card is ready to use.");
   } else
    {
    Serial.println("SD card initialization failed");
    return;
    }
}
  

         
void loop() 
{


 //Send Day-of-Week
  Serial.print(rtc.getDOWStr());
  Serial.print(" ");
  
  // Send date
  Serial.print(rtc.getDateStr());
  Serial.print(" -- ");
  // Send time
  Serial.println(rtc.getTimeStr());

 
 // insert Blackfin Loop
  
     //if defined, this sets up and times the test pulses produced on pin 7
#ifdef TESTPULSES    
    static bool
        bTestState = false;
    static unsigned long
        testPulse = 0;
    unsigned long
        timeNow;

    //change the value (e.g. 10) to lengthen the pulses
    timeNow = millis();
    if( (timeNow - testPulse) > 10 )
    {
        //when the time has passed, set the current time as the last time
        testPulse = timeNow;
        //toggle the internal flag representing the pin state (high or low, true or false, 1 or 0 etc)
        bTestState ^= true;
        //then set the pin to that state
/       digitalWrite( pinTestOutput, (bTestState)?HIGH:LOW );
        
    }//if
#endif  

    //check to see if too much time has elapsed since the last anamometer rising edge   
    CheckPulseTimeout(); 
    //see if it's time to send a windspeed message
    CheckSpeed(); 
 
// end Blackfin loop

 if (fWindSpeed > maxspeed) maxspeed = fWindSpeed; //track highest windspeed 
 
  float v = pzem.voltage(ip);
  if (v < 0.0) v = 0.0;
  Serial.print(v);Serial.print("V; ");
 
  float i = pzem.current(ip);
  if(i >= 0.0){ Serial.print(i);Serial.print("A; "); }
  
  float p = pzem.power(ip); // power = watts
  if(p >= 0){ Serial.print(p);Serial.print("W; "); }
  
  float e = pzem.energy(ip); // energy = watt hours
  if(e >= 0.0){ Serial.print(e);Serial.print("Wh; "); }

  //num = num + 1; // start a counter which will reset every 30 cycles
 // Serial.print(num);
 // Serial.println(); 

   //if (num > 30) //print to sd card every 30th count if producing energy
   //{
    if (p > 10) // only write to sd card if turbine is making more than 10 watts
      {   
         myFile = SD.open("test.txt", FILE_WRITE); // print to sd card: date, time, windspeed and watts
        if (myFile) { 
          myFile.print(rtc.getDateStr());
          myFile.print(", ");
          myFile.print(rtc.getTimeStr()); 
          myFile.print(", ");  
          myFile.print(fWindSpeed);
          myFile.print(" mph =");    
          myFile.print(p);
          myFile.println(" watts, ");
          myFile.close(); // close the file
          }
 
       else {
          Serial.println("error opening test.txt");
          }
       }
   //    num = 0;    // reset counter to 0  
    //}

  lcd.clear();
  lcd.setCursor(0, 0); 
  lcd.print(fWindSpeed);
  lcd.setCursor(6,0);
  lcd.print("MPH");
  lcd.setCursor(13,0);
  lcd.print(maxspeed);
  lcd.setCursor(17,0);
  lcd.print("MAX");

  lcd.setCursor(0,1);
  lcd.print(p);  
  lcd.setCursor(6,1); 
  lcd.print("Watts");

  lcd.setCursor(4,2);
  lcd.print(e);
  lcd.setCursor(10,2);
  lcd.print("Watt hours");

  lcd.setCursor(4,3);
  lcd.print(i);
  lcd.setCursor(10,3);
  lcd.print("Amps");
}

//insert remaining Blackfin code

void CheckSpeed( void )
{
    char
        szSpd[20];
  // I had to remove the rest of the code to get under the 9000 character limit for this post.

Can you re-post your entire INO file as an attachment? It's too long for in-line posting but you can click attachments and the whole thing will be available.

I didn't know that. I have attached the entire ino file.

vortex_meter_blackfin.ino (8.41 KB)

Made a few changes based on a couple of assumptions (e.g. you don't need to update the LCD or serial monitor every pass of loop; updating the PZEM at 10Hz is probably sufficient...) Let me know in PMs if you want changes.

It won't compile for me due to a bunch of DS3231 library errors. Going to assume it compiles for you so I didn't touch any of that. However, because it didn't compile for me I wasn't able to check the other stuff I changed.

vortex_meter_blackfin.ino (10.9 KB)