Arduino IR library

Alternative IR remote library using the Timer4 hardware interrupt on Arduino boards, featuring a more robust decoder for the common NEC infrared protocol.

If you’re running into issues with a TSOP4838 IR receiver (or similar) on Arduino boards using the default IRremote library, this custom library might be worth a try. It features a robust NEC decoder and uses the otherwise rarely occupied Timer4 as interrupt.

As the default Stepper and IRremote libraries both use the 16bit Timer1 per default, this one uses the hardware Timer4 (in Output Compare Interrupt mode) to avoid collisions. Timer4 might not always be available, but is supported and most probably unused e.g. on Arduino Mega boards.

The custom NEC protocol parser is designed to match rather fuzzy and is thus robust with respect to signal quality. For example the widely available and cheap TSOP4838 receiver did not work reliably with standard libraries. This more robust library however comes with the tradeoff that features such as repeating or combined keypresses are not supported (yet).

IR library installation and usage

As with most standalone Arduino libraries, the usage is rather plug-and-play:

The checkResults method will signal whether a keycode is pending. If so, it will be kept available until the next getResults call – otherwise it will check for a new one right before. This however is optional, as getResults checks and returns keycodes right away.

Background: IR protocol decoding

The most interesting parts during this small project were the interrupt-based reading of the IR sensor, and the decoding of the NEC IR protocol that is commonly used by remote controls.

Arduino output compare interrupts

In order to periodically read the IR sensor pin, an interrupt context is needed. We’ll be using the 16bit Timer4 with a period of 50µs. This gives enough precision for detecting the shortest NEC pulse of 562.5µs (see below).

// interrupt sampling rate in microseconds
#define SRATE 50

// reset some unused 16bit timer (e.g. the 4th, controlling also PWM pins 6, 7, 8 on arduino mega)
noInterrupts();
TCCR4A = 0;
TCCR4B = 0;

We’ll using the output compare interrupt, as described here (in German). It counts in 1-steps (no prescale) to a certain value (output compare register), calls its handler, and starts from scratch (clear timer on compare match).

// start at 0 in CTC mode for resetting it
TCNT4 = 0;
TCCR4B |= (1 << WGM12); // clear timer on compare match

// e.g 16MHz -> 1/16us -> 800 for 50us
TCCR4B |= (1 << CS10); // no prescale (1)
OCR4A = (F_CPU / 1000000UL) * SRATE; // output compare register

// start output compare interrupt
TIMSK4 |= (1 << OCIE4A);
interrupts();

The interrupt handler can then be used to periodically read the sensor’s pin. The readings should only be buffered for later processing in the main program context, though.

ISR(TIMER4_COMPA_vect) {
    // read from IR sensor pin and store result...
}

NEC IR protocol decoding

The NEC IR transmission protocol is rather straight-forward. Only fixed-level binary bursts are transmitted and the data is encoded by the timing in-between. A full message transmission consists of:

The message can thus be decoded by measuring the duration of sensor high and low values. As the interrupt handler runs every 50µs, the number of consecutive highs/lows should be granular enough to tell the values apart.

Code & Download