So I made an issue a couple of years ago regarding Houghton (RecPro brand) RV air conditioners. They use the Carrier_AC128 protocol. I never had any luck getting it to work with ESP8266, but that might have been my own incompetence issue. I eventually got it working well enough in nodered to live with it until recently, when work generously paid for a Claude subscription. I dumped in the protocol work I had already done and was able to spit out an esphome external component that works wonderfully as a climate entity.
Figured I would add back here - Claude seemed to think this was kind of a big deal.
- ETA - Claude didn't detail the Tx counter (ignored) byte. It increments by second based on the remote timer, and the actual rooftop unit discards values until it hasn't received another one within some set amount of time. EG: keep pressing up on the temp button, only the last setting receieved before the timer reset is acted on. My nodered function just 0'ed this out because I didn't know what it was, and it still worked fine.
Summary
I've completed a full byte-level reverse-engineering of the CARRIER_AC128 protocol from 398 raw IR captures on a RecPro/Houghton RV air conditioner (unit: RP-AC3501-W-KT, remote: HD01-C1). This is the same protocol identified in #1797 by @MarkEvens (Carrier 40GKX0E2006) and my earlier issue #2009.
The analysis covers all 16 bytes: mode, fan speed, temperature (BCD in both °C and °F), power, eco, sleep, lock, LED, timers, clock sync, and the dual checksum algorithm. I've validated it against all 398 captures with zero mismatches, and built a working ESPHome climate component that controls the AC from Home Assistant (repo).
Cross-validation with Carrier 40GKX0E2006
@MarkEvens' two captures from #1797 decode correctly under this byte map:
ON: {0x16, 0x22, 0x38, 0x00, 0x93, 0x91, 0x25, 0xB8, 0x11, 0x22, 0x08, 0x00, 0x00, 0x00, 0x00, 0xE0}
OFF: {0x16, 0x22, 0x38, 0x00, 0x93, 0x91, 0x25, 0xB8, 0x11, 0x22, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x20}
| Field |
ON |
OFF |
Notes |
| Header (byte 1) |
0x16 ✓ |
0x16 ✓ |
Fixed protocol header |
| Mode (byte 2) |
High fan + Cool |
High fan + Cool |
Fan=0x2, Mode=0x2 |
| Clock (bytes 3-4) |
00:38 |
00:38 |
BCD 24hr clock |
| Timer-on (byte 5) |
Enabled, 13:xx |
Enabled, 13:xx |
|
| Timer-off (byte 6) |
Enabled, 11:xx |
Enabled, 11:xx |
|
| Temp °C (byte 7) |
25°C |
25°C |
BCD |
| Celsius flag (byte 8) |
Yes |
Yes |
Byte 12 = 0x00 confirms no °F sent |
| Checksum 1 (byte 8) |
0xB ✓ |
0xB ✓ |
Digit-sum mod 16 |
| Timer-on mins (byte 9) |
:11 |
:11 |
|
| Timer-off mins (byte 10) |
:22 |
:22 |
|
| Power (byte 11) |
0x08 = ON |
0x0C = OFF |
Only difference |
| Checksum 2 (byte 16) |
0xE ✓ |
0x2 ✓ |
Correctly reflects power change |
Both checksums validate. The byte semantics are identical between the Carrier 40GKX0E2006 and RecPro/Houghton units.
Complete Byte Map
| Byte |
High Nibble |
Low Nibble |
| 1 |
0x1 (fixed) |
0x6 (fixed) |
| 2 |
Fan speed |
Operating mode |
| 3 |
Clock minutes tens |
Clock minutes ones |
| 4 |
Clock hours tens |
Clock hours ones |
| 5 |
Timer-on enable + hour tens |
Timer-on hour ones |
| 6 |
Timer-off enable + hour tens |
Timer-off hour ones |
| 7 |
Set point °C tens |
Set point °C ones |
| 8 |
Checksum 1 |
Flags: Celsius, Sleep |
| 9 |
Timer-on minutes tens |
Timer-on minutes ones |
| 10 |
Timer-off minutes tens |
Timer-off minutes ones |
| 11 |
Tx counter (ignored) |
Flags: Power |
| 12 |
Set point °F tens |
Set point °F ones |
| 13 |
(unused) |
Flags: Eco |
| 14 |
Flags: Lock, LED |
(unused) |
| 15 |
Clock seconds tens |
Clock seconds ones |
| 16 |
Checksum 2 |
(unused) |
Fan Speed (byte 2, high nibble)
| Value |
Speed |
| 0x8 |
Low |
| 0x4 |
Medium |
| 0x2 |
High |
| 0x1 |
Auto |
Mode (byte 2, low nibble)
| Value |
Mode |
| 0x1 |
Dehumidify |
| 0x2 |
Cool |
| 0x4 |
Fan Only |
| 0x8 |
Heat |
| 0xA |
Maintain (Heat+Cool) |
Temperature
- Celsius: BCD in byte 7, range 16-30°C
- Fahrenheit: BCD in byte 12, range 60-86°F
- In Celsius display mode, byte 12 = 0x00 (no °F transmitted)
- In Fahrenheit mode, both bytes populated
Power (byte 11, low nibble)
| Value |
State |
| 0x08 |
ON |
| 0x0C |
OFF |
| 0x00 |
ON (alternate, seen in some captures) |
Flags
- Byte 8 bit 5 (0x08): Celsius display (1=°C, 0=°F)
- Byte 8 bit 7 (0x02): Sleep mode
- Byte 13 bit 6 (0x04): Eco mode
- Byte 14 bit 1 (0x80): Control lock
- Byte 14 bit 2 (0x40): LED display off
Checksum Algorithm
Both halves use the same algorithm: sum all individual hex digits, then mod 16.
- CS1 (byte 8 high nibble): Sum hex digits of bytes 1-7 + low nibble of byte 8, mod 16
- CS2 (byte 16 high nibble): Sum hex digits of bytes 9-15 + low nibble of byte 16, mod 16
Timer Bytes (5-6)
Bit 1 (MSB) = enable flag. Bits 2-4 = BCD hour tens. Bits 5-8 = BCD hour ones. Minutes are in bytes 9-10.
What I Can Contribute
I'm happy to submit a PR adding an IRCarrierAc128 state management class with:
- Getters/setters for all fields (power, mode, fan, temp, eco, sleep, lock, LED, timers)
- Checksum calculation and validation
toCommon() / toString() support
- Unit tests using real captured data
Full protocol documentation: docs/protocol-spec.md
Hardware Tested
- Unit: RecPro RP-AC3501-W-KT (Houghton RV AC)
- Remote: HD01-C1
- IR blaster: ESP8285 ESP-01M (GPIO4 TX, GPIO14 RX)
- Firmware: ESPHome with custom CARRIER_AC128 climate component
- Status: Fully working — sends commands, receives remote, two-way sync with Home Assistant
So I made an issue a couple of years ago regarding Houghton (RecPro brand) RV air conditioners. They use the Carrier_AC128 protocol. I never had any luck getting it to work with ESP8266, but that might have been my own incompetence issue. I eventually got it working well enough in nodered to live with it until recently, when work generously paid for a Claude subscription. I dumped in the protocol work I had already done and was able to spit out an esphome external component that works wonderfully as a climate entity.
Figured I would add back here - Claude seemed to think this was kind of a big deal.
Summary
I've completed a full byte-level reverse-engineering of the CARRIER_AC128 protocol from 398 raw IR captures on a RecPro/Houghton RV air conditioner (unit: RP-AC3501-W-KT, remote: HD01-C1). This is the same protocol identified in #1797 by @MarkEvens (Carrier 40GKX0E2006) and my earlier issue #2009.
The analysis covers all 16 bytes: mode, fan speed, temperature (BCD in both °C and °F), power, eco, sleep, lock, LED, timers, clock sync, and the dual checksum algorithm. I've validated it against all 398 captures with zero mismatches, and built a working ESPHome climate component that controls the AC from Home Assistant (repo).
Cross-validation with Carrier 40GKX0E2006
@MarkEvens' two captures from #1797 decode correctly under this byte map:
Both checksums validate. The byte semantics are identical between the Carrier 40GKX0E2006 and RecPro/Houghton units.
Complete Byte Map
0x1(fixed)0x6(fixed)Fan Speed (byte 2, high nibble)
Mode (byte 2, low nibble)
Temperature
Power (byte 11, low nibble)
Flags
Checksum Algorithm
Both halves use the same algorithm: sum all individual hex digits, then mod 16.
Timer Bytes (5-6)
Bit 1 (MSB) = enable flag. Bits 2-4 = BCD hour tens. Bits 5-8 = BCD hour ones. Minutes are in bytes 9-10.
What I Can Contribute
I'm happy to submit a PR adding an
IRCarrierAc128state management class with:toCommon()/toString()supportFull protocol documentation: docs/protocol-spec.md
Hardware Tested