Learn

Control lamps with an IR remote and an Arduino

  • 192parts on the bench
  • 25boards running now
  • 1.00xreal time, on every board
Arduino Uno: IR remote lampslive0.000 s 0.00x
Click to open it in the editor
The irlamp example: a VS1838B receiver on pin 2 and three lamps on 5, 6 and 7. Open it in the editor and click the handset.

The little remote and receiver pair in most Arduino kits speaks a protocol called NEC, and once you understand it, a lot of the confusion around IR remote tutorials goes away. The circuit above is an Arduino Uno with a VS1838B receiver on pin 2 and three lamps. Press 1, 2 or 3 on the handset to pick a lamp, 0 to turn them all off, and CH+ and CH- to change the brightness.

Open it in the editor and click the handset’s buttons. The serial monitor prints the address and command of every frame it decodes.

What you need

  • An Arduino Uno
  • A VS1838B IR receiver (or similar 38 kHz module)
  • An NEC remote, like the one in most kits
  • Red, green and blue LEDs with a 220 ohm resistor each
  • A breadboard and jumper wires

Wiring

Part and pin
Goes to
Note
IR receiver OUT
Pin 2
IR receiver VCC and GND
5 V and GND
Red LED, through 220 ohm
Pin 5
Button 1
Green LED, through 220 ohm
Pin 6
Button 2
Blue LED, through 220 ohm
Pin 7
Button 3

How NEC works

The receiver module filters the 38 kHz carrier out for you and gives the board a clean digital signal. The first surprise is that it inverts: OUT idles HIGH and goes LOW while light is arriving. Every timing in a decoder is written from that side.

An NEC frame starts with a 9 millisecond burst and a 4.5 millisecond gap, the leader. Then come 32 bits, where every bit is one short burst of 562.5 microseconds followed by either a short gap (a nought) or a gap three times as long (a one). The information is in the width of the gaps, not in the levels. A decoder measures each gap and splits them at about 1.1 milliseconds.

The 32 bits are the address, the address inverted, the command, and the command inverted, each sent least significant bit first. The two inverted copies are the whole of the error checking: if a byte and its inverse do not match, the frame is thrown away.

Why the codes in tutorials look different

Many older tutorials list the buttons as long hex numbers like 0xFFA25D. This sketch calls that same button command 0x45. They are the same 32 bits. The older decoder assembled the bits in the opposite order to the one they arrive in, so every byte came out reversed: 0x45 written backwards in binary is 0xA2. The example prints both, so you can match it to whatever table you are reading.

Hold a button down and the handset does not resend the whole frame. It sends one full frame, then a short repeat code every 110 milliseconds. A sketch that treats each repeat as a new press is how volume controls run away. This one only acts on repeats for the two brightness buttons, where holding is the point.

Acting on a frame, from the irlamp example (trimmed)
void loop() {
  IrFrame frame = ir.receive(300);
  if (!frame.valid) return;

  if (frame.repeated) {
    if (lastCommand == IR_CH_DOWN || lastCommand == IR_CH_UP) {
      applyBrightness(lastCommand == IR_CH_UP ? 25 : -25);
    }
    return;
  }

  lastCommand = frame.command;
  switch (frame.command) {
    case IR_1: pick(0); break;
    case IR_2: pick(1); break;
    case IR_3: pick(2); break;
    case IR_0: pick(-1); break;
  }
}

What is simulated, and what is not

In Mokxi two parts only meet on a net, so the beam between the handset and the receiver is drawn as a wire from the handset’s IR pin to the receiver. Everything after that is what happens on a real desk: the receiver inverts, the timings are NEC’s, the repeats come every 110 milliseconds, and the decoding is done by the sketch on the simulated Uno. What is missing is the optical side: there is no angle, no distance and no sunlight to get in the way.

Try it in the editor

Open the circuit in the editor, press a few buttons on the handset, and read the serial monitor. Each press prints the address, the command, and the older reversed code that tutorials list, side by side.

Then hold down CH+ or CH-. The brightness keeps changing while you hold it, because those two buttons act on repeat codes. Hold down 1 instead and nothing repeats, because the sketch deliberately ignores repeats for every other button.

To add a fourth lamp, wire another LED and resistor to a spare PWM pin, add it to the LAMPS array, and give it a case in the switch for IR_4, then make the loops that walk the lamps count to four.

Common mistakes

Wiring the receiver’s pins in the wrong order. The pin order differs between the bare component and the small modules, and a receiver powered backwards gets hot on a real bench. Read the module’s markings.

Treating repeat codes as presses, or ignoring them when you wanted auto-repeat.

Using a pin that the IR library does not support. The example decodes the signal itself, so any digital pin works, but some libraries want a particular pin or timer.

Comparing against the wrong code format, reversed or not, and deciding the remote is broken.

Questions

Do I need the IRremote library?

Not here. The IRremote library is not one of the headers the browser compiler carries; the example uses Mokxi’s own NEC decoder, which is short enough to read.

Why does my remote print FFFFFFFF?

That is how the older IRremote library reported an NEC repeat code: the button is being held. Newer decoders, including this one, flag it as a repeat instead.

Will any TV remote work?

Only if it speaks the same protocol. Kit remotes use NEC. TV remotes often use RC5, RC6 or a maker’s own protocol, which this decoder does not read.

Build this for real

Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.