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
Callout Tip: A common beginner mistake is wiring the sensor's DAT pin to Physical Pin 17 because the BCM number is 17. Physical Pin 17 is actually 3.3V Power. Always map by BCM number or double-check a physical pinout diagram.

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.

  1. De-energize the Pi: Shut down the Pi (sudo shutdown -h now) and disconnect the USB-C power supply.
  2. Wire the Sensor: Connect Sensor VCC to Pi Pin 1, Sensor GND to Pi Pin 6, and Sensor DAT to Pi Pin 11.
  3. Boot and Access Terminal: Power the Pi back on and open a terminal (or SSH in).
  4. 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).
  5. Add the Overlay: Scroll to the bottom of the file and add this exact line:
    dtoverlay=gpio-ir,gpio_pin=17
  6. Save and Reboot: Press Ctrl+O, Enter, Ctrl+X to save. Then type sudo reboot.
  7. 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.

Target Board Variant: This code is tested and compiled for the Raspberry Pi 4 Model B (4GB) running Raspberry Pi OS (Bookworm 64-bit). It is fully forward-compatible with the Raspberry Pi 5.
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

  1. Verify the Event Path: The kernel assigns /dev/input/eventX dynamically. If you hardcode event0 but your USB mouse took that slot, the IR receiver will be on event1 or event2. Run cat /proc/bus/input/devices and look for the block named gpio_ir_recv to find the correct eventX number. (The provided Python script automates this).
  2. Check the Boot Partition Path: If you edited /boot/config.txt on Raspberry Pi OS Bookworm, you edited a dummy file. The active configuration lives at /boot/firmware/config.txt. Ensure your dtoverlay is in the firmware directory.
  3. 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 detects KEY_PLAY, publish a payload to homeassistant/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.

Summary: For custom embedded projects where you need raw scancodes and GPIO integration, the TSOP38238 + gpio-ir overlay + evdev pipeline is the 2026 standard. For plug-and-play media centers, use a USB HID receiver.