/* Soil Sensor Firmware * Soil Moisture and Vcc Measurement * Sensiplicity Systems * * Copyright 2015 * * Department of Botany and Plant Pathology * Center for Genome Research and Biocomputing * Oregon State University * Corvallis, OR 97331 * * This program is not free software; you can not redistribute it and/or * modify it at all. * * 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. */ #include "moisture.h" #define F_CPU 2000000UL /* 16Mhz crystal / 8. */ #include "util/delay.h" #include /* Disable and shut down power to the ADC. */ static void shutdown_adc(void){ ADCSRA &= ~_BV(ADEN); /* Disable ADC. */ PRR |= _BV(PRADC); } /* Turn on power to the ADC. */ static void startup_adc(void){ PRR &= ~_BV(PRADC); } /* Initialize the ADC. */ void init_adc(void){ /* Disable digital inputs to save power. */ DIDR0 = _BV(7) | _BV(6) | _BV(ADC1D) | _BV(ADC0D); ADCSRA = _BV(ADPS2); /* Clk/16 */ shutdown_adc(); } /* Get the number of zones on this device. * Currently just returns 1. */ uint8_t num_zones(void){ DDRB &= ~(_BV(PB1) | _BV(PB2)); PORTB |= _BV(PB1) | _BV(PB2); uint8_t z = 4; // if(PINB & _BV(PB1) && PINB & _BV(PB2))z = 1; /* See schematic: X X */ // if(PINB & _BV(PB1) && ~(PINB & _BV(PB2)))z = 2; /* X | */ // if(~(PINB & _BV(PB1)) && PINB & _BV(PB2))z = 4; /* | X */ PORTB &= ~(_BV(PB1) | _BV(PB2)); DDRB |= _BV(PB1) | _BV(PB2); return z; } /* Perform an ADC conversion and returns the result. This * function blocks until the conversion is complete. */ static uint16_t adc_conversion(void){ ADCSRA |= _BV(ADEN) | _BV(ADSC); /* Enable ADC and start conversion. */ while(ADCSRA & _BV(ADSC)){ /* Wait for conversion to finish. */ } uint16_t result; ATOMIC_BLOCK(ATOMIC_RESTORESTATE){ result = ADCL; result |= (uint16_t) ADCH << 8; } return result; } /* Measure the moisture level of a given zone. * Returns a value between 0 and 2^(10 + OVERSAMPLING_BITS). * Lower values indicate more moisture. */ uint16_t get_moisture(uint8_t zone){ startup_adc(); switch(zone){ /* Rev 5 */ // case 0: // ADMUX = 6; // break; // case 1: // ADMUX = 1; // break; // case 2: // ADMUX = 0; // break; // case 3: // ADMUX = 1; // break; /* Rev 6 */ case 0: ADMUX = 6; break; case 1: ADMUX = 7; break; case 2: ADMUX = 0; break; case 3: ADMUX = 1; break; } ADMUX |= _BV(REFS0); /* Select AVcc as reference. */ adc_conversion(); /* Throw away first result. */ uint32_t value = 0; uint32_t total = 0; for(int a=0; a < ADC_AVERAGES; a++){ value = 0; for(int s=0; s < (1 << (2 * ADC_OVERSAMPLING_BITS)); s++){ value += adc_conversion(); } value >>= ADC_OVERSAMPLING_BITS; total += value; } total /= ADC_AVERAGES; shutdown_adc(); /* Reset peak detector. */ DDRC |= _BV(PC1) | _BV(PC0); DDRE |= _BV(PE3) | _BV(PE2); _delay_ms(1); DDRC &= ~(_BV(PC1) | _BV(PC0)); DDRE &= ~(_BV(PE3) | _BV(PE2)); return total; /* Improving accuracy/stability of the soil moisture sensor: * (Using multiple zone didn't work, due to the inductance on the signal * lines beyond the first zone.) * (Note: r5 measurement capacitor minimum capacitance ~3pF.) * Issues: * -Drift in water content/dielectric constant of PCB (coat with moisture-barrier epoxy) * -Drift in diode Vf * -Change in diode Vf with temperature (MASSIVE) * -(maybe?) Drift in smoothing cap (likely, but shouldn't affect the result) * -Drift in 100k resistor * All four diodes should be identical and fabricated on the same substrate. Even if they aren't they're * certainly in the same package, so their temperature should match pretty closely. * So, use another channel with a constant capacitance (say 10pF). Measure the output of that channel * and use it to temperature-compensate the moisture measurement(s). */ } /* Measure Vcc. * Returns Vcc in 1/32V increments. */ uint16_t get_vcc(void){ startup_adc(); _delay_us(100); /* Wait for 1.1V reference to stabilize. */ ADMUX = 14; /* Measure 1.1V reference. */ ADMUX |= _BV(REFS0); /* Select AVcc as reference. */ ADCSRA |= _BV(ADEN) | _BV(ADSC); /* Enable ADC and start conversion. */ adc_conversion(); /* Throw away first result. */ uint16_t result = adc_conversion(); shutdown_adc(); result = 36045 / result; /* (1.1 * 1024 * 32) / result */ return result; }