This project allows control turn ON/OFF LEDs with IR remote control from your TV. The big part of the code shows how to decode a command and address of NEC protocol from IR signals using a cheap IR receiver. It’s worth to note that the algorithm which is responsible for decoding IR signals is nonblocking and detects repeat-codes. Project has been tested with ATtiny13a @9.6 MHz and VCC=5V. The code is on Github, click here.
Parts Required
- ATtiny13 – i.e. MBAVR-1 (Minimalist Development Board)
- Resistors R1, R2, R3, R4 – 560Ω, see LED Resistor Calculator
- LED1÷LED4 – basic LED
- TSOP31238 – IR receiver (38kHz)
Circuit Diagram
Firmware
This code is written in C and can be compiled using the avr-gcc. More details on how compile this project is here.
/** * Copyright (c) 2016, Łukasz Marcin Podkalicki <lpodkalicki@gmail.com> * ATtiny13/011 * Control LEDs with IR remote control. Example of monblocking * IR signal reader (38kHz, TSOPxxx) and NEC protocol decoder. * MCU Settings: * FUSE_L=0x7A * FUSE_H=0xFF * F_CPU=9600000 */ #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> #define LED1_PIN PB0 #define LED2_PIN PB2 #define LED3_PIN PB3 #define LED4_PIN PB4 #define IR_IN_PIN PB1 #define IR_IN_PORT PORTB #define IR_OCR0A (122) #define LOW (0) #define HIGH (1) #define IR_SUCCESS (0) #define IR_ERROR (1) #define IR_EVENT_IDLE (0) #define IR_EVENT_INIT (1) #define IR_EVENT_FINI (2) #define IR_EVENT_PROC (3) #define IR_PROTO_EVENT_INIT (0) #define IR_PROTO_EVENT_DATA (1) #define IR_PROTO_EVENT_FINI (2) #define IR_PROTO_EVENT_HOOK (3) volatile uint16_t IR_timeout = 0; volatile uint16_t IR_counter = 0; volatile uint32_t IR_rawdata = 0; uint8_t IR_event = 0; uint8_t IR_proto_event = 0; uint8_t IR_index = 0; uint32_t IR_data = 0; static void IR_init() { DDRB &= ~_BV(IR_IN_PIN); // set IR IN pin as INPUT PORTB &= ~_BV(IR_IN_PIN); // set LOW level to IR IN pin TCCR0A |= _BV(WGM01); // set timer counter mode to CTC TCCR0B |= _BV(CS00); // set prescaler to 1 TIMSK0 |= _BV(OCIE0A); // enable Timer COMPA interrupt OCR0A = IR_OCR0A; // set OCR0n to get ~38.222kHz timer frequency GIMSK |= _BV(INT0); // enable INT0 interrupt handler MCUCR &= ~_BV(ISC01); // trigger INTO interrupt on raising MCUCR |= _BV(ISC00); // and falling edge sei(); // enable global interrupts } static int8_t IR_NEC_process(uint16_t counter, uint8_t value) { int8_t retval = IR_ERROR; switch(IR_proto_event) { case IR_PROTO_EVENT_INIT: /* expecting a space */ if (value == HIGH) { if (counter > 330 && counter < 360) { /* a 4.5ms space for regular transmition of NEC Code; counter => 0.0045/(1.0/38222.0) * 2 = 344 (+/- 15) */ IR_proto_event = IR_PROTO_EVENT_DATA; IR_data = IR_index = 0; retval = IR_SUCCESS; } else if (counter > 155 && counter < 185) { /* a 2.25ms space for NEC Code repeat; counter => 0.00225/(1.0/38222.0) * 2 = 172 (+/- 15) */ IR_proto_event = IR_PROTO_EVENT_FINI; retval = IR_SUCCESS; } } break; case IR_PROTO_EVENT_DATA: /* Reading 4 octets (32bits) of data: 1) the 8-bit address for the receiving device 2) the 8-bit logical inverse of the address 3) the 8-bit command 4) the 8-bit logical inverse of the command Logical '0' – a 562.5µs pulse burst followed by a 562.5µs (<90 IR counter cycles) space, with a total transmit time of 1.125ms Logical '1' – a 562.5µs pulse burst followed by a 1.6875ms (>=90 IR counter cycles) space, with a total transmit time of 2.25ms */ if (IR_index < 32) { if (value == HIGH) { IR_data |= ((uint32_t)((counter < 90) ? 0 : 1) << IR_index++); if (IR_index == 32) { IR_proto_event = IR_PROTO_EVENT_HOOK; } } retval = IR_SUCCESS; } break; case IR_PROTO_EVENT_HOOK: /* expecting a final 562.5µs pulse burst to signify the end of message transmission */ if (value == LOW) { IR_proto_event = IR_PROTO_EVENT_FINI; retval = IR_SUCCESS; } break; case IR_PROTO_EVENT_FINI: /* copying data to volatile variable; raw data is ready */ IR_rawdata = IR_data; break; default: break; } return retval; } static void IR_process() { uint8_t value; uint16_t counter; /* load IR counter value to local variable, then reset counter */ counter = IR_counter; IR_counter = 0; /* read IR_IN_PIN digital value (NOTE: logical inverse value = value ^ 1 due to sensor used) */ value = (PINB & (1 << IR_IN_PIN)) > 0 ? LOW : HIGH; switch(IR_event) { case IR_EVENT_IDLE: /* awaiting for an initial signal */ if (value == HIGH) { IR_event = IR_EVENT_INIT; } break; case IR_EVENT_INIT: /* consume leading pulse burst */ if (value == LOW) { if (counter > 655 && counter < 815) { /* a 9ms leading pulse burst, NEC Infrared Transmission Protocol detected, counter = 0.009/(1.0/38222.) * 2 = 343.998 * 2 = 686 (+/- 30) */ IR_event = IR_EVENT_PROC; IR_proto_event = IR_PROTO_EVENT_INIT; IR_timeout = 7400; } else { IR_event = IR_EVENT_FINI; } } else { IR_event = IR_EVENT_FINI; } break; case IR_EVENT_PROC: /* read and decode NEC Protocol data */ if (IR_NEC_process(counter, value)) IR_event = IR_EVENT_FINI; break; case IR_EVENT_FINI: /* clear timeout and set idle mode */ IR_event = IR_EVENT_IDLE; IR_timeout = 0; break; default: break; } } static int8_t IR_read(uint8_t *address, uint8_t *command) { if (!IR_rawdata) return IR_ERROR; *address = IR_rawdata; *command = IR_rawdata >> 16; IR_rawdata = 0; return IR_SUCCESS; } ISR(INT0_vect) { IR_process(); } ISR(TIM0_COMPA_vect) { /* When transmitting or receiving remote control codes using the NEC IR transmission protocol, the communications performs optimally when the carrier frequency (used for modulation/demodulation) is set to 38.222kHz. */ if (IR_counter++ > 10000) IR_event = IR_EVENT_IDLE; if (IR_timeout && --IR_timeout == 0) IR_event = IR_EVENT_IDLE; } int main(void) { uint8_t addr, cmd; /* setup */ DDRB |= _BV(LED1_PIN)|_BV(LED2_PIN)|_BV(LED3_PIN)|_BV(LED4_PIN); IR_init(); /* loop */ while (1) { if (IR_read(&addr, &cmd) == IR_SUCCESS) { if (addr != 0x01) continue; switch(cmd) { case 0x01: /* turn all LEDs off */ PORTB &= ~(_BV(LED1_PIN)|_BV(LED2_PIN)|_BV(LED3_PIN)|_BV(LED4_PIN)); break; case 0x00: PORTB ^= _BV(LED1_PIN); // toggle LED1 break; case 0x07: PORTB ^= _BV(LED2_PIN); // toggle LED2 break; case 0x06: PORTB ^= _BV(LED3_PIN); // toggle LED3 break; case 0x04: PORTB ^= _BV(LED4_PIN); // toggle LED4 break; default: break; }; } } }
Дружище а можно поправить код , нужно чтоб кнопки пульта работали без фиксации при этом светодиоды должны гореть постоянно соответственно после отпускания кнопок светодиоды должны выключиться
Hello bro, many thanks for that tuto, but I have a question:
After compiling the code with your makefile I found the .hex file is 2.2Kb and Attiny13 only has 1K flash, so, how did you burn the code onto it !!?
Dear friend, can you share the hex code?
Alguien puediera ayudar con esto, estoy usando WinAVR para compilar el código; si alguien supiera de cual está mal me avise por favor soy nuevo en esto mi correo es alfredoperez8709@gmail.com
gracias de antemano a cualquier opinión es bienvenida.
[main] sh 12012 sync_with_child: child 6476(0x1E4) died before initialization with status code 0xC0000142
10793 [main] sh 12012 sync_with_child: *** child state waiting for longjmp
/usr/bin/sh: fork: Resource temporarily unavailable
0 [main] sh 8740 sync_with_child: child 1452(0x1E4) died before initialization with status code 0xC0000142
31178 [main] sh 8740 sync_with_child: *** child state waiting for longjmp
/usr/bin/sh: fork: Resource temporarily unavailable
——– begin ——–
avr-gcc (WinAVR 20100110) 4.3.3
Copyright (C) 2008 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
POSIBLE ERROR ESTA EN ESTA PARTE
Compiling C: main.c
avr-gcc -c -mmcu=attiny45 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=./main.lst -std=gnu99 -MMD -MP -MF .dep/main.o.d main.c -o main.o
main.c:51: warning: function declaration isn’t a prototype
main.c:123: warning: function declaration isn’t a prototype
main.c:238: fatal error: opening dependency file .dep/main.o.d: No such file or directory
compilation terminated.
make.exe: *** [main.o] Error 1
I do upload.. but i dont have that nec remote. S0 it turns nothing… .. i hope there couldbe a code you could actualy select the key ir code… so dumb of me.. please help..
Sorry ı m new on attiny. which lines defined certain buttons nec code of ır remote controller . ı want to change for my ır device. and ı didnt find but, is there any project to change led bright using same way.
ayuda alguien que tenga conocimiento acerca de este error en WinAVR , trato de compilar el codigo fuente que esta publicado en este blog, pero me sales lo siguiente mensaje
ompiling C: main.c
avr-gcc -c -mmcu=attiny45 -I. -gdwarf-2 -DF_CPU=9600000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=./main.lst -std=gnu99 -MMD -MP -MF .dep/main.o.d main.c -o main.o
main.c:51: warning: function declaration isn’t a prototype
main.c: In function ‘IR_init’:
main.c:56: error: ‘TIMSK0’ undeclared (first use in this function)
main.c:56: error: (Each undeclared identifier is reported only once
main.c:56: error: for each function it appears in.)
main.c: At top level:
main.c:123: warning: function declaration isn’t a prototype
main.c:238: fatal error: opening dependency file .dep/main.o.d: No such file or directory
compilation terminated.
make.exe: *** [main.o] Error 1
Can you change one chennel of this project as regulator ? So we can use it as home_outomation
Hi, what do you mean?
/L
Thank you for a wonderful series of articles.
In my case, IR Recivier works only when sending a replay after the main message
I think, if we set event = IR_PROTO_EVENT_HOOK and leave the IR_NEC_process, we will no longer enter the IR_NEC_process and will not reach IR_PROTO_EVENT_FINI, since the impulse (interrupt) is no longer expected
If set IR_proto_event to IR_PROTO_EVENT_FINI while end processing IR_PROTO_EVENT_DATA, all works as expected
Alexander, thank you for sharing the improvement! I marked this example as “require review” in future and will try to reproduce that case.
/L
hey,
how to change, program,,for my ir remote,
Hey, here is a simple remote codes analyzer you can use to check what codes your remote generetes.
Thankyou very match!..
#Łukasz Podkalicki# Can i know how to program Attiny13a ic with ir receiver using ARDUINO?
Hi, you can try make an experiment w/ libraries that are available for Arduino. However, all my work regarding Attiny13 is to make the firmware small size what can be hard to achieve using Arduino code.
Hi friends, I want to try at home, whether the remote control used may be different? is there any way of setting it if using different remote control? thank you
in this program what do u mean by “case 0x01” ?????
plz give me ans…..
hi ,
i am prashant from india.i have deployed NEC protocol on ATTINY13A controller using above steps and code, but i am facing range issue, when i am trying to operate receiver from 4-5 feet its not responding.how to tackel this issue
Hi, due to ATtiny13 is using internal oscillator it needs sometimes a little callibration (incr/decr the value of IR_OCR0A) to work with valid frequency.
hello bro I am student of EEE.
plz help me to programming ir reciver for attiny13a.
Can I use an Universal controller? If that’s the case which brand should i use, or what control are you using?
And by brand i mean which program code brand.