A plain-C port of the IRremoteESP8266 library, designed to compile and run on multiple platforms including Zephyr RTOS and standard Linux.
This library is licensed under the GNU Lesser General Public License v2.1 (LGPL-2.1), the same license as the original IRremoteESP8266 project.
IR_Remote_Universal/
├── src/ # Core library source
│ ├── ir_Send.h / ir_Send.c # IR signal encoding & burst generation
│ ├── ir_receive.h / ir_receive.c # IR signal decoding
│ ├── ir_ac.h / ir_ac.c # AC protocol state machine & common interface
│ ├── ir_Class.h # Protocol enums, structs, and type definitions
│ ├── ir_utils.h / ir_utils.c # Bit manipulation, matching, and helpers
│ ├── ir_protocol.h # Protocol constants and feature flags
│ ├── irtext.h / irtext.c # Human-readable string output
│ ├── IRmacros.h # Shared macros
│ ├── i18n.h # Internationalization support
│ ├── locale/ # 13 locale variants (en-US, de-DE, fr-FR, etc.)
│ └── ir_*.h / ir_*.c # Per-protocol encoder/decoder modules (60+)
├── test_apps/
│ ├── ir_test_ubuntu/ # Linux host test application
│ └── zephy_ir_test/ # Zephyr RTOS test application (ESP32-S3)
├── LICENSE.txt
├── .gitignore
└── README.md
The library supports 59 AC protocols and many additional IR protocols including:
Samsung, Gree, Daikin (7 variants), Mitsubishi (4 variants), Fujitsu, Hitachi (3 variants), Panasonic, LG, Electra, Haier (3 variants), Whirlpool, Toshiba, Sharp, Coolix, Midea, Voltas, Rhoss, Bosch, Argo, Carrier, Corona, Kelvinator, Neoclima, Vestel, Teco, TCL, Transcold, Truma, Ecoclim, Mirage, Airton, Airwell, Amcor, Delonghi, Goodweather, Kelon, Teknopoint, Trotec, and more.
The library uses an ir_send_ctx_t context struct that holds the burst buffer and HAL callback. You provide a buffer for IR timing data and a hardware-specific function to actually drive the IR LED:
#include "ir_Class.h"
#include "ir_Send.h"
#include "ir_receive.h"
#include "ir_ac.h"
#define IR_BURST_CAP 1024
static ir_data_t ir_burst_buf[IR_BURST_CAP];
static ir_ac_protocol_t ir_ac = {0};
static ir_send_ctx_t ir_send_ctx = {
.pin = 0,
.modulation = true,
.dutycycle = 50,
.carrier_hz = 38000,
.burst_buf = ir_burst_buf,
.burst_cap = IR_BURST_CAP,
.burst_idx = 0,
.send_hal = my_ir_send_hal, // your platform HAL function
.hal_ctx = NULL,
.outputOn = 1,
.outputOff = 0,
};Use sendAcFromValues() to encode and transmit an AC command in a single call:
// Send: Samsung AC, Cool mode, 24C, Auto fan
bool ok = sendAcFromValues(
&ir_send_ctx, &ir_ac,
SAMSUNG_AC, // protocol
-1, // model (-1 = default)
true, // power on
kCool, // mode
24.0f, // temperature (Celsius)
true, // celsius
kAutoFanSpeed, // fan speed
kAutoSwingV, // vertical swing
kAutoSwingH, // horizontal swing
false, // quiet
false, // turbo
false, // econo
false, // light
false, // filter
false, // clean
false, // beep
-1, // sleep (-1 = off)
-1 // clock (-1 = off)
);After capturing raw IR timing data into a rawbuf array (mark/space ticks), decode it:
ir_recv_ctx_t recv_ctx = {
.tolerance = 25,
.unknown_threshold = 12,
.recv_hal = NULL,
.hal_ctx = NULL,
};
recv_ctx.params.rawbuf = rawbuf;
recv_ctx.params.rawlen = rawlen;
recv_ctx.params.bufsize = 2048;
recv_ctx.params.overflow = 0;
recv_ctx.params.rcvstate = 5; // kStopState
decode_results results;
memset(&results, 0, sizeof(results));
results.rawbuf = rawbuf;
results.rawlen = rawlen;
irparams_t save;
uint16_t save_rawbuf[2048];
save.rawbuf = save_rawbuf;
save.bufsize = 2048;
if (decode(&recv_ctx, &results, &save, 0, 0)) {
// Successfully decoded
char proto_name[32];
typeToString(proto_name, sizeof(proto_name), results.decode_type, false);
printf("Protocol: %s\n", proto_name);
}state_t common;
initStateObj(&common);
if (decodeToState(&ir_ac, &results, &common, NULL)) {
printf("Power: %s\n", common.power ? "ON" : "OFF");
printf("Temp: %.0fC\n", common.degrees);
printf("Mode: %d\n", common.mode);
printf("Fan: %d\n", common.fanspeed);
}On Zephyr/ESP32, a typical HAL drives the IR LED with a carrier wave:
static int ir_send_hal(const ir_data_t *buffer, uint32_t length,
uint32_t carrier_hz, uint8_t duty_percent,
void *user_ctx) {
uint32_t period_us = 1000000 / carrier_hz;
uint32_t on_us = period_us * duty_percent / 100;
uint32_t off_us = period_us - on_us;
for (uint32_t i = 0; i < length; i++) {
if (buffer[i].duty_cycle_us == 0) continue;
if (i % 2 == 0) {
// Mark: modulate carrier
ir_carrier_mark(buffer[i].duty_cycle_us, on_us, off_us);
} else {
// Space: LED off
k_busy_wait(buffer[i].duty_cycle_us);
}
}
return 0;
}A self-contained test that runs on any Linux host (no hardware required). It performs an encode-decode round-trip for all 59 supported AC protocols:
- Encodes an AC command into a burst buffer using the library's send path
- Converts the burst buffer into a simulated
rawbuf(as if captured by an IR receiver) - Decodes the
rawbufback and verifies the protocol, temperature, mode, and fan speed match
Building and running:
cd test_apps/ir_test_ubuntu
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)
./build/ir_testExpected output: 58 of 59 protocols pass. TEKNOPOINT is the one expected failure because its encoding is identical to TCL112AC, which the decoder matches first.
The test also supports locale testing via test_locales.sh to verify all 13 locale variants produce identical results.
A full hardware test for ESP32-S3 running Zephyr RTOS. Features:
- WiFi connectivity with a TCP command server on port 7001
- Real IR LED transmission via GPIO with carrier modulation
- IR receiver with ISR-based capture into
rawbuf - Companion Python script (
send_ir_server.py) for sending AC commands over the network
Building for ESP32-S3:
west build -b esp32s3_devkitc/esp32s3/procpu test_apps/zephy_ir_test
west flash-
No hardware dependency inside the library, so cross compiling for any platform is simple.
-
All the library state is managed by a context owned by the user's code, so no dynamic memory allocation, unless the user decides to use it.
This library was originally based on Ken Shirriff's work (https://github.com/shirriff/Arduino-IRremote/).
Mark Szabo updated the IRsend class to work on ESP8266 and Sebastien Warin updated the receiving and decoding part (IRrecv class).
As of v2.0, the library was almost entirely re-written with the ESP8266's resources in mind. The project grew to support a huge number of AC protocols under the stewardship of David Conran (crankyoldgit) at https://github.com/crankyoldgit/IRremoteESP8266.
This variant is a refactor of v2.8.4, rewritten from C++ to plain C to be compilable on multiple platforms such as Zephyr RTOS and standard Linux.