// ECE 4760 Final Project - Charles Leeper and Michael Cirineo - Spring 2009 // Weather Canvas (Code for outdoor unit) // This code is for the ATmega644 contained in the weather station outside. // The weather station cycles through four different sensors at an interval // specified by the "data_interval" definiton below. If the interval is one // minute (60000, since definition is in ms), the station will gather data // from all four sensors once every minute. These sensors include: // 1. Temperature sensor - LM34 with voltage output (10 mV / deg F), fed into ADC7. // 2. Humidity sensor - Honeywell HCH-1000-001 with capacitance output - for // this sensor, we need to measure its capacitance, which varies with // relative humidity (~330 pF @ 55% RH). Use the capacitance measurement // technique from Lab 1. // 3. Solar panel - PWR1241 Sun Power (x2) recharges batteries and measures intensity. // Using the voltage drop over a 1-ohm shunt resistor placed on the ground // side of the solar panel, we measure sunlight intensity (ADC2). // Code also monitors the 4xAA battery voltage on ADC6, turns off a BUZ71 // when batteries are full to block charging. // 4. Wind gauge - Homemade "Dinosaur Egg" Anemometer generates a voltage by // spinning a DC motor. This voltage is amplified and fed into ADC0. // 5. Doppler Radar rain gauge - Hot Wheels Radar Gun, interfaced so this code // can turn on the radar for number of seconds specified in "radar_timer," // detect any rain falling in its line of sight. Smaller drops are focused // with a funnel and result in 5-6 mph reading. Larger drops fall from higher // above and are represented by a 10+ mph reading. The output from the radar's // analog chain is an input to our ADC1. // **** I/O SUMMARY ************************** // Temperature - ADC7 // Humidity - B.2 and B.3 // Solar Panel - voltage output, ADC2 // Battery voltage, ADC6 // BUZ71 gate, B.7 // Wind Gauge - (+)ADC1, (-)ADC0 (x10) // Radar - ADC5, B.6 trigger, B.5 off trigger // // TRANSMIT - B.1 Data, B.0 Valid Tx (VT) // ******************************************* // // After reading all four sensors, the code prepares and transmits the data over // the TXE-418-KH encoder/transmitter IC. #include #include #include #include #include #include #include #include #include #include "uart.h" // UART file descriptor // putchar and getchar are in uart.c FILE uart_str = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW); void initialize(void); // all the usual mcu stuff void temperature(void); // temperature task void humidity(void); // humidity task void solar(void); // solar task void battery(void); // battery task void wind(void); // wind task void radar(void); // radar task void send(void); // send the info to indoor unit volatile unsigned int wait, radar_timer, radar_sample_timer, wind_timer, wind_sample_timer, humid_timer, tx_timer, radar_turnoff_timer; // timing variables (wait is the primary main() task scheduler timer) // all the task-done indicators unsigned char temp_ready, temp_done, humidity_done, solar_done, battery_done, wind_done, radar_done, sending_done; // weather conditions - threshold-exceeded indicators unsigned int rain_drops, raining, lowbat, low_wind_count, med_wind_count, high_wind_count; // transmission bits unsigned char temperature1, temperature2, sunlight1, sunlight2, wind1, wind2, humidity1, humidity2, rain1, rain2; int Ain, tx_bit; double temp, rh, sunlight, battery_voltage, wind_speed; #define pause_at_start 3000 // time to pause when first turned on #define data_interval 50000 // down time (~60sec) between 4-measurement bursts #define measurement_interval 1000 // small interval (~1sec) between measurements #define quarter_second 250 // amount of time to run capacitance measurement #define radarONlength 15000 // how long is radar turned on for during each measurement cycle? (~20-30sec) #define radar_rate 1 // how often does ADC grab radar output when radar is turned on (~1-100ms) #define rain_ADC_threshold 125 // ADC radar conversions above this number represent rain...more above = more rain #define rain_drops_threshold 14 // how many ADC conversions above rain_ADC_threshold are required to confirm rain #define radar_offtime 40000 // how long before it's safe to power down entire MCU (so radar has at least 20 secs to power down) #define windONlength 15000 // how long is wind ADC run #define wind_rate 500 // how often is wind ADC run during ADC-on-time #define wind_threshold_low 3 // categorize wind according to three thresholds (mph) #define wind_threshold_med 5 #define wind_threshold_high 7 #define wind_count_threshold 4 // how many instances above *_ADC_threshold needed to confirm low,med,or high wind? #define temp_coeff 0.440392 // ADC reference voltage coeff. for temperature (150/( (3V/refvolt) *255)) #define humidity_coeff 1 // convert capacitance into relative humidity (~330pF = 55% RH) (if desired, 1 for now) #define solar_coeff 1//0.051 // convert solar ADC value into something (if desired, 1 for now) #define bat_coeff 0.04935 // convert raw ADC value to battery voltage #define wind_coeff 0.431373 // convert raw ADC wind voltages into mph (gauge is calibrated such that mV ~ mph) Vin = 1000*(1.1V/(255*gain))*ADC #define radar_coeff 1 // convert raw ADC radar voltages into mph (if desired, 1 for now) // weather condition thresholds for decision making #define lowtemp_thresh 60 //degrees #define medtemp_thresh 70 //degrees #define hightemp_thresh 80 //degrees #define lowrh_thresh 400 //humidity (relative scale: 55%rh = 460) #define medrh_thresh 460 #define highrh_thresh 500 #define night_thresh 10 #define medsunlight_thresh 40 #define highsunlight_thresh 60 #define low_batt_thresh 3 //volts #define tx_length 400 //number of milliseconds to transmit each bit of 8-bit picture-decider sequency, //then wait for same amount before transmitting next bit (8-bit bursts occur at the 'data_interval') #define begin { #define end } // Humidity (capacitance measurement) variables / definitions #define R2 100000 double Capac; // timer 1 capture variable volatile unsigned int T1capture, T1overflow; // ********************************************************** // Timer 0 overflow ISR ISR (TIMER0_COMPA_vect) begin //Decrement the time if not already zero if (wait > 0) --wait; if (radar_timer > 0) --radar_timer; if (radar_sample_timer > 0) --radar_sample_timer; if (wind_timer > 0) --wind_timer; if (wind_sample_timer > 0) --wind_sample_timer; if (humid_timer > 0) --humid_timer; if (tx_timer > 0) --tx_timer; if (radar_turnoff_timer > 0) --radar_turnoff_timer; end // ********************************************************** // Central scheduler - start with temp (need to start ADC0 conversion for temp in main), // wait measurement_interval, do humidity task, wait, do solar task, wait, // do wind task, wait, start data_interval and 20-30 second radar task, then wait // remainder of data_interval and repeat. // Each task prepares the necessary ADC conversion for the next (main() starts the first one) // Don't keep ADC on during wait time (radar turns ADEN off) int main(void) begin initialize(); // Measurement tasks scheduler loop while(1) begin //start conversion for temp. (don't keep ADC on!) ... then use "if (ADIF) if (wait == 0 && sending_done == 1) begin sending_done = 0; //proceed with the new round of measurements // Turn on ADC conversion for the first measurement, temperature ADMUX = 0b10100111; // bits 7:6: (REFS1,REFS0) reference voltage // (0,0) is external Aref (connect Aref jumper, when using STK500) // (0,1) when using protoboard (AVCC with external capacitor at AREF pin) // (1,0) is 1.1V internal ref voltage // (1,1) is 2.56V internal ref voltage // bit 5: ADC left adjust result (ADLAR = 1) // bits 4:0: Channel select (MUX4:0 = 00111 for ADC CHANNEL 7, ADC7) // Start a new conversion (temperature) ADCSRA |= (1<= lowtemp_thresh && temp < medtemp_thresh) {temperature1 = 0; temperature2 = 1;} //cool else if (temp >= medtemp_thresh && temp < hightemp_thresh) {temperature1 = 1; temperature2 = 0;} //warm else if (temp > hightemp_thresh) {temperature1 = 1; temperature2 = 1;} //hot fprintf(stdout, "Temperature transmission: %d %d\n\r", temperature1, temperature2); //Trigger radar (do it here so early voltage spikes are discarded) PORTB = PORTB | 0b01000000; // B.6 = 1 PORTB = PORTB & 0b11011111; // B.5 = 0 PORTB = PORTB & 0b01111111; // disconnect solar panel in preparation for solar voltage reading (in 2 seconds) temp_done = 1; // note: the next measurement, humidity, does not require an ADC conversion, or else we'd start it here end // ********************************************************* void humidity(void) begin temp_done = 0; // Turn on ADC conversion for the third measurement, sunlight intensity ADMUX = 0b10100010; // bits 7:6: (REFS1,REFS0) reference voltage // (0,0) is external Aref (connect Aref jumper, when using STK500) // (0,1) when using protoboard (AVCC with external capacitor at AREF pin) // bit 5: ADC left adjust result (ADLAR = 1) // bits 4:0: Channel select (MUX4:0 = 01101 for ADC CHANNEL 3(+)/2(-), WITH 10x GAIN) // Start a new conversion (sunlight intensity) ADCSRA |= (1<0) begin if (humid_timer == 0) begin // MUST connect port B.3 (AIN1) to port D.7 (OC2) // timer1 capture ISR will compute a period from the capture // Capture yields cycle-accurate period measurement //make B.2 an output and pull it down to 0 DDRB = DDRB | 0x04; //(1 << PINB2);// PORTB = PORTB & 0b11111011; //Compute capacitance based on C = t / R2 //T1capture is in clock ticks, so multiply by 6.25e-8 to get seconds //Answer is very small, so multiply by 10^12 to get picoFarads //In the following line, 6.25e-8 has been combined with 10^12 to get 62500. Capac=((long)T1capture)*62500/R2; TCNT1 = 0; //reset TCNT1L = 0; TCNT1H = 0; T1capture=0; //set B.2 to an input (start charging again) DDRB = DDRB & 0xfb; //(0 << PINB2); // rh = Capac*humidity_coeff; humid_timer = quarter_second; end end fprintf(stdout,"Humidity: %d rh\n\r",(int)rh); // Humidity decision (for transmission) if (rh < lowrh_thresh) {humidity1 = 0; humidity2 = 0;} else if (rh >= lowrh_thresh && rh < medrh_thresh) {humidity1 = 0; humidity2 = 1;} else if (rh >= medrh_thresh && rh < highrh_thresh) {humidity1 = 1; humidity2 = 0;} else if (rh > highrh_thresh) {humidity1 = 1; humidity2 = 1;} fprintf(stdout, "Humidity transmission: %d %d\n\r", humidity1, humidity2); humidity_done = 1; end //humidity // Humidity ISRs (timer1): //********************************************************** //timer 1 overflow ISR ISR (TIMER1_COMPA_vect) begin // set overflow variable T1overflow = 1; end //********************************************************** // timer 1 capture ISR ISR (TIMER1_CAPT_vect) begin // read timer1 input capture register T1capture = ICR1 ; end // ********************************************************* void solar(void) begin humidity_done = 0; Ain = ADCH; // Ain comes in with range 0 to 255 sunlight = Ain*solar_coeff; fprintf(stdout, "Sunlight Intensity: %d \n\r", (int)Ain); // Sunlight decision (for transmission) if (sunlight < night_thresh) {sunlight1 = 0; sunlight2 = 0;} else if (sunlight >= night_thresh && sunlight < medsunlight_thresh) {sunlight1 = 0; sunlight2 = 1;} else if (sunlight >= medsunlight_thresh && sunlight < highsunlight_thresh) {sunlight1 = 1; sunlight2 = 0;} else if (sunlight > highsunlight_thresh) {sunlight1 = 1; sunlight2 = 1;} fprintf(stdout, "Sunlight transmission: %d %d\n\r", sunlight1, sunlight2); // Turn on ADC conversion for the fourth measurement, battery voltage ADMUX = 0b10100110; // bits 7:6: (REFS1,REFS0) reference voltage // (0,0) is external Aref (connect Aref jumper, when using STK500) // (0,1) when using protoboard (AVCC with external capacitor at AREF pin) // bit 5: ADC left adjust result (ADLAR = 1) // bits 4:0: Channel select (MUX4:0 = 00110 for ADC CHANNEL 6, ADC6) // Start a new conversion (battery) ADCSRA |= (1< night_thresh) // If battery voltage is less than 4.6V and there is enough light // **use a voltage divider to get battery between 0 and 1.1 V // **should we check about light here, or just use diode to block back-current? {PORTB = PORTB | 0b10000000; fprintf(stdout, "Solar is connected\n\r");} // Reconnect solar panel by turning on gate of BUZ71 (output pin B.7 = 1) else {PORTB = PORTB & 0b01111111; fprintf(stdout, "Solar is NOT connected\n\r");} // If battery is full or it's dark, disconnect solar panel (output pin B.7 = 0) // to avoid overcharging or draining //**remember to cut off solar before battery measurement // Low battery (ultimately, not used for anything, but leave option for low battery transmission) if (battery_voltage < low_batt_thresh) lowbat = 1; else lowbat = 0; // Turn on ADC conversion for the fifth measurement, wind speed ADMUX = 0b10101001; // bits 7:6: (REFS1,REFS0) reference voltage // (0,0) is external Aref (connect Aref jumper, when using STK500) // (0,1) when using protoboard (AVCC with external capacitor at AREF pin) // bit 5: ADC left adjust result (ADLAR = 1) // bits 4:0: Channel select (MUX4:0 = 01001 for ADC CHANNEL 1(+)/2(-1), WITH 10x GAIN!) // Start a new conversion (wind) ADCSRA |= (1< 0) begin if (wind_sample_timer == 0) begin wind_sample_timer = wind_rate; //Do ADC conversions as often as specified in 'wind_rate' Ain = ADCH; // Ain comes in with range 0 to 255 wind_speed = Ain*wind_coeff; fprintf(stdout, "Wind Speed: %d mph\n\r", (int)wind_speed); if (wind_speed > wind_threshold_low && wind_speed <= wind_threshold_med && wind_speed < 100) {++low_wind_count;} if (wind_speed > wind_threshold_med && wind_speed <= wind_threshold_high && wind_speed < 100) {++med_wind_count;} if (wind_speed > wind_threshold_high && wind_speed < 100) {++high_wind_count;} // Start a new conversion ADCSRA |= (1< wind_count_threshold) { wind1 = 0; wind2 = 1; if (med_wind_count > low_wind_count) { wind1 = 1; wind2 = 0; if (high_wind_count > med_wind_count) { wind1 = 1; wind2 = 1; } } } else {wind1 = 0; wind2 = 0;} fprintf(stdout, "Wind transmission: %d %d\n\r", wind1, wind2); // Turn on ADC conversion for the sixth and final measurement, DOPPLER RADAR RAIN GAUGE ADMUX = 0b10100101; // bits 7:6: (REFS1,REFS0) reference voltage // (0,0) is external Aref (connect Aref jumper, when using STK500) // (0,1) when using protoboard (AVCC with external capacitor at AREF pin) // bit 5: ADC left adjust result (ADLAR = 1) // bits 4:0: Channel select (MUX4:0 = 00101 for ADC CHANNEL 5, ADC5) // Start a new conversion (radar) ADCSRA |= (1< 0) begin if (radar_sample_timer == 0) begin radar_sample_timer = radar_rate; //Do ADC conversions as often as specified in 'radar_rate' Ain = ADCH; // Ain comes in with range 0 to 255 fprintf(stdout, "%d \n\r", Ain); if (Ain > rain_ADC_threshold) {++rain_drops;} //fprintf(stdout, "drop \n\r"); // Start a new conversion ADCSRA |= (1< rain_drops_threshold) {raining = 1;} //If more than required number of ADC values above rain_ADC_threshold else {raining = 0;} if (raining == 1) {fprintf(stdout, "It's raining!\n\r");} else {fprintf(stdout, "It's NOT raining.\n\r");} // Rain decision - // choose from these 4 pictures: if (raining == 0) {rain1 = 0; rain2 = 0;} // "meadow" (no rain) if (raining == 1) {rain1 = 0; rain2 = 1;} // "lightning" (rain), if (raining == 1 && temperature1 == 0) {rain1 = 1; rain2 = 0;} // "ice" (rain + low temp), if (raining == 1 && sunlight1 == 1) {rain1 = 1; rain2 = 1;} // "rainbow" (rain + sunny) fprintf(stdout, "Rain transmission: %d %d\n\r", rain1, rain2); radar_turnoff_timer = radar_offtime; radar_done = 1; end //********************************************************** void send(void) begin radar_done = 0; fprintf(stdout, "Sending code: %d %d %d %d %d %d %d %d %d %d\n\r", sunlight1, sunlight2, humidity1, humidity2, rain1, rain2, temperature1, temperature2, wind1, wind2); //TRANSMIT //Now, transmit each of the 8-bit picture-decider in sequence, in a for-loop for (tx_bit=0; tx_bit<=9; tx_bit++) begin fprintf(stdout, "Transmitting bit: %d ", tx_bit); // which bit are we sending? (set data transmission bit B.1) if (tx_bit == 0) { if (sunlight1 == 1) {PORTB = PORTB | 0b00000010;} else {PORTB = PORTB & 0b11111101;} } else if (tx_bit == 1) { if (sunlight2 == 1) {PORTB = PORTB | 0b00000010;} else {PORTB = PORTB & 0b11111101;} } else if (tx_bit == 2) { if (humidity1 == 1) {PORTB = PORTB | 0b00000010;} else {PORTB = PORTB & 0b11111101;} } else if (tx_bit == 3) { if (humidity2 == 1) {PORTB = PORTB | 0b00000010;} else {PORTB = PORTB & 0b11111101;} } else if (tx_bit == 4) { if (rain1 == 1) {PORTB = PORTB | 0b00000010;} else {PORTB = PORTB & 0b11111101;} } else if (tx_bit == 5) { if (rain2 == 1) {PORTB = PORTB | 0b00000010;} else {PORTB = PORTB & 0b11111101;} } else if (tx_bit == 6) { if (temperature1 == 1) {PORTB = PORTB | 0b00000010;} else {PORTB = PORTB & 0b11111101;} } else if (tx_bit == 7) { if (temperature2 == 1) {PORTB = PORTB | 0b00000010;} else {PORTB = PORTB & 0b11111101;} } else if (tx_bit == 8) { if (wind1 == 1) {PORTB = PORTB | 0b00000010;} else {PORTB = PORTB & 0b11111101;} } else if (tx_bit == 9) { if (wind2 == 1) {PORTB = PORTB | 0b00000010;} else {PORTB = PORTB & 0b11111101;} } tx_timer = tx_length; PORTB = PORTB | 0b00000001; //turn on VT (B.0) PORTD = PORTD & 0b11111011; //turn on LED (D.2) while (tx_timer > 0) //transmit individual bit for duration tx_length begin fprintf(stdout, "."); end tx_timer = tx_length; PORTB = PORTB & 0b11111110; //turn off VT (B.0) PORTD = PORTD | 0b00000100; //turn off LED (D.2) while (tx_timer > 0) //wait tx_length before transmitting next bit begin fprintf(stdout, "."); end fprintf(stdout, "\n\r"); end fprintf(stdout,"Please wait before turning off the WeatherCanvasTM outdoor station...\n\r"); sending_done = 1; end //********************************************************** void initialize(void) begin //set up timer 0 for 1 mSec timebase TIMSK0= (1<