Reverse engineering of the polarity-free two-wire bus between the wired controller and the indoor unit of Midea central air conditioners.
Related project: Midea D1/D2 Protocol
The wall-mounted control panel of a Midea central air conditioner connects to the indoor unit main board through a single two-core wire. This wire carries both power supply and bidirectional communication, and is polarity-free (either wire can be swapped) — a typical private "power + communication" multiplexed bus.
Project goal: tap into this bus with an ESP8266, decode Midea's proprietary protocol (XYE protocol family), and report the AC status.
Through oscilloscope capture and analysis, the physical layer parameters of the bus were finally confirmed:
| Parameter | Value |
|---|---|
| Bus DC voltage | approx. 18–20 V |
| Carrier frequency | 200 kHz |
| Carrier amplitude | approx. 1 V peak-to-peak |
| Modulation | OOK / ASK (carrier present/absent represents 0/1) |
| Bit width | approx. 208 µs (4800 bps UART, 1 bit ≈ 208 µs) |
| Bus polarity | None; the two wires are interchangeable |
Final conclusion: the physical layer is essentially a 4800 bps UART signal transmitted via OOK modulation on a 200 kHz carrier.
Polarity-free bus
│
├──→ Bridge rectifier → Inductor → DC-DC → 3.3 V power supply
│
└──→ Coupling capacitor → Bias → Envelope detector → Comparator → ESP8266 GPIO
DC blocking and biasing:
| Component | Value | Purpose |
|---|---|---|
| C1, C2 | 0.1 µF / 100 V SMD C0G | Block the 20 V DC, pass only the 200 kHz carrier |
| R6/R7, R4/R5 | 100 kΩ + 100 kΩ | Independent bias to 1.65 V, one pair each for A' and B' |
Envelope detection:
| Component | Value | Purpose |
|---|---|---|
| D3, D4 | BAT54S Schottky | Dual-channel detection, handles polarity-free input |
| R10 | 10 kΩ | Charging current limit |
| C3 | 470 pF | Filter out the 200 kHz carrier |
| R_14 | 200 kΩ | Capacitor discharge path |
The detected signal swings between 1.55 V and 1.27 V. So 1.41 V was chosen as the comparator reference voltage.
Comparator:
| Component | Value | Purpose |
|---|---|---|
| U1 | LM393 | Open-collector output, low cost |
| R11 | 10 kΩ | Pull-up to 3.3 V |
| R_12/R_13 | 4.7 kΩ / 3.6 kΩ | Voltage divider producing approx. 1.41 V reference |
The comparator outputs a clean 3.3 V-level signal.
Power supply path:
| Component | Value | Purpose |
|---|---|---|
| Bridge rectifier | ABS210 | Polarity-free rectification |
| Inductor | 470 µH | Isolates the 200 kHz communication signal |
| DC-DC | 20 V → 3.3 V | Powers the ESP8266 |
The demodulated baseband signal is simply a 4800 bps UART stream. The upper-layer protocol is Midea's proprietary XYE protocol family.
[0] : 0xAA (frame header)
[1] : Protocol family (e.g. 0x23 / 0x20 / 0x70 / 0x71 / 0x76)
[2..5] : Source address and destination address (4 bytes)
[6] : Payload length (DataLen)
[7..N-5] : Business data segment (DataLen bytes)
[N-4..N-3] : 2-byte CRC16 checksum (low byte first, high byte second)
[N-2..N-1] : 0x55 0xFE (frame tail)
CRC-16/MODBUS is used:
- Polynomial: 0xA001 (reflected)
- Initial value: 0xFFFF
- Calculation range: from the 2nd byte (skipping the 0xAA frame header) to just before the checksum field (len − 5 bytes in total)
Match conditions:
- Frame header fixed at 0xAA
- Frame tail fixed at 0x55 0xFE
- Command family 0x23 (indoor unit communication)
- Source address 0xF8 (indoor unit main board)
- Function code 0x65 (indoor unit operating parameters / status report response)
- Payload length pBuf[6] >= 10
Payload field decoding:
| Byte | Meaning | Decoding |
|---|---|---|
| pData[0] | Mode / power flag | bit6 or bit7 = power on; low 4 bits = mode (0 auto, 1 fan, 2 cool, 3 heat, 6 dry) |
| pData[1] | Fan speed | 0x80 = auto; 0x01–0x07 = levels 1–7 |
| pData[2] | Set temperature | ((b2 & 0xFE) >> 1) − 40 |
| pData[3] | Fan speed (auxiliary) | — |
| pData[4] | Swing / auxiliary status | — |
| pData[9] | Indoor temperature | b9 − 30 |
Mode flag examples:
- 0x00: power off (Bit 6 = 0)
- 0x42: power on + cooling
- 0x46: power on + dry (dehumidification)
- 0x41: power on + fan only
- 0x43: power on + heating
- 0xC0: power on + auto mode, actually running in cooling
Serial reception:
- SoftwareSerial receives the 4800 bps UART data
- A 10 ms inter-frame idle timeout marks a frame as complete
- 256-byte receive buffer
Decoding flow:
- Validate the whole frame (header, tail, length, CRC16)
- Match the indoor unit status response command
- Extract the payload and compare with the previous data to detect changes
- Decode power state, mode, set temperature, fan speed, and indoor temperature
Symptom: after connecting the bridge rectifier + DC-DC, the panel and the main board could no longer communicate.
Cause: the DC-DC input capacitor presents a low impedance to the 200 kHz signal, short-circuiting the communication signal.
Solution: insert a 470 µH inductor between the bridge rectifier positive output and the DC-DC input to isolate the 200 kHz signal.
- Physical layer parameters fully confirmed (200 kHz OOK, 20 V bus, polarity-free)
- Confirmed the physical layer is essentially a 4800 bps UART
- Demodulation circuit works properly; the comparator outputs clean 0/3.3 V baseband pulses
- Power supply and communication do not interfere with each other
- Successfully reverse-engineered the XYE protocol family frame format and CRC16 checksum
- Implemented indoor unit status frame decoding (power, mode, set temperature, fan speed, indoor temperature)
This project covers the complete workflow of "physical layer reverse engineering → demodulation circuit design → protocol reverse engineering → software implementation → Home Assistant integration", providing a fully reproducible solution for smart-home retrofit of Midea's two-wire polarity-free bus.
The core challenges were small-signal handling on a polarity-free bus, the discharge path design of the envelope detector, and the isolation between power and communication. Once the physical layer was confirmed to be a 4800 bps UART, the protocol reverse engineering became systematic, and both the XYE protocol family frame format and the CRC16 checksum were successfully decoded.
If you would rather not build the discrete analog front end, there is an integrated option: the Analog Devices MAX22088 (and the pin-compatible MAX22288), an HBS (Home Bus System) compatible transceiver. It puts the whole physical layer into one chip — most notably an active inductor that removes the bulky external AC-blocking inductor, plus an integrated 5 V linear regulator (up to 70 mA) to power the node, dynamic cable termination, and adjustable receiver hysteresis/thresholds. Up to 200 kbps, 24-pin 4 mm × 4 mm TQFN, −40 °C to +105 °C.
Why this project still uses a discrete front end:
| Aspect | MAX22088 | Discrete front end (this project) |
|---|---|---|
| Chip price | ~$3.63 at 1ku; $7.94–$14.28 for single units | — |
| Evaluation kit | $104–$183, two boards (master + remote), ±1 kV surge tested | — |
| Board-level cost | the transceiver alone dominates the BOM | a few passive components only |
| Assembly | 4 mm × 4 mm TQFN, needs hot air / reflow | SOIC / through-hole, hand-solderable |
| Bring-up | Low: the front end is standardized | Higher: needs a scope and stage-by-stage analog debugging |
Notes:
- For a quick physical-layer validation (checking whether the Midea X1/X2 bus really works with this transceiver), the MAX22088 EVKIT is the lowest-risk route — schematics and PCB layout are public.
- Official resources: driver plus a
two_nodesmaster/slave example — https://github.com/analogdevicesinc/max22x88-driver; application note How to Transmit UART Packets Using a Home Bus System (HBS) Compatible Transceiver — https://www.analog.com/en/design-notes/how-to-transmit-uart-packets-using-a-home-bus-system-hbs-compatible-transceiver.html - Bottom line: the MAX22088 standardizes the "dirty work" of the physical layer and is a great reference/validation starting point, but it is not a chip optimized for low-cost DIY. For a finished build, the discrete approach described above is usually the better trade-off.




