Skip to content

CARRIER_AC128: Complete byte-level protocol analysis (398 captures, tested on real hardware) #2247

@mrmees

Description

@mrmees

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions