When global makers search for a sensor infravermelho arduino setup, they usually land on the ubiquitous KY-022 or a bare VS1838B diode. The direct answer for a reliable 5V build is to use the KY-022 module on Pin 2 of an Arduino Uno R3, paired with the IRremote v4.x library. If you are stepping down to 3.3V logic for an ESP32, abandon the KY-022 and use a TSOP38238 breakout instead to avoid comparator brownouts.
Choosing the Right IR Hardware for Your Bench
Not all 38kHz IR receivers are created equal. The physical packaging and onboard support components dictate whether a module will work on a 5V Uno or a 3.3V ESP32. The KY-022, for instance, includes a status LED and a current-limiting resistor wired directly to the VCC rail. If you feed a KY-022 with 3.3V, the internal LED won't fire, and the sensor's internal preamplifier may fail to trigger the comparator, resulting in silent drops.
| Module Variant | Sensor IC | VCC Range | Carrier Freq | Output Logic & Notes |
|---|---|---|---|---|
| KY-022 | VS1838B (Generic) | 4.5V - 5.5V | 38 kHz | Active LOW. Fails on 3.3V rails. |
| TSOP382 Breakout | Vishay TSOP38238 | 2.5V - 5.5V | 38 kHz | Active LOW. Excellent noise rejection. |
| Bare VS1838B | VS1838B | 2.7V - 5.5V | 38 kHz | Active LOW. Requires 4.7kΩ pull-up. |
| OS-Opto OS-38B | OS-38B | 2.7V - 5.5V | 38 kHz | Active LOW. Good for tight spaces. |
Parts List and Pin Mapping
For this build, we are targeting the Arduino Uno R3. The ATmega328P operates at 5V logic, making it perfectly matched to the KY-022. You will need:
- 1x Arduino Uno R3 (or compatible clone with CH340/ATmega16U2)
- 1x KY-022 IR Receiver Module (or TSOP382 breakout)
- 1x Standard IR Remote (NEC or RC5 protocol)
- 3x Male-to-Female jumper wires (22 AWG)
Pin Mapping Table
The IRremote library relies on hardware interrupts for precise microsecond timing. On the Uno R3, Pins 2 and 3 are the only hardware interrupt pins (INT0 and INT1). We use Pin 2 to leave Pin 3 free for PWM output if you later add an RGB LED.
| Arduino Uno R3 Pin | KY-022 Module Pin | Wire Color (Std) | Function |
|---|---|---|---|
| 5V | VCC (or +) | Red | Power (Must be 5V for KY-022) |
| GND | GND (or -) | Black | Common Ground |
| Pin 2 | S (or OUT) | Yellow | Demodulated IR Data (Active LOW) |
Compilable Code: IRremote v4.x with Error Handling
The Arduino-IRremote library underwent a massive API overhaul between v2 and v4. The code below is written strictly for v4.x. It includes explicit pin definitions, overflow checking, and repeat-flag handling to prevent your serial monitor from drowning in duplicate keypresses.
#include <IRremote.hpp>
// --- PIN DEFINITIONS ---
#define IR_RECEIVE_PIN 2
#define STATUS_LED_PIN LED_BUILTIN
void setup() {
Serial.begin(115200);
while (!Serial); // Wait for serial port (native USB boards)
// Initialize IRreceiver with LED feedback disabled to save power/noise
IrReceiver.begin(IR_RECEIVE_PIN, DISABLE_LED_FEEDBACK);
pinMode(STATUS_LED_PIN, OUTPUT);
digitalWrite(STATUS_LED_PIN, LOW);
Serial.println("IR Receiver Ready. Awaiting 38kHz signal...");
}
void loop() {
if (IrReceiver.decode()) {
// 1. Check for Buffer Overflow (happens if signal is too long/noisy)
if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_OVERFLOW) {
Serial.println("ERROR: IR Buffer Overflow. Signal too long.");
IrReceiver.resume();
return;
}
// 2. Handle Repeat Codes (e.g., holding down the Volume button)
if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT) {
Serial.println("REPEAT command received.");
digitalWrite(STATUS_LED_PIN, HIGH);
delay(50);
digitalWrite(STATUS_LED_PIN, LOW);
IrReceiver.resume();
return;
}
// 3. Process Valid Decoded Data
Serial.print("Protocol: ");
Serial.print(IrReceiver.decodedIRData.protocol);
Serial.print(" | Hex: ");
Serial.println(IrReceiver.decodedIRData.decodedRawData, HEX);
// Example: Trigger action on a specific NEC code
if (IrReceiver.decodedIRData.decodedRawData == 0xBA45FF00) {
Serial.println(">> Power Button Pressed!");
digitalWrite(STATUS_LED_PIN, !digitalRead(STATUS_LED_PIN));
}
// Resume receiving
IrReceiver.resume();
}
}
Debugging: Exact Error Strings and the First Three Checks
IR builds are notorious for failing silently or throwing cryptic compiler errors. If your build fails, run through this exact decision path.
The Compiler Errors
Error String: fatal error: IRremote.h: No such file or directory
Cause: You haven't installed the library, or you are using the wrong include name. In v4.x, the preferred header is <IRremote.hpp>, though .h often still maps. Open the Arduino IDE Library Manager, search for 'IRremote' by shirriff/ArminJo, and install the latest 4.x release.
Error String: error: 'class IRrecv' has no member named 'decode'
Cause: You are mixing v2 syntax with the v4 library. In v2, you instantiated an object (IRrecv irrecv(PIN);) and called irrecv.decode(&results). In v4, the library uses a global IrReceiver object. Use the code block provided above.
Hardware Fails: The First Three Things to Check
If the code compiles and uploads, but the Serial Monitor is dead when you press buttons on your remote, check these three physical layer issues:
- VCC Rail Mismatch: If you wired a KY-022 to the 3.3V pin on an Uno (or are using an ESP32), the module will not power its internal comparator. Fix: Move the red jumper to the 5V rail. If you must use 3.3V logic, switch to a bare TSOP38238 sensor.
- Ambient IR Saturation: Take the board outside or point it at a CFL bulb. If the serial monitor suddenly spams random hex codes, your sensor is saturated by environmental 940nm noise. Fix: Shield the sensor with heat-shrink tubing, or upgrade to a Vishay TSOP module with better optical filtering (SparkFun IR Guide).
- Wrong Remote Protocol: The code above prints the raw hex. If your remote uses the RC5 or Sony SIRC protocol, the hex values will look different than NEC. Fix: Rely on
IrReceiver.printIRResultShort(&Serial);to let the library auto-format the protocol and command byte, rather than hardcoding raw 32-bit hex strings.
Extending and Simplifying the Build
Once you have raw hex codes printing to the serial monitor, you have two paths forward depending on your project goals.
Simplifying for Quick Prototyping
If you just want to map buttons without writing complex if/else trees, replace the manual hex parsing in the loop() with the built-in formatter:
if (IrReceiver.decode()) {
IrReceiver.printIRResultShort(&Serial);
IrReceiver.resume();
}
This outputs a clean string like Protocol=NEC Address=0x0 Command=0x18. You can then map actions purely on the 8-bit Command byte, ignoring the address header.
Extending for Home Automation
To turn this into a practical appliance controller, you need to switch mains voltage. Never wire an Arduino GPIO directly to a relay coil. The back-EMF will fry the ATmega328P. Instead:
- Add an optocoupler (PC817) or a pre-built 5V Relay Module with an onboard optoisolator.
- Map specific IR hex commands to toggle the relay GPIO.
- For WiFi integration, swap the Uno R3 for an ESP32-WROOM-32. Use the attachInterrupt() documentation to verify ESP32 pin compatibility, and push the decoded IR commands to an MQTT broker via the PubSubClient library to integrate with Home Assistant.






