Wind Sonic

 

First note

Don't do this project for a real outside anemometer, there is some problem with stability of reading.  You can get bad reading because of sensitivity of this sensor.  This project is more for your knowledge, and fun!

 

History

This project looks simple, but it’s one of the most complicated projects I've ever made. In books it looks simple, in real life it's really hard though. The main goal is to measure wind speed with the speed of sound. In the beginning I used a cheap 40 kHz ultrasound transceiver. That was working fine until I tried to replace those transceivers with something that will be able to last outside though rain and snow. I tried to make the transceivers closed frame, but as soon as I replaced the open frame with the closed frame, nothing was working, and the output levels were way too low. I started a topic on http://www.avrfreaks.net and by chance, I got heaps of help and ideas. Guillem told me that he had tried something like using 20 closed face transceivers, but he found that only one was working. Guillem was generous enough to send me 6 of his closed face transceivers. As soon as I put in the transceivers that he had sent me, the prototype began to work. Normally if you check all the schematic of ultrasonic measurement you will see that the measure time from North to South and South to North, this way it will remove the difference of sound speed versus temperature, but in fact it doesn't work because it makes a lot of cross talk between the Tx and Rx pins, you need to send a 40 kHz burst at 5V with the same IC that you amplify the signal by 4700...  So it measures the time between South to North and West to East, to adjust the calculation. I had use a DS18B20 to measure the air temperature and this way I can have both valid wind speed add valid Temperature

It worked this way. I had to send a very small burst of 40 kHz (3 pulses). I started a counter in the AVR and when the sound arrived at the other side of the transceiver it generates an interrupt, the time between the Tx and Rx is supposed to be the speed of sound at this temperature, if it's faster it's because you have wind in this axe North South, if it's slower it's because you have wind in this axe South North.  With 4 transceivers and some formulas you can have the wind speed in any direction and you can know what the direction of the wind is. I want to thanks all the friends that help me on www.avrfreaks.net

 

You can check all the discution about this project http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=219485#219485

 

Senscomp ultrasonic transducer http://www.senscomp.com/products.htm#transducers

 

Here is some formula that I had use in this project

 

SonicSpeed = (331.4 + (0.6 * SonicTemperature));

D = SonicSpeed * SonicOffsetNorthSouth;
Vn = D / SonicTimeNorthSouth;
Vn = Vn - SonicSpeed;

D = SonicSpeed * SonicOffsetEastWest;
Ve = D / SonicTimeEastWest;
Ve = Ve - SonicSpeed;

MeterPerSecond = sqrt(pow(Vn,2) + pow(Ve,2));
Tmp = abs(MeterPerSecond * 3.6);
SonicKmh = (Tmp + (4 * SonicKmh)) / 5;

if (SonicKmh > 2) SonicAngle = abs(atan2(Vn,Ve) * 180 / _PI);

 

Features

puce No mechanical part
puce Accuracy of 2km/h

 

Pictures

Click to enlarge

Transceiver

Wave

 

Sources codes & Schematics

-Schematic of the windsonic in PDF format

//*****************************************************************************
// WindSonic
// Version 1.0 Feb 2004
//
// 1.0 -> -Everything is new
//
// Sylvain Bissonnette
//*****************************************************************************
//
//                R E T U R N   S T A C K   128
//                      X T A L  16 MHZ
//                        No BootLoader
//
//*****************************************************************************
//
//                     F U S E   B I T
//
//( )7      ( )6     ( )BL12 ( )BL11  ( )BL02   ( )BL01    ( )Lock2   ( )Lock1
//( )7      ( )6     ( )     ( )      ( )       ( )M103C   ( )WDTON   ( )
//( )OCDEN  ( )JTAGE (X)SPIEN(X)CKOPT (X)EESAVE ( )BOOTSZ1 (X)BOOTSZ0 (X)BOOTRST
//(X)BODLEV (X)BODEN ( )SUT1 (X)SUT0  ( )CKSEL3 (X)CKSEL2  ( )CKSEL1  (X)CKSEL0
//
//*****************************************************************************
//                      P I N   U S A G E
//
// PA0 -> n/c
// PA1 -> n/c
// PA2 -> n/c
// PA3 -> n/c
// PA4 -> n/c
// PA5 -> n/c
// PA6 -> n/c
// PA7 -> n/c
//
// PB0 -> n/c
// PB1 -> n/c
// PB2 -> n/c
// PB3 -> n/c
// PB4 -> n/c
// PB5 -> n/c
// PB6 -> n/c
// PB7 -> n/c
//
// PC0 -> n/c
// PC1 -> n/c
// PC2 -> n/c
// PC3 -> n/c
// PC4 -> n/c
// PC5 -> n/c
// PC6 -> n/c
// PC7 -> n/c
//
// PD0 -> Anemometer Tx Weat
// PD1 -> Anemometer Tx South
// PD2 -> Anemometer Rx Select East
// PD3 -> Anemometer Rx Select North
// PD4 -> Anemometer Rx
// PD5 -> Anemometer Wind Temperature
// PD6 -> n/c
// PD7 -> n/c
//
// PE0 -> n/c
// PE1 -> n/c
// PE2 -> n/c
// PE3 -> n/c
// PE4 -> n/c
// PE5 -> n/c
// PE6 -> n/c
// PE7 -> n/c
//
//*****************************************************************************
//                      T I M E R   U S A G E
//
// Timer 0
// Timer 1 is use by sonic anemometer
// Timer 2
// Timer 3
//
//*****************************************************************************
//                         I N C L U D E
//*****************************************************************************
#include <iom128v.h>
#include <macros.h>
#include <stdlib.h>
#include <stdio.h>
#include <eeprom.h>
#include <math.h>
#include <shortnametype.h>

//*****************************************************************************
//                         D E F I N E
//*****************************************************************************
#define VERSION               10
#define TRUE                  1
#define FALSE                       0
#define XTAL                        16000000

#define SERIALSPEED              19200

#define SONIC_DDR                DDRD
#define SONIC_PIN                PIND
#define  SONIC_PORT              PORTD

#define SONIC_SOUND_NS        0x02
#define SONIC_SOUND_EW        0x01
#define SONIC_RX_NS              0x08
#define SONIC_RX_EW              0x04
#define SONIC_RX                 0x10

#define SONIC_SOUND              0x20
#define SONIC_TICKTIME        0.0000000625      // 62.5nS
#define SONIC_DISTANCE        0.3
#define SONIC_NORTH_SOUTH     0
#define SONIC_EAST_WEST       1

#define ONEWIRE_DDR        DDRD
#define ONEWIRE_PORT       PORTD
#define ONEWIRE_PIN        PIND
#define ONEWIRE_DEVICE        0x20

#define UART                        0

#if (UART == 0)
#define  UCSRA                      UCSR0A
#define  UCSRB                   UCSR0B
#define  UCSRC                      UCSR0C
#define  UBRRL                   UBRR0L
#define  UBRRH                      UBRR0H
#define  UDR                        UDR0
#endif                       

#if (UART == 1)              
#define  UCSRA                      UCSR1A
#define  UCSRB                   UCSR1B
#define  UCSRC                      UCSR1C
#define  UBRRL                   UBRR1L
#define  UBRRH                      UBRR1H
#define  UDR                        UDR1
#endif


//*****************************************************************************
//                      P R O T O T Y P E
//*****************************************************************************
void main(void);
void _StackOverflowed(char c);

float GetTemp(void);
void OneWireReset(ushort);
void OneWireWriteByte(ushort,ushort);
ushort OneWireReadByte(ushort);
void OneWireWaitBusy(ushort mask);
void OneWireCalcCRC(ushort *CRCVal,ushort value);
void OneWireDelay_500us(void);
void OneWireDelay_70us(void);
void OneWireDelay_7us(void);

void SonicInit(void);
void SonicStartSound(ushort Dir);
void SonicDebug(void);
void SonicEnableSonicInputCaptureINT(void);
void SonicPulseLostINT(void);
void SonicInputCaptureINT(void);

void SerialPortInit(void);
void TxString(char *Ptr);
void TxChar(unsigned char ch);

//*****************************************************************************
//                G L O B A L   V A R I A B L E
//*****************************************************************************
ushort SonicBusy,SonicDir,SonicCalibration;

float SonicTime,
      SonicTimeNorthSouth,
      SonicTimeEastWest,
      SonicOffsetNorthSouth,
      SonicOffsetEastWest,
      SonicTemperature,
      SonicKmh,
      SonicAngle,
      SonicSpeed;

//*****************************************************************************
//                         M A I N
//*****************************************************************************
void main()
{
int i,j;
ushort Dir;

SerialPortInit();

SEI();
SonicInit();

while (1)
     {
      SonicTemperature = GetTemp();
      SonicSpeed = (331.4 + (0.6 * SonicTemperature));
      while (SonicBusy == TRUE) WDR();
      SonicStartSound(Dir);
      while (SonicBusy == TRUE) WDR();
      Dir++;
      if (Dir > 1) Dir = 0;
         for (i=0;i<30000;i++);
      }
}

//*****************************************************************************
//        O N E W I R E     F U N C T I O N
//*****************************************************************************
/******************************************************************************

Name:         float GetTemp(void)

Description:  Get Temp

Input:        float -> Temp

Output:       float

Misc:

******************************************************************************/
float GetTemp()
{
   int i;

   ushort CRCValue,CRC;
   ushort byte[8];
   ushort negative;
   float Temp=0;
   static float LastTemp=0;

   OneWireReset(ONEWIRE_DEVICE);
   OneWireWriteByte(ONEWIRE_DEVICE,0xcc);
   OneWireWriteByte(ONEWIRE_DEVICE,0x44);

   OneWireWaitBusy(ONEWIRE_DEVICE);

   OneWireReset(ONEWIRE_DEVICE);
   OneWireWriteByte(ONEWIRE_DEVICE,0xcc);
   OneWireWriteByte(ONEWIRE_DEVICE,0xbe);

   CRCValue = 0x00;
   WDR();

   for (i=0;i<8;i++)
   {
      OneWireCalcCRC(&CRCValue,byte[i]=OneWireReadByte(ONEWIRE_DEVICE));
   }

   CRC = OneWireReadByte(ONEWIRE_DEVICE);

   if (CRC == CRCValue)
   {
      negative = FALSE;
      i = (byte[1] << 8) + byte[0];  //  temp contient la temperature reelle x 16
      if (i < 0)
      {
         negative = TRUE;
         i = -i;
      }
      i = (i * 10) / 16;
      if (negative == TRUE) i = -i;  //  Restore le signe.
      Temp = (float)i;
      Temp /= 10;
      LastTemp = Temp;
      return Temp;
   }
   else
   {
      return LastTemp;
   }
}

/******************************************************************************

Name:         ushort OneWireReset(ushort mask)

Description:  Send a reset on the buss,

Input:        ushort -> mask of the OneWireBuss

Output:       none

Misc:

******************************************************************************/
void OneWireReset(ushort mask)
{
  CLI();
  ONEWIRE_PORT &= ~(mask);          // Normal input no pull up
  ONEWIRE_DDR |= mask;              // out at 0
  SEI();
  OneWireDelay_500us();
  CLI();
  ONEWIRE_DDR &= ~(mask);           // Set to input
  OneWireDelay_500us();
  SEI();
}

/******************************************************************************

Name:         void OneWireWriteByte(ushort mask,ushort data)

Description:  Write a byte on the OneWire buss,

Input:        ushort -> mask of the OneWireBuss
              ushort -> data to be writen on the bus

Output:       none

Misc:

******************************************************************************/
void OneWireWriteByte(ushort mask, ushort data)
{
  ushort i;

  CLI();
  ONEWIRE_PORT &= ~(mask);
  SEI();
  for (i=0;i<=7;i++)
  {
    if (data & 0x01)
    {
      CLI();
      ONEWIRE_DDR |= mask;
      OneWireDelay_7us();        // Send 1
      ONEWIRE_DDR &= ~(mask);
      OneWireDelay_70us();
      SEI();
    }
    else
    {
      CLI();
      ONEWIRE_DDR |= mask;
      OneWireDelay_70us();     // Send 0
      ONEWIRE_DDR &= ~(mask);
      OneWireDelay_7us();
      SEI();
    }
    data>>=1;
  }
}

/******************************************************************************

Name:         ushort OneWireReadByte(ushort mask)

Description:  Read a byte on the OneWire buss,

Input:        ushort -> mask of the OneWireBuss

Output:       ushort -> byte read from the OneWireBuss

Misc:

******************************************************************************/
ushort OneWireReadByte(ushort mask)
{
  ushort data = 0;
  ushort i;

  CLI();
  ONEWIRE_PORT &= ~(mask);      // Output '0' or input without pullup
  SEI();
  for (i=0;i<=7;i++)
  {
    CLI();
    ONEWIRE_DDR |= mask;        // Set output to '0'
    OneWireDelay_7us();
    ONEWIRE_DDR &= ~(mask);     // Set to input
    OneWireDelay_7us();
    data >>=1;
    if (ONEWIRE_PIN & mask) data |= 0x80;
    else data &= 0x7f;
    SEI();
    OneWireDelay_70us();
  }
  return data;
}

/******************************************************************************

Name:         void OneWireWaitBusy(ushort mask)

Description:  Wait until the device is not busy

Input:        ushort -> mask of the OneWireBuss

Output:       none

Misc:

******************************************************************************/
void OneWireWaitBusy(ushort mask)
{
   uint TimeOut=0;

  while(OneWireReadByte(mask) != 0xff)
  {
   OneWireDelay_500us();
   if (TimeOut++ > 1000)   break;
  }
}

/******************************************************************************
Name:           void OneWireCalcCRC(uint8 *CRCVal, uint8 value)

Description:    Calcule the CRC of the string ponted by CRCVal

Input:          *CRCVal -> pointer to CRCValue
                Value   -> Value to calc the CRC


Output:         void

Misc:               
******************************************************************************/
void OneWireCalcCRC(ushort *CRCVal,ushort value)
{
  ushort odd,bcnt;

  for (bcnt=0 ; bcnt<8 ; bcnt++)
  {
    odd = (value ^ *CRCVal) & 0x01;
    *CRCVal >>=1;
    value >>=1;
    if (odd != 0) *CRCVal ^= 0x8c;
  }
}

/******************************************************************************

Name:         void OneWireDelay_500us(void)

Description:  Delay of 500 us

Input:        none

Output:       none

Misc:         For 16Mhz Xtal

******************************************************************************/
void OneWireDelay_500us(void)
{
  uint i;

   for (i=0;i<(int)(XTAL/17777L);i++) WDR();//900
}

/******************************************************************************

Name:         void OneWireDelay_70us(void)

Description:  Delay of 70 us

Input:        none

Output:       none

Misc:          For 16Mhz Xtal

******************************************************************************/
void OneWireDelay_70us(void)
{
  uint i;

   for (i=0;i<(int)(XTAL/133333L);i++) WDR();//120
}

/******************************************************************************

Name:         void OneWireDelay_7us(void)

Description:  Delay of 7 us

Input:        none

Output:       none

Misc:          For 16Mhz Xtal

******************************************************************************/
void OneWireDelay_7us(void)
{
  uint i;

  for (i=0;i<(int)(XTAL/1333333L);i++) WDR();//12
}

//*****************************************************************************
//                W i n d S p e e d   F u n c t i o n
//*****************************************************************************
/******************************************************************************

Name:       void SonicInit(void)

Description:   Initialization for the sonic anemometer           

Input:         none

Output:        none

Misc:       use port PD4 -> for Sound Reception
                   PD5 -> for Sound Generation
                   PD6 -> Selection of direction
                   PD7 -> Selection of direction
******************************************************************************/
void SonicInit(void)
{
unsigned char i,j;
float Temp;

SONIC_DDR |= SONIC_SOUND_NS + SONIC_SOUND_EW + SONIC_RX_NS + SONIC_RX_EW;
SonicBusy = FALSE;
SonicTemperature = 25.0;
SonicOffsetNorthSouth = 0.0009285f;
SonicOffsetEastWest   = 0.000932f;
}

/******************************************************************************

Name:       void SonicStartSound(char Dir)

Description:              

Input:         uchar Dir -> Direction to messure (NS,SN,EW,WE)

Output:        none

Misc:      
******************************************************************************/
void SonicStartSound(ushort Dir)
{
   int i,j;

  SonicDir = Dir;
   while (SonicBusy == TRUE) WDR();

   SonicBusy = TRUE;

   TCCR1A = 0x00;                                  // No compare toggle
   TCCR1B = 0x00;                                  // Stop Timer1
   OCR1A = 12800;                                  // Wait 800us before enabling Input Capture 12800
   OCR1B = 65530;                                  // If more than 2000us pulse lost 32000
   TIFR |= (1<<ICF1) + (1<<OCF1A) + (1<<OCF1B);     // Clear pending Interrupt
   TCNT1 = 0;                                      // Clear counter
   TCCR1B = (1<<ICNC1) + (1<<CS10);          // Start Timer with Prescaler / 1

   // Input capture falling and noise canceler

   if (Dir == SONIC_NORTH_SOUTH) // 0
   {
      SONIC_PORT &= ~(SONIC_RX_EW);
      SONIC_PORT |= SONIC_RX_NS;
      for (i=0;i<12;i++)                              // Send 3 pulse of 40khz 8
      {
         SONIC_PORT ^= SONIC_SOUND_NS;
         for (j=0;j<27;j++);
      }
   }

   if (Dir == SONIC_EAST_WEST)   // 1
   {
      SONIC_PORT &= ~(SONIC_RX_NS);
      SONIC_PORT |= SONIC_RX_EW;
      for (i=0;i<12;i++)                              // Send 3 pulse of 40khz 8
      {
         SONIC_PORT ^= SONIC_SOUND_EW;
         for (j=0;j<27;j++);
      }
   }
   TIMSK |= (1<<OCIE1A) + (1<<OCIE1B);    // Enable 500us mask time
   // Enable 1000us pulse lost timeout
}

/******************************************************************************

Name:       void SonicEnableSonicInputCaptureINT(void)

Description:   Enable the input capture after 800us
           
Input:         none

Output:        none

Misc:      
******************************************************************************/
#pragma interrupt_handler SonicEnableSonicInputCaptureINT:13
void SonicEnableSonicInputCaptureINT(void)
{
TIFR |= (1<<TICIE1);     // Clear pending any Input Capture Interrupt
TIMSK |= (1<<TICIE1);    // Enable Input Capture Interrupt
}

/******************************************************************************

Name:       void SonicPulseLostINT(void)

Description:   if more than 2000us the pulse is lost
           
Input:         none

Output:        none

Misc:      
******************************************************************************/
#pragma interrupt_handler SonicPulseLostINT:14
void SonicPulseLostINT(void)
{
TCCR1B = 0x00;                    // Stop Timer1
SonicBusy = FALSE;
}

/******************************************************************************

Name:       void SonicInputCaptureINT(void)

Description:   Pulse is received, make the calculation for wind speed and
            direction
           
Input:         none

Output:        none

Misc:      
******************************************************************************/
#pragma interrupt_handler SonicInputCaptureINT:12
void SonicInputCaptureINT(void)
{
float Time;
float Vn,Ve,D,MeterPerSecond,Tmp;

TCCR1B = 0x00;           // Stop Timer1
TIMSK &= ~(1<<TICIE1);   // Disable Input Capture interrupt

SonicTime = (SONIC_TICKTIME * ICR1);

if (SonicDir == SONIC_NORTH_SOUTH) SonicTimeNorthSouth = SonicTime;
else SonicTimeEastWest = SonicTime;

D = SonicSpeed * SonicOffsetNorthSouth;
Vn = D / SonicTimeNorthSouth;
Vn = Vn - SonicSpeed;

D = SonicSpeed * SonicOffsetEastWest;
Ve = D / SonicTimeEastWest;
Ve = Ve - SonicSpeed;

MeterPerSecond = sqrt(pow(Vn,2) + pow(Ve,2));
Tmp = abs(MeterPerSecond * 3.6);
SonicKmh = (Tmp + (4 * SonicKmh)) / 5;

if (SonicKmh  > 2) SonicAngle = abs(atan2(Vn,Ve) * 180 / _PI);

SonicBusy = FALSE;
SonicDebug();
}

/******************************************************************************

Name:       void SonicDebug()

Description:  

Input:         float MeterPerSecond
            float Angle

Output:        none

Misc:

******************************************************************************/
void SonicDebug()
{
char Buffer[40];
float Tmp;

sprintf(&Buffer[0],"%c[2H\0",0x1b);
TxString(&Buffer[0]);


sprintf(&Buffer[0],"Temp :%+03.1f   Deg\n\r\0",SonicTemperature);
TxString(&Buffer[0]);

sprintf(&Buffer[0],"Ang :%+03.1f   And\n\r\0",SonicAngle);
TxString(&Buffer[0]);

sprintf(&Buffer[0],"Kmh :%03.1f   Kmh\n\r\0",SonicKmh);
TxString(&Buffer[0]);

TxString("\n\r\0");

Tmp = (float)(SonicTimeNorthSouth * 1000000);
sprintf(&Buffer[0],"Time NS :%+03.0f   us\n\r\0",Tmp);
TxString(&Buffer[0]);

Tmp = (float)(SonicTimeEastWest * 1000000);
sprintf(&Buffer[0],"Time EW :%+03.0f   us\n\r\0",Tmp);
TxString(&Buffer[0]);

TxString("\n\r\0");

Tmp = (float)(SonicOffsetNorthSouth * 1000000);
sprintf(&Buffer[0],"Offset NS :%+05.2f   us\n\r\0",Tmp);
TxString(&Buffer[0]);

Tmp = (float)(SonicOffsetEastWest * 1000000);
sprintf(&Buffer[0],"Offset EW :%+05.2f   us\n\r\0",Tmp);
TxString(&Buffer[0]);
}

//*****************************************************************************
//     S E R I A L   P O R T   F U N C T I O N
//*****************************************************************************
/******************************************************************************

Name:       void SerialPortInit(char Port,int Speed)

Description:   Initialise the serial port

Input:         none

Output:        none

Misc:

******************************************************************************/
void SerialPortInit(void)
{
char Buffer[20];

UBRRH = 0x00;                             //set baud rate hi
UBRRL = (XTAL / (16 * SERIALSPEED)) - 1;    //set baud rate
UCSRA = 0x00;
UCSRB = (1<<RXEN0)+(1<<TXEN0);
UCSRC = (1<<UCSZ01) + (1<<UCSZ00);      // 8 bit

sprintf(&Buffer[0],"%c[2J\0",0x1b);
TxString(&Buffer[0]);
}

/******************************************************************************

Name:       void TxString(char *Ptr)

Description:   Send on the serial port the given string

Input:         Char * -> Pointer to the string       

Output:        none

Misc:

******************************************************************************/
void TxString(char *Ptr)
{
while(*Ptr != 0) TxChar(*Ptr++);
}

/******************************************************************************

Name:       void TxChar(uSONIC_NORTHSOUTHigned char)

Description:   Send a char on the serial port

Input:         char

Output:        none

Misc:

******************************************************************************/
void TxChar(unsigned char ch)
{
while (!(UCSRA & (1 << UDRE0))) WDR(); // Wait for empty traSONIC_NORTHSOUTHmit buffer
UDR = ch;                              // Write char
}