Extend the range of any infrared remote control over your local WiFi network — no cloud, no app, no WiFi pairing button required.
Two ESP modules communicate over UDP on your local network:
- RX module — sits near you (e.g. on the sofa). Captures IR signals from your remote control and sends them as JSON over UDP.
- TX module — sits near the target device (TV, amplifier, AC, etc.). Receives the JSON and re-emits the IR signal.
Both modules discover each other automatically using UDP broadcast. No configuration beyond WiFi credentials is required.
[Remote] ──IR──► [RX module] ──WiFi/UDP──► [TX module] ──IR──► [Device]
- Automatic discovery and pairing — modules find each other on the LAN without any manual IP configuration
- 90+ IR protocols supported via IRremoteESP8266 (NEC, Sony, Samsung, LG, Panasonic, Daikin, …); unknown protocols fall back to raw pulse replay
- Captive portal WiFi setup — on first boot, module opens an access point with a web-based configuration page; includes WiFi network scan
- WiFi reconfiguration — change the network at any time via the web dashboard, no factory reset needed
- Dual-color LED status — clear visual feedback for every system state (see table below)
- Multi-click button handler — single click toggles LED; 10-second hold triggers factory reset
- Built-in web dashboard — shows network status, paired module IP, and the last IR frame with full decode details
- SSDP / mDNS — modules are discoverable by UPnP-capable network tools
- Platform-agnostic — designed for ESP modules (developed and tested on Wemos D1 Mini / ESP8266); portable to ESP32 with minor adjustments
| Function | GPIO | Wemos D1 Mini |
|---|---|---|
| IR receiver (e.g. TSOP4838) | GPIO14 | D5 |
| LED Green | GPIO12 | D6 |
| LED Red | GPIO13 | D7 |
| Config button | GPIO4 | D2 |
| Function | GPIO | Wemos D1 Mini |
|---|---|---|
| IR LED (via NPN transistor) | GPIO5 | D1 |
| LED Green | GPIO12 | D6 |
| LED Red | GPIO13 | D7 |
| Config button | GPIO4 | D2 |
The dual-color LED uses two separate pins (active HIGH). Green = GPIO12 HIGH, Red = GPIO13 HIGH, both LOW = off.
The project uses PlatformIO (VSCode extension or CLI).
project/
├── ESP_IR_RX/ ← RX module firmware
│ ├── src/
│ ├── include/
│ └── platformio.ini
└── ESP_IR_TX/ ← TX module firmware
├── src/
├── include/
└── platformio.ini
Flash the RX project to the receiver module and the TX project to the transmitter module.
To enable serial debug output, add to platformio.ini:
build_flags = -D ENABLE_DEBUG=1- Flash RX firmware to the RX module and TX firmware to the TX module.
- Power on the RX module — it starts in AP mode (red LED solid).
- Connect your phone/computer to
IR_RX_XXXX(last 4 digits of MAC). - The captive portal opens automatically — select your WiFi, enter the password, save.
- Repeat for the TX module (AP name:
IR_TX_XXXX). - Once both modules are on the same network, they pair automatically within seconds (green heartbeat LED).
| Color | Pattern | State |
|---|---|---|
| 🔴 Red | Solid | AP mode — configuration portal active |
| 🔴 Red | Long blink (300/300 ms) | No WiFi connection |
| 🔴 Red | Short blink (10/950 ms) | WiFi connected, searching for partner |
| 🟢 Green | Long blink (300/300 ms) | Discovery / pairing in progress |
| 🟢 Green | Rare heartbeat (5/4900 ms) | Paired and ready |
| 🟢 Green | Single flash (5 ms) | IR frame received / transmitted |
| — | Off | LED disabled (toggled by button) |
| Action | Result |
|---|---|
| Single click | Toggle LED on/off |
| Double click | Reserved (future: manual pairing mode) |
| Hold ≥ 10 s | Factory reset — clears WiFi credentials, restarts |
Each module exposes a small HTTP API on port 80:
| Endpoint | Method | Description |
|---|---|---|
/ |
GET | Web dashboard |
/status |
GET | JSON: RSSI, paired IP, pairing state |
/ir |
GET | JSON: last IR frame with age_ms |
/wificfg |
GET | WiFi reconfiguration page |
/wificfg |
POST | Save new SSID/password and restart |
/scan |
GET | JSON: WiFi network scan results |
/description.xml |
GET | SSDP device description |
{
"proto": "NEC",
"value": "0x20DF10EF",
"bits": 32,
"address": "0x04",
"command": "0x08",
"repeat": false,
"raw": [9024, 4512, 562, 562, ...],
"age_ms": 1340
}Modules communicate over UDP port 4210.
| Packet | Direction | Meaning |
|---|---|---|
IREX_rx |
RX → broadcast/TX | Discovery: RX looking for TX |
IREX_tx |
TX → RX | Discovery reply: TX acknowledges |
{...} (JSON) |
RX → TX | IR frame data |
RX sends broadcast discovery every 5 seconds until TX is found. Once paired, both modules monitor keepalive timing — if 5 seconds pass with no reply, the link is considered lost and discovery restarts.
- IRremoteESP8266
- ESP8266 Arduino core (or ESP32 equivalent)
- Standard Arduino libraries:
EEPROM,ESP8266WiFi,ESP8266WebServer,ESP8266mDNS,ESP8266SSDP,DNSServer,WiFiUdp
ESP_IR_RX/
├── include/
│ ├── config.h # All timing constants and pin definitions
│ ├── button.h # Multi-click button handler API
│ ├── ir_receiver.h # IR capture and JSON encoding API
│ ├── led.h # LED mode definitions and API
│ ├── network.h # UDP / HTTP / SSDP API
│ └── wifi_config.h # WiFi credential management API
└── src/
├── main.cpp # setup() and loop()
├── button.cpp # State-machine button handler
├── ir_receiver.cpp # IRremoteESP8266 wrapper + JSON builder
├── led.cpp # Non-blocking LED blink engine
├── network.cpp # UDP, HTTP server, SSDP, discovery logic
└── wifi_config.cpp # EEPROM storage + AP captive portal
ESP_IR_TX/ (mirror structure, ir_receiver → ir_sender)
MIT License — see LICENSE for details.
- Sławek (SK-SpeedBit) — hardware design, firmware architecture, PCB layout
- Claude (Anthropic) — firmware co-development, code review, documentation
Built with IRremoteESP8266 by David Conran and contributors — an outstanding library that makes supporting 90+ IR protocols a one-liner.



