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.

Difficulty Rating: Intermediate (Requires basic transistor biasing and serial debugging)
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

  1. Library Version Syntax: Are you using v2.x tutorials with the v4.x library? The old IRrecv object is gone. You must use IrReceiver and IrSender.
  2. Timer Conflicts: The Arduino Uno has only three hardware timers. IRremote defaults to Timer2. If you include Servo.h or Tone.h, they will fight for Timer2 and crash the compiler.
  3. 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

Error String 1: 'IRrecv' does not name a type or 'IRsend' does not name a type
Cause: 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().
Error String 2: 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); }

⚠️ HIGH VOLTAGE SAFETY WARNING: If you are switching 120V/240V AC mains with a relay, you MUST use a properly rated opto-isolated relay module (e.g., Songle SRD-05VDC-SL-C rated for 10A 250VAC). Never breadboard mains voltage. Ensure all mains connections are enclosed in a grounded, fire-rated junction box. Local electrical codes (NEC/IEC) may require this work to be performed or inspected by a licensed electrician.

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.