An advent wreath in late spring, you ask? Yes, the timing is a bit off, and that’s not just because the coldest spring in ages has not finished yet. While browsing for the topic of my last post, I discovered a nice little one-evening-project: Geeky advent from tinkerlog.
I had all the required parts here, so I just gave it a try. The adaptation from the AtTiny13 to an AtTiny45 was straight forward. But finding the right threshold value for the ambient light sensor was a bit trickier. Especially, as the ADC didn’t work at first. That was probably a difference between the two AtTiny’s. But once I configured the ADC properly for the AtTiny45, I flashed it a couple of times with different values, and turned the room light on an off, until I had a good threshold value.
It’s interesting how the flickering is done with the random values and the manual PWM. And especially, how one of the LED’s is used to sense the ambient light was intriguing. To save battery power during the day, it goes to sleep and waits for the watchdog timer to wake it up. It then senses the ambient light. If it is bright, it goes straight back to sleep. If it’s dark, it lights up the LED’s. Going through the four modes for the four weeks of advent is done by resetting, or just quickly disconnecting the power from the battery.
But now I look forward for the summer to come, before we can put the mini advent wreath to use…
As my modified code is so similar to the original, it’s not really worth to create a project on github. So, I just pasted the code below.
/* ----------------------------------------------------------------------- * Title: advent.c * Flicker 4 LEDs * Author: Alexander Weber * http://tinkerlog.com/2009/12/12/geeky-advent/ * Date: 22.11.2009 * Hardware: ATtiny13v * Software: CrossPack-AVR-20090415 * * Modified to use an AtTiny45 by Richard Ulrich <richi@paraeasy.ch> 11.06.2013 * * Credits: * This code is based heavily on sritesmods version. * Find the original at http://spritesmods.com/?art=minimalism&f=gpl * * Changes: * - support 4 LEDs * - added a bit of sampling for light detection * - moved the "power down" out of the ISR, was always resetting * - removed callibration, replaced by hardwired value. */ #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> #include <avr/eeprom.h> #include <avr/pgmspace.h> #include <avr/sleep.h> #include <avr/wdt.h> #define LED1 PB4 #define LED2 PB3 #define LED3 PB2 #define LED4 PB1 #define ADC2 2 #define AMBIENT_LIGHT 55 #define TRUE 1 #define FALSE 0 //Bunch o' random numbers: I'm too lazy to write or port a real random number //generator. //generated using bash: //for x in `seq 0 255`; do echo -n $(($RANDOM%256)),; done const uint8_t randomvals[] PROGMEM = { 234,191,103,250,144,74,39,34,215,128,9,122,144,74,137,105,123,218,158,175,205, 118,149,13,98,7,173,179,194,97,115,110,213,80,220,142,102,102,36,152,90,135, 105,176,173,49,6,197,48,140,176,122,4,53,83,216,212,202,170,180,214,53,161, 225,129,185,106,22,12,190,97,158,170,92,160,194,134,169,98,246,128,195,24, 198,165,156,77,126,113,136,58,156,196,136,41,246,164,84,138,171,184,42,214, 203,128,89,39,198,85,140,148,149,36,215,78,170,234,131,124,152,239,154,214, 130,194,49,3,69,248,120,179,101,163,131,124,184,148,213,118,213,81,177,149, 58,213,33,201,63,10,195,215,190,7,86,245,128,9,8,40,102,51,125,94,92,5,159, 75,253,158,40,4,6,178,241,92,124,73,248,1,157,61,50,86,136,113,22,16,171,209, 230,144,240,14,188,2,167,22,88,57,50,86,171,73,114,175,34,226,245,57,180,111, 220,186,170,242,141,229,49,158,30,82,161,49,124,65,139,24,95,14,133,65,238, 116,180,190,49,130,30,30,59,93,173,139,19,187,2,163,102,26,255,23,239,196,19, 6,162 }; uint8_t mode_ee EEMEM = 0; // stores the mode in eeprom static volatile uint8_t sleep = 0; // Gets a semi-random number between 0 and 255 uint8_t getRandom(void) { //This'll probably give a warning because we use it uninitialised. Little //does the compiler know: that's actually what we _want_ :) static uint8_t random1, random2; random1++; if(random1 == 0) random2 += 0x41; return pgm_read_byte(randomvals + random1) ^ random2; } uint16_t getLight(void) { uint16_t val = 0; uint8_t i; ADMUX = (0 << REFS0) // Set ADC reference to AVCC | (0 << REFS1) // Set ADC reference to AVCC | (1 << MUX1); // Use ADC2 or PB4 pin for Vin // | ADC2; // use ADC2 on PB4 ADCSRA = (1 << ADEN) // enable ADC | 3; // prescaler 8 // kill all leds PORTB &= ~((1 << LED1) | (1 << LED2) | (1 << LED3) | (1 << LED4)); _delay_ms(5); DDRB &= ~(1 << LED1); // let led generate some voltage _delay_ms(5); // warm up the ADC, discard the first conversion ADCSRA |= (1 << ADSC); while(ADCSRA & (1 << ADSC)) ; // wait for the measurement to complete for(i = 0; i < 4; i++) { _delay_ms(5); ADCSRA |= (1 << ADSC); while(ADCSRA & (1 << ADSC)) ; // wait for the measurement to complete val += ADC; } val >>= 2; // divide by 4 ADCSRA = 0; // disable adc DDRB |= (1 << LED1); // re-enable led return val; } ISR(WDT_vect) { //check if it's still dark sleep = (getLight() > AMBIENT_LIGHT) ? TRUE : FALSE; } int main(void) { uint8_t lval1, lval2, lval3, lval4; uint8_t i, x, y; uint8_t mode; // set up wdt wdt_enable(WDTO_2S); WDTCR |= 0x40; // WDT generates interrupts instead of resets now. // We want interrupts because a reset clears our nice random // seeds, and an interrupt doesn't. for(i = 0; i < 10; i++) { _delay_ms(100); } // retrieve mode from eeprom and write back mode + 1 mode = eeprom_read_byte(&mode_ee); mode = mode % 4; eeprom_write_byte(&mode_ee, mode + 1); sei(); // enable interrupts // go directly into sleep mode and lets wake up by the wdt sleep = TRUE; // enable leds DDRB = (1 << LED1) | (1 << LED2) | (1 << LED3) | (1 << LED4); while(1) { WDTCR |= 0x40; // make sure wdt keeps generating an int instead of a reset if(sleep) { // switch off all LEDs and power down PORTB &= ~((1 << LED1) | (1 << LED2) | (1 << LED3) | (1 << LED4)); set_sleep_mode(SLEEP_MODE_PWR_DOWN); sleep_mode(); } else { // get a random value for the leds intensity lval1 = getRandom(); lval2 = getRandom(); lval3 = getRandom(); lval4 = getRandom(); // Manually do some pwm for(x = 0; x < 20; x++) { if(mode == 0) PORTB |= (1 << LED1); else if(mode == 1) PORTB |= (1 << LED1) | (1 << LED2); else if(mode == 2) PORTB |= (1 << LED1) | (1 << LED2) | (1 << LED3); else if(mode == 3) PORTB |= (1 << LED1) | (1 << LED2) | (1 << LED3) | (1 << LED4); for(y = 0; y != 255; y++) { if(y == lval1) PORTB &= ~(1 << LED1); if(y == lval2) PORTB &= ~(1 << LED2); if(y == lval3) PORTB &= ~(1 << LED3); if(y == lval4) PORTB &= ~(1 << LED4); _delay_us(5); } } } } }
Leave a Reply