Skip to content

Instantly share code, notes, and snippets.

@kramolnic
Last active February 1, 2021 17:44
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kramolnic/c5911da146c887f441b7abac021dbfab to your computer and use it in GitHub Desktop.
Save kramolnic/c5911da146c887f441b7abac021dbfab to your computer and use it in GitHub Desktop.
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