/* * meteo2.c ... firmware for the open hardware meteostation based on ATmega128 clocked at 14.7456MHz * * Copyright (C) 2010-2013 Jiri Pittner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * */ //device paramteres #define DEVICE_TYPE M2_METEOSTATION #define SERIAL 1 //repeat transmition of each datagram to prevent loss #define TXCOUNT 3 //PINOUT: //optional display //PC0-3=DB4-7 //PC4=E //PC5=RW //PC6=RS //PC7=light // //PB1,PE0,PE1,RESET ... in system programming port //PB7= 433 MHZ TX power //PB5=OC1A =433 MHZ modulation //PB4 anemo_CS //PB6 anemo signal // //PE5 rain gauge bucket signal INT5 //PE4 rain sensor INT4 (optional) //PE6 rain gauge SMT160 temperature sensor (optional) //PE3 rain gauge heater relay (optional) //PE7 - second smt160 (optional) //PE2 - power for second smt160 (optional) // //PF0 = ADC0 pressure sensor //PF1 = ADC1 pressure sensor reference for differential measurements (not used presently) //PF7 ADC7 photoresistor2 (optional) //PF6 ADC6 photoresistor1 //PF4 ADC4 sun sensor (optional) //PF5 =ADC5 input voltage 150k/33k divisor // //PD1 = SDA sht75_sda //PD0 = SCK sht75_sck //PD4 sht75_sda_pulldown transistor //PD5 sht75_power //PD2=UART1 RX //PD3=UART1 TX //calibration results //voltage stability has no direct impact on the meteorological measurements, //therefore no accurate reference is used; these two values are relevant only for checking the incoming voltage to the device //to detect possible power problems #define VCC5 4.962 #define VOLTAGE_DIVISOR (183./33.) //exact value of the pulldown resistor #define R_PHOTO1_GND 9987. //UART speed for debugging #define BAUD 115200 #include "backward.h" #include #include #include #include #include #include #include #include #include #include #include #include "meteo2.h" #if defined(at90s2313) || defined(at90s8535) #else #define ATmega #endif #if (XTAL == 14745600L) #define oversampling 4 #else #define oversampling 2 #endif #ifndef MCU #define emulate #else #define AVR_assembler #endif watchdog(uint8_t onoff) { if(onoff) {wdt_enable(WDTO_2S); wdt_reset();} else {wdt_reset();wdt_disable();} } #define itoa10(N,S) itoa(N,S,10) #define itoa16(N,S) itoa(N,S,16) void printP (PGM_P string){ char c; c=pgm_read_byte(string); while (c) { loop_until_bit_is_set(UCSR1A, UDRE); UDR1 = c; c=pgm_read_byte(++string); } return; } void print (char *string){ while (*string) { loop_until_bit_is_set(UCSR1A, UDRE); UDR1 = *string++; } return; } //UART initialize #define UART_INIT(baud) { \ UBRR1H=0; \ UBRR1L= (XTAL/baud+15)/16-1; \ UCSR1B=(1<0) //skip the first measurement after channel change to let it stabilize { if(mux>=8 && v>=512) v-=1024; //differential r += v; } } ADCSRA=0; sbi(SREG,7); return r; } uint16_t voltage(void) //integer, in 0.01 V { uint32_t v; v=adcread(5,16); float u= v*VCC5/1023.*VOLTAGE_DIVISOR/16; return (uint16_t) 100*u; } // pressure measurement routines #define N_PRESSURE 200 int32_t getpressure(uint8_t mux) //in pascal { //direct measurement of the sensor or of the reference, respectively int32_t r=adcread(mux<=1?mux:1,N_PRESSURE); float p0= r/1023./N_PRESSURE; p0 += 0.095; p0 /= 0.009; if(mux<=1) return (uint32_t) (1000.*p0); //do it the differential way employing analogue amplification if requested r=adcread(mux==2?9:11,N_PRESSURE); #if 0 { char c[64]; sprintf(c,"reference pressure=%.2f; difference readout=%d\n",p0*10,r); print(c); } #endif if(r>=512L*N_PRESSURE) r-= 1024L*N_PRESSURE; return (int32_t) 1000*(p0 - r/(0.009*1023.*N_PRESSURE*(mux==2?10.:200.))); } //CRC routines uint8_t crc8(uint8_t crc, uint8_t *data, uint16_t len, const uint8_t polynom) { for(; len>0; --len) { crc ^= *data++; uint8_t bit; for(bit=8; bit>0; --bit) { if(crc&0x80) crc = (crc<<1)^polynom; else crc <<=1; } } return crc; } uint8_t revbyte(uint8_t x)//reverse bit order { x= (x&0x55)<<1 | (x&0xaa)>>1; x= (x&0x33)<<2 | (x&0xcc)>>2; x= (x&0x0f)<<4 | (x&0xf0)>>4; return x; } #define POLY 0x8408 /* The crc16 routine has been downloaded from a public domain internet source 16 12 5 this is the CCITT CRC 16 polynomial X + X + X + 1. This works out to be 0x1021, but the way the algorithm works lets us use 0x8408 (the reverse of the bit pattern). The high bit is always assumed to be set, thus we only use 16 bits to represent the 17 bit value. */ uint16_t crc16(uint8_t *data_p, uint16_t length) { unsigned char i; unsigned int data; unsigned int crc = 0xffff; if (length == 0) return (~crc); do { for (i=0, data=(uint16_t)0xff & *data_p++; i < 8; i++, data >>= 1) { if ((crc & 0x0001) ^ (data & 0x0001)) crc = (crc >> 1) ^ POLY; else crc >>= 1; } } while (--length); crc = ~crc; data = crc; crc = (crc << 8) | (data >> 8 & 0xff); return (crc); } #undef POLY // temperature and humidity routines uint8_t shtcomm(uint8_t x, uint16_t *readout) { uint8_t data[4]; data[0]=x; int8_t i,j; TWCR=0; sbi(DDRD,PD0); //SCL sbi(DDRD,PD4); //SDA pulldown sbi(PORTD,PD0);cbi(PORTD,PD4); delay_sht(); for(i=0; i<10; ++i) {cbi(PORTD,PD0);delay_sht();sbi(PORTD,PD0);delay_sht();} sbi(PORTD,PD4); //pull SDA LOW delay_sht(); cbi(PORTD,PD0); //scl low delay_sht(); sbi(PORTD,PD0); //scl high delay_sht(); cbi(PORTD,PD4); //sda high delay_sht(); cbi(PORTD,PD0); //scl low delay_sht();delay_sht();delay_sht(); for(i=7; i>=0; --i) { if(x&(1<=0; --i) { sbi(PORTD,PD0); //scl high delay_sht(); data[j] |= (PIND>>1)&1; if(i) data[j]<<=1; cbi(PORTD,PD0); //scl low delay_sht(); } //generate Ack sbi(PORTD,PD4); //sda low delay_sht(); sbi(PORTD,PD0); //scl high delay_sht(); cbi(PORTD,PD0); //scl low delay_sht(); cbi(PORTD,PD4); //sda high } cbi(DDRD,PD0);cbi(DDRD,PD4); *readout= data[1]; *readout<<=8; *readout|= data[2]; //check CRC calculated from the command byte and two output bytes, assuming status register is the default 0 uint8_t crc=revbyte(crc8(0,data,3,0x31)); #if 0 char c[64]; sprintf(c,"CRC %x %x\n",crc,data[3]); print(c); #endif return crc-data[3]; //0 is OK } int16_t temperature(int16_t *h) //temperature in 0.01C { //power up sensor sbi(DDRD,PD5); sbi(PORTD,PD5); delay_ms(30); //wait for temperature sensor to stabilize int16_t t=M2_UNDEFINED_TEMP; *h=M2_UNDEFINED_HUMID; uint16_t readout; //read temperature if(shtcomm(3,&readout)) goto end; #if 0 printP(PSTR("temperature readout = 0x")); { char c[8]; itoa16(readout,c); print(c);print("\n"); } #endif //calibrate (cf. SHT75 datasheet) t = readout; t -= 4010; float temp=0.01*t; //read humidity if(shtcomm(5,&readout)) goto end; #if 0 printP(PSTR("humidity readout = 0x")); { char c[8]; itoa16(readout,c); print(c);print("\n"); } #endif //calibrate non-linearity of humidity sensor (cf. SHT75 datasheet) float hum = ((-1.5955e-6*readout)+0.0367)*readout-2.0468; //calibrate temperature dependence hum += (temp-25.)*(0.01+0.00008*readout); *h = (int16_t) (10.*hum); //power down sensor to prevent self-heating end: cbi(PORTD,PD5); return t; } //anemometer and rain gauge routines uint8_t heater=0; void anemo_reset(void) { cbi(PORTE,PE3); sbi(DDRE,PE3); heater=0; //heater off since we will not measure temperature cbi(DDRB,PB6); cbi(PORTB,PB6);//input without pullup sbi(DDRB,PB4); sbi(PORTB,PB4); // negative device select uint8_t i; for(i=0; i<20;++i) {watchdog(1); delay_ms(500);} sbi(DDRB,PB4); cbi(PORTB,PB4); // negative device select for(i=0; i<20;++i) {watchdog(1); delay_ms(500);} } #define DELAY_ONE delay_xs(XTAL/4*1.21e-3) #define DELAY_HALF delay_xs(XTAL/4*1.21e-3/2) uint8_t read_raw(uint64_t *msg) //returns error if it cannot read message { uint8_t i; *msg = 0; { for(i=0; i<40; ++i) { uint32_t l; watchdog(1); for(l=100000; l>0; --l) if(bit_is_clear(PINB,PB6)) goto active; } return 9; active:; } watchdog(1); DELAY_HALF; for(i=0; i<41; ++i) { if(bit_is_set(PINB,PB6)) *msg |= (1LL<=5 && i%4 == 1) print(" "); print((raw>>i)&1?"1":"0");} printP(PSTR("\n"));} #endif if((raw&0x1fLL)!=4) return 1; //error raw>>=5; //header *dir = check = raw&0x0fLL; raw>>=4; *speed = raw&0x0fffLL; raw>>=4; raw>>=4; raw>>=4; uint8_t checksum = raw&0x0fLL; uint8_t mycheck = *dir + *speed + (*speed>>4) + (*speed>>8); mycheck &= 15; if(checksum!=mycheck) return 3; raw>>=4; uint8_t invdir = raw&0x0fLL; raw>>=4; uint16_t invs = raw&0x0fffLL; if(*dir != (invdir^0x0f)) return 2; if((invs^0x0fff) != *speed) return 4; if(*speed >= 2048) return 5; //unphysical #if 1 char c[8]; printP(PSTR("Direction = ")); itoa10(*dir,c);print(c); printP(PSTR(" Speed = ")); itoa10(*speed,c);print(c); printP(PSTR("\n")); #endif return 0; } #define TEMPPORT PINE #define TEMPPORTOUT PORTE #define TEMPPORTDDR DDRE #define TEMPPIN PE6 uint8_t check_thermo() { cbi(TEMPPORTDDR,TEMPPIN); sbi(TEMPPORTOUT,TEMPPIN); //pullup to prevent random noise uint8_t i,last,l; watchdog(1); last= bit_is_set(TEMPPORT,TEMPPIN); for(i=0; i<100; ++i) { delay_xs(XTAL/8000); if((l=bit_is_set(TEMPPORT,TEMPPIN))^last) {cbi(TEMPPORTOUT,TEMPPIN); return 1;} last=l; } cbi(TEMPPORTOUT,TEMPPIN); return 0; } //SMT160 temperature in rain gauge int16_t temperature_smt0(void) // thermometer returns in 0.005 C { int16_t t; uint8_t i; uint16_t lcount,hcount; watchdog(1); //maybe it sometimes hangs here??? //SMT160-30 duty=0.320+0.00470*T, T=(duty-0.32)/0.00470 lcount=hcount=0; //interrupts have not been disabled intentionally, catching a rain bucket has a higher priority than the accuracy of temperature { uint8_t i; for(i= #if (XTAL == 3686400L) 0 #else #if (XTAL == 14745600L) 64 #else 128 #endif #endif ; --i;) { //optimized compilation results in irreproducible and wrong timings, therefore inline assembly asm volatile ( //loop_until_bit_is_set(TEMPPORT,TEMPPIN); "TEMP1:\n\t" "sbis %4,%5" "\n\t" "rjmp TEMP1" "\n\t" //loop_until_bit_is_clear(TEMPPORT,TEMPPIN); "TEMP2:\n\t" "sbic %4,%5" "\n\t" "rjmp TEMP2" "\n\t" //while(bit_is_clear(TEMPPORT,TEMPPIN)) ++lcount; "TEMP3:\n\t" "adiw %1,1" "\n\t" "sbis %4,%5" "\n\t" "rjmp TEMP3" "\n\t" //while(bit_is_set(TEMPPORT,TEMPPIN)) ++hcount; "TEMP4:\n\t" "adiw %0,1" "\n\t" "sbic %4,%5" "\n\t" "rjmp TEMP4" "\n\t" //parameter lists : "=w" (hcount), "=w" (lcount) //outputs : "0" (hcount), "1" (lcount), "M" ((uint8_t) &(TEMPPORT) - 0x20), "I" (TEMPPIN) //inputs : "cc" //clobber ); } } #if 0 { uint32_t x; x=10000; x*=hcount; x /=((uint32_t)hcount+lcount); char c[8]; itoa10(x,c); printP(PSTR("10000*Duty=")); print(c); printP(PSTR("\n")); } #endif uint32_t x; x=68085L; x*=hcount; x /=((uint32_t)hcount+lcount); x*=10; int32_t y = x-217870; y>>= 4; return (int16_t) y; } #define NREP_SMT_LOG 5 int16_t temperature_smt(void) //in 0.01C { uint8_t i; int32_t t=0; for(i=0; i<(1<>= 1+NREP_SMT_LOG; return t; } //rain gauge interrupt static volatile uint8_t nbuckets =0; static volatile uint32_t counter2 =0; INTERRUPT(SIG_INTERRUPT5) //set as falling edge interrupt { #if (XTAL >= 14745600L) if(counter2<50) return; #else if(counter2<25) return; //too short time since last bucket - software debounce #endif if(bit_is_clear(PINE,PE5)) { UDR1='R'; ++nbuckets; //check the bit a few cycles after the edge interrupt to avoid spurious ultrashort impulses TCNT2=0; counter2=0; } else UDR1='?'; //indicate spurious edge return; } INTERRUPT(SIG_OVERFLOW2) { ++counter2; } // wireless communication routines //for testing the RF amplifier void txcw(void) { sbi(DDRB,PB5); sbi(DDRB,PB7); sbi(PORTB,PB5); sbi(PORTB,PB7); uint8_t i; for(i=0; i<60; ++i) {watchdog(1); delay_ms(1000);} cbi(PORTB,PB5); cbi(PORTB,PB7); } //transmit routines volatile uint8_t outcode[sizeof(METEO2_DATAGRAM)]; volatile uint8_t outcodelen=0; //0 indicates ready to receive, non-zero indicates ready to read it in main volatile uint8_t waittime=0; volatile uint8_t txcount=0; //repeat several times volatile static uint8_t obitcount=0, obytecount=0; //working counters typedef enum {init,oupmark,downmark,data,trailer,wait} OSTATE; static OSTATE ostate=init; static uint8_t ocount=0; //modulation like in keeloq remote controls void txcar(void) { switch (ostate) { case wait: if(--waittime) return; case init: //the user has just written the request to transmit //check maxcodelen ocount=13; ostate=oupmark; cbi(TIMSK,TOIE1); ICR1=(uint16_t)(XTAL*8./10000+.5)-1; OCR1A=((uint16_t)(XTAL*4./10000+.5)-1); TCNT1=0; cbi(TCCR1A,COM1A0); cbi(TCCR1A,COM1A1); sbi(TIMSK,TOIE1); sbi(TIMSK,OCIE1A); break; case oupmark: if(--ocount) {sbi(PORTB,PB5); return;} cbi(PORTB,PB5); cbi(TIMSK,TOIE1); TCNT1=0; ICR1=(uint16_t)(XTAL*6.1/10000+.5)-1; sbi(TIMSK,TOIE1); ostate=downmark; ocount=2; break; case downmark: if(--ocount) return; obitcount=obytecount=0; ostate=data; cbi(TIMSK,TOIE1); ICR1=(uint16_t)(XTAL*12./10000+.5)-1; sbi(TIMSK,TOIE1); break; case data: { uint8_t n; n=obitcount+(obytecount<<3); if(n>outcodelen+1) { //data finished cbi(PORTB,PB5); cbi(TIMSK,OCIE1A); ostate=trailer; ocount=13; OCR1A=0; } else { if(n) sbi(PORTB,PB5); //avoid spurious first peak if(n>outcodelen) { OCR1A=((uint16_t)(XTAL*12./10000+.5)-1)*1/3; ++obitcount; } else { OCR1A=((uint16_t)(XTAL*12./10000+.5)-1)* ((outcode[obytecount]& (1<counter= ++counter; #endif //compute CRC msg->crc = crc16((void *)msg,sizeof(METEO2_DATAGRAM)-2); //little endian assumed here as well as on the receiver //wait for end of previous transmit or switch on transmitter if(txcount || outcodelen) while(txcount || outcodelen) {delay_ms(10); watchdog(1);} else do_transmit(1); watchdog(1); delay_ms(50); //stabilize transmitter //transmit the code a few times memcpy(outcode,msg,sizeof(METEO2_DATAGRAM)); txcount=count; outcodelen=sizeof(METEO2_DATAGRAM)*8; //leave the receiver enough time to process individual packets watchdog(1); delay_ms(1000); watchdog(1); delay_ms(1000); watchdog(1); delay_ms(1000); } void txping(void) { METEO2_DATAGRAM msg; msg.device_type=M2_METEOSTATION; msg.serial=SERIAL; msg.report_type=M2_PING; uint16_t i; for(i=0; i<1024; ++i) { msg.arg2= i; transmit(&msg,TXCOUNT); } } //////////////////////////////////////////////////////// //rain gauge heater on/off thresholds in 0.01C static int16_t hyst_on __attribute__ ((section(".eeprom"))) = 350; static int16_t hyst_off __attribute__ ((section(".eeprom"))) = 450; //averaging definitions #define N_AVER_PRESSURE 32 #define N_AVER_LIGHT 128 #define N_LIGHT 128 #define N_AVER_WIND 64 main() { cbi(PORTE,PE3); sbi(DDRE,PE3); heater=0; sbi(SREG,7); UART_INIT(BAUD); {char dummy=UDR1;} sbi(UCSR1B,RXCIE); delay_ms(10); printP(PSTR("Reset!\n")); //check endianity { union {int i; char z[sizeof(int)];} u; u.i=1; if(u.z[0]!=1) printP(PSTR("Something is wrong, AVR/GCC should be little endian - swap bytes in transmit()!\n")); } //check presence of anemometer anemo_reset(); uint8_t anemo_present; anemo_present = check_anemo(); if(!anemo_present) printP(PSTR("NO ")); printP(PSTR("anemometer found\n")); //check presence of thermometer in the rain gauge uint8_t thermo_present; thermo_present = check_thermo(); if(!thermo_present) printP(PSTR("NO ")); printP(PSTR(" r.g. thermometer found\n")); //start timer2 TCNT2=0; TCCR2=5; //CK1024 sbi(TIMSK,TOIE2); //overflow interrupts //start raing gauge interrupt INT5, no pullup cbi(DDRE,PE5); cbi(PORTE,PE5); delay_ms(10); sbi(EICRB,ISC51); cbi(EICRB,ISC50); sbi(EIMSK,INT5); //falling edge METEO2_DATAGRAM msg; msg.device_type=M2_METEOSTATION; msg.serial=SERIAL; //transmit reset report msg.report_type=M2_RESET; msg.arg1= anemo_present; msg.arg2= thermo_present; transmit(&msg,TXCOUNT); //variables to detect changes int16_t tempold= M2_UNDEFINED_TEMP; int16_t temprainold= M2_UNDEFINED_TEMP; int16_t humidold= M2_UNDEFINED_HUMID; int32_t oldpressure= 0; int32_t oldohms= 0; int32_t sumpressure = 0; uint16_t npressure=0; uint16_t nlight=0; int32_t sumlight=0; volatile uint8_t nrain; uint8_t heater_count=0; uint8_t dirmax=0; uint16_t speedmax=0; float sumspeedx=0., sumspeedy=0.; uint16_t nanemo=0; uint16_t voltsold=0; //count problems with anemometer uint16_t nbad=0; uint16_t pass=0; uint16_t rainpass=0; while(1) { ++pass; ++rainpass; //wait for end of transmit and switch off transmitter then while(txcount || outcodelen) { watchdog(1); delay_ms(10); } do_transmit(0); watchdog(1); //process commands from UART if(inuart[inuartlen-1]== '\n'||inuart[inuartlen-1]=='\r') { inuart[inuartlen]=0; inuart[inuartlen-1] = '\n'; print(inuart); //execute the command switch(inuart[0]) { case 'h': { if(strncmp(inuart,"high=",5)) break; int16_t t; char c[8]; t=atoi(inuart+5); itoa10(t,c); printP(PSTR("HYST_OFF set to ")); print(c); printP(PSTR("\n")); eeprom_write_word(&hyst_off,t); } break; case 'l': { if(strncmp(inuart,"low=",4)) break; int16_t t; char c[8]; t=atoi(inuart+4); itoa10(t,c); printP(PSTR("HYST_ON set to ")); print(c); printP(PSTR("\n")); eeprom_write_word(&hyst_on,t); } break; case 'T': printP(PSTR("testing transmitter by CW\n")); txcw(); break; case 'P': printP(PSTR("testing transmitter by PING\n")); txping(); break; default: printP(PSTR("Unknown command: "));; print(inuart); break; } //flush buffer and allow uart receive again cbi(UCSR1B,RXCIE); inuartlen=0; sbi(UCSR1B,RXCIE); watchdog(1); } //perform measurements char c[128]; watchdog(1); int16_t temp; int16_t humid; temp=temperature(&humid); int16_t temprain; if(thermo_present) temprain =temperature_smt(); else temprain = M2_UNDEFINED_TEMP; int32_t pressure =getpressure(0); uint32_t light= adcread(6,N_LIGHT); uint16_t volts= voltage(); uint8_t dir; uint16_t speed; uint8_t r_anemo=0; if(anemo_present) r_anemo=read_anemo(&dir,&speed); if(r_anemo) {sprintf(c,"anemo returned %d\n",r_anemo); print(c);} if(r_anemo) ++nbad; else nbad=0; // reset it when OK //process measurement results //SHT sensor if(temp!=M2_UNDEFINED_TEMP && humid != M2_UNDEFINED_HUMID) { sprintf(c,"T= %.2fC H=%.1f%%\n",0.01*temp,0.1*humid); print(c); if( ((temp-tempold)>=15 || (temp-tempold)<=-15 || (humid-humidold)>=15|| (humid-humidold)<=-15) || (pass&0x1f)==0) //report every 15 minutes or when change { msg.device_type=M2_METEOSTATION; msg.serial=SERIAL; msg.report_type=M2_TEMP_HUMID; msg.arg2=humid; msg.arg3=temp; transmit(&msg,TXCOUNT); tempold=temp; humidold=humid; printP(PSTR("transmit temperature\n")); } } //pressure sensor sumpressure += pressure; ++npressure; if(npressure == N_AVER_PRESSURE) { pressure = sumpressure/N_AVER_PRESSURE; npressure=0; sumpressure=0; sprintf(c,"P= %.2fhPa\n",pressure*0.01); print(c); { msg.report_type=M2_PRESSURE; msg.arg1= humid/10; msg.arg2=temp; msg.arg3=pressure; transmit(&msg,TXCOUNT); oldpressure=pressure; printP(PSTR("transmit pressure\n")); } } //light - report ohms of the photoresistor ++nlight; sumlight += light; if(nlight == N_AVER_LIGHT) { float ohms = 1023.*R_PHOTO1_GND*N_LIGHT*N_AVER_LIGHT/sumlight - R_PHOTO1_GND; sprintf(c,"R = %g ohms\n",ohms); print(c); sumlight=0; nlight=0; msg.report_type=M2_LIGHT; if(sizeof(uint32_t) == sizeof(float)) { memcpy(&msg.arg3,&ohms,sizeof(uint32_t)); //raw IEEE float is transmitted transmit(&msg,TXCOUNT); printP(PSTR("transmit light\n")); } } //anemometer if(anemo_present && r_anemo==0) { if(speed>speedmax) {speedmax=speed; dirmax=dir;} sumspeedx += speed * cosf(2*M_PI*dir/16); sumspeedy += speed * sinf(2*M_PI*dir/16); ++nanemo; //debug { char c[32]; sprintf(c,"anemo samples %d\n",nanemo); print(c); } if(nanemo==N_AVER_WIND) // transmit - N_AVER_WIND is large enough not to be too frequent { //compute and transmit average (256* integer sum and direction) sumspeedx *= 256./N_AVER_WIND; sumspeedy *= 256./N_AVER_WIND; float fspeed = sqrtf(sumspeedx*sumspeedx + sumspeedy*sumspeedy); float tmp= fspeed==0.?0.:atan2f(sumspeedy,sumspeedx); float fdir = (tmp<0.?2.*M_PI+tmp:tmp)/2./M_PI*16*256; msg.report_type=M2_WIND; msg.arg1=0; msg.arg2=(uint16_t) fdir; msg.arg3=(uint32_t) fspeed; transmit(&msg,TXCOUNT); printP(PSTR("transmit anemo\n")); while(txcount || outcodelen) {delay_ms(10); watchdog(1);} watchdog(1); delay_ms(1000); //separate the two transmits watchdog(1); delay_ms(1000); //separate the two transmits watchdog(1); delay_ms(1000); //separate the two transmits //transmit maximum msg.report_type=M2_WIND_GUSTS; msg.arg1=0; msg.arg2=(uint16_t) dirmax <<8; msg.arg3=(uint32_t) speedmax<<8; transmit(&msg,TXCOUNT); printP(PSTR("transmit anemo gusts\n")); //zero sums nanemo=0; dirmax=0; speedmax=0; sumspeedx= sumspeedy=0.; } } //rain gauge if(thermo_present) {sprintf(c,"Train= %.2fC\n",0.01*temprain); print(c);} if( ((temprain-temprainold)>=10 || (temprain-temprainold)<=-10) || (pass&0x7f)==0 || rainpass>1023 || nbuckets > 0) { cbi(SREG,7); nrain=nbuckets; nbuckets=0; sbi(SREG,7); char c[32]; sprintf(c,"Rain = %d\n",nrain); print(c); msg.report_type=M2_RAIN; msg.arg1= nrain; msg.arg2= temprain; transmit(&msg,TXCOUNT+(nrain>0?5:0)); //message about a bucket must not be lost printP(PSTR("transmit temp+rain\n")); temprainold=temprain; nrain=0; rainpass=0; } //control heater relay with some hysteresis and max duty cycle if(thermo_present) { if(heater==0 && temprain < (int16_t) eeprom_read_word(&hyst_on) ) {printP(PSTR("heater on\n")); sbi(PORTE,PE3); heater=1; heater_count=0; } if((heater==1 || bit_is_set(PORTE,PE3)) && temprain > (int16_t) eeprom_read_word(&hyst_off) ) {printP(PSTR("heater off\n")); cbi(PORTE,PE3); heater=0;} if(heater==1) //manage duty cycle { if((heater_count&31) < 24) sbi(PORTE,PE3); else cbi(PORTE,PE3); ++heater_count; } else cbi(PORTE,PE3); } //voltage sprintf(c,"U=%.2fV\n",0.01*volts); print(c); if(volts>voltsold+100 || voltsold>volts+100 || (pass&0x3ff)==0 ) { voltsold=volts; msg.report_type=M2_VOLTAGE; msg.arg2= volts; transmit(&msg,TXCOUNT); printP(PSTR("transmit voltage\n")); } //reset anemometer if it stops working if(nbad>=50) { printP(PSTR("too many anemo errors, anemo_reset\n")); anemo_reset(); anemo_present=check_anemo(); msg.report_type=M2_WIND; msg.arg1=1; msg.arg2=anemo_present; transmit(&msg,TXCOUNT); printP(PSTR("transmit anemo\n")); } //check if anemo has been connected in the meantime if(!anemo_present && (pass & 255 )==0 ) anemo_present=check_anemo(); watchdog(1); //delay_ms(1000); no, would be too late to read next anemo message } }