#include <avr/io.h> // for GPIO
#include <avr/sleep.h> // for sleep functions
#include <avr/interrupt.h> // for interrupts
#include <util/delay.h> // for delays
// Pin definitions
#define NEO_PIN PB3 // Pin for neopixels
#define IR_PIN PB4 // Pin for IR receiver
uint8_t led1 = 0;
uint8_t led2 = 1;
uint8_t led3 = 2;
// IR codes
#define IR_ADDR 0x0 // IR device address
#define IR_POWER 0xA2 // IR code for power on/off
#define IR_BRIGHT 0x30 // IR code for brightness
#define IR_SPEED 0x18 // IR code for animation speed
#define IR_DENSE 0x7A // IR code for color density
#define IR_FAIL 0xFF // IR fail code
// ===================================================================================
void of() {
digitalWrite(led1, LOW);
digitalWrite(led2, LOW);
digitalWrite(led3, LOW);
}
// ===================================================================================
// IR Receiver Implementation (NEC Protocol)
// ===================================================================================
// IR receiver definitions and macros
#define IR_init() PORTB |= (1<<IR_PIN) // pullup on IR pin
#define IR_available() (~PINB & (1<<IR_PIN)) // return true if IR line is low
// IR wait for signal change and measure duration
uint8_t IR_waitChange(uint8_t timeout) {
uint8_t pinState = PINB & (1 << IR_PIN); // get current signal state
uint8_t dur = 0; // variable for measuring duration
while ((PINB & (1 << IR_PIN)) == pinState) { // measure length of signal
if (dur++ > timeout) return 0; // exit if timeout
_delay_us(100); // count every 100us
}
return dur; // return time in 100us
}
// IR read data byte
uint8_t IR_readByte(void) {
uint8_t result;
uint8_t dur;
for (uint8_t i = 8; i; i--) { // 8 bits
result >>= 1; // LSB first
if (IR_waitChange(11) < 3) return IR_FAIL; // exit if wrong burst length
dur = IR_waitChange(21); // measure length of pause
if (dur < 3) return IR_FAIL; // exit if wrong pause length
if (dur > 11) result |= 0x80; // bit "0" or "1" depends on pause duration
}
return result; // return received byte
}
// IR read data according to NEC protocol
uint8_t IR_read(void) {
uint16_t addr; // variable for received address
if (!IR_available()) return IR_FAIL; // exit if no signal
if (!IR_waitChange(100)) return IR_FAIL; // exit if wrong start burst length
if (IR_waitChange(55) < 35) return IR_FAIL; // exit if wrong start pause length
uint8_t addr1 = IR_readByte(); // get first address byte
uint8_t addr2 = IR_readByte(); // get second address byte
uint8_t cmd1 = IR_readByte(); // get first command byte
uint8_t cmd2 = IR_readByte(); // get second command byte
if (IR_waitChange(11) < 3) return IR_FAIL; // exit if wrong final burst length
if ((cmd1 + cmd2) < 255) return IR_FAIL; // exit if command bytes are not inverse
if ((addr1 + addr2) == 255) addr = addr1; // check if it's extended NEC-protocol ...
else addr = ((uint16_t)addr2 << 8) | addr1; // ... and get the correct address
if (addr != IR_ADDR) return IR_FAIL; // wrong address
return cmd1; // return command code
}
// Go to standby mode
void standby(void) {
of(); //off all led
while (1) {
GIFR |= (1 << PCIF); // clear any outstanding interrupts
sei(); // enable interrupts
sleep_mode(); // sleep until IR interrupt
cli(); // disable interrupts
if ( (IR_available()) && (IR_read() == IR_POWER) ) break; // exit on power button
}
}
// Pin change interrupt service routine
EMPTY_INTERRUPT(PCINT0_vect); // just wake up from sleep
int main(void) {
// Local variables
uint8_t start = 0;
uint8_t speed = 3;
uint8_t dense = 2;
// Disable unused peripherals and prepare sleep mode to save power
ACSR = (1 << ACD); // disable analog comperator
DIDR0 = ~(1 << IR_PIN) & 0x1F; // disable digital intput buffer except IR pin
PRR = (1 << PRADC); // shut down ADC
GIMSK |= (1 << PCIE); // enable pin change interrupts
PCMSK |= (1 << IR_PIN); // enable interrupt on IR pin
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set sleep mode to power down
// Setup
//NEO_init(); // init Neopixels
IR_init(); // init IR receiver
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
pinMode(led3, OUTPUT);
// Loop
while (1) {
// Set new start value
uint8_t current = start;
start += speed;
if (start >= 192) start -= 192;
// Check IR receiver and change parameters; delay 80ms
for (uint8_t i = 80; i; i--) {
if (IR_available()) {
uint8_t command = IR_read();
switch (command) {
case IR_POWER: standby(); break;
case IR_BRIGHT: digitalWrite(led1, HIGH); break;
case IR_SPEED: digitalWrite(led2, HIGH); break;
case IR_DENSE: digitalWrite(led3, HIGH); break;
default: break;
}
}
_delay_ms(1);
}
}
}