Airfresher photo controller firmware for ATtiny10
#include <avr/io.h> | |
#include <avr/interrupt.h> | |
#include <avr/pgmspace.h> | |
#include <avr/sleep.h> | |
#include <avr/wdt.h> | |
#include <stdint.h> | |
// Airfresher photo controller for an ATTiny 4/5/9/10 | |
// by kramolnic | |
/////////////////////////////////////////////////////////////////////////////////////// | |
// SETTINGS (you can change this values) | |
// Light time, sec: | |
#define LIGHT_SECS_TO_FIRE 150 | |
// Dark time, sec: | |
#define DARK_SECS_TO_FIRE 5 | |
/////////////////////////////////////////////////////////////////////////////////////// | |
//(Input) Light sensor is PB2 (PCINT2 / INT0) pin | |
#define LIGHT_SENSOR_PIN _BV(2) | |
//(Output) LED indicator | |
#define LED_PIN _BV(1) | |
//(Output) Drive control is PB0 (OC0A) pin | |
#define DRIVE_CONTROL_PIN _BV(0) | |
enum State | |
{ | |
s1 = 0, //Active mode - time counting | |
s2 //Waiting for watchdog reset | |
}; | |
volatile enum State state; | |
volatile uint32_t lightTimeCounter; | |
volatile uint32_t darkTimeCounter; | |
//Light sensor INT0 external interrupt (low-level async interrupt) | |
ISR (INT0_vect) | |
{ | |
asm("nop"); | |
} | |
//Timer compare A match interrupt (one-second timer) | |
ISR (TIM0_COMPA_vect) | |
{ | |
if (PINB & LIGHT_SENSOR_PIN) // dark | |
{ | |
PORTB ^= LED_PIN; | |
darkTimeCounter++; | |
} | |
else // light | |
{ | |
PORTB &= ~LED_PIN; | |
lightTimeCounter++; | |
darkTimeCounter = 0; | |
} | |
} | |
int main() | |
{ | |
///////////////////////////////////////////////////////////////////////////////////// | |
cli(); // Initialization begin | |
// disable watchdog timer (activated once, still active after reset!) | |
wdt_reset(); | |
RSTFLR &= ~(1<<WDRF); | |
CCP = 0xD8; | |
WDTCSR &= ~(1<<WDE); | |
// disable Analog comparator (enabled by default) | |
ACSR = (1<<ACD); | |
// disable Voltage level monitor | |
VLMCSR = 0; | |
// configure output pins | |
DDRB = DRIVE_CONTROL_PIN | LED_PIN; | |
// configure INT0 interrupt from light sensor | |
EICRA = (0<<ISC01) | (0<<ISC00); //Low level on INT0 generates an interrupt request | |
EIMSK = (1<<INT0); //Enable external INT0 from light sensor | |
// configure timer (OCR0A = 500 = 0x01F4) => 1Hz timer | |
OCR0AH = 0x01; | |
OCR0AL = 0xF4; | |
// reset counters and state | |
state = s1; | |
lightTimeCounter = 0; | |
darkTimeCounter = 0; | |
// Configure sleep mode | |
set_sleep_mode(SLEEP_MODE_PWR_DOWN); | |
// configure Main Clock Source to 500Hz | |
CCP = 0xD8; //Enable CCP. | |
CLKMSR = (0<<CLKMS1) | (1<<CLKMS0); //Internal 128 kHz Oscillator (WDT Oscillator) | |
CCP = 0xD8; | |
CLKPSR = (1<<CLKPS3) | (0<<CLKPS2) | (0<<CLKPS1) | (0<<CLKPS0); //500 Hz Main Clock operation (128 kHz / 256) | |
sei(); // Initialization end | |
///////////////////////////////////////////////////////////////////////////////////// | |
// Enter Power-down mode | |
sleep_enable(); | |
sleep_cpu(); | |
cli(); | |
EIMSK = (0<<INT0); //Disable external INT0 | |
sleep_disable(); | |
//activate timer | |
TCCR0A = (0<<COM0A1) | (0<<COM0A0) | (0<<COM0B1) | (0<<COM0B0) | (0<<WGM01) | (0<<WGM00); //Toggle OC0B on compare match | |
TCCR0B = (0<<ICNC0) | (0<<ICES0) | (0<<WGM03) | (1<<WGM02) | (0<<CS02) | (0<<CS01) | (1<<CS00); //clkIO/1 (no prescaling) => 1Hz | |
TIMSK0 = (1<<OCIE0A); // Output compare A match int enable | |
set_sleep_mode(SLEEP_MODE_IDLE); | |
sei(); | |
while(1) | |
{ | |
sleep_enable(); | |
sleep_cpu(); | |
switch (state) | |
{ | |
case s1: | |
{ | |
if (darkTimeCounter > DARK_SECS_TO_FIRE) // the light is off longer than DARK_SECS... | |
{ | |
if (lightTimeCounter > LIGHT_SECS_TO_FIRE) // the light was ON longer than LIGHT_SECS... | |
{ | |
PORTB |= DRIVE_CONTROL_PIN; //enable drive | |
} | |
// enable watchdog | |
wdt_reset(); | |
wdt_enable(WDTO_1S); | |
state = s2; | |
} | |
} | |
break; | |
case s2: //NOTE: nothing to do - waiting for watchdog reset | |
break; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment