Recently I bought a small IR remote control + matching (38kHz) demodulating receiver at Adafruit. I just couldn’t find such a small remote over here. As it turns out the remote uses the extended NEC data format, which is nicely described on this site.

Each frame looks like this:

 start condition $S:\usepackage[electronic]{ifsym}\textifsym{|HHHHHH|LL}\dots$ (9.0ms, 4.5ms) address low-byte $L:\usepackage[electronic]{ifsym}\textifsym{LLLLL|H|L|H|}$ logic values shown address high-byte $H:\usepackage[electronic]{ifsym}\textifsym{|H|L|H|LLLLL}$ logic values shown command byte $C:\usepackage[electronic]{ifsym}\textifsym{|H|L|H|LLL|H|L|}$ logic values shown command byte $\bar{C}:\usepackage[electronic]{ifsym}\textifsym{|L|H|L|HHH|L|H|}$ logic values shown “something” $X:\usepackage[electronic]{ifsym}\textifsym{|h|LLLLLLL}\dots$ (0.5ms 40.0ms) repeat-code $R:\usepackage[electronic]{ifsym}\textifsym{|HHHHHH|L|h|L}\dots$ (9.0ms, 2.25ms, 0.5ms)

A logic 0 is encoded like this: $0:\usepackage[electronic]{ifsym}\textifsym{|HH|LL|}$ – a 560µs burst and 560µs of silence.

A logic 1 is encoded as: $1:\usepackage[electronic]{ifsym}\textifsym{|HH|LLLLLL}$ – a 560µs burst and 1690µs of silence.

In the standard NEC data format the address high-byte would be the logic inverse of the low-byte. In the extended format the 16bit addresses which are made up of $(L,\bar{L})$ are invalid.

The part I named “something” doesn’t seems to be in the protocol, or at least I can’t find it. But what I’ve found out is that my remote seems to mess this part up depending on how hard/long I press the buttons. Therefore I don’t check this section in the IR-code matching algorithm. It chops of the start condition and the last pulses that are always the same (including the “something” part).

A measured key-code in raw data (µs/10) for my remote looks like this:

(882, 438)
(54, 56)
(52, 58)
(52, 54)
(56, 54)
(54, 56)
(54, 54)
(56, 54)
(54, 56)
(54, 164)
(52, 166)
(54, 166)
(52, 166)
(52, 166)
(56, 164)
(54, 54)
(54, 166)
(52, 56)
(54, 56)
(52, 58)
(52, 54)
(56, 54)
(54, 56)
(54, 54)
(56, 54)
(54, 164)
(56, 164)
(52, 166)
(52, 166)
(56, 164)
(54, 164)
(52, 166)
(56, 164)
(54, 4002)
(878, 218)
(54, 0)

(00000000, 11111101, 11111111, 00000000) = (0x00, 0xFD, 0xFF, 0x00), which happens to be the “volume down” button on my unit.

The values got from the Arduino board are pretty close to the logic analyzer results. 9000µs vs. 8820µs, 558µs vs. 520µs… pretty good. What I didn’t test was repeatability, neither with the Arduino, nor with the logic analyzer. The code has a ‘FUZZINESS’ factor, that accepts a certain variation.

I took the first measurements with some code from Adafruit (github), but then immediately started my own thing for the detection. I wanted this done with a pin-change-interrupt, so it wouldn’t block the main code.

Every time there is a change in signal level, the interrupt fires, takes the current time, counts the pulses, calculates and stores the last pulse-length and quits. This is pretty time-efficient and doesn’t block. The main loop polls for new data. The decision to say “there is new data” is made by looking at how long ago the last interrupt has fired and if it has fired at all (ever).

The ‘atomic block’ is used to make sure this happens undisturbed by any interrupt. Especially flipping the two buffers (the pointers to them to be precise) should not happen in the middle of a write access to one of them.

The interrupt itself is pretty short, lean and mean:

That’s all it takes to receive raw IR messages using a demodulating receiver. I don’t really need any decoding, only checking against reference data which is stored in PROGMEM. A lot of space could be saved by using the information on how 0s and 1s are encoded, but that would also limit the receiver to a certain data format.

Identifying the incoming IR data is pretty simple. The pulse-train is stored in RAM and compared against the reference data step by step, allowing for a certain percentage of deviation. If everything matches, the pulse has been identified. The code [1],[2] I wrote of course does just that and spits out a number, which can easily be processed in a switch statement.

As a first application I’ve thrown the IR code and some stuff I wrote earlier (using a slightly modified version of the V1.03 ShiftPWM library) into one file and pretty quickly got a remote controlled mood light. Unfortunately it seems that the ShiftPWM library is pretty picky about what else goes on in the little AVR, so there is some flickering when an IR message is received. But we already know from the Matrix movies that things just have to flicker if something is changed, don’t we? The next logical step is to remove the burden of IR receiving and matching to another micro (ATtiny85) and send simply byte-sized instructions to the main processor using either SPI or bit-banged serial. I²C would be nice as well, but that seems too complicated for now.

Most of the flickering however is caused by my camera. The only noticeable flickering takes place when IR data is received (moving messages on the screen). Everything else is an artifact of the PWM and camera shutter.

The TSOP38238 is a good receiver btw.

The current firmware (navigate to the “/85/IR_receiver/” folder) supports up to 9 ‘board addresses’ and a ‘general call’, which is very useful when more than one receivers are within a room. First press a digit to indicate which board should listen, then press the command buttons as usual. There is a 4 second timeout. If you press the ‘general call’ address, all boards will listen.

This entry was posted in Arduino., Electronics. and tagged , , , , . Bookmark the permalink.