Reverse engineering del protocollo IR (HITACHI_AC 28 byte) del telecomando RAR-6NE1 e custom component ESPHome bidirezionale (TX + RX) per Home Assistant. - esphome/: custom component hitachi_rar6ne1 (climate_ir::ClimateIR) + config - src/: firmware Arduino di cattura IR con web UI (strumento di diagnostica) - README.md: documentazione completa (protocollo, decode, checksum, gotcha) - Segreti esclusi dal versionamento (vedi *.example e .gitignore) - Licenza GPL-3.0 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Hitachi RAR-6NE1 — Reverse Engineering protocollo IR per ESPHome
Obiettivo
Decodificare il protocollo IR del telecomando Hitachi RAR-6NE1 (compatibile con unità interne RAK-15QPE/18RPE/25RPE/35RPE/42RPE/50RPE) per realizzare un climate control via ESP32 + ESPHome.
Stato attuale
2026-06-30 — Implementazione ESPHome realizzata e funzionante in bidirezionale. Il condizionatore si comanda da Home Assistant (TX) e lo stato si sincronizza quando si usa il telecomando fisico (RX). Vedi la sezione Implementazione ESPHome (fase finale) in fondo per architettura, file e i "gotcha" risolti.
Reverse engineering sostanzialmente completo sul lato protocollo. Mappatura solida di tutti i campi principali (power, mode, temp, fan, swing, silent, powerful) e algoritmo di checksum determinato e verificato al 100% — coincide esattamente con IRHitachiAc::calcChecksum() della libreria IRremoteESP8266: il telecomando RAR-6NE1 è quindi compatibile byte-per-byte e bit-per-bit con quella classe, non solo strutturalmente. Possiamo generare comandi custom validi da zero.
L'ipotesi "frame periodico di temperatura ambiente dal telecomando" (sensore IR lato trasmettitore) è stata verificata e scartata: con la web UI di monitoraggio (vedi sotto) si è osservato un periodo di ~80 minuti di silenzio totale a telecomando fermo, con unità accesa, e tutti i frame ricevuti sono risultati essere comandi da pressione tasto. Il telecomando non invia trasmissioni spontanee periodiche.
L'implementazione finale ESPHome non ha riusato la classe IRHitachiAc: è un
custom component che estende climate_ir::ClimateIR e costruisce i 28 byte +
checksum a mano (più portabile, nessuna dipendenza da IRremoteESP8266). Dettagli
nella sezione Implementazione ESPHome (fase finale) in fondo.
File in questa cartella
| File | Scopo |
|---|---|
platformio.ini + src/main.cpp |
Progetto PlatformIO. Lo sketch cattura i segnali IR (ricevitore su GPIO27, ESP32 DevKitC) e li logga in una web UI accessibile in WiFi, con timestamp NTP, decodifica a 28 byte on-device e verifica checksum. Pensato per lasciare l'ESP vicino al condizionatore e osservare i frame nel tempo (vedi sezione "Monitor web"). Logga anche su seriale (formato IRrecvDumpV2) se collegato via USB. |
src/secrets.h |
Credenziali WiFi + hostname mDNS + timezone per lo sketch PlatformIO (formato C, da compilare). Distinto da secrets.yaml. |
hitachi-ir-dump.yaml |
Config ESPHome alternativa per dump raw via remote_receiver (dump: all). Nota: ESPHome non ha un decoder Hitachi generico in remote_base (il decode Hitachi esiste solo dentro i climate component hitachi_ac344/hitachi_ac424), quindi questo file produce solo raw non interpretato — usare il progetto PlatformIO per il riconoscimento automatico del protocollo. |
secrets.yaml |
Credenziali WiFi per la config ESPHome (placeholder, da compilare). |
Monitor web (osservazione frame nel tempo)
Lo sketch src/main.cpp espone una web UI per registrare ogni trama IR ricevuta — utile per lasciare l'ESP32 vicino al condizionatore e verificare, ad esempio, se il telecomando (che ha un sensore di temperatura integrato) invia frame periodici "spontanei" all'unità.
Setup:
- Compila
src/secrets.hcon SSID/password della rete WiFi (la stessa del PC da cui interroghi l'endpoint). pio run -t uploadcon ESP collegato via USB, poi sposta l'ESP vicino al condizionatore (alimentato).- Apri
http://hitachi-ir.local/(mDNS) ohttp://<IP>/(l'IP è stampato sul seriale al boot).
Endpoint:
| Endpoint | Risposta |
|---|---|
GET / |
Pagina HTML con tabella auto-refresh (ogni 3s): orario, Δ dal frame precedente, bits, rawlen, checksum OK/BAD, campi decodificati (power/mode/temp/fan/swing/silent/powerful/tasto) e i 28 byte. |
GET /api/captures |
JSON riassuntivo di tutte le catture nel ring buffer (ultime 30) + uptime/totale/IP. Per polling automatico. |
GET /api/raw?i=N |
Array raw in microsecondi della cattura con sequenza N (per ri-analizzare frame anomali che non decodificano a 28 byte). |
GET /api/clear |
Svuota il buffer. |
Il ring buffer tiene le ultime 30 catture in RAM (si perde al reset). Ogni cattura conserva anche il raw grezzo (fino a 460 valori) così i frame di lunghezza/struttura inattesa non vanno persi.
Perché non è un protocollo "già supportato"
- Verificato su esphome.io: i soli climate component Hitachi nativi sono
hitachi_ac344ehitachi_ac424. - Verificato su
ir_Hitachi.h(libreria IRremoteESP8266): i modelli di telecomando documentati per ciascuna variante (AC1, AC2, AC3, AC264, AC296, AC344, AC424) non includono RAR-6NE1 né le sue varianti (RAR-6N1/6N2/6N3/6NE4/6N5). - Empiricamente: lo sketch
IRrecvDumpV2, che prova il decode con tutti i protocolli noti, riconosce solo alcune trame (es. lo stato OFF) comeHITACHI_ACvalido; la maggior parte risultaUNKNOWN.
Metodo di decode (validato)
Le trame, una volta corrette per un bug di cattura ricorrente, risultano essere il protocollo HITACHI_AC standard a 28 byte (224 bit) — la classe più "base" della libreria (distinta da AC264/296/344/424) — con codifica a distanza:
- Header: mark ~3300-3400µs, space ~1700µs
- Bit: mark costante ~400µs, space
526µs→bit0,1296µs→bit1 - 28 byte totali, MSB-first per byte, ordine byte diretto (nessuna inversione)
Bug di cattura da correggere sempre
Molti dump (in particolare il primo dopo un periodo di idle) perdono il mark iniziale lungo dell'header (~3300-3400µs), registrando come primo valore solo la sua space (~1700µs). Risultato: l'array raw appare "shiftato" di una posizione.
Come riconoscerlo: guarda il primo valore del raw array.
- Se è > 3000µs → header completo, scarta i primi 2 valori (mark+space header) prima di accoppiare il resto.
- Se è < 2000µs (tipicamente ~1650-1720) → header tagliato, scarta solo il primo valore.
Poi accoppia i valori restanti (mark, space): bit = 1 se space > 800, altrimenti 0. Bit-pack MSB-first in byte, 28 byte totali.
Questo metodo è stato calibrato e validato contro una trama riconosciuta nativamente come HITACHI_AC dalla libreria (stato OFF, con checksum verificato dalla libreria stessa) — non è una supposizione.
Mappatura campi (stato a 28 byte, indice da 0)
| Campo | Byte | Valori osservati | Note |
|---|---|---|---|
| Power | [17] bit0 |
0x00=OFF, 0x01=ON |
Confermato, coincide col layout documentato in ir_Hitachi.h |
| Mode | [10] |
Cool=0x20, Fan-only=0x30, Auto=0x40, Dry=0xA0, Heat=0xC0 |
Confermato con test isolato (solo mode cambiato) |
| Temp | [11] |
26°C=0x2C, 25°C=0x4C |
Azzerato a 0x01 in Fan-only (temp non applicabile) |
| Fan speed | [13] |
0x40=1/4, 0xC0=2/4, 0x20=3/4, 0xA0=4/4, 0x80=Auto |
Confermato con test isolato (sequenza 3→2→1→Auto→4→3). Dry forza automaticamente 0xC0. |
| Checksum | [27] |
Cambia ad ogni trasmissione | Algoritmo determinato e verificato su tutti i 18 stati raccolti finora (match perfetto, nessuna eccezione) — è esattamente IRHitachiAc::calcChecksum() della libreria IRremoteESP8266 (ir_Hitachi.cpp:173-178). Vedi sezione dedicata sotto. |
| Swing Verticale | [14] |
0xE0=ON, 0x60=OFF |
Confermato con test isolato (sequenza V+H→solo H→OFF→solo V→V+H) |
| Swing Orizzontale | [15] |
0xE0=ON, 0x60=OFF |
Confermato con lo stesso test |
| Silent | [26] bit4 (0x10) |
0x10=ON, 0x00=OFF |
Confermato con test isolato ON→OFF |
| Powerful | [25] bit2 (0x04) |
0x34=ON, 0x30=OFF |
Confermato con test isolato ON→OFF |
[9] |
— | 0xE0 (idle/solo mode), 0xC0 (tasto power on/off), 0xB0 (tasto swing V), 0x70 (tasto swing H), 0x30 (tasto fan speed), 0x38 (tasto Silent), 0xA8 (tasto Powerful) |
Non è uno stato persistente: codifica il tasto/categoria appena premuto sul telecomando, non il risultato — resta identico tra ON e OFF dello stesso tasto (es. 0x38 sia per Silent ON che OFF, 0xC0 sia per power ON che OFF). Confermato su 15+ trasmissioni isolate (swing, fan, silent, powerful, power). |
Algoritmo di checksum (confermato)
Coincide esattamente con la classe IRHitachiAc standard della libreria IRremoteESP8266 (ir_Hitachi.cpp, righe 173-178). Verificato in Python su tutti gli 18 stati raccolti (baseline mode/temp/power, swing, fan speed) — match perfetto al 100%, nessuna eccezione:
def reverse_bits(x, nbits=8):
r = 0
for i in range(nbits):
r |= ((x >> i) & 1) << (nbits - 1 - i)
return r
def calc_checksum(state): # state = 28 byte, l'ultimo e' il checksum stesso
s = 62
for b in state[:-1]: # primi 27 byte
s = (s - reverse_bits(b, 8)) & 0xFF
return reverse_bits(s, 8)
Implicazione: possiamo generare comandi custom mai catturati (es. combinazioni nuove di mode/temp/fan/swing) calcolando il checksum con questa formula, senza dover fare solo replay di raw catturati. Probabile che la classe IRHitachiAc esistente nella libreria possa essere riusata direttamente (o con un wrapper minimale) per l'implementazione ESPHome finale, anziché scrivere un protocollo custom da zero — da verificare.
Stati di riferimento catturati (esadecimale, 28 byte)
OFF : 80 08 0C 02 FD 80 7F 88 48 C0 20 2C 00 20 E0 E0 00 00 00 00 00 00 00 01 80 30 00 40
ON (26C cool, fan 3/4): 80 08 0C 02 FD 80 7F 88 48 C0 20 2C 00 20 E0 E0 00 01 00 00 00 00 00 01 80 30 00 41
Cool 25°C : 80 08 0C 02 FD 80 7F 88 48 90 20 4C 00 20 E0 E0 00 01 00 00 00 00 00 01 80 30 00 7E
Fan only : 80 08 0C 02 FD 80 7F 88 48 E0 30 01 00 20 E0 E0 00 01 00 00 00 00 00 01 80 30 00 54
Auto : 80 08 0C 02 FD 80 7F 88 48 E0 40 2C 00 20 E0 E0 00 01 00 00 00 00 00 01 80 30 00 01
Heat : 80 08 0C 02 FD 80 7F 88 48 E0 C0 2C 00 20 E0 E0 00 01 00 00 00 00 00 01 80 30 00 FE
Dry : 80 08 0C 02 FD 80 7F 88 48 E0 A0 2C 00 C0 E0 E0 00 01 00 00 00 00 00 01 80 30 00 7E
Cool (return): 80 08 0C 02 FD 80 7F 88 48 E0 20 2C 00 20 E0 E0 00 01 00 00 00 00 00 01 80 30 00 7E
Fan 2/4 : 80 08 0C 02 FD 80 7F 88 48 30 20 2C 00 C0 E0 E0 00 01 00 00 00 00 00 01 80 30 00 5E
Come riprodurre/continuare il lavoro
- Hardware: ESP32 DevKitC + ricevitore IR (TSOP38xx/VS1838B) su GPIO27, 3.3V.
- Cattura:
pio run -t upload -t monitorda questa cartella (richiede PlatformIO). Puntare il telecomando e premere un tasto. - Decode manuale: usare il metodo sopra descritto (vedi anche script Python di calibrazione usati in sessione, non salvati in questa cartella — ricostruibili facilmente dal metodo descritto).
- Verifica checksum: aprire
ir_Hitachi.cppsu github.com/crankyoldgit/IRremoteESP8266 (classeIRHitachiAc, funzionecalcChecksum()) e confrontare con gli stati di riferimento sopra.
Prossimi passi
Implementazione ESPHome finale✅ FATTA (2026-06-30) — custom componentclimate_ir, vedi sezione Implementazione ESPHome (fase finale). Resta solo lo sweep interattivo completo dei comandi in TX da confermare sul campo.- (Opzionale) Test temperatura impostata su più valori per confermare la formula
tempC = reverseBits(byte[11]) / 2su tutto il range (16–32°C), finora derivata da 25/26°C.
Verificato e chiuso
- Frame spontanei / sensore di temperatura del telecomando: ipotesi scartata. Monitoraggio via web UI con ~80 min di silenzio totale a telecomando fermo (unità accesa); tutti i frame ricevuti sono comandi da pressione tasto. Il RAR-6NE1 non trasmette periodicamente.
- byte[9]: confermato = codice del tasto premuto (non stato persistente).
Riferimenti
- https://esphome.io/components/climate/climate_ir — protocolli IR climate supportati da ESPHome
- https://github.com/crankyoldgit/IRremoteESP8266 —
src/ir_Hitachi.h/ir_Hitachi.cpp - Nota progetto su Obsidian:
projects/hitachi-ir-esphome.md
Implementazione ESPHome (fase finale)
Realizzata il 2026-06-30. Sostituisce il monitor Arduino (src/main.cpp, che
resta come strumento di cattura/diagnostica) con un'integrazione ESPHome nativa che
espone il condizionatore come entità climate in Home Assistant, bidirezionale.
Architettura
Custom external component che estende climate_ir::ClimateIR. Non riusa la
classe IRHitachiAc della libreria: costruisce i 28 byte e il checksum a mano
(stesso algoritmo verificato) ed emette/decodifica via i componenti nativi ESPHome
remote_transmitter/remote_receiver (RMT). Vantaggi: nessuna dipendenza esterna,
entità climate completa, TX e RX (sync col telecomando fisico).
File (esphome/)
esphome/
├── hitachi-ir.yaml # config: wifi/api/ota + TX GPIO25 + RX GPIO27 + climate
├── secrets.yaml # template wifi
└── components/hitachi_rar6ne1/
├── __init__.py · climate.py # registrazione platform (API ESPHome 2025+/2026.x)
├── hitachi_rar6ne1.h # classe, costanti timing, mappa fan/swing/preset
└── hitachi_rar6ne1.cpp # build 28 byte + checksum + transmit_state + on_receive
Importazione: hitachi-ir.yaml e la cartella components/ nella stessa directory di
config ESPHome (es. /config/), poi build/flash dall'add-on ESPHome di HA (primo
flash USB, poi OTA).
Hardware
- RX: ricevitore IR (TSOP/VS1838B) su GPIO27 (invertito, pull-up).
- TX: LED IR + driver a transistor su GPIO25 —
GPIO25 →[1k]→ base NPN;3V3 →[100R]→ LED IR(A→K) → collector;emitter → GND. Portante 38 kHz.
Mappatura campi → 28 byte (in build_state_)
Identica alla tabella sopra: mode [10], temp [11]=reverseBits(°C×2), fan [13]
(Quiet/Low/Med/High→1/2/3/4 + Auto), swing V [14]/H [15] (0xE0/0x60), power
[17] bit0, preset Eco→Silent [26] bit4, Boost→Powerful [25] bit2,
checksum [27]. byte[9] (codice tasto) impostato a 0xE0 via la costante
kButtonCode — non critico (vedi gotcha).
Gotcha risolti (utili per progetti AC simili su ESPHome/ESP32)
- API
climate_irdipende dalla versione. Su 2024.12.x serviva la costanteCLIMATE_IR_WITH_RECEIVER_SCHEMA+register_climate_ir; dalla 2025.x in poi gli helper a funzioneclimate_ir_with_receiver_schema(cls)+new_climate_ir. Ilclimate.pyattuale usa la forma moderna (testato su 2026.6.3). external_components: pathdeve puntare alla cartella contenitore (components), non alla cartella del componente (components/hitachi_rar6ne1): altrimenticlimate.pyviene scambiato per il coreclimate→ import circolare (partially initialized module 'esphome.components.climate').- RX troncato. Il frame Hitachi è 224 bit ≈ 226 simboli RMT, ma l'ESP32
classico ne cattura di default ~192 → frame tagliato, decode fallito (nessun
RX ok). Fix:rmt_symbols: 384+receive_symbols: 384sulremote_receiver(su questa versione di ESPHome; il vecchiomemory_blocksnon esiste più). byte[9]non è critico. L'unità accetta il frame full-state a prescindere: in TX un OFF conbyte[9]=0xC0ha spento il condizionatore; in RX il telecomando reale ha inviatobyte[9]=0x10(= il default diIRHitachiAc::stateReset).- Tenere
dump: allspento in esercizio: con frame lunghi blocca il loop ~200 ms (remote_receiver took a long time). Riattivare solo per debug raw.
Stato di verifica (2026-06-30)
- ✅ TX: comando OFF dall'ESP → condizionatore spento (emettitore e accettazione frame provati sul campo).
- ✅ RX: telecomando fisico → frame completo decodificato, checksum valido,
entità
climatein HA aggiornata (mode/temp/fan/swing letti correttamente). - ✅ Build/validazione su ESPHome 2026.6.3; fix RMT e path confermati dai log.
- ⏳ Da confermare sul campo: sweep completo dei comandi dalla card HA in TX (tutti i modi, range temp, tutte le fan, swing V/H/Both, preset Eco/Boost). Il protocollo è interamente validato, quindi atteso funzionante; manca solo la prova esaustiva interattiva.
Edge case & robustezza (2026-06-30, sera)
- Range temperatura dell'entità ristretto a 18–30 °C (
kMinTempC/kMaxTempCinhitachi_rar6ne1.h), scelta conservativa. ⚑ Flag aperto: il range realmente supportato dal RAR-6NE1 è da verificare agli estremi (16/32) — se accettati si può riallargare. - Preset ↔ ventola accoppiati (override
control()inhitachi_rar6ne1.cpp): come il telecomando reale, attivando Eco/Silent la ventola va al minimo (Quiet) e Boost/Powerful al massimo (High); cambiando la ventola si esce dal preset. Evita stati incoerenti in HA (es. Boost + Quiet). - Setpoint in modalità Auto: confermato assoluto come in Cool (nessuna modifica necessaria).
- OFF: confermato funzionante in ogni modo (
byte[9]=0xE0). - ⏳ In sospeso: semantica della temperatura in Dry —
byte[11]reale da catturare dal telecomando (per ora si invia il setpoint come in Cool).