Project Overview and Hardware Specifications
Decoding and transmitting infrared signals is a foundational embedded skill, but the ecosystem is rife with outdated tutorials. This guide targets the modern Arduino IRremote v4.x library (maintained by Armin Joachimshof), abandoning the deprecated v2.x syntax that causes 90% of beginner compilation errors. We will build a robust transceiver circuit using an Arduino Uno R3 (ATmega328P DIP-28), a VS1838B 38kHz receiver, and a transistor-driven 940nm IR LED transmitter.
Estimated Time: 45 minutes
Target Board Variant: Arduino Uno R3 (AVR ATmega328P, 5V logic, 16MHz clock)
Bill of Materials (Exact Variants)
- Microcontroller: Arduino Uno R3 (DIP-28 ATmega328P)
- IR Receiver: VS1838B 38kHz Infrared Receiver Module (3-pin breakout)
- IR Transmitter: 940nm Infrared LED (5mm, high-power)
- Driver Transistor: 2N2222 NPN BJT (TO-92 package)
- Resistors: 1x 100Ω (1/4W) for LED current limiting, 1x 1kΩ (1/4W) for transistor base
- Power: 5V USB or 7-12V DC barrel jack
Component Specification Sheet
| Component | Parameter | Value / Rating | Design Note |
|---|---|---|---|
| VS1838B Receiver | Operating Voltage | 2.7V to 5.5V DC | Safe for direct 5V Arduino connection; do not exceed 6V. |
| VS1838B Receiver | Carrier Frequency | 38kHz ± 5% | Matches standard NEC/RC5 consumer remotes. |
| VS1838B Receiver | Reception Angle | ±45° (Half-angle) | Off-axis signal strength drops by ~50% at 45 degrees. |
| 940nm IR LED | Forward Voltage (Vf) | 1.2V to 1.4V @ 100mA | Requires current limiting; never drive directly from GPIO. |
| 940nm IR LED | Peak Wavelength | 940nm | Matches VS1838B peak sensitivity (human eye cannot see it). |
| 2N2222 Transistor | Max Collector Current | 600mA (Continuous) | Allows pulsing the IR LED at 100mA+ for extended range. |
IR Protocol Decoding: NEC vs. RC5 vs. Sony
Before writing code, you must understand what the Arduino IRremote library is actually decoding. IR remotes do not just send a "button number"; they send structured packets with carrier frequencies, headers, and checksums. The VS1838B strips the 38kHz carrier wave, leaving the digital envelope for the Arduino's timer to measure.
| Protocol | Carrier Freq | Bit Length | Repeat Mechanism | Common Applications |
|---|---|---|---|---|
| NEC | 38kHz | 32-bit (Addr + Cmd) | Special repeat code (no data) | Most modern TVs, DIY kits, car audio |
| RC5 / RC6 | 36kHz | 12 or 14-bit | Toggle bit flips on new press | Philips, older European audio gear |
| Sony SIRC | 40kHz | 12, 15, or 20-bit | Entire frame repeated 3x | Sony Bravia, PlayStation, camcorders |
| Samsung | 38kHz | 32-bit | Entire frame repeated | Samsung TVs, soundbars, AC units |
For a deep dive into the NEC protocol timing and leader/pause pulses, refer to the reverse-engineering documentation at SB Projects IR Knowledge Base.
Wiring the VS1838B Receiver and Transmitter
A common mistake is wiring the IR LED directly to an Arduino GPIO pin. An Arduino Uno pin can safely source only 20mA continuous current. A high-power 940nm IR LED needs 100mA pulses for reliable room-wide transmission. We use a 2N2222 NPN transistor as a low-side switch to drive the LED from the 5V rail, controlled by the Arduino's PWM/Timer pin.
Pin Mapping Table
| Component | Component Pin | Arduino Uno R3 Pin | Wiring Notes |
|---|---|---|---|
| VS1838B | OUT (Signal) | D2 (Digital Pin 2) | Requires external interrupt pin on AVR. |
| VS1838B | VCC | 5V | Do not use 3.3V on the Uno. |
| VS1838B | GND | GND | Connect to common ground. |
| IR LED | Anode (+) | 5V (via 100Ω Resistor) | Resistor limits current to ~38mA continuous (pulsed higher). |
| IR LED | Cathode (-) | 2N2222 Collector | Current flows through LED into transistor. |
| 2N2222 | Base | D3 (via 1kΩ Resistor) | D3 is the default IRremote transmit pin on Uno. |
| 2N2222 | Emitter | GND | Connect to common ground. |
Complete Arduino Code: Receiving and Transmitting
The following code uses the modern IRremote v4.x object-oriented syntax. It initializes the receiver on Pin 2, waits for a decoded NEC signal, prints the hex value to the Serial Monitor, and then immediately re-transmits that exact signal via the IR LED on Pin 3. This is perfect for cloning a remote button.
/*
* IRremote v4.x Transceiver Example
* Target Board: Arduino Uno R3 (ATmega328P)
* Library: IRremote by Armin Joachimshof (v4.x)
*/
#include <IRremote.h>
// --- PIN DEFINITIONS ---
#define IR_RECEIVE_PIN 2 // Must be an interrupt-capable pin on AVR
#define IR_SEND_PIN 3 // Default hardware timer pin for IRremote on Uno
#define STATUS_LED_PIN 13 // Built-in LED for visual feedback
// --- PROTOCOL CONFIGURATION ---
// Uncomment the protocols you want to decode to save RAM and CPU cycles
// By default, v4.x enables all, which can cause memory issues on ATmega328P
#define DECODE_NEC
// #define DECODE_SONY
// #define DECODE_RC5
void setup() {
Serial.begin(115200);
while (!Serial); // Wait for serial port (native USB boards)
pinMode(STATUS_LED_PIN, OUTPUT);
// Initialize Receiver
// DISABLE_LED_FEEDBACK prevents the receiver from using the built-in LED
IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);
// Initialize Sender
IrSender.begin();
Serial.println(F("IRremote v4.x Transceiver Ready."));
Serial.println(F("Point your remote at the VS1838B receiver..."));
}
void loop() {
// Check if IR data has been received
if (IrReceiver.decode()) {
// Blink status LED to confirm reception
digitalWrite(STATUS_LED_PIN, HIGH);
// Print decoded results to Serial Monitor
IrReceiver.printIRResultShort(&Serial);
// Error Handling: Check for overflow or unknown protocols
if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_WAS_OVERFLOW) {
Serial.println(F("ERROR: Buffer overflow. Signal too long or CPU busy."));
} else if (IrReceiver.decodedIRData.protocol == UNKNOWN) {
Serial.println(F("WARNING: Unknown protocol. Try adjusting sampling rate."));
} else {
// --- TRANSMIT CLONED SIGNAL ---
// We only re-transmit if it's a known protocol (e.g., NEC)
Serial.println(F("Re-transmitting captured signal..."));
// write() automatically handles the protocol, address, command, and repeats
IrSender.write(&IrReceiver.decodedIRData, true);
}
digitalWrite(STATUS_LED_PIN, LOW);
// CRITICAL: Resume receiving after processing
IrReceiver.resume();
}
}
Debugging: Compilation Errors and Signal Drops
When working with hardware timers and legacy codebases, things break. If your build fails or your range is terrible, follow this diagnostic path.
The First Three Things to Check When It Fails
- Library Version Syntax: Are you using v2.x tutorials with the v4.x library? The old
IRrecvobject is gone. You must useIrReceiverandIrSender. - Timer Conflicts: The Arduino Uno has only three hardware timers. IRremote defaults to Timer2. If you include
Servo.horTone.h, they will fight for Timer2 and crash the compiler. - Ambient Light Saturation: Modern CFL and LED room lighting often flicker at high frequencies that bleed into the 38kHz band. If your serial monitor spams random noise, cover the VS1838B with your hand. If the noise stops, you have ambient IR pollution.
Exact Error Strings and Ranked Causes
'IRrecv' does not name a type or 'IRsend' does not name a typeCause: You are using v2.x syntax (
IRrecv irrecv(RECV_PIN);) with the modern v4.x library installed in your IDE.Fix: Rewrite your code to use the v4.x global objects:
IrReceiver.begin(PIN) and IrSender.begin().
multiple definition of '__vector_7' or multiple definition of '__vector_5'Cause: Hardware Timer Conflict. Another library (like
Servo.h or SoftwareSerial.h) has claimed the same AVR interrupt vector that IRremote needs for carrier generation.Fix: In the
IRremote.h file (or via compiler flags), force IRremote to use a different timer. Alternatively, on the Uno, move your Servo to a software-servo library to free up Timer1/Timer2.
Hardware Debugging: Why is my transmit range only 2 inches?
If your receiver works but your transmitter requires you to press the LED against the sensor, your IR LED is under-driven.
Measurement Threshold: Use your multimeter to measure the voltage across the 100Ω current-limiting resistor during a transmit pulse. If you see less than 1.5V, your transistor isn't saturating, or your GPIO isn't outputting a clean 5V PWM signal. Ensure the 2N2222 base has a 1kΩ resistor and that the IR LED is wired Anode-to-5V, Cathode-to-Collector.
Extending the Build: Relays and Smart Home Integration
How to Extend: Mains Control via Relays
To turn this IR decoder into a universal remote for your home, map specific NEC hex codes to a 5V relay module.
Example logic: if (IrReceiver.decodedIRData.command == 0x18) { digitalWrite(RELAY_PIN, HIGH); }
How to Simplify: All-in-One Alternatives
If managing transistor biasing and AVR timer conflicts feels like overkill for your project, consider simplifying the hardware layer:
- M5Stack Atom IR: An ESP32-based micro-module with a built-in IR transmitter and receiver pre-wired to the correct GPIOs. It eliminates breadboarding entirely.
- ESP32 RMT Peripheral: If you move from the Uno to an ESP32 DevKit, you bypass hardware timer conflicts entirely. The ESP32 uses the Remote Control (RMT) peripheral, which handles IR carrier generation in hardware without tying up CPU interrupts, making it vastly superior for multitasking IoT devices running MQTT alongside IR decoding.






