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:
- Extract the tarball into
libraries/ir_nec_4
for standard installation or use it locally. #include <ir_nec_4.h>
- Assign the IR receiver input pin using the constructor, e.g.
IR_NEC_4 ir(42);
Please note that PWM pins 6, 7, and 8 will be controlled by the timer interrupt as well. - In
setup()
, callir.enable()
to start checking for input. - In your main program, use
ir.checkResults()
andir.getResults()
to receive keycodes.
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:
- 9ms leading pulse for identifying the message start
- 4.5ms silence
- two actual payload bytes that are also repeated bitwise negated for error checking
- binary
0
: 562.5µs pulse followed by 562.5µs silence - binary
1
: 562.5µs pulse followed by 1.6875ms silence
- binary
- 562.5µs trailing pulse for identifying the message end
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.