ESPHome offers the Remote Receiver and Remote Transmitter components to interact with IR remote controls and IR remote receivers. In this guide I will show you how to control IR receiver units (like TVs or LED controllers) by sending fake signals from ESPHome.
The demo object here will be this simple LED controller with a 44 key infrared remote.
(There is also a 24 key version that uses the same protocol but different command codes.)
But the information in this guide can be applied to any other IR remote.
We want to control that little white box from an ESP8266 or ESP32.
Using the manual remote in parallel will still be possible.
The ESP I use will be an ESP8266 D1 Mini. For other ESPs you only need to adjust the basic ESP config and the GPIO pin names in the YAML code below.
The tasks at hand are:
- Attach an infrared emitter diode to the ESP
- Temporarily attach an IR receiving transistor to the ESP (needed for tasks 3. and 5.)
- Analyse the protocol of the IR remote handheld
- Try to send the same protocol(s) from ESPHome
- Get the command data for all keys of the handheld in the chosen protocol
Let’s go:
1. Attach an IR sending diode to the ESP
I chose this IR transmitter board from AliExpress. It runs on 5 Volts Vcc, has a driver stage so the input can be controlled with 3.3 Volt signals and – most importantly – it features two IR diodes in series which results in a stronger signal. In my setup it easily bridges several meters with rock solid performance. The board also has a small red SMD LED that blinks visibly when the unit is sending.
Connect the board to the ESP like this:
ESP – IR transmitter
---------
D7 – DAT
5V – VCC
GND – GND
Hint: There are also IR emitters available that are already installed in nice little casings with a cable attached. See Brian’s comment.
2. Attach an IR receiving transistor to the ESP
The receiver is only needed for the analysis steps. It can be removed later.
This board from AliExpress is basically the counterpart to the sender chosen above.
Connect the board to the ESP like this:
ESP – IR receiver
---------
D5 – DATA
5V – VCC
GND – GND
I did not add any level shifter between this board and my ESP. Fortunately the ESP did not complain. If you want to play it safe you might want to connect to the ESP using a voltage divider 1.8 kOhm / 3.3 kOhm like so:
DATA – 1.8k – D5 – 3.3k – GND
Update 11/2024:
Meanwhile I tried another IR receiver that works even better.
It is a KY-022 module with a 1838 receiver chip on it. You can find it on Amazon and Ali Express:
It can operate from 2.7 to 5.5 Volts, so it can be fed from the 3.3 Volt connector of the ESP, so there is definitely no level shifting required on the GPIO of the ESP.
This receiver detects much less static and results in clear and stable dumps of the received IR transmissions. Much recommended.
Connect that board to the ESP like this:
ESP – IR receiver KY-022
---------
D5 – "S" (=Signal)
3.3V – mid terminal (=VCC)
GND – "–" (=GND)
3. Analyse the protocol of the IR remote handheld
Now we have to find out what protocol the IR remote sends so we can emulate it later.
The following YAML will program the ESP so that it listens on the IR receiver and dumps any incoming signals to the log.
Make sure you have /config/esphome/secrets.yaml in place to provide the secret parts.
esphome:
name: "ir-project"
platform: ESP8266
board: d1_mini
wifi:
ssid: !secret my_wlan_ssid
password: !secret my_wlan_pwd
captive_portal:
logger:
level: VERBOSE
api:
password: !secret my_esp_api_pwd
ota:
password: !secret my_esp_ota_pwd
remote_receiver:
pin:
number: D5
inverted: true
dump: all
Compile, upload and keep the log open.
Then take your 44 key handheld and push some buttons.
With each button press your log should show something like this:
[08:53:41][D][remote.jvc:048]: Received JVC: data=0x00FF
[08:53:41][D][remote.lg:053]: Received LG: data=0x00FF02FD, nbits=32
[08:53:41][D][remote.nec:068]: Received NEC: address=0xFF00, command=0xBF40
[08:53:41][D][remote.pioneer:144]: Received Pioneer: rc_code_X=0x0040
This is the protocol data we are looking for.
And there will be many occasional lines like this:
[08:53:41][D][remote.raw:041]: Received Raw: 206
[08:53:41][D][remote.raw:041]: Received Raw: 261, -1864, 267
[08:53:41][D][remote.raw:041]: Received Raw: 182, -704, 208
[08:53:41][D][remote.raw:041]: Received Raw: 472
This is IR static that the receiver constantly picks up. You can safely ignore it.
Note:
If you only receive raw data and none that is interpreted as a known protocol, chances are that your receiver is giving you inverted signals. If so change the inverted:
setting in the above YAML. You can recognize a wrong polarity by looking at the raw data stream. The very first number should always be positive, never negative. Here is a correct example:
[11:12:42][D][remote.raw:028]: Received Raw: 8995, -4553, 523, -655, 498, -632, 549, -607, 548, -607, 522, -632, 550, -604, 551, -605, 523, -632, 522, -1753, 577, -1698, 549, -1726, 548, -1727, 522, -1751, 550, -1724, 550, -1724, 550, -1725, 549, -605, 550, -604, 550, -606, 550, -604,
[11:12:42][D][remote.raw:041]: 551, -604, 549, -606, 550, -1725, 522, -632, 550, -1724, 576, -1698, 550, -1723, 578, -1696, 551, -1723, 550, -1723, 576, -579, 550, -1723, 551
Now let’s have a look at the protocol data (first log above):
We have set our remote_receiver
to dump:all
in our YAML. So with every transmission ESPHome tries to make sense of it by decoding it along various possible protocol patterns. In our case it could be interpreted as JVC protocol, but also as LG, NEC or Pioneer.
4. Try to send the same protocol(s) from ESPHome
To find out which protocol the controller really understands, we now try to send each of these protocols from ESPHome and see how the LED controller reacts. My example above is from pressing the Power button on the remote. So let’s add some YAML code and see, whether we can fake a Power On/Off from ESPHome.
Add the following lines to the above YAML:
remote_transmitter:
pin: D7
carrier_duty_percent: 50%
button:
- platform: template
name: "On/Off JVC"
on_press:
- remote_transmitter.transmit_jvc:
data: 0x00FF
- platform: template
name: "On/Off LG"
on_press:
- remote_transmitter.transmit_lg:
data: 0x00FF02FD
nbits: 32
- platform: template
name: "On/Off NEC"
on_press:
- remote_transmitter.transmit_nec:
address: 0xFF00
command: 0xBF40
- platform: template
name: "On/Off Pioneer"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x0040
As you can see, we created 4 buttons that send data in the 4 different protocols that we received earlier. The payload data that we send with each protocol is exactly what we received in the log above.
Compile this extended YAML and make sure you import the new ESPHome integration in Home Assistant, so the 4 buttons appear in the HA entities list.
If you now press any of the buttons in HA you should see the red LED on the transmitter board blinking. Your LED controller should react to some of these buttons, but not to all of them. In my case I could reliably control the LED strip with both the Pioneer and the NEC protocol. LG worked sometimes but was not at all reliable. JVC never worked.
Because Pioneer seemed to be a tiny bit faster than NEC, I chose the Pioneer protocol.
Note:
It is also possible to dump the raw data received by the IR receiver (set dump:
to - raw
). You can then simply replay it from ESPHome without any upfront protocol interpretation. While you will find several guides on the web that choose that approach, I do definitely not recommend it. The reason is that the raw data is in fact an analog data stream. You can see that because the raw data of the same key press changes a bit with each transmission. If you replay that it will contain all static and distortions that happened during recording. The result is reduced transmission success rate. That’s surely not what you want. Sending raw data should basically be a last resort if the protocol of your remote is really unknown to ESPHome.
5. Get the command data for all keys in the chosen protocol
Now you can use the handheld and the log once more to find the command codes of all keys that you want to emulate from ESPHome. If you want to concentrate on the protocol you have chosen, you can change the dump:
parameter to show just that protocol:
dump:
- pioneer
When done add all command codes that you found as buttons to your YAML.
As an example here are all the definitions for the first 6 rows / 24 keys including all the color buttons:
button:
- platform: template
name: "IR remote, brighter"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x005C
- platform: template
name: "IR remote, darker"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x005D
- platform: template
name: "IR remote, play"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x0041
- platform: template
name: "IR remote, on/off"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x0040
- platform: template
name: "IR remote, red"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x0058
- platform: template
name: "IR remote, green"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x0059
- platform: template
name: "IR remote, blue"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x0045
- platform: template
name: "IR remote, white"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x0044
- platform: template
name: "IR remote, dark orange"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x0054
- platform: template
name: "IR remote, orange"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x0050
- platform: template
name: "IR remote, warm yellow"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x001C
- platform: template
name: "IR remote, yellow"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x0018
- platform: template
name: "IR remote, emerald"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x0055
- platform: template
name: "IR remote, turquoise"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x0051
- platform: template
name: "IR remote, cyan green"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x001D
- platform: template
name: "IR remote, cyan"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x0019
- platform: template
name: "IR remote, blue violet"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x0049
- platform: template
name: "IR remote, violet"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x004D
- platform: template
name: "IR remote, red violet"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x001E
- platform: template
name: "IR remote, magenta"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x001A
- platform: template
name: "IR remote, white rose"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x0048
- platform: template
name: "IR remote, rose"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x004C
- platform: template
name: "IR remote, white cyan"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x001F
- platform: template
name: "IR remote, light cyan"
on_press:
- remote_transmitter.transmit_pioneer:
rc_code_1: 0x001B
The IR receiver board and its YAML lines (remote_receiver:...
) can be removed now.
Your fake IR remote control is ready to play.
Enjoy!