Archive for May, 2012

Reversing an RGB LED remote

Thursday, May 10th, 2012

I have this dream to someday light our basement with RGB LEDs. They often come with remotes and controllers, which are surprisingly inexpensive. The problem with the remotes you get for cheap on ebay is that you *have* to use the remote to change the lights, and that of course limits you to the buttons on the remote.

I’d like to make an in-wall dimmer/color changer for LED mood lighting, but with the added feature of being compatible with the existing cheap LED remotes on the market.

That means reverse-engineering one. And here it is:


What’s the first thing I do when I get a new gadget? Take it apart, of course. Inside, it’s pretty simple:

There’s a 256-Byte two-wire serial EEPROM in the top left, a 5V linear regulator along the lower edge, and an unmarked 16-pin IC that is the main controller. Three transistors along the side drive the three colors of LEDs, and an external IR receiver connects to the board via the Red/Blue/Black triad.

I’m really not sure what the serial EEPROM is there for–perhaps the memory setting I couldn’t get to work?

IR Protocol

I soldered a couple leads to appropriate points and hooked it up to the oscilloscope to capture the incoming signals. Some day I hope to own a logic analyzer, but for now the oscope will suffice, especially when I only need one or two channels.

Run a Single trigger, press a button on the remote, and here’s what I got:

A bunch of zooming/shifting/saving/etc later, I had a bunch of data captured. Here’s the signal you get when pressing the “Red” button (click for big):

When the IR LED is on, the signal goes low, and when it’s off, the signal goes high.

There’s a 9ms low pulse, a 4.5ms high pulse, and then the data starts. Off I ran to find some IR protocols. I found myself in a familiar place–SBProjects, which contains a remarkably thorough knowledgebase on IR remotes and protocols. As it turns out, the protocol is the NEC protocol Each bit is encoded by a 560uS on pulse followed by an off pulse of varying length–560uS off is a logical 0, 1680uS off is a logical one. There are 32 bytes total. The first byte is an address, the second is the inverse of the address, the third is the command, and the fourth is the inverse of the command, with a final on pulse after the last bit. It’s little-endian, so least-significant bit comes first for each byte.

I mapped out all the remote’s buttons, and here’s the result. For all buttons, the address was 0xFF. Only in the command did it differ. The following table contains the buttons and commands. Note that these are little-endian, and written in hex.

Up  Down Play Pwr
0x3A 0xBA 0x82 0x02

Red  Grn  Blu  Wht
1A   9A   A2   22

2A   AA   92   12

0A   8A   B2   32

38   B8   78   F8

18   98   58   D8

RUp  GUp  BUp  Quick
28   A8   68   E8

RDn  GDn  BDn  Slow
08   88   48   C8

30   B0   70   F0

DIY4 DIY5 DIY6 Flash
10   90   50   D0

JMP3 JMP7 Fade Fade7
20   A0   60   E0

It’s pretty much a binary key matrix–the remote is just sending a code for the key that was pressed, rather than RGB values or anything more complex. The first four rows have A as the second nybble (little-endian, though!), the next four have 8 (0xA minus 0x1), the last three have 0 (0x8 minus 0x8). The second column is the same as the first column except for the first bit being flipped. The third column is the same as the first, except the second bit is flipped, and the third column has both the first and second bits flipped compared to the first column.

All the decoding must be done in the controller.


The controller outputs a PWM signal for each color. I noticed that the PWM frequency didn’t seem all that high–I could at times detect the flashing. (As an aside, it drives me nuts that car companies use stupidly low PWM frequencies for LED tail/brake lights, but that’s another subject) So I checked out the wave form for the outputs.

Each output is turned on for 840uS per cycle. To vary the brightness, the controller adjusts the off time. That varies from 5.36mS at the dimmest setting to 320uS at the brightest. That means the controller only gets up to about 72% duty cycle at its brightest setting, and down to 14% at the lowest setting. Since humans percieve brightness logarithmically, there’s a big low end that’s getting missed, although it might be too dim at that end to be useful.

Where am I going next with this? I’m not sure yet, but at least it’s now documented.