logo elektroda
logo elektroda
X
logo elektroda

Hisense AC WiFi module/protocol reverse engineering

DeDaMrAz 141 0

TL;DR

  • Reverse engineering of the Hisense AC WiFi module’s UART protocol focuses on frame formats, opcodes, routing bytes, and the long 0x97 status/state response.
  • The packet structure appears to use F4 F5 headers and F4 FB footers, with source/direction, family, opcode, routing block, payload, checksum, and trailing control bytes.
  • Known frame families include 0x0C short poll, 0x13 short query/telemetry, 0x29 control/update, 0x7B direct response, and 0x97 longer status/state report.
  • One decoded 0x97 frame shows 0x12 wind status, 26°C temperature, and 0xDF outdoor/load data, while horizontal swing adds 0x02 to the mode byte.
  • The parser is still WIP, many payload fields remain uncertain, and most of the long status body is not yet mapped.
Generated by the language model.
ADVERTISEMENT
📢 Listen (AI):
  • I am going to start this thread as a WIP for decoding (another unknown) UART based protocol.

    I am going to present the findings so far but in a rough state as I am still working on figuring out what is in the packets. Assumption that I started with was that the packet is similar to IR commands where multiple states are sent/reported all at once and I was not wrong. So here goes some of it.

    ### General frame structure
    
    | Byte offset(s) | Example                                                | Working meaning                                                          | Confidence |
    | -------------- | ------------------------------------------------------ | ------------------------------------------------------------------------ | ---------- |
    | `0:1`          | `F4 F5`                                                | Frame header / sync                                                      | High       |
    | `2`            | `00` / `01`                                            | Source or direction byte. `00` = Wi-Fi module TX, `01` = AC-side TX      | High       |
    | `3`            | `40`                                                   | Protocol family / constant channel byte                                  | High       |
    | `4`            | `0C`, `13`, `29`, `7B`, `97`                           | Opcode / frame kind. Does not currently behave like a simple length byte | High       |
    | `5:12`         | `00 00 01 01 FE 01 00 00` or `01 00 FE 01 01 01 01 00` | Routing / endpoint / addressing block                                    | Medium     |
    | `13:14`        | `65 00`, `66 00`, `1E 00`                              | Packet type / subtype family                                             | High       |
    | `15:n-4`       | varies                                                 | Payload / body                                                           | High       |
    | `n-4`          | `01`, `02`, `04`, `05`                                 | Trailing control / count byte, exact purpose unknown                     | Low        |
    | `n-3`          | e.g. `B3`, `5C`, `7A`, `B1`                            | Checksum byte                                                            | Medium     |
    | `n-2:n-1`      | `F4 FB`                                                | Frame footer / terminator                                                | High       |


    Important and right now focus of my exploration is frame 0x97 that contains all the telemetry and status bytes from the AC

    #### `0x97` long status/state response
    
    | Offset(s) | Example bytes             | Working meaning             |
    | --------- | ------------------------- | --------------------------- |
    | `0:1`     | `F4 F5`                   | Header                      |
    | `2`       | `01`                      | Sent by AC side             |
    | `3`       | `40`                      | Protocol family             |
    | `4`       | `97`                      | Long status opcode          |
    | `5:12`    | `01 00 FE 01 01 01 01 00` | Routing block               |
    | `13:14`   | `66 00`                   | Status family               |
    | `15:n-4`  | long variable payload     | State / telemetry data      |
    | `n-4`     | e.g. `04`, `05`           | Trailing control/count byte |
    | `n-3`     | e.g. `B1`, `7A`           | Checksum                    |
    | `n-2:n-1` | `F4 FB`                   | Footer                      |


    and 0x29 frame which is a WiFi module control packet

    #### `0x29` control/update
    
    | Offset(s) | Example bytes                     | Working meaning                                |
    | --------- | --------------------------------- | ---------------------------------------------- |
    | `0:1`     | `F4 F5`                           | Header                                         |
    | `2`       | `00`                              | Sent by Wi-Fi module                           |
    | `3`       | `40`                              | Protocol family                                |
    | `4`       | `29`                              | Control/update opcode                          |
    | `5:12`    | `00 00 01 01 FE 01 00 00`         | Routing block                                  |
    | `13:14`   | `65 00`                           | Control/update family                          |
    | `15:45`   | variable control payload          | Desired state / control fields                 |
    | `18:19`   | e.g. `5C 2D`, `3C 2D`, `00 33`    | High-value control pair within payload         |
    | `46`      | usually `02`                      | Trailing control/count byte                    |
    | `47`      | e.g. `5C`, `3C`, `06`             | Checksum                                       |
    | `48:49`   | `F4 FB`                           | Footer 


    These are the known frames so far (a lot of guess work still)

    ### Known frame families
    
    | Opcode byte `[4]` | Direction | Typical type/subtype | Working meaning                               | Confidence |
    | ----------------- | --------- | -------------------- | --------------------------------------------- | ---------- |
    | `0C`              | TX        | `66 00`              | Short poll request                            | High       |
    | `13`              | TX/RX     | `1E 00`              | Short query / telemetry / confirmation family | Medium     |
    | `29`              | TX        | `65 00`              | Control / update request                      | High       |
    | `7B`              | RX        | `65 00`              | Direct response / ack to `0x29`               | High       |
    | `97`              | RX        | `66 00`              | Longer status / state report                  | High       |


    Using a tool I am developing to capture data so right now one of the 0x97 frames partialy decoded looks like this

    PARSED STRUCTURE:
      header      : F4 F5 [0:2]
      src         : 01 (ac) [2:3]
      family      : 40 [3:4]
      opcode      : 97 (status) [4:5]
      route       : 01 00 FE 01 01 01 01 00 [5:13]
      type        : 66 [13:14]
      subtype     : 00 [14:15]
      body        : 01 12 00 08 1A 1A 1A 80 80 00 01 01 00 00 00 00 00 00 00 00 40 00 80 05 00 00 00 00 00 0C 2C 0E 00 00 00 DF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [15:156]
      trail       : 05 [156:157]
      checksum    : 96 [157:158]
      footer      : F4 FB [158:160]
      body_len    : 8D
    RULESET:   Main Status
    ROUTING:   Key=STATUS_66_POLL -> Targets=[Main Status]
    Pair:      TX seq 15 (Δt=+199.7ms)
    
    DECODED:
      STATUS_DATA_MARKER : stable_status_page_marker_candidate (01)
      STATUS_WIND_STATUS : high (12)
      STATUS_SLEEP_STATUS : off (00)
      STATUS_MODE_RUN_DIRECTION_PACKED_CANDIDATE : fan_only_on_horizontal_swing_off (08)
      STATUS_INDOOR_TEMPERATURE_SETTING_DISPLAY : 26
      STATUS_INDOOR_TEMPERATURE_STATUS_DISPLAY : 26
      STATUS_INDOOR_PIPE_TEMPERATURE_C : 26
      STATUS_INDOOR_HUMIDITY_SETTING : unavailable (80)
      STATUS_INDOOR_HUMIDITY_STATUS : unavailable (80)
      STATUS_FEEL_DISPLAY_CONTEXT_09_11_CANDIDATE : 00 01 01
      STATUS_PRESET_OR_FLAGS_11 : alt_runtime_context_candidate (01)
      STATUS_TIMER_RTC_BLOCK_12_19_CANDIDATE : 00 00 00 00 00 00 00 00
      STATUS_PRESET_STAGE_20_CANDIDATE : vertical_swing_on_candidate (40)
      STATUS_PRESET_STAGE_21_CANDIDATE : normal_or_clear_candidate (00)
      STATUS_FEATURE_PRESET_STATE_20_23_CANDIDATE : 40 00 80 05
      STATUS_FEATURE_EXTENSION_24_25_CANDIDATE : 00 00
      STATUS_RUNTIME_TELEMETRY_26_31_CANDIDATE : 00 00 00 0C 2C 0E
      STATUS_OUTDOOR_TELEMETRY_32_39_CANDIDATE : 00 00 00 DF 00 00 00 00
      STATUS_POWER_LOAD_INDEX_35_CANDIDATE : DF
      STATUS_OUTDOOR_SYSTEM_STATE_40_45_CANDIDATE : 00 00 00 00 00 00
      STATUS_RESERVED_ZERO_46_55_CANDIDATE : 00 00 00 00 00 00 00 00 00 00
      STATUS_BLOCK_56_71 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      STATUS_BLOCK_72_87 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      STATUS_BLOCK_88_103 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
      STATUS_BLOCK_104_120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    
    RAW:
    F4 F5 01 40 97 01 00 FE 01 01 01 01 00 66 00 01 12 00 08 1A 1A 1A 80 80 00 01 01 00 00 00 00 00 00 00 00 40 00 80 05 00 00 00 00 00 0C 2C 0E 00 00 00 DF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 96 F4 FB
    
    PAYLOAD:
    01 12 00 08 1A 1A 1A 80 80 00 01 01 00 00 00 00 00 00 00 00 40 00 80 05 00 00 00 00 00 0C 2C 0E 00 00 00 DF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    ASCII:     ....................@.........,..............................................................................................................


    Doing this to spark some interest and justify my absence from the development right now 😁

    More to come in the next weeks but it is doable.
    Let me know if anybody has anything to add or has done anything like this I would appreciate it.

    One interesting finding for example is that horizontal swing is adding 0x02 bytes to mode byte (3) so for example fan_only: 08 becomes 0A, heat: 18 -> 1A and cool: 28 -> 2A etc....

    Decoded data for this model (will update what model this is at the latter stage) is:

    - power
    - HVAC mode (cool, heat, fan only, etc.)
    - target temperature
    - fan speed
    - horizontal swing on/off
    - vertical swing on/off
    - sleep
    - eco
    - fan mute
    - super
    -
    iFeel
    temperature-source override behavior
    - dimmer/display-state reporting
    - smart operating-context reporting
    - temperature unit (
    C/F
    )

    P.S. Ariston is not forgotten as well, first drivers version is working and now in full use, after confirmation it will be updated and incorporated into main. Also non wifi boards are on the "healing bench" atm and will be worked on at some point.

    Cool? Ranking DIY
    About Author
    DeDaMrAz
    Level 22  
    Offline 
    DeDaMrAz wrote 600 posts with rating 127, helped 34 times. Been with us since 2020 year.
  • ADVERTISEMENT
📢 Listen (AI):

FAQ

TL;DR: With 5 known frame families and a 141-byte status payload, this WIP shows that “it is doable” to reverse engineer the Hisense AC WiFi UART protocol. It is for makers who want to decode full-state HVAC packets, map bytes to features, and build reliable control or telemetry tooling for Hisense air conditioners. [#21893661]

Why it matters: This thread turns raw serial traffic into a practical roadmap for decoding status, control, swing, and telemetry fields on a Hisense AC.

Approach What it captures Strength in this thread Limitation
UART sniffing Full serial frames Revealed opcodes 0x97, 0x29, 0x7B, routing bytes, and checksums Many payload fields still need mapping
IR-style state comparison Full-state behavior patterns Helped confirm that multiple states are sent together Does not expose UART framing details

Key insight: The most valuable packet is opcode 0x97: it carries the long AC-side status report, including decoded mode, fan, temperatures, swing, and several telemetry candidates. Once you can capture and diff 0x97, the protocol becomes much easier to map. [#21893661]

Quick Facts

  • The thread identifies 5 known frame families: 0x0C, 0x13, 0x29, 0x7B, and 0x97, each tied to a repeatable direction and packet role. [#21893661]
  • A parsed 0x97 sample is 160 bytes long overall, with a 141-byte body (0x8D), plus header F4 F5, checksum, and footer F4 FB. [#21893661]
  • One captured status frame paired to its transmit sequence with Δt = +199.7 ms, which is useful when correlating polls and responses. [#21893661]
  • The sample status payload reports 26 °C for target temperature, 26 °C for displayed indoor temperature, and 26 °C for indoor pipe temperature. [#21893661]
  • Horizontal swing changes the packed mode byte by +0x02: for example, fan-only 0x08 becomes 0x0A, heat 0x18 becomes 0x1A, and cool 0x28 becomes 0x2A. [#21893661]

How can I start reverse engineering the UART protocol used by a Hisense AC WiFi module?

Start by logging traffic between the WiFi module and the AC board, then diff packets while changing one HVAC setting at a time. The thread’s working method assumes full-state packets, similar to AC IR logic, and that assumption already exposed 5 frame families. A practical 3-step flow is: 1. capture raw UART frames, 2. group them by opcode and direction, 3. compare byte changes against real actions like mode or swing. This approach already isolated 0x97 as the main status frame and 0x29 as the control frame. [#21893661]

What is the general frame structure of the Hisense AC UART protocol, including the header, routing block, checksum, and footer?

The working frame layout is fixed at the edges and variable in the middle. Frames start with header F4 F5, then source byte 00 or 01, family byte 40, an opcode at byte 4, an 8-byte routing block at bytes 5:12, type/subtype at bytes 13:14, a variable payload, one trailing control byte, one checksum byte, and footer F4 FB. The checksum sits at n-3, and the trailer byte sits at n-4. That pattern holds across short and long frames described in the thread. [#21893661]

How do the 0x97 status frames from the AC differ from the 0x29 control/update frames sent by the WiFi module?

0x97 is the long AC-to-module status report, while 0x29 is the module-to-AC control/update request. In the thread, 0x97 uses source 01, type/subtype 66 00, and a long payload carrying state and telemetry. 0x29 uses source 00, type/subtype 65 00, and a shorter control payload, with important bytes noted at offsets 18:19. The sample 0x97 body is 0x8D bytes long, while the shown 0x29 layout runs to byte 49 including checksum and footer. [#21893661]

What is the meaning of opcode 0x97 in the Hisense AC protocol, and which status or telemetry fields have been identified so far?

Opcode 0x97 is the long status/state response sent by the AC side. The thread already maps fields for power, HVAC mode, target temperature, fan speed, horizontal swing, vertical swing, sleep, eco, fan mute, super, iFeel, dimmer/display state, smart operating context, and temperature unit. In one parsed example, the decoder reports high fan, fan-only mode, horizontal swing off, vertical swing on candidate, and three separate 26 °C values. Several telemetry blocks remain labeled as candidates rather than confirmed meanings. [#21893661]

What is a routing or endpoint block in a UART packet, and how might it work in the Hisense AC WiFi protocol?

The routing block is an 8-byte address-like field that appears between the opcode and the type/subtype bytes and stays structured across frame families. "Routing block" is a packet field that identifies message path or endpoints, separating who sends, who receives, and which logical channel is used, even when the payload format stays similar. In this thread, 0x97 commonly shows 01 00 FE 01 01 01 01 00, while 0x29 uses 00 00 01 01 FE 01 00 00. That reversal suggests direction-aware addressing between the WiFi module and the AC side. [#21893661]

What does the checksum byte likely represent in these Hisense AC UART frames, and how can I verify it?

The checksum byte is likely a frame-integrity value calculated from earlier bytes, but the exact formula is still unresolved. The thread places it consistently at n-3, with examples such as B1, 7A, 5C, 3C, 06, and 96. Verify it by capturing repeated frames, flipping one known field, and checking whether the checksum changes in sync with payload edits while header F4 F5 and footer F4 FB stay fixed. If one altered byte causes a predictable checksum shift, you can narrow the algorithm quickly. [#21893661]

Why does enabling horizontal swing add 0x02 to the mode byte in the Hisense AC protocol, such as 0x08 to 0x0A or 0x18 to 0x1A?

Horizontal swing appears to be encoded as a bit flag packed into the same mode byte. The thread gives three direct examples: fan-only 0x08 becomes 0x0A, heat 0x18 becomes 0x1A, and cool 0x28 becomes 0x2A. Each change adds exactly 0x02, which strongly suggests a single feature bit rather than a new operating mode. That matters because you should decode the byte as combined state, not as a flat mode enumeration. [#21893661]

Which HVAC functions have already been decoded from this Hisense AC model, including power, mode, target temperature, fan speed, and swing settings?

The decoded set already covers the core HVAC controls and several extras. Confirmed items listed in the thread are power, HVAC mode, target temperature, fan speed, horizontal swing on/off, vertical swing on/off, sleep, eco, fan mute, super, iFeel temperature-source override behavior, dimmer/display-state reporting, smart operating-context reporting, and temperature unit in C/F. That is enough to support a useful driver even before every telemetry byte is named. The author also states that first working driver versions for another related project are already in full use. [#21893661]

How do I capture and parse 0x97 long status packets from a Hisense AC using a custom UART logging tool?

Capture 0x97 by polling or waiting for AC-side reports, then split the frame into fixed offsets before decoding candidates. A simple 3-step method is: 1. detect header F4 F5, 2. read through footer F4 FB, 3. label bytes by offsets such as source, family, opcode, route, type, payload, trailer, and checksum. The thread’s parser marks the sample as source 01, family 40, opcode 97, type/subtype 66 00, body range 15:156, and body length 0x8D. It also links the packet to TX sequence 15 at +199.7 ms. [#21893661]

What is a protocol family byte in a serial frame, and what does the constant 0x40 likely indicate in this Hisense AC implementation?

A protocol family byte is a classifier that groups packets under one shared framing or command set. "Protocol family byte" is a frame field that tags packets as belonging to one protocol branch, helping parsers route opcodes and payload rules while staying constant across many message types. In this thread, byte 3 is always 0x40 across 0x0C, 0x13, 0x29, 0x7B, and 0x97. That consistency suggests 0x40 marks the Hisense AC UART command family rather than carrying state data. [#21893661]

How should I interpret the trailing control or count byte near the end of Hisense AC packets when its exact purpose is still unknown?

Treat the trailing byte as a stable metadata field, not as payload, until repeated captures prove its role. The thread places it at n-4 and shows values such as 01, 02, 04, and 05. In the sample 0x97 frame, the trailer is 05; in 0x29, it is usually 02. That pattern suggests a counter, control marker, or page indicator. Keep it separate in your parser so you do not mislabel application data and corrupt later field mapping. [#21893661]

What are the known frame families in this Hisense AC reverse engineering work, and how are 0x0C, 0x13, 0x29, 0x7B, and 0x97 used?

Five frame families are currently tracked, each with a distinct working role. 0x0C is a short poll request, usually TX with type 66 00. 0x13 is a short query, telemetry, or confirmation family used in both directions with type 1E 00. 0x29 is the WiFi module’s control/update request with type 65 00. 0x7B is the direct response or acknowledgment to 0x29, and 0x97 is the longer AC status/state report with type 66 00. This family map gives you a practical backbone for a parser. [#21893661]

UART sniffing vs IR command analysis for air conditioners: which approach is better for decoding full state packets on a Hisense AC?

UART sniffing is better here because it exposes the actual serial frames, fixed offsets, routing bytes, and checksum position. The author started with an IR-style assumption that multiple states are sent together, and that assumption helped, but the real progress came from UART captures showing F4 F5, family 40, opcodes, route bytes, and long 0x97 payloads. Use IR-style comparison as a decoding strategy, but use UART as the primary data source when you want implementable protocol details. [#21893661]

How can I map unknown payload bytes in a Hisense AC 0x97 frame to real-world values like indoor temperature, humidity, swing state, and power load?

Map unknown bytes by changing one real-world setting at a time and diffing multiple 0x97 frames. In the sample, bytes already correlate with target temperature 26, display temperature 26, pipe temperature 26, humidity placeholders 80 80, a vertical swing candidate 40, and power load candidate DF at offset 35. Keep a table of unchanged versus changed offsets across mode, swing, sleep, and fan tests. Edge case: many blocks from offsets 46 through 120 stayed all zero, so do not force meanings onto bytes that never move. [#21893661]

What safety and troubleshooting steps should I follow when probing the serial interface on a mains-powered Hisense air conditioner control board?

The thread does not provide electrical safety procedures, voltage levels, or connector pinouts, so you should limit yourself to non-invasive capture until you identify the interface safely. It does confirm active work on WiFi and non-WiFi boards and shows protocol progress without publishing board-level probing instructions. The safest troubleshooting step from this source is methodological: capture existing traffic first, parse frames offline, and avoid assuming any UART signal details that the post never states. That prevents decoding errors and avoids inventing unsafe hardware steps from incomplete data. [#21893661]
Generated by the language model.
ADVERTISEMENT