ATtiny10 Moisture sensor
Built an ATiny10 moisture sensor with multiple indicators to show how wet or dry soil is. Also it has the feature of low voltage alert (using VLM).
Microchip Studio Code:
/* * Tiny 10 Moisture sensore and indicator v2.1 * with voltage detection works 2.4v is trigger * * Sherman Stebbins * 2022-02-17 * * Firmware v2 * * * Attiny10; +====+; SCK -> PB0 |* | PB3 (RESET); GND | | Vcc; MOSI -> PB1 | | PB2 ; +====+
Updated 2.1 on 3-19-22changed light alerts and added moreset at 8mhz in arduino ide to get correct timing2** Updated 3-19/22* changed when lit and more green and red light alert.* Faster is less good. * * Green slow - full of water* Green fast - good on water* Green and red - medium on water* Red slow - low water* Red Fast - dry on water* Test showed in sleep 5uA !!!!!! */#include <util/delay.h>#include <avr/interrupt.h>#define F_CPU 1000000UL#define Moisture PB0#define RedLed PB1#define GreenLed PB2//#define SleepTimes 4 //8=58 sec //4=29 secvolatile uint8_t SleepTimes=4; //8=58 sec //4=29 sec //2=15sec //1=8sec//uint8_t LowVoltage =0;
volatile unsigned char count __attribute__((section(".noinit"))); // Not cleared on RESET
int main(void){ CCP = 0xD8; // unlock protected register //CLKPSR = 0; // No divider full 8mhz CLKPSR = 0b00000000; // No divider full ?mhz VLMCSR |= (1<<VLM0)|(1<<VLM1)|(1<<VLMIE); WDTCSR &= ~(1<<WDE); //must have or won't sleep WDTCSR &= ~(1<<WDIE); DDRB = 0b00000110; while(1){ //if Battery below 2.4v gives warning light flash if(VLMCSR & (1<<VLMF)){ //VLMCSR & 0b10000000 either one reads VLMF flag for(int i=0;i<20;i++){ LightLed(GreenLed); _delay_ms(50); LedOff(); _delay_ms(50); LightLed(RedLed); _delay_ms(50); LedOff(); _delay_ms(50); } } //xc mm cc if(count==0){ uint8_t Wetness=0; for(int i=0;i<6;i++){ Wetness = analog_read(Moisture); //get reading ////display wetness: if(Wetness >= 242){ LightLed(GreenLed); _delay_ms(150); LedOff(); _delay_ms(150); SleepTimes=4; //8=58 sec //4=29 sec //2=15sec //1=8sec }else if(Wetness <=242 && Wetness >= 230){ LightLed(GreenLed); _delay_ms(50); LedOff(); _delay_ms(50); SleepTimes=4; //8=58 sec //4=29 sec //2=15sec //1=8sec }else if(Wetness <=229 && Wetness >= 218){ LightLed(RedLed); LightLed(GreenLed); _delay_ms(150); LedOff(); _delay_ms(150); SleepTimes=2; //8=58 sec //4=29 sec //2=15sec //1=8sec }else if(Wetness <=217 && Wetness >= 200){ LightLed(RedLed); //LightLed(GreenLed); _delay_ms(150); LedOff(); _delay_ms(150); SleepTimes=1; //8=58 sec //4=29 sec //2=15sec //1=8sec }else if( Wetness < 200){ for(int j=0;j<4;j++){ LightLed(RedLed); _delay_ms(50); LedOff(); _delay_ms(50); SleepTimes=1; //8=58 sec //4=29 sec //2=15sec //1=8sec } } } } count++; if(count>=SleepTimes){ count=0; } VLMCSR = 0b00000000; //shutdown VLM ADCSRA &= ~(1<<ADEN); //shut off ADC goToSleep(); }}
//low voltage interuptISR(VLM_vect){ /* LowVoltage=1; //didn't carry over due to sleep DUHHHH use volitile.. like: volatile unsigned char count __attribute__((section(".noinit"))); // Not cleared on RESET */}
void goToSleep(void){ SMCR |= (1<<2); //SMCR.SM bit 2 (010) Power-down SMCR |= (1<<0); //SMCR.SE =1; SET JUST before Sleep starts sei() ; //set Global Interrupt Enable //__asm__ __volatile__("sleep") ; //enter sleep, waiting for interrupt -- This worked great!!! WDTCSR = 0b01100001; //1000 4 sec 0111 2 sec watchdog timer 1001 8 sec. __asm__ __volatile__ ( "sleep" "\n\t" :: ); //Sleep instruction to put controller to sleep (found this syntax later, probably more proper) }//since reset is used, not needed:/*ISR(INT0_vect){ GIMSK = 0; //disable external interrupts (only need one to wake up)}*/
void LedOff(void){ PORTB &= ~(1<<PB1); PORTB &= ~(1<<PB2);}void LightLed(uint8_t Led){ PORTB |= (1<<Led);}
uint8_t analog_read(uint8_t pin){ switch(pin) { case PB0: ADMUX = 0; DIDR0 = _BV(ADC0D); break; // ADC0 case PB1: ADMUX = _BV(MUX0); DIDR0 = _BV(ADC1D); break; // ADC1 case PB2: ADMUX = _BV(MUX1); DIDR0 = _BV(ADC2D); break; // ADC2 //default: ADMUX = 0; break; }
ADCSRA |= _BV(ADEN); // Enable ADC ADCSRA |= _BV(ADSC); // Run single conversion while(bit_is_set(ADCSRA, ADSC)); // Wait conversion is done
// Read values return ADCL;}
Updated 2.1 on 3-19-22changed light alerts and added moreset at 8mhz in arduino ide to get correct timing2** Updated 3-19/22* changed when lit and more green and red light alert.* Faster is less good. * * Green slow - full of water* Green fast - good on water* Green and red - medium on water* Red slow - low water* Red Fast - dry on water* Test showed in sleep 5uA !!!!!! */#include <util/delay.h>#include <avr/interrupt.h>#define F_CPU 1000000UL#define Moisture PB0#define RedLed PB1#define GreenLed PB2//#define SleepTimes 4 //8=58 sec //4=29 secvolatile uint8_t SleepTimes=4; //8=58 sec //4=29 sec //2=15sec //1=8sec//uint8_t LowVoltage =0;
volatile unsigned char count __attribute__((section(".noinit"))); // Not cleared on RESET
int main(void){ CCP = 0xD8; // unlock protected register //CLKPSR = 0; // No divider full 8mhz CLKPSR = 0b00000000; // No divider full ?mhz VLMCSR |= (1<<VLM0)|(1<<VLM1)|(1<<VLMIE); WDTCSR &= ~(1<<WDE); //must have or won't sleep WDTCSR &= ~(1<<WDIE); DDRB = 0b00000110; while(1){ //if Battery below 2.4v gives warning light flash if(VLMCSR & (1<<VLMF)){ //VLMCSR & 0b10000000 either one reads VLMF flag for(int i=0;i<20;i++){ LightLed(GreenLed); _delay_ms(50); LedOff(); _delay_ms(50); LightLed(RedLed); _delay_ms(50); LedOff(); _delay_ms(50); } } //xc mm cc if(count==0){ uint8_t Wetness=0; for(int i=0;i<6;i++){ Wetness = analog_read(Moisture); //get reading ////display wetness: if(Wetness >= 242){ LightLed(GreenLed); _delay_ms(150); LedOff(); _delay_ms(150); SleepTimes=4; //8=58 sec //4=29 sec //2=15sec //1=8sec }else if(Wetness <=242 && Wetness >= 230){ LightLed(GreenLed); _delay_ms(50); LedOff(); _delay_ms(50); SleepTimes=4; //8=58 sec //4=29 sec //2=15sec //1=8sec }else if(Wetness <=229 && Wetness >= 218){ LightLed(RedLed); LightLed(GreenLed); _delay_ms(150); LedOff(); _delay_ms(150); SleepTimes=2; //8=58 sec //4=29 sec //2=15sec //1=8sec }else if(Wetness <=217 && Wetness >= 200){ LightLed(RedLed); //LightLed(GreenLed); _delay_ms(150); LedOff(); _delay_ms(150); SleepTimes=1; //8=58 sec //4=29 sec //2=15sec //1=8sec }else if( Wetness < 200){ for(int j=0;j<4;j++){ LightLed(RedLed); _delay_ms(50); LedOff(); _delay_ms(50); SleepTimes=1; //8=58 sec //4=29 sec //2=15sec //1=8sec } } } } count++; if(count>=SleepTimes){ count=0; } VLMCSR = 0b00000000; //shutdown VLM ADCSRA &= ~(1<<ADEN); //shut off ADC goToSleep(); }}
//low voltage interuptISR(VLM_vect){ /* LowVoltage=1; //didn't carry over due to sleep DUHHHH use volitile.. like: volatile unsigned char count __attribute__((section(".noinit"))); // Not cleared on RESET */}
void goToSleep(void){ SMCR |= (1<<2); //SMCR.SM bit 2 (010) Power-down SMCR |= (1<<0); //SMCR.SE =1; SET JUST before Sleep starts sei() ; //set Global Interrupt Enable //__asm__ __volatile__("sleep") ; //enter sleep, waiting for interrupt -- This worked great!!! WDTCSR = 0b01100001; //1000 4 sec 0111 2 sec watchdog timer 1001 8 sec. __asm__ __volatile__ ( "sleep" "\n\t" :: ); //Sleep instruction to put controller to sleep (found this syntax later, probably more proper) }//since reset is used, not needed:/*ISR(INT0_vect){ GIMSK = 0; //disable external interrupts (only need one to wake up)}*/
void LedOff(void){ PORTB &= ~(1<<PB1); PORTB &= ~(1<<PB2);}void LightLed(uint8_t Led){ PORTB |= (1<<Led);}
uint8_t analog_read(uint8_t pin){ switch(pin) { case PB0: ADMUX = 0; DIDR0 = _BV(ADC0D); break; // ADC0 case PB1: ADMUX = _BV(MUX0); DIDR0 = _BV(ADC1D); break; // ADC1 case PB2: ADMUX = _BV(MUX1); DIDR0 = _BV(ADC2D); break; // ADC2 //default: ADMUX = 0; break; }
ADCSRA |= _BV(ADEN); // Enable ADC ADCSRA |= _BV(ADSC); // Run single conversion while(bit_is_set(ADCSRA, ADSC)); // Wait conversion is done
// Read values return ADCL;}
ATtiny10 actual size..