An IR Remote Controls the ESP32: the VS1838B Receiver and the NEC Protocol
Any old TV remote becomes a wireless keypad. The VS1838B receiver, the IRremote 4.x library, how an NEC frame is built, repeat codes when a key is held — and three LEDs switched from the remote.
About the project
An infrared remote is the cheapest wireless control there is: no Wi-Fi, no pairing, no battery on the receiver side. The VS1838B receiver (or a TSOP38238) filters out the 38 kHz carrier by itself and outputs clean digital pulses, and the IRremote library turns them into key codes. We control three LEDs from the common 21-key "Car MP3" remote.
What you need
- ESP32 DevKit
- VS1838B IR receiver (bare or on a module) + any remote
- 3 LEDs and 3 × 220 Ω resistors
- The IRremote library, version 4.x
Wiring
| Receiver | ESP32 |
|---|---|
| OUT (left, looking at the lens) | GPIO15 |
| GND (middle) | GND |
| VCC (right) | 3V3 |
LEDs: GPIO25 / 26 / 27 → 220 Ω → LED → GND.
The pinout of VS1838B parts and modules varies, so check yours. Swapping VCC and GND kills the receiver instantly.
The NEC protocol
The remote blinks its LED at 38 kHz, and the receiver outputs LOW while it sees that light. An NEC frame:
9 ms LOW │ 4.5 ms HIGH │ 32 bits: address, ~address, command, ~command │ stop
bit "0": 562 µs LOW + 562 µs HIGH bit "1": 562 µs LOW + 1687 µs HIGH
- The address selects the device (often 0x00 on cheap remotes), the command selects the key.
- The inverted copies (~address, ~command) are a check, so interference does not become a wrong command.
- Holding a key: instead of repeating the frame, the remote sends a short repeat code every 108 ms. IRremote marks it with the
IRDATA_FLAGS_IS_REPEATflag.
Code
// IR remote (NEC, 21-key "Car MP3") → three LEDs. Library: IRremote 4.x
#include <IRremote.hpp>
const int IR_PIN = 15;
const int LEDS[] = {25, 26, 27};
bool state[3] = {false, false, false};
void setup() {
Serial.begin(115200);
for (int i = 0; i < 3; i++) pinMode(LEDS[i], OUTPUT);
IrReceiver.begin(IR_PIN, ENABLE_LED_FEEDBACK);
Serial.println("Press 1, 2, 3 or 0 on the remote");
}
void toggle(int i) {
state[i] = !state[i];
digitalWrite(LEDS[i], state[i]);
}
void loop() {
if (!IrReceiver.decode()) return;
IrReceiver.printIRResultShort(&Serial); // protocol, address and command to the port
// a held key sends repeat codes: react only to the first press
if (!(IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT)) {
switch (IrReceiver.decodedIRData.command) {
case 0x0C: toggle(0); break; // 1
case 0x18: toggle(1); break; // 2
case 0x5E: toggle(2); break; // 3
case 0x16: // 0 — all off
for (int i = 0; i < 3; i++) { state[i] = false; digitalWrite(LEDS[i], LOW); }
break;
}
}
IrReceiver.resume(); // ready for the next code
}
Finding your remote's codes
Leave only printIRResultShort(&Serial) and resume() in loop(), press the keys and write down the Command=0x… values. Then put them into the switch.
Older tutorials use the IRremote 2.x API (IRrecv irrecv(pin); decode_results results; results.value) with codes like 0xFFA25D. That is the same frame, just read with the bits in reverse order. Version 4.x splits it into address and command.
Good to know
- Sunlight and energy-saving lamps produce IR noise. Keep the receiver in the shade and add 100 nF between VCC and GND right at it.
- Don't forget
resume(), or no further codes are received. - You can transmit too: a 940 nm IR LED driven through a transistor plus
IrSender.sendNEC(address, command, 0)turns the ESP32 into a remote for an air conditioner or a TV.