WeatherDuino Forum

Full Version: Peet Bros Anemometer > WeatherDuino converter
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
To connect my Peet Bros Anemometer to the Weatherduino, I will use an Arduino Nano as convertor.


When ready and working is confirmed, it will be available of-course for others so this great anemometer/dir combo can be added to the supported device list of the Weatherduino system.


I'm ready with the basics so what I have now is this:
  • wind speed output is in MPH
  • -wind direction is in degrees 0-360
  • debounce checks
  • the by Peter Bros (PB) recommended calibration formulas are incorporated
QUESTION:
What do I need to change to make the convertor board output Davis 6410 compatibel?

- windspeed output in m/s?

- wind direction- 0-360 degrees to ...?

Thanks!



PB Info:

The ULTIMETER PRO Anemometer is a patented, all-digital design for measuring both the
speed and direction of wind. The anemometer houses two magnetically-actuated reed
switches. Both open and close once each revolution of the anemometer assembly. We call the
lower reed switch, "the speed reed," and the upper reed switch, "the direction reed." The wind
speed measurement is derived from the speed reed, and the wind direction measurement is
derived from the speed reed and direction reed (both signals must be present).

In ULTIMETER Weather Stations, speed is determined by measuring the time interval between
two successive closures of the speed reed. Calibration is done as follows (RPS = revolutions
per second):

0.010 < RPS < 3.229 (approximately 0.2 < MPH < 8.2):
MPH = -0.1095(RPS 2 ) + 2.9318(RPS) – 0.1412

3.230 < RPS < 54.362 (approximately 8.2 < MPH < 136.0):
MPH = 0.0052(RPS 2 ) + 2.1980(RPS) + 1.1091

54.363 < RPS < 66.332 (approximately 136.0 < MPH < 181.5):
MPH = 0.1104(RPS 2 ) – 9.5685(RPS) + 329.87

Conversions used are: mph * 0.86897 = knots; mph * 1.6094 = kmph; mph * 0.48037 = m/s

Direction is calculated as the relative timing (phase relationship) between contact closures of the
two reed switches. This is a non-linear, sinusoidal relationship. We regard repeated actuation
of the speed reed as the reference and arbitrarily define "North" as the vane orientation that
causes the two reed switches to close at the same time. "South" is the vane orientation that
causes the direction reed to close exactly halfway between two closures of the speed reed.

We include several data checks in our systems:

We require that there be one and only one contact closure from one reed switch between
two successive closures of the other reed switch. This involves some special treatment
when the closures are nearly coincident.

At speeds above 10 mph, we measure speed twice, through two successive revolutions
of the anemometer. The two readings must agree within preset limits that vary with
speed.

We likewise measure direction twice, through two successive revolutions of the
anemometer. If the two readings do not agree within preset limits that vary with speed,
we do not display the new direction reading in the output. This is done to emphasize the
predominant wind direction and minimize excessive display fluctuation, without sacrificing
responsiveness.
I need some help Huh 


I now have a working converter for direction (pulses to analog 0-5V)  BUT the output of the speed is in MPH!

For the WD we need pulses as input but Peet Bros has THREE calibration calculations (see previous post)
which must then be done in th WD software?!

what's the best way to move forward here...?

Thanks!


Idea I bought a MCP4725 DAC to convert the direction from degrees into analog output but this is not yet incorporated in the sketch below...
Code:
// Peet Bros wind sensor convertor


#define windSpeedPin 2
#define windDirPin 3
#define windSpeedINT 0 // INT0
#define windDirINT 1   // INT1

#define windSpeedOutPin 8
#define windDirOutPin   9

// Pin 13 has an LED connected on most Arduino boards.
int LED = 13;

const unsigned long DEBOUNCE = 10000ul;      // Minimum switch time in microseconds
const unsigned long DIRECTION_OFFSET = 0ul;  // Manual direction offset in degrees, if required
const unsigned long TIMEOUT = 1500000ul;       // Maximum time allowed between speed pulses in microseconds
const unsigned long UPDATE_RATE = 500ul;     // How often to send out NMEA data in milliseconds

volatile unsigned long speedPulse = 0ul;    // Time capture of speed pulse
volatile unsigned long dirPulse = 0ul;      // Time capture of direction pulse
volatile unsigned long speedTime = 0ul;     // Time between speed pulses (microseconds)
volatile unsigned long directionTime = 0ul; // Time between direction pulses (microseconds)
volatile boolean newData = false;           // New speed pulse received
volatile unsigned long lastUpdate = 0ul;    // Time of last serial output
int windDirection = 0;
volatile boolean ignoreNextReading = false;

boolean debug = true;

void setup()
{
 pinMode(LED, OUTPUT);
 pinMode(windSpeedOutPin, OUTPUT);
 pinMode(windDirOutPin, OUTPUT);

 Serial.begin(115200, SERIAL_8N1);

 pinMode(windSpeedPin, INPUT_PULLUP);
 attachInterrupt(windSpeedINT, readWindSpeed, FALLING);

 pinMode(windDirPin, INPUT_PULLUP);
 attachInterrupt(windDirINT, readWindDir, FALLING);

 interrupts();
}


void readWindSpeed()
{
 // Despite the interrupt being set to FALLING edge, double check the pin is now LOW
 if (((micros() - speedPulse) > DEBOUNCE) && (digitalRead(windSpeedPin) == LOW))
 {
   // Work out time difference between last pulse and now
   speedTime = micros() - speedPulse;

   // Direction pulse should have occured after the last speed pulse
   if (dirPulse - speedPulse >= 0) directionTime = dirPulse - speedPulse;

   newData = true;
   speedPulse = micros();    // Capture time of the new speed pulse
 }
}

void readWindDir()
{
 if (((micros() - dirPulse) > DEBOUNCE) && (digitalRead(windDirPin) == LOW))
 {
   dirPulse = micros();        // Capture time of direction pulse
 }
}

void calcWindSpeedAndDir()
{
 unsigned long dirPulse_, speedPulse_;
 unsigned long speedTime_;
 unsigned long directionTime_;
 unsigned long windDirection = 0l, rps = 0l, mph = 0l;
// Peet Bros wind sensor convertor


#define windSpeedPin 2
#define windDirPin 3
#define windSpeedINT 0 // INT0
#define windDirINT 1   // INT1

#define windSpeedOutPin 8
#define windDirOutPin   9

// Pin 13 has an LED connected on most Arduino boards.
int LED = 13;

const unsigned long DEBOUNCE = 10000ul;      // Minimum switch time in microseconds
const unsigned long DIRECTION_OFFSET = 0ul;  // Manual direction offset in degrees, if required
const unsigned long TIMEOUT = 1500000ul;       // Maximum time allowed between speed pulses in microseconds
const unsigned long UPDATE_RATE = 500ul;     // How often to send out NMEA data in milliseconds

volatile unsigned long speedPulse = 0ul;    // Time capture of speed pulse
volatile unsigned long dirPulse = 0ul;      // Time capture of direction pulse
volatile unsigned long speedTime = 0ul;     // Time between speed pulses (microseconds)
volatile unsigned long directionTime = 0ul; // Time between direction pulses (microseconds)
volatile boolean newData = false;           // New speed pulse received
volatile unsigned long lastUpdate = 0ul;    // Time of last serial output
int windDirection = 0;
volatile boolean ignoreNextReading = false;

boolean debug = true;

void setup()
{
  pinMode(LED, OUTPUT);
  pinMode(windSpeedOutPin, OUTPUT);
  pinMode(windDirOutPin, OUTPUT);

  Serial.begin(115200, SERIAL_8N1);

  pinMode(windSpeedPin, INPUT_PULLUP);
  attachInterrupt(windSpeedINT, readWindSpeed, FALLING);

  pinMode(windDirPin, INPUT_PULLUP);
  attachInterrupt(windDirINT, readWindDir, FALLING);

  interrupts();
}


void readWindSpeed()
{
  // Despite the interrupt being set to FALLING edge, double check the pin is now LOW
  if (((micros() - speedPulse) > DEBOUNCE) && (digitalRead(windSpeedPin) == LOW))
  {
    // Work out time difference between last pulse and now
    speedTime = micros() - speedPulse;

    // Direction pulse should have occured after the last speed pulse
    if (dirPulse - speedPulse >= 0) directionTime = dirPulse - speedPulse;

    newData = true;
    speedPulse = micros();    // Capture time of the new speed pulse
  }
}

void readWindDir()
{
  if (((micros() - dirPulse) > DEBOUNCE) && (digitalRead(windDirPin) == LOW))
  {
    dirPulse = micros();        // Capture time of direction pulse
  }
}

void calcWindSpeedAndDir()
{
  unsigned long dirPulse_, speedPulse_;
  unsigned long speedTime_;
  unsigned long directionTime_;
  unsigned long windDirection = 0l, rps = 0l, mph = 0l;

  // Get snapshot of data into local variables. Note: an interrupt could trigger here
  noInterrupts();
  dirPulse_ = dirPulse;
  speedPulse_ = speedPulse;
  speedTime_ = speedTime;
  directionTime_ = directionTime;
  interrupts();

  // Make speed zero, if the pulse delay is too long
  if (micros() - speedPulse_ > TIMEOUT) speedTime_ = 0ul;

  // The following converts revolutions per 100 seconds (rps) to mph x 100
  // This calculation follows the Peet Bros. piecemeal calibration data
  if (speedTime_ > 0)
  {

    // to prevent calculation errors with float numbers, make them larger
    rps = 100000000 / speedTime_;

    if (1 < rps && rps < 323)
    {
      //      float mph = (-0.1095 * (rps * rps)) + (2.9318 * rps) - 0.1412;
      mph = (-1095 * rps * rps + 29318 * rps * 100 - 14120000) / 1000000;
    }
    else if (323 <= rps && rps < 5436)
    {
      //            mph = 0.0052 * (rps * rps) + 2.1980 * rps + 1.1091;
      mph = (52 * rps * rps + 21980 * rps * 100 + 110910000) / 1000000;
    }
    else if (5436 <= rps && rps < 6633)
    {
      //            mph = 0.1104 * (rps * rps) - 9.5685 * rps + 329.87;
      mph = (1104 * rps * rps - 95685 * rps * 100 + 32987000000) / 1000000;
    }

    
    // Remove the possibility of negative speed
    if (mph < 0l) mph = 0l;
    
    // only calculate direction if we have wind
    if (mph >= 0)
    {
      // Calculate direction from captured pulse times
      windDirection = ( ( (directionTime_ * 360) / speedTime_) + DIRECTION_OFFSET) % 360;
    }
    else
    {
      mph = 0;
    }

    //============================================
    // output to Weatherduino

    //int degrees2analog = map(dirOut, 0, 360, 0, 255);
    analogWrite(windDirOutPin, map(windDirection, 0, 360, 0, 255));

    //============================================


    if (debug)
    {
      Serial.print("speedTime_: "); Serial.println(speedTime_);
      Serial.print("rps: "); Serial.println(rps);
      Serial.print("dir: "); Serial.println(windDirection);
      Serial.print("mph: "); Serial.println(mph);
      Serial.println("");
    }
  }
}


void loop()
{
  int i;
  const unsigned int LOOP_DELAY = 50;
  const unsigned int LOOP_TIME = TIMEOUT / LOOP_DELAY;

  digitalWrite(LED, !digitalRead(LED));    // Toggle LED

  i = 0;
  // If there is new data, process // Peet Bros wind sensor convertor


#define windSpeedPin 2
#define windDirPin 3
#define windSpeedINT 0 // INT0
#define windDirINT 1   // INT1

#define windSpeedOutPin 8
#define windDirOutPin   9

// Pin 13 has an LED connected on most Arduino boards.
int LED = 13;

const unsigned long DEBOUNCE = 10000ul;      // Minimum switch time in microseconds
const unsigned long DIRECTION_OFFSET = 0ul;  // Manual direction offset in degrees, if required
const unsigned long TIMEOUT = 1500000ul;       // Maximum time allowed between speed pulses in microseconds
const unsigned long UPDATE_RATE = 500ul;     // How often to send out NMEA data in milliseconds

volatile unsigned long speedPulse = 0ul;    // Time capture of speed pulse
volatile unsigned long dirPulse = 0ul;      // Time capture of direction pulse
volatile unsigned long speedTime = 0ul;     // Time between speed pulses (microseconds)
volatile unsigned long directionTime = 0ul; // Time between direction pulses (microseconds)
volatile boolean newData = false;           // New speed pulse received
volatile unsigned long lastUpdate = 0ul;    // Time of last serial output
int windDirection = 0;
volatile boolean ignoreNextReading = false;

boolean debug = true;

void setup()
{
  pinMode(LED, OUTPUT);
  pinMode(windSpeedOutPin, OUTPUT);
  pinMode(windDirOutPin, OUTPUT);

  Serial.begin(115200, SERIAL_8N1);

  pinMode(windSpeedPin, INPUT_PULLUP);
  attachInterrupt(windSpeedINT, readWindSpeed, FALLING);

  pinMode(windDirPin, INPUT_PULLUP);
  attachInterrupt(windDirINT, readWindDir, FALLING);

  interrupts();
}


void readWindSpeed()
{
  // Despite the interrupt being set to FALLING edge, double check the pin is now LOW
  if (((micros() - speedPulse) > DEBOUNCE) && (digitalRead(windSpeedPin) == LOW))
  {
    // Work out time difference between last pulse and now
    speedTime = micros() - speedPulse;

    // Direction pulse should have occured after the last speed pulse
    if (dirPulse - speedPulse >= 0) directionTime = dirPulse - speedPulse;

    newData = true;
    speedPulse = micros();    // Capture time of the new speed pulse
  }
}

void readWindDir()
{
  if (((micros() - dirPulse) > DEBOUNCE) && (digitalRead(windDirPin) == LOW))
  {
    dirPulse = micros();        // Capture time of direction pulse
  }
}

void calcWindSpeedAndDir()
{
  unsigned long dirPulse_, speedPulse_;
  unsigned long speedTime_;
  unsigned long directionTime_;
  unsigned long windDirection = 0l, rps = 0l, mph = 0l;

  // Get snapshot of data into local variables. Note: an interrupt could trigger here
  noInterrupts();
  dirPulse_ = dirPulse;
  speedPulse_ = speedPulse;
  speedTime_ = speedTime;
  directionTime_ = directionTime;
  interrupts();

  // Make speed zero, if the pulse delay is too long
  if (micros() - speedPulse_ > TIMEOUT) speedTime_ = 0ul;

  // The following converts revolutions per 100 seconds (rps) to mph x 100
  // This calculation follows the Peet Bros. piecemeal calibration data
  if (speedTime_ > 0)
  {

    // to prevent calculation errors with float numbers, make them larger
    rps = 100000000 / speedTime_;

    if (1 < rps && rps < 323)
    {
      //      float mph = (-0.1095 * (rps * rps)) + (2.9318 * rps) - 0.1412;
      mph = (-1095 * rps * rps + 29318 * rps * 100 - 14120000) / 1000000;
    }
    else if (323 <= rps && rps < 5436)
    {
      //            mph = 0.0052 * (rps * rps) + 2.1980 * rps + 1.1091;
      mph = (52 * rps * rps + 21980 * rps * 100 + 110910000) / 1000000;
    }
    else if (5436 <= rps && rps < 6633)
    {
      //            mph = 0.1104 * (rps * rps) - 9.5685 * rps + 329.87;
      mph = (1104 * rps * rps - 95685 * rps * 100 + 32987000000) / 1000000;
    }

    
    // Remove the possibility of negative speed
    if (mph < 0l) mph = 0l;
    
    // only calculate direction if we have wind
    if (mph >= 0)
    {
      // Calculate direction from captured pulse times
      windDirection = ( ( (directionTime_ * 360) / speedTime_) + DIRECTION_OFFSET) % 360;
    }
    else
    {
      mph = 0;
    }

    //============================================
    // output to Weatherduino

    //int degrees2analog = map(dirOut, 0, 360, 0, 255);
    analogWrite(windDirOutPin, map(windDirection, 0, 360, 0, 255));

    //============================================


    if (debug)
    {
      Serial.print("speedTime_: "); Serial.println(speedTime_);
      Serial.print("rps: "); Serial.println(rps);
      Serial.print("dir: "); Serial.println(windDirection);
      Serial.print("mph: "); Serial.println(mph);
      Serial.println("");
    }
  }
}


void loop()
{
  int i;
  const unsigned int LOOP_DELAY = 50;
  const unsigned int LOOP_TIME = TIMEOUT / LOOP_DELAY;

  digitalWrite(LED, !digitalRead(LED));    // Toggle LED

  i = 0;
  // If there is new data, process it, otherwise wait for LOOP_TIME to pass
  while ((newData != true) && (i < LOOP_TIME))
  {
    i++;
    delayMicroseconds(LOOP_DELAY);
  }

  calcWindSpeedAndDir();    // Process new data
  newData = false;
}it, otherwise wait for LOOP_TIME to pass
  while ((newData != true) && (i < LOOP_TIME))
  {
    i++;
    delayMicroseconds(LOOP_DELAY);
  }

  calcWindSpeedAndDir();    // Process new data
  newData = false;
}
 // Get snapshot of data into local variables. Note: an interrupt could trigger here
 noInterrupts();
 dirPulse_ = dirPulse;
 speedPulse_ = speedPulse;
 speedTime_ = speedTime;
 directionTime_ = directionTime;
 interrupts();

 // Make speed zero, if the pulse delay is too long
 if (micros() - speedPulse_ > TIMEOUT) speedTime_ = 0ul;

 // The following converts revolutions per 100 seconds (rps) to mph x 100
 // This calculation follows the Peet Bros. piecemeal calibration data
 if (speedTime_ > 0)
 {

   // to prevent calculation errors with float numbers, make them larger
   rps = 100000000 / speedTime_;

   if (1 < rps && rps < 323)
   {
     //      float mph = (-0.1095 * (rps * rps)) + (2.9318 * rps) - 0.1412;
     mph = (-1095 * rps * rps + 29318 * rps * 100 - 14120000) / 1000000;
   }
   else if (323 <= rps && rps < 5436)
   {
     //            mph = 0.0052 * (rps * rps) + 2.1980 * rps + 1.1091;
     mph = (52 * rps * rps + 21980 * rps * 100 + 110910000) / 1000000;
   }
   else if (5436 <= rps && rps < 6633)
   {
     //            mph = 0.1104 * (rps * rps) - 9.5685 * rps + 329.87;
     mph = (1104 * rps * rps - 95685 * rps * 100 + 32987000000) / 1000000;
   }

   
   // Remove the possibility of negative speed
   if (mph < 0l) mph = 0l;
   
   // only calculate direction if we have wind
   if (mph >= 0)
   {
     // Calculate direction from captured pulse times
     windDirection = ( ( (directionTime_ * 360) / speedTime_) + DIRECTION_OFFSET) % 360;
   }
   else
   {
     mph = 0;
   }

   //============================================
   // output to Weatherduino

   //int degrees2analog = map(dirOut, 0, 360, 0, 255);
   analogWrite(windDirOutPin, map(windDirection, 0, 360, 0, 255));

   //============================================


   if (debug)
   {
     Serial.print("speedTime_: "); Serial.println(speedTime_);
     Serial.print("rps: "); Serial.println(rps);
     Serial.print("dir: "); Serial.println(windDirection);
     Serial.print("mph: "); Serial.println(mph);
     Serial.println("");
   }
 }
}


void loop()
{
 int i;
 const unsigned int LOOP_DELAY = 50;
 const unsigned int LOOP_TIME = TIMEOUT / LOOP_DELAY;

 digitalWrite(LED, !digitalRead(LED));    // Toggle LED

 i = 0;
 // If there is new data, process it, otherwise wait for LOOP_TIME to pass
 while ((newData != true) && (i < LOOP_TIME))
 {
   i++;
   delayMicroseconds(LOOP_DELAY);
 }

 calcWindSpeedAndDir();    // Process new data
 newData = false;
}
Hi edr1924,

in the software of the TX module main tab beginning from line 100 you can find the different impulse <-> wind speed correlations of the different devices. Depending on which anemometer you want to emulate you can calculate a output frequency corresponding to your actual wind speed.

Best regards,
engolling
(14-05-2019, 22:04)engolling Wrote: [ -> ]Hi edr1924,

in the software of the TX module main tab beginning from line 100 you can find the different impulse <-> wind speed correlations of the different devices. Depending on which anemometer you want to emulate you can calculate a output frequency corresponding to your actual wind speed.

Best regards,
engolling

Thanks, yes I have to go that route, changing the WD software itself. 
I was hoping to make a Davis 6410 compatible 'plugin' converter but alas...
(15-05-2019, 11:43)edr1924 Wrote: [ -> ]
(14-05-2019, 22:04)engolling Wrote: [ -> ]Hi edr1924,

in the software of the TX module main tab beginning from line 100 you can find the different impulse <-> wind speed correlations of the different devices. Depending on which anemometer you want to emulate you can calculate a output frequency corresponding to your actual wind speed.

Best regards,
engolling

Thanks, yes I have to go that route, changing the WD software itself. 
I was hoping to make a Davis 6410 compatible 'plugin' converter but alas...
Hi edr1924,

I don't think you have to change the WeahterDuino TX Software. Your converter can just emulate the Output pulses of a davis anemomenter.
My idea would be:
- You converter determines the actual windspeed in m/s - from the comments in the TX Software you can calculate which frequency of pulses corresponds to the windspeed of e.g. a davis Anemometer.
Then you simple calculate back your frequency according to the actual windspeed and output it on a digital I/O.

You will have to be aware of the potential of the output depending on your voltage source of the converter you will have to use a galvanic isolation.

Regards,
engolling
(15-05-2019, 12:29)engolling Wrote: [ -> ]
(15-05-2019, 11:43)edr1924 Wrote: [ -> ]
(14-05-2019, 22:04)engolling Wrote: [ -> ]Hi edr1924,

in the software of the TX module main tab beginning from line 100 you can find the different impulse <-> wind speed correlations of the different devices. Depending on which anemometer you want to emulate you can calculate a output frequency corresponding to your actual wind speed.

Best regards,
engolling

Thanks, yes I have to go that route, changing the WD software itself. 
I was hoping to make a Davis 6410 compatible 'plugin' converter but alas...
Hi edr1924,

I don't think you have to change the WeahterDuino TX Software. Your converter can just emulate the Output pulses of a davis anemomenter.
My idea would be:
- You converter determines the actual windspeed in m/s - from the comments in the TX Software you can calculate which frequency of pulses corresponds to the windspeed of e.g. a davis Anemometer.
Then you simple calculate back your frequency according to the actual windspeed and output it on a digital I/O.

You will have to be aware of the potential of the output depending on your voltage source of the converter you will have to use a galvanic isolation.

Regards,
engolling

Thanks again Engolling,  That was my goal but my math is not so good... It took me ages to make the Barograph part on my Gorgy Meteo Clock but at the end it did work so I have to dig in again...

So the convertor board has an output which has a small lag but that's no problem.

ReadManual