ESP32 IR Receiver for Home Assistant: Learn Remote Codes with ESPHome

An ESP32 IR receiver is one of the simplest ways to learn remote control codes for Home Assistant. Instead of guessing which protocol your TV, air conditioner, fan or LED strip remote uses, you can point the remote at a cheap IR receiver module and let ESPHome show you the decoded signal.

This is especially useful if you are building an ESP32 IR blaster. First you use the receiver to learn the original remote codes. Then you copy those codes into your IR blaster configuration and let Home Assistant control the device automatically.

ESPHome includes a remote_receiver component that can receive and decode common infrared and RF remote control signals. It can also dump received codes to the ESPHome logs, which makes it ideal for learning unknown remote buttons.

What this project does

This project lets you:

  • Capture IR remote codes using an ESP32
  • View decoded IR codes in ESPHome logs
  • Identify buttons such as power, volume, mode, temperature up and temperature down
  • Create Home Assistant binary sensors for specific remote buttons
  • Trigger Home Assistant automations from a normal IR remote
  • Use the captured codes later with an ESP32 IR blaster

This is not only for TVs. It can also work with many infrared-controlled devices, including:

  • Air conditioners
  • Fans
  • LED strip controllers
  • Soundbars
  • Projectors
  • Media boxes
  • Cheap RGB lamps
  • Old Hi-Fi equipment

Parts needed

You only need a few parts:

PartNotes
ESP32 development boardESP32 DevKit, ESP32-S3, ESP32-C3 or similar
IR receiver moduleVS1838B, TSOP38238, KY-022 or similar
Jumper wiresFemale-to-female wires are easiest
USB cableFor flashing and powering the ESP32
Home Assistant with ESPHomeESPHome add-on or ESPHome Device Builder

A typical IR receiver module has three pins:

IR receiver pinConnect to ESP32
VCC3.3V
GNDGND
OUT / DATA / SGPIO23

GPIO23 is only an example. You can use another safe GPIO pin if needed. Avoid using boot strapping pins unless you know exactly what your board does during startup.

How an IR receiver works

An IR remote sends short bursts of infrared light. The receiver module detects those bursts and outputs a digital signal to the ESP32.

ESPHome then tries to decode the signal. If the protocol is recognised, you may see something like Samsung, NEC, Sony, Panasonic or LG in the logs. If ESPHome does not recognise the protocol, it can still show the raw timing data. ESPHome’s IR guide explains that known codecs are shown as decoded values, while unknown ones can still be captured as raw data.

That means you do not always need to know the protocol in advance.

Basic ESPHome YAML for learning IR codes

Create a new ESPHome device and use this as a starting point:

esphome:
name: esp32-ir-receiver
friendly_name: ESP32 IR Receiver

esp32:
board: esp32dev

logger:
level: DEBUG

api:
encryption:
key: "PASTE_YOUR_API_KEY_HERE"

ota:
- platform: esphome
password: "PASTE_YOUR_OTA_PASSWORD_HERE"

wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password

ap:
ssid: "ESP32 IR Receiver Fallback"
password: "fallback-password"

captive_portal:

remote_receiver:
id: ir_receiver
pin:
number: GPIO23
inverted: true
mode:
input: true
pullup: true
dump: all
tolerance: 25%
filter: 50us
idle: 10ms

The important part is this:

remote_receiver:
pin:
number: GPIO23
inverted: true
mode:
input: true
pullup: true
dump: all

dump: all tells ESPHome to print any decoded remote codes in the logs. The ESPHome documentation also notes that some IR receiver modules may need an internal pull-up and inverted input, which is why this configuration is a good practical starting point.

How to capture IR remote codes

After flashing the ESP32:

  • Open the ESPHome logs
  • Point your remote control at the IR receiver
  • Press one button at a time
  • Watch the log output
  • Copy the decoded code or raw data
  • Repeat the same button several times to confirm it is consistent

You may see output like this:

[D][remote.nec:098]: Received NEC: address=0x00FF, command=0x807F

Or something like this:

[D][remote.samsung:098]: Received Samsung: data=0xE0E040BF, nbits=32

For an unknown protocol, you may see raw timing data instead:

[D][remote.raw:041]: Received Raw: 9024, -4478, 563, -563, 563, -1687, 563, -563

The decoded format is easier to use, but raw codes are still useful. ESPHome’s documentation says raw output is a sequence of pulse widths in microseconds, with positive values for marks and negative values for spaces.

Recommended workflow for learning buttons

Do not capture the whole remote randomly. Make a small table while testing.

Example:

ButtonProtocolCode
PowerNECaddress 0x00FF, command 0x807F
Volume UpSamsungdata 0xE0E0E01F
Volume DownSamsungdata 0xE0E0D02F
MuteSamsungdata 0xE0E0F00F
InputRawcopy full raw code

Press every button at least three times. Some remotes send a repeat code when you hold the button down, so use short presses when learning.

Creating Home Assistant sensors for remote buttons

Once you know a code, you can create a binary sensor for that exact remote button.

Example for a NEC remote button:

binary_sensor:
- platform: remote_receiver
name: "TV Remote Power"
nec:
address: 0x00FF
command: 0x807F

Example for a Samsung remote button:

binary_sensor:
- platform: remote_receiver
name: "Samsung Remote Volume Up"
samsung:
data: 0xE0E0E01F
nbits: 32

When ESPHome receives that exact code, the binary sensor briefly turns on and then off again. ESPHome documents this behaviour for remote_receiver binary sensors.

This is useful when you want Home Assistant to react to normal remote control buttons.

Triggering Home Assistant automations from an IR remote

You can use the IR receiver as a simple Home Assistant remote control.

For example:

  • Press a spare TV remote button
  • ESP32 detects the IR code
  • Home Assistant toggles a lamp, scene, speaker or automation

Here is an ESPHome example using a binary sensor:

binary_sensor:
- platform: remote_receiver
name: "IR Remote Lamp Button"
nec:
address: 0x00FF
command: 0x40BF

Then in Home Assistant:

alias: Toggle lamp from IR remote
trigger:
- platform: state
entity_id: binary_sensor.ir_remote_lamp_button
to: "on"
action:
- service: light.toggle
target:
entity_id: light.living_room_lamp
mode: single

This turns an old remote control into a cheap physical controller for Home Assistant.

Better method: use ESPHome events

For more advanced setups, you can send a Home Assistant event from ESPHome when a specific IR code is received.

Example:

api:
encryption:
key: "PASTE_YOUR_API_KEY_HERE"

remote_receiver:
id: ir_receiver
pin:
number: GPIO23
inverted: true
mode:
input: true
pullup: true
dump: all

binary_sensor:
- platform: remote_receiver
name: "IR Remote Scene Button"
nec:
address: 0x00FF
command: 0x40BF
on_press:
then:
- homeassistant.event:
event: esphome.ir_remote_button
data:
button: scene_button
room: living_room

ESPHome can send events to the Home Assistant event bus using the native API, but the event name must begin with esphome.. Home Assistant actions from ESPHome also need to be allowed from the ESPHome integration options.

In Home Assistant, you can listen for that event:

alias: IR remote scene button
trigger:
- platform: event
event_type: esphome.ir_remote_button
event_data:
button: scene_button
action:
- service: scene.turn_on
target:
entity_id: scene.movie_mode
mode: single

This keeps your ESPHome device simple and lets Home Assistant handle the actual automation logic.

Using decoded buttons directly inside ESPHome

ESPHome also supports protocol-specific automation triggers such as on_samsung, on_nec, on_raw and others. The official documentation recommends considering these on_* triggers for new projects because they are better suited to momentary button press events than binary sensors.

Example:

remote_receiver:
id: ir_receiver
pin:
number: GPIO23
inverted: true
mode:
input: true
pullup: true
dump: all

on_samsung:
then:
- if:
condition:
lambda: 'return x.data == 0xE0E040BF;'
then:
- homeassistant.event:
event: esphome.ir_remote_button
data:
button: samsung_power

This is cleaner if you are handling several buttons from the same remote.

Using the learned codes with an IR blaster

Once you have captured the code, you can use it with an ESP32 IR transmitter.

For example, if your receiver logs this:

Received Samsung: data=0xE0E040BF, nbits=32

You can transmit it from an IR blaster like this:

button:
- platform: template
name: "Samsung TV Power"
on_press:
- remote_transmitter.transmit_samsung:
data: 0xE0E040BF
nbits: 32

ESPHome’s remote_transmitter component is designed to send common remote control signals, including infrared and 433 MHz RF signals.

This is why the IR receiver and IR blaster work well as a pair:

  • Receiver learns the original code
  • Blaster sends the learned code
  • Home Assistant controls the device

Full example: IR receiver with three learned buttons

This example creates three Home Assistant-visible buttons from an IR remote:

esphome:
name: esp32-ir-receiver
friendly_name: ESP32 IR Receiver

esp32:
board: esp32dev

logger:
level: DEBUG

api:
encryption:
key: "PASTE_YOUR_API_KEY_HERE"
# Optional: use this if very fast IR button presses are missed
# batch_delay: 0ms

ota:
- platform: esphome
password: "PASTE_YOUR_OTA_PASSWORD_HERE"

wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password

remote_receiver:
id: ir_receiver
pin:
number: GPIO23
inverted: true
mode:
input: true
pullup: true
dump: all
tolerance: 25%
filter: 50us
idle: 10ms

binary_sensor:
- platform: remote_receiver
name: "IR Remote Power"
nec:
address: 0x00FF
command: 0x807F
on_press:
then:
- homeassistant.event:
event: esphome.ir_remote_button
data:
button: power

- platform: remote_receiver
name: "IR Remote Lamp"
nec:
address: 0x00FF
command: 0x40BF
on_press:
then:
- homeassistant.event:
event: esphome.ir_remote_button
data:
button: lamp

- platform: remote_receiver
name: "IR Remote Movie Mode"
nec:
address: 0x00FF
command: 0x20DF
on_press:
then:
- homeassistant.event:
event: esphome.ir_remote_button
data:
button: movie_mode

Then create Home Assistant automations for each button.

Example:

alias: IR remote movie mode
trigger:
- platform: event
event_type: esphome.ir_remote_button
event_data:
button: movie_mode
action:
- service: scene.turn_on
target:
entity_id: scene.movie_mode
mode: single

Troubleshooting

Nothing appears in the logs

Check these first:

  • Make sure logger.level is set to DEBUG
  • Make sure dump: all is enabled
  • Check the IR receiver wiring
  • Try another GPIO pin
  • Try inverted: true if you used inverted: false
  • Try inverted: false if your module behaves differently
  • Move the remote closer to the receiver
  • Check that the receiver is powered from 3.3V and GND

Some IR receivers are sensitive to wiring and noise. Keep the wires short while testing.

Logs are full of random raw data

This usually means noise, unstable wiring, or a floating input.

Try:

pin:
number: GPIO23
inverted: true
mode:
input: true
pullup: true

Also move the ESP32 away from strong sunlight, LED strips, plasma TVs or other sources of infrared noise.

The same button gives slightly different raw values

That is normal with raw IR captures. Timing values can vary slightly. If ESPHome decodes the protocol, prefer the decoded protocol output instead of raw data.

For example, use this if available:

Received NEC: address=0x00FF, command=0x807F

Rather than relying on a long raw timing list.

Quick button presses are missed

ESPHome notes that rapid remote button presses can be missed when using IR remote binary sensors, and suggests batch_delay: 0ms in the API configuration if needed. This sends state changes immediately, although it can increase network traffic.

Example:

api:
batch_delay: 0ms

Only use this if you actually have missed presses.

Air conditioner remote codes are huge

This is normal. Many AC remotes send the full state every time, not just “temperature up” or “mode”. The code may include:

  • Power state
  • Mode
  • Fan speed
  • Temperature
  • Swing setting
  • Timer status

For air conditioners, you usually need to capture each useful state separately, such as:

  • Cool 22°C auto fan
  • Cool 23°C auto fan
  • Heat 22°C auto fan
  • Off

This is more annoying, but it is normal.

Best uses for an ESP32 IR receiver

An ESP32 IR receiver is useful for more than just learning codes.

Good uses include:

  • Learning codes for an ESP32 IR blaster
  • Turning an old remote into a Home Assistant scene controller
  • Controlling smart lights with a TV remote
  • Triggering automations without buying extra Zigbee buttons
  • Detecting when someone uses the original remote
  • Debugging unknown IR devices
  • Building a universal remote control system

Limitations

This project is useful, but it has limits:

  • It only receives IR signals; it does not transmit unless you add an IR LED
  • Some remotes use unusual carrier frequencies
  • Some AC remotes send very long state-based codes
  • Raw codes can be messy
  • Direct sunlight can interfere with reception
  • Very cheap receiver modules may be inconsistent

For most normal TV, fan, LED controller and simple remote projects, it works very well.

Conclusion

An ESP32 IR receiver is a cheap but very useful Home Assistant tool. With one ESP32 and one IR receiver module, you can capture remote control codes, identify useful buttons, and use those buttons inside Home Assistant automations.

The best way to use it is as part of a two-step setup:

  • Use the ESP32 IR receiver to learn the original remote codes
  • Use an ESP32 IR blaster to send those codes from Home Assistant

That gives you local control of old infrared devices without cloud accounts, manufacturer apps or expensive universal remote hubs.

Share your love