Wind Sonic
First note
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
|
|
No mechanical part |
|
|
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
}