Porting the Arduino IRremote library to the ATtiny4313

Note: This covers the IR receive and decode part only. I may do the IR send part at some other time.

Ken Shirriff wrote a popular multi-protocol IR library for the Arduino. It’s easy to use and works really well. When I wanted to add an IR receiver to a project based on the ATtiny2313, I turned to this library to see whether it could be ported to run on an ATtiny. It turned out to be next to impossible to squeeze the code into the 2K of flash memory the ATtiny2313 has, but I succeeded using the pin-compatible ATtiny4313. The ‘4313 is more or less identical to the ‘2313, but has twice the amount of ram and flash memory.

Step 1

I started from Ken Shirriff’s code in http://arcfn.com/files/IRremote.zip. The Arduino code is C++, so the very first thing to do was to rename the files and switch from C++ classes to C structs and functions.

IRremote.cpp	-> irrecv.c
IRremote.h	-> irrecv.h
IRremoteInt.h  	-> irrecvint.h

The IR receive code uses a timer interrupt function called every 50 microseconds. The original code used TIMER2 which does not exist in the ‘4313. This chip has an 8-bit TIMER0 and a 16-bit TIMER1. Since I wanted to keep TIMER0 free for other uses, I changed to use TIMER1.

Everything compiled fine and I timed the interrupt function to clock in every 50us using an oscilloscope. A skeleton program using the IR receive library would look something like this:

#include "irrecv.h"

void process(decode_results *r) { /* do cool stuff here */ }

int main(void)
{
    decode_results r;
    setup_irrecv();
    for (;;) {
	if (irrecv_decode(&r)) {
	    process(&r);
	    irrecv_resume();
	}
    }
    return 0;
}

But, there is a problem:

avr-size -t main.o irrecv.o
   text	   data	    bss	    dec	    hex	filename
     64	      0	      0	     64	     40	main.o
   1612	      0	      0	   1612	    64c	irrecv.o
   1676	      0	      0	   1676	    68c	(TOTALS)
avr-size main.elf
   text	   data	    bss	    dec	    hex	filename
   4676	    264	    140	   5080	   13d8	main.elf

The size of the object code of the library and the minimal main program is way less than 2K, but the resulting binary has about 5k. This won’t fit in the 4K available on the ‘4313.

Step 2

The additional code must come from the C library the avr-gcc links to. So, let’s disassemble the .hex file to see what’s in there.

avr-objdump -d main.elf

Whoa! What’s the deal with all those floating point functions? I do not recall seeing any float variables in the IR code.

000006dc <_fpadd_parts>:
0000096c <__addsf3>:
000009ba <__subsf3>:
00000a10 <__mulsf3>:
000006dc <_fpadd_parts>:
[...]

The code allows the timing of the incoming IR signals to be off be a certain percentage. The percentage is first calculated using floating point, but the result will be converted to an integer in the end:

#define TOLERANCE 25  // percent tolerance in measurements
#define LTOL (1.0 - TOLERANCE/100.)
#define UTOL (1.0 + TOLERANCE/100.)
/* ... */
#define TICKS_LOW(us) (int) (((us)*LTOL/USECPERTICK))
#define TICKS_HIGH(us) (int) (((us)*UTOL/USECPERTICK + 1))

I changed this to to get rid of the intermediate floating point arithmetic.

#define LTOL (100 - TOLERANCE)
#define UTOL (100 + TOLERANCE)
/* ... */
#define TICKS_LOW(us) (int) (( (long) (us) * LTOL / (USECPERTICK * 100) ))
#define TICKS_HIGH(us) (int) (( (long) (us) * UTOL / (USECPERTICK * 100) + 1))

This results in a much reduced code size:

avr-size main.elf
   text	   data	    bss	    dec	    hex	filename
   1950	      0	    140	   2090	    82a	main.elf

Step 3 – Putting it all together

I tested this using a modified Larson Scanner circuit from Evil Mad Science. The schematics are here and this is what I have changed:

  1. Used ‘4313 instead of ‘2312
  2. Replaced the button at port PB4 with an IR detector. I used a Vishay TSOP 4838.
  3. Added an activity indicator LED at port PB5.

The remote control is a cheap RC5 universal remote from dx.com.

DSC 0412

The code repository is on github.

git clone git://github.com/alohr/avr.git
cd avr/irrecv
# edit Makefile according to your programmer and clock frequency

# update flash *and* change AVR fuses:
make install

# flash only:
make flash

Clock frequencies I tried were 8 Mhz using the avr’s internal oscillator and 16 Mhz with an external crystal.

Some links I found useful during this project

Advertisements
This entry was posted in attiny2313, attiny4313, infrared. Bookmark the permalink.

16 Responses to Porting the Arduino IRremote library to the ATtiny4313

  1. Anonymous says:

    Hello. I tried your library but there always comes an error “IRrecvDemo:12: error: expected constructor, destructor, or type conversion before ‘;’ token”
    Here is my program:

    #include “irrecv.h”

    int RECV_PIN = 0;
    decode_results results;
    setup_irrecv (RECV_PIN);

    void setup()
    {
    }

    void loop() {
    if (irrecv_decode(&results)) {

    irrecv_resume(); // Receive the next value
    }
    }

    Could you, please, say where I made the mistake?

  2. ubiyubix says:

    Hi,

    please bear in mind that this is a port of an Arduino library to a “pure” avr-gcc environment. It looks like you would like to use this library in an Arduino environment though. In this case I’d recommend using Ken Shirriff’s original:

    https://github.com/shirriff/Arduino-IRremote

    if you do want to use pure avr-gcc, take a look here http://balau82.wordpress.com/2011/03/29/programming-arduino-uno-in-pure-c/

    Hope this helps,
    – ubiyubix

  3. Dan says:

    Awesome stuff, I know very little about the timers and all the stuff, but using your code I was able to get some IR receiving working on the attiny85(through arduino enviornment). I am not getting the exact codes that I am getting on the arduino, but I am at least detecting input.

  4. Moe says:

    Thanks for taking the time to share this code.

  5. Scott says:

    Do you think I can use your program to read 4 IR receivers and have the ATTiny4313 act as an I2C slave?

  6. ubiyubix says:

    Hi Scott,

    The timer interrupt routine reads into a static buffer which is then used by the decodeXXX() functions.

    typedef struct {
    uint8_t rcvstate; // state machine
    unsigned int timer; // state timer, counts 50uS ticks.
    unsigned int rawbuf[RAWBUF]; // raw data
    uint8_t rawlen; // counter of entries in rawbuf
    } irparams_t;

    You would need 4 of those structures. RAWBUF is 68. Four times 68 bytes is bigger than the SRAM size of the ‘4313 (256 bytes). So, no you can’t use this out of the box.

    The nice thing about this library (and the original Arduino library of course) is that it accepts many different IR protocols. If you do not need to accept input from different kinds of remote controls, then you may want to consider a different library tailored for one specific protocol. This will probably need less memory.

    You could also try modifying this library to use a single bit instead of a byte to store the pin readings.

    … or use a different chip with more memory to begin with.

    – ubiyubix

  7. Scott says:

    Thanks for the info. I’m actually not reading commands from an IR remote. I’m building a snow level detector. I’ll have an emitter sendinding data all the time, when one of the receivers stops seeing the emitter data, I’ll assume it’s being blocked by the snow. It’s essentially beam break detection.

  8. I tried your code on ATtiny4313 and always getting “UNKNOWN” for irresults.decode_type and zero for irresults.value. What am I doing wrong? I tried 3 remotes in my house. Apply remote, SONY TV remote and an LG Aircon remote. Please help

  9. ubiyubix says:

    Take a look in irrecv.h. I disabled most protocols to save memory:

    #undef COMPILE_DECODE_NEC
    #undef COMPILE_DECODE_SONY
    #undef COMPILE_DECODE_RC6
    #undef COMPILE_DECODE_JVC

    Be aware that if you enable any of those, then the raw buffer size to store the incoming bits increases. See my blog post https://ubiyubix.wordpress.com/2012/06/09/larson-scanner-remote-controlled/ in the section “RC5 protocol only”.

  10. Thanks ubiyubix, that worked great You made my day!

  11. Dhiren says:

    Please tell me if your code will also work for ATTiny85. If not, can you help me in that? Thanks….

  12. ubiyubix says:

    Hi Dhiren,

    you would have to change the TIMER1 settings to make sure the interrupt service routine for TIMER1 is called every 50 microseconds. The attiny4313 has a 16-bit TIMER1 whereas the attiny85 has an 8-bit TIMER1. The original Arduino library uses the 8-bit TIMER2 of the atmega328p. Take a look there to see how it’s done.

    -ubiyubix

  13. John Keefe says:

    Attempting the same as Dhiren, would welcome any guidance on how to change the TIMER1 settings. Also curious where you tweak the values based on your oscilloscope readings. Thanks a ton.

  14. ubiyubix says:

    Hi John, ok, I’ll try to write it up over the weekend.

  15. John Keefe says:

    Wow! Thank you. It makes much more sense now. I’ll let you know if I manage to pull off my project.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s