parts:
- ATtiny2313 @4MHz (internal oscillator)
- IR LED (series with 220-ohm resistor)
- Tact switches
- 3.7V lithium ion battery & Capacitor




avr-gcc (main.c) code:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* http://projectproto.blogspot.com/2017/05/attiny-ir-remote.html | |
*/ | |
#include <avr/io.h> | |
#include <avr/interrupt.h> | |
#include <avr/sleep.h> | |
#include <util/delay.h> | |
// led (OC0B) pin | |
#define IRLED_ENABLE() DDRD |= _BV(DDD5); PORTD &= ~_BV(PD5); | |
#define IRLED_DISABLE() DDRD &= ~_BV(DDD5); | |
// push buttons | |
#define POWER_BTN _BV(PB3) | |
#define FUNCTION_BTN _BV(PB2) | |
#define VOLUP_BTN _BV(PB4) | |
#define VOLDOWN_BTN _BV(PB5) | |
#define TUNEUP_BTN _BV(PB7) | |
#define TUNEDOWN_BTN _BV(PB6) | |
#define ALL_BTN (POWER_BTN|FUNCTION_BTN|VOLUP_BTN|VOLDOWN_BTN|TUNEUP_BTN|TUNEDOWN_BTN) | |
#define TIMER_ENABLE_PWM() (TCCR0A |= _BV(COM0B1)) | |
#define TIMER_DISABLE_PWM() (TCCR0A &= ~(_BV(COM0B1))) | |
#define TIMER_CONFIG_KHZ(val) ({ \ | |
const uint8_t pwmval = F_CPU / 2000 / (val); \ | |
TCCR0A = _BV(WGM00); \ | |
TCCR0B = _BV(WGM02) | _BV(CS00); \ | |
OCR0A = pwmval; \ | |
OCR0B = pwmval / 3; \ | |
}) | |
#define MARK(us) TIMER_ENABLE_PWM(); _delay_us(us) | |
#define SPACE(us) TIMER_DISABLE_PWM(); _delay_us(us) | |
#define SONY_HDR_MARK 2400 | |
#define SONY_HDR_SPACE 600 | |
#define SONY_ONE_MARK 1200 | |
#define SONY_ZERO_MARK 600 | |
//http://elektrolab.wz.cz/katalog/samsung_protocol.pdf | |
#define SAMSUNG_HDR_MARK 4500 | |
#define SAMSUNG_HDR_SPACE 4500 | |
#define SAMSUNG_BIT_MARK 560 | |
#define SAMSUNG_ONE_SPACE 1690 | |
#define SAMSUNG_ZERO_SPACE 560 | |
void enableIROut (int khz) | |
{ | |
IRLED_ENABLE(); | |
TIMER_CONFIG_KHZ(khz); | |
} | |
uint32_t reverseBits(uint32_t input, uint8_t nbits) { | |
uint32_t output = input; | |
for (uint8_t i = 1; i < nbits; i++) { | |
output <<= 1; | |
input >>= 1; | |
output |= (input & 1); | |
} | |
return output; | |
} | |
uint32_t encodeSony (uint8_t nbits, uint8_t command, | |
uint8_t address, uint8_t extended) | |
{ | |
uint32_t result = 0; | |
switch (nbits) { | |
case 12: // 5 address bits. | |
result = address & 0x1F; | |
break; | |
case 15: // 8 address bits. | |
result = address & 0xFF; | |
break; | |
case 20: // 5 address bits, 8 extended bits. | |
result = address & 0x1F; | |
result |= (extended & 0xFF) << 5; | |
break; | |
default: | |
return 0; | |
} | |
result = (result << 7) | (command & 0x7F); | |
return reverseBits(result, nbits); | |
} | |
void sendSony (uint32_t data, int nbits) | |
{ | |
// Set IR carrier frequency | |
enableIROut(40); | |
// Header | |
MARK(SONY_HDR_MARK); | |
SPACE(SONY_HDR_SPACE); | |
// Data | |
for (uint32_t mask = 1UL << (nbits - 1); mask; mask >>= 1) { | |
if (data & mask) { | |
MARK(SONY_ONE_MARK); | |
SPACE(SONY_HDR_SPACE); | |
} else { | |
MARK(SONY_ZERO_MARK); | |
SPACE(SONY_HDR_SPACE); | |
} | |
} | |
} | |
void sendSamsung (uint32_t data, int nbits) | |
{ | |
// Set IR carrier frequency | |
enableIROut(38); | |
// Header | |
MARK(SAMSUNG_HDR_MARK); | |
SPACE(SAMSUNG_HDR_SPACE); | |
// Data | |
for (uint32_t mask = 1UL << (nbits - 1); mask; mask >>= 1) { | |
if (data & mask) { | |
MARK(SAMSUNG_BIT_MARK); | |
SPACE(SAMSUNG_ONE_SPACE); | |
} else { | |
MARK(SAMSUNG_BIT_MARK); | |
SPACE(SAMSUNG_ZERO_SPACE); | |
} | |
} | |
// Footer | |
MARK(SAMSUNG_BIT_MARK); | |
SPACE(0); // Always end with the LED off | |
} | |
void enterSleep(void) | |
{ | |
IRLED_DISABLE(); | |
// Enable Pin Change Interrupts on all buttons | |
GIMSK |= _BV(PCIE); | |
PCMSK |= ALL_BTN; | |
sleep_enable(); | |
sei(); // Enable interrupts | |
sleep_cpu(); | |
} | |
ISR(PCINT_vect) { | |
cli(); // Disable interrupts | |
PCMSK &= ~ALL_BTN; // clear interrupt flags | |
sleep_disable(); | |
_delay_ms(1); | |
switch ( (~PINB) & ALL_BTN ) | |
{ | |
#if 1 // sony dvd | |
case POWER_BTN: | |
sendSony(encodeSony(15, 21, 80, 0), 15); // on/off | |
break; | |
case FUNCTION_BTN: | |
sendSony(encodeSony(15, 105, 208, 0), 15); // source | |
break; | |
case VOLUP_BTN: | |
sendSony(encodeSony(15, 18, 80, 0), 15); // volume + | |
break; | |
case VOLDOWN_BTN: | |
sendSony(encodeSony(15, 19, 80, 0), 15); // volume - | |
break; | |
case TUNEUP_BTN: | |
sendSony(encodeSony(20, 52, 16, 16), 20); // tune + | |
break; | |
case TUNEDOWN_BTN: | |
sendSony(encodeSony(20, 51, 16, 16), 20); // tune - | |
break; | |
#else // samsung tv | |
// https://github.com/lepiaf/IR-Remote-Code | |
case POWER_BTN: | |
sendSamsung(0xE0E040BF, 32); // on/off | |
break; | |
case FUNCTION_BTN: | |
sendSamsung(0xE0E0807F, 32); // source | |
break; | |
case VOLUP_BTN: | |
sendSamsung(0xE0E0E01F, 32); // volume + | |
break; | |
case VOLDOWN_BTN: | |
sendSamsung(0xE0E0D02F, 32); // volume - | |
break; | |
case TUNEUP_BTN: | |
sendSamsung(0xE0E048B7, 32); // channel + | |
break; | |
case TUNEDOWN_BTN: | |
sendSamsung(0xE0E008F7, 32); // channel - | |
break; | |
#endif | |
} | |
} | |
int main(void) | |
{ | |
// analog comparator off | |
ACSR &= ~_BV(ACD); | |
DIDR |= _BV(AIN1D) | _BV(AIN0D); | |
// initially all input pins with pull-up's | |
DDRA = DDRB = DDRD = 0; | |
PORTA = PORTB = PORTD = 0xFF; | |
IRLED_ENABLE(); | |
set_sleep_mode(SLEEP_MODE_PWR_DOWN); | |
while(1) { | |
enterSleep(); | |
} | |
} |
References:
ESP8266 IR Remote
z3t0/Arduino-IRremote
ATtiny Low Power