To interface an IR sensor with a Raspberry Pi, wire a 38kHz TSOP38238 receiver to BCM GPIO 17, enable the gpio-ir device tree overlay in your boot configuration, and decode remote presses using Python's evdev library. This modern approach bypasses the notoriously buggy and deprecated LIRC daemon, reading decoded scancodes directly from the Linux input subsystem.
Hardware Selection: Raw TSOP vs. Breakout Modules
Before wiring, you need to choose your IR receiver format. Infrared light from a standard remote control is modulated at a specific carrier frequency (usually 38kHz) to prevent the receiver from triggering on ambient sunlight or incandescent bulbs. The receiver demodulates this signal and outputs a clean digital HIGH/LOW pulse train to the Pi's GPIO.
| Component / Module | Form Factor | Carrier Freq | Wiring Complexity | Typical Price (2026) |
|---|---|---|---|---|
| Vishay TSOP38238 | Raw 3-pin DIP | 38 kHz | High (Requires 10kΩ pull-up, 100Ω current limiter, 4.7µF decoupling cap) | $1.50 / ea |
| KY-022 Sensor Module | PCB Breakout | 38 kHz | Low (Passives pre-soldered, 3-pin header) | $2.00 / ea |
| Generic VS1838B | Raw 3-pin DIP | 38 kHz | High (Same as TSOP, but lower noise immunity) | $0.20 / ea (bulk) |
| USB MCE Receiver | USB Dongle | 38 kHz | None (Plug and play HID keyboard) | $8.00 - $15.00 |
Source: Component specifications derived from the Vishay TSOP38238 Datasheet and standard maker-market breakout schematics.
For this guide, we will use the KY-022 breakout module (which houses a VS1838B or TSOP equivalent with onboard passives). It saves you from having to wire a decoupling capacitor and pull-up resistor on a breadboard, which is critical because the Pi's 3.3V rail can be noisy enough to cause phantom IR triggers without proper decoupling.
Parts List and GPIO Pin Mapping
Here is the exact bill of materials and pinout for the build. Do not use 5V for the sensor if you are wiring directly to the Pi's GPIO pins; the Pi's GPIO is strictly 3.3V tolerant. Feeding 5V into the data line will destroy the BCM chip.
Required Parts
- Board: Raspberry Pi 4 Model B (2GB, 4GB, or 8GB) or Raspberry Pi 5
- Sensor: KY-022 IR Receiver Module (3-pin)
- Wiring: 3x Female-to-Female Dupont jumper wires
- Remote: Any standard NEC or RC5 protocol IR remote (e.g., old TV or DVD player remote)
Pin Mapping Table
| KY-022 Sensor Pin | Pi Physical Pin | Pi BCM GPIO | Function |
|---|---|---|---|
| DAT / OUT | Pin 11 | BCM 17 | Digital Signal (Active LOW) |
| VCC / + | Pin 1 | N/A | 3.3V Power |
| GND / - | Pin 6 | N/A | Ground Reference |
Wiring Procedure and Bookworm Device Tree Overlays
Modern Raspberry Pi OS (Bookworm and later) changed the boot partition mount point and deprecated LIRC in favor of the kernel's built-in gpio-ir overlay. Follow these steps exactly to configure the kernel to listen on BCM 17.
- De-energize the Pi: Shut down the Pi (
sudo shutdown -h now) and disconnect the USB-C power supply. - Wire the Sensor: Connect Sensor VCC to Pi Pin 1, Sensor GND to Pi Pin 6, and Sensor DAT to Pi Pin 11.
- Boot and Access Terminal: Power the Pi back on and open a terminal (or SSH in).
- Edit the Boot Configuration: Open the config file. Note the path change for Bookworm:
sudo nano /boot/firmware/config.txt
(If you are on an older Bullseye release, the path is/boot/config.txt). - Add the Overlay: Scroll to the bottom of the file and add this exact line:
dtoverlay=gpio-ir,gpio_pin=17 - Save and Reboot: Press
Ctrl+O,Enter,Ctrl+Xto save. Then typesudo reboot. - Verify the Overlay Loaded: After reboot, run
dmesg | grep gpio_ir. You should see a line confirming the IR receiver was registered on gpio17.
Python Code: Reading IR Events with evdev
Forget LIRC and irrecord. The modern, robust way to read IR remotes in Python is via the evdev library, which treats the IR receiver exactly like a USB keyboard. First, install the library: sudo apt install python3-evdev.
import evdev
from evdev import InputDevice, categorize, ecodes
import sys
import os
# The kernel assigns an eventX ID to the IR receiver.
# It is rarely event0 if you have a USB keyboard/mouse attached.
# We dynamically find the device named 'gpio_ir_recv'.
DEVICE_PATH = None
for device_path in evdev.list_devices():
device = InputDevice(device_path)
if 'gpio_ir_recv' in device.name:
DEVICE_PATH = device_path
break
if not DEVICE_PATH:
print('ERROR: No gpio_ir_recv device found. Did you set the dtoverlay and reboot?')
sys.exit(1)
def main():
try:
dev = InputDevice(DEVICE_PATH)
print(f'Successfully bound to {dev.name} at {DEVICE_PATH}')
except PermissionError as e:
print(f'PERMISSION ERROR: {e}')
print('Fix: Run this script with sudo, or add your user to the input group.')
sys.exit(1)
except Exception as e:
print(f'UNEXPECTED ERROR: {e}')
sys.exit(1)
print('Listening for IR remote presses... (Press Ctrl+C to exit)')
# Read loop with graceful error handling
try:
for event in dev.read_loop():
# EV_KEY indicates a button press/release. value 1 = press, 0 = release
if event.type == ecodes.EV_KEY and event.value == 1:
key_event = categorize(event)
# Scancode is the raw hex value from the remote (e.g., 0x10 for '1')
print(f'Button Pressed: {key_event.keycode} | Raw Scancode: {hex(key_event.scancode)}')
# Example: Trigger an action on a specific key
if 'KEY_POWER' in key_event.keycode:
print('>> Power button detected! Triggering system action...')
except OSError as e:
print(f'DEVICE ERROR: The IR device was disconnected or failed. {e}')
except KeyboardInterrupt:
print('\nScript terminated by user.')
if __name__ == '__main__':
main()
Reference: Event categorization logic follows the official python-evdev documentation.
Debugging: Exact Errors and the First 3 Checks
When an IR sensor Raspberry Pi project fails, it is almost always a configuration mismatch rather than a broken sensor. If your Python script throws an error or prints nothing, perform these first three checks in order.
The First 3 Things to Check
- Verify the Event Path: The kernel assigns
/dev/input/eventXdynamically. If you hardcodeevent0but your USB mouse took that slot, the IR receiver will be onevent1orevent2. Runcat /proc/bus/input/devicesand look for the block namedgpio_ir_recvto find the correcteventXnumber. (The provided Python script automates this). - Check the Boot Partition Path: If you edited
/boot/config.txton Raspberry Pi OS Bookworm, you edited a dummy file. The active configuration lives at/boot/firmware/config.txt. Ensure yourdtoverlayis in the firmware directory. - Test the Remote's Carrier Frequency: Point your remote at a smartphone camera and press a button. If you don't see a faint purple/white light flashing on the screen, the remote is dead or uses a non-standard frequency (like 40kHz or 56kHz) that the 38kHz TSOP sensor cannot reliably demodulate.
Exact Error Strings and Fixes
| Exact Error String | Root Cause | Resolution |
|---|---|---|
FileNotFoundError: [Errno 2] No such file or directory: '/dev/input/event0' |
The script is looking for event0, but the IR receiver was assigned a different event ID, or the dtoverlay failed to load. | Run cat /proc/bus/input/devices to find the correct path, or check dmesg | grep gpio_ir for overlay errors. |
PermissionError: [Errno 13] Permission denied: '/dev/input/event2' |
The current user lacks read permissions for the raw input device node. | Run the script with sudo python3 ir_reader.py or create a udev rule to grant the 'input' group read access. |
OSError: [Errno 19] No such device |
The script was running, but the physical sensor lost connection or the kernel module crashed due to a power brownout. | Check physical jumper wire seating. Ensure the Pi's power supply is rated for at least 3A to prevent 3.3V rail brownouts. |
Extending and Simplifying the Build
Once you have raw scancodes printing to the console, you can adapt this setup to fit your specific project constraints.
How to Extend the Build
- MQTT Integration for Home Assistant: Wrap the
read_loop()in a Paho-MQTT publisher. When the script detectsKEY_PLAY, publish a payload tohomeassistant/media_player/command. This turns a $2 IR remote into a smart home controller. - Hardware Relay Triggering: If you want to physically toggle a 120V AC lamp or a 12V DC motor, do not wire a relay directly to the Pi's GPIO. Use the IR event to trigger a secondary GPIO pin connected to an optocoupler (like the PC817) or a ULN2003 Darlington array to safely drive a 5V relay coil without injecting back-EMF into the Pi's BCM chip.
- Fluorescent Light Rejection: If your sensor triggers randomly in a garage or workshop, compact fluorescent (CFL) and cheap LED ballasts emit broadband IR noise. Wrap the TSOP sensor in a piece of heat-shrink tubing with a small window, or add a physical bandpass filter (a piece of dark red acrylic) over the lens.
How to Simplify the Build
If you are building a production kiosk or a media center and do not want to deal with device tree overlays, GPIO wiring, or Python scripts, ditch the GPIO entirely. Purchase a standard USB Windows MCE IR Receiver (often found on eBay for $5) or a Flirc USB dongle ($20).
These plug into a USB port and register natively as a standard HID Keyboard. You can map remote buttons to keystrokes (like 'Space' for play/pause) using the Flirc GUI software, requiring zero code, zero overlays, and zero wiring on the Raspberry Pi.
gpio-ir overlay + evdev pipeline is the 2026 standard. For plug-and-play media centers, use a USB HID receiver.






