-
Notifications
You must be signed in to change notification settings - Fork 119
S21 Protocol
The "S21 protocol" or officially "Daikin proprietary S21 protocol" is a serial protocol used by a wide range of Daikin indoor units over what the service manual calls the "HA connector" (which corresponds to header S21 on most boards, and less often header S403), used to connect wired thermostats and Wi-Fi controllers to residential mini-split installations. No official documentation is publically available, so it is being reverse engineered by inspecting the flows going between indoor air conditioning units and the standard wifi controllers, as well as by sending arbitrary queries to see how the unit reacts or how the results change over time.
Commercial and multi-split units, like the Daikin VRV, SkyAir or Altherma lines, will usually use the P1P2 protocol instead, while ducted HVAC units (called "Unitary" by Daikin One) likely have their own different protocol. Faikout also supports the serial protocols sent over ports X50A and CN_WIRED, though these are very different from S21, and little if any information found here applies to these other two protocols.
The data is a simple 8-bit serial at 2400 baud, even parity, two stop bits. The packet format uses ASCII a lot, and each packet starts with STX (0x02), ends with ETX (0x03) and gets an ACK (0x06) response. All communication is driven externally. There's no unsolicited output from the unit itself, it's purely polled.
Packet format (example request from host)
| Hex | Description |
|---|---|
02 |
Packets start 02 (STX) |
46 31
|
Command code - Indicates the request type. Example: “F1” |
| Optional additional payload - Arguments for write commands. Often ASCII, but not always | |
77 |
Sum of bytes after 02 to end of payload |
03 |
Packets end 03 (ETX) |
Acknowledgement (from unit)
| Hex | Description |
|---|---|
06 |
Ack |
For read requests, the unit will then reply with the requested data:
Packet format (example reply from unit)
| Hex | Description |
|---|---|
02 |
Packets start 02 (STX) |
47 31
|
Reply code - Indicates the reply type. Example: "G1" |
31 34 46 41
|
Payload. Example: "14FA" |
64 |
Sum of bytes after 02 to end of payload |
03 |
Packets end 03 (ETX) |
Acknowledgement (from host)
| Hex | Description |
|---|---|
06 |
Ack |
Since certain bytes have special meaning, they cannot be present in data payload. These bytes include: 02(STX), 03(ETX), 06(ACK), 0A, 15(NAK). This extends to the checksum. When the calculated checksum is 02 (STX), 03 (ETX), 06 (ACK) or 15 (NAK), 2 is added to encode the value on the wire instead, resulting in these being transmitted as 04 (EOT), 05 (ENQ), 08 (BS) or 17 (ETB), respectively. Most bitfields also have the upper bits remain in a static pattern (usually 0b0011_xxxx/0x30, which is ASCII for '0'), which is likely to prevent these 4 special byte values from appearing in data stream, and are refered to as "shield bits" below. This has the added benefit of of making most data payloads be decodable as printable ASCII.
The easiest way to probe the S21 pins is to use Faikout's command/myFaikoutUnit/send MQTT command (which will require that you have your units connected to an MQTT broker already, like people usually do with Home Assistant). That command takes a payload of either a single plain text ASCII command (such as D113BA) or a JSON array of commands to run in order (such as ["D113BA", "F1", "RC", "RG"]). In response, Faikout will send the debug output of each command over the info/myFaikoutUnit/tx and info/myFaikoutUnit/rx topics (which can be subscribed together as info/myFaikoutUnit/#).
Topic: info/myFaikoutUnit/tx
{"protocol":"S21","dump":"0246317703","F1":""}
Topic: info/myFaikoutUnit/rx
{"protocol":"S21","dump":"02473130334C416803","G1":"03LA"}
Warning
Faikout's S21 command queue can get overloaded if the MQTT command/myFaikoutUnit/send command is sent repeatedly without waiting, causing timing issues and garbage data to be sent and/or recieved. Make sure to wait for the replies to come back before sending another MQTT command, especially in automated scripts, or batch your commands into a JSON array. Avoid making the array too long as well, some dropped commands issues have been known to occur past 20-30 elements.
In the case of JSON, both for the payload and the replies, non-ASCII values can be sent as Unicode escape sequences for code points between \u0000 and \u00FF, where the hexadecimal codepoint value is converted to a byte with that exact value. For instance, the command "D1" with payload "1 2 0x80 A" can be sent as ["D112\u0080A"].
Commands that are rejected by the Daikin unit, whether because they don't exist, because the checksum is incorrect or because the payload isn't valid will have their rx MQTT response message contain the "nak": true key. Similarly, commands that do not issue replies will still be acknowledged by the indoor unit, and contain a "ack": true key to reflect that.
Topic: info/myFaikoutUnit/tx
{"protocol":"S21","dump":"02494E56414C7A03","IN":"VAL"}
Topic: info/myFaikoutUnit/rx
{"protocol":"S21","cmd":"IN","payload":"56414C","text":"VAL","nak":true}
Topic: info/myFaikoutUnit/tx
{"protocol":"S21","dump":"02443431A903","D4":"1"}
2025-08-07 19:15:51:690
Topic: info/myFaikoutUnit/rx
{"protocol":"S21","cmd":"D4","payload":"31","text":"1","ack":true}
Warning
This page documents current knowledge of how the protocol works, but a lot of what these commands do and mean is still unknown.
The "core" of the S21 protocol consists mostly of commands which are two alphanumerical characters long. A number of 4-character "extension commands" (like FY** or Rz**) also exist, and newer versions of the protocol tend to mostly add features through these. The way these extension commands are distributed suggests that the two "extension code" characters are meant to be read backwards, such that "FY71" is actually
There are also a handful of "single letter" commands, like A, M and V. Little is known about these, and some might be only kept for legacy support.
There appear to be different versions of the S21 protocol supported by different units. The protocol version appears to determine which commands a unit is known to respond to, and official controllers will alter their behavior based version probing. The list of supported R commands doesn't seem to vary across versions.
For version 2 and below FY00 command isn't supported and causes a NAK response. In this case protocol version is indicated by older F8 and corresponding D8 response. Note that for protocol v3 and above this command still indicates version 2, probably for backwards compatibility with some old software.
Version 3 and beyond is indicated by FY00 command. The response code is GY00, returned version may affect the meaning and/or presence of various other commands.
Additionally, F2, FK, and FU00 commands, if supported. are used to query for optional features supported by the unit.
BRP069B41 also has own protocol version; reported in /aircon/get_model_info as cpv= and cpv_minor= parameters. The controller tries to match own version against reported by the conditioner; and uses lowest common demoninator. For example, with version set to 3.10 in the simulator: pv=3.10,cpv=3,cpv_minor=01 . This means it treats the simulated A/C as conforming to version 3.01, which it knows, despite a higher number has been reported. Based on this we can understand which versions can be seen in the wild. The following version numbers have been found to be recognized by BRP069B41: v0, v2, v3.00, v3.01, v3.20.
Note
Note the model number nomenclature that Daikin follows when adding a model to the list. The first 4 letters denote the device class, not the revision.
Note
Below commands, queried by BRP069B41 controller are listed in their order. This gives a good overview of which commands are actually known by the controller and officially used by Daikin. [sensor] denotes one R family command out of sensor query sequence (TBD). I. e. the controller asks one particular each poll iteration. On next iteration next sensor will be queried.
Note
DJ and D7 commands aren't understood. DJ payload varies (saw also DJ2010 and DJ2030). It's unclear what it depends on. Maybe daytime after all ?
- "F8" result: NAK
- "FY00" result: NAK
- Reported BRP069 protocol version: TBD
- Known models: CDK**HVE
- Supported R commands: RA, RB, RC, RD, RE, RF, RG, RH, RI, RK, RL, RM, RN, RW, RX, Ra, Rb, Rd, Re, Rg
- Supported F commands: F1, F2, F3, F4, F5
- Supported miscellaneous commands: A (presumed), V
- BRP069B41 startup sequence: TBD
- BRP069B41 polling loop: F2 F1 F3 F4 F5
- BRP069B41 sensor polling sequence: Rd RL RH RN RI Ra RX
Similar to version 0, but does not support either protocol versionning commands (F8/FY00). Reports from the (so far) only unit shown to return this also showed that it did not support commands M or Rz**. It also returned NAK on command RN, but that is most likely because it was a slim-duct unit with no fins or louvers.
- "F8" result: "G8" '0' 0x00 0x00 0x00
- "FY00" result: NAK
-
Reported BRP069 protocol version:
pv=0,cpv=0,cpv_minor=00 - Known models: FTXS**L, FTXS**G
- Supported R commands: RA, RB, RC, RD, RE, RF, RG, RH, RI, RK, RL, RM, RN, RW, RX, Ra, Rb, Rd, Re, Rg, Rz
- Supported F commands: F1, F2, F3, F4, F5, F8
- Supported miscellaneous commands: A, M, V
- BRP069B41 startup sequence: F2 F1 F3 F4 F5 F8 RH Ra M
- BRP069B41 polling loop: F2 F1 F3 F4 F5 F8 [sensor]
- BRP069B41 sensor polling sequence: TBD
- "F8" result: "G8" '0' '1' 0x00 0x00
- "FY00" result: NAK
- Reported BRP069 protocol version: TBD
- Known models: FTXZ**N
- Supported R commands: RA, RB, RC, RD, RE, RF, RG, RH, RI, RK, RL, RM, RN, RW, RX, Ra, Rb, Rd, Re, Rg, Rz
- Supported F commands: F1, F2, F3, F4, F5, F8, F9, FA, FB, FC
- Supported miscellaneous commands: A, M, V
- BRP068B41 startup sequence: TBD
- BRP068B41 polling loop: TBD
- BRP068B41 sensor polling sequence: TBD
- "F8" result: "G8" '0' '2' 0x00 0x00
- "FY00" result: NAK
-
Reported BRP069 protocol version:
pv=2,cpv=2,cpv_minor=00 - Known models: ATX20K2VTB
- Supported R commands: RA, RB, RC, RD, RE, RF, RG, RH, RI, RK, RL, RM, RN, RW, RX, Ra, Rb, Rd, Re, Rg, Rz
- Supported miscellaneous commands: A, M, V, VS000M
- BRP068B41 startup sequence: F2 F1 F3 F4 F5 F8 F9 F6 F7 FB FG FK FM FN FP FQ FS FT FC FY00 M DJ2010
- BRP068B41 polling loop: F2 F1 F3 F4 F5 F8 F9 F6 F7 FB FG FK FM FN FP FQ FS FT [sensor]
- BRP068B41 sensor polling sequence: TBD
FY00 is used to distinguish between v2 and v3 protocols (see F8 description). v2 protocol units respond with NAK
If model code, returned by FC is '0000' (the case on ATX20KV1B), FY00 command is skipped completely.
- "F8" result: "G8" '0' '2' 0x00 0x00
- "FY00" result: "GY00 0030"
-
Reported BRP069 protocol version:
pv=3.00,cpv=3,cpv_minor=00 - Known models: S22ZTES-W
- BRP069B41 startup sequence: F2 F1 F3 F4 F5 F8 F9 F6 F7 FB FG FK FM FN FP FQ FS FT FC FY00 FY10 FY20 M DJ2030 DJ2010
- BRP069B41 polling loop: F2 F1 F3 F4 F5 F8 F9 F6 F7 FB FG FK FM FN FP FQ FS FT [sensor]
- BRP069B41 sensor polling sequence: TBD
- "F8" result: "G8 0200"
- "FY00" result: "GY00 0030"
-
Reported BRP069 protocol version:
pv=3.00,cpv=3,cpv_minor=00 - Known models: None; found by probing on BRP069
- BRP069B41 startup sequence: F2 F1 F3 F4 F5 F8 F9 F6 F7 FB FG FK FM FN FP FQ FS FT FC FY00 FY10 FY20 M VS000M DJ4030
- BRP069B41 polling loop: F2 F1 F3 F4 F5 F8 F9 F6 F7 FB FG FK FM FN FP FQ FS FT [sensor]
- BRP069B41 sensor polling sequence: TBD
The BRP069 uses a slightly different startup sequence when F8 returns "0200" instead of '0' '2' 0x00 0x00 while FY00 returns "0030". No units using that protocol version have been found so far.
- "F8" result: "G8 0200"
- "FY00" result: "GY00 0230"
-
Reported BRP069B41 protocol version:
pv=3.20,cpv=3,cpv_minor=20 - Known models: FTXF**D, FVXM**?, FVXM**A
- BRP069B41 startup sequence: F2 F1 F3 F4 F5 F8 F9 F6 F7 FB FG FK FM FN FP FQ FS FT FC FY00 FY10 FY20 FU00 FU02 VS000M DJ5010 D70000
- BRP069B41 polling loop: F2 F1 F3 F4 F5 F8 F9 F6 F7 FB FG FK FM FN FP FQ FS FT FU02 FU04 [sensor]
- BRP069B41 sensor polling sequence: TBD
- BRP069C41 startup sequence: F2 F1 F3 F4 F5 F8 F9 F6 F7 FB FG FK FM FN FP FQ FS FT FC FY00 FY10 FY20 FU00 FU02 VS000M Rd RL RH RN RI Ra RX FX00 FX10 FX20 FX30 FX40 FX50 FX60 FX70 FX80 Rz52 Rz72 FX90 FXA0 FXB0 FXC0 DY10 DY20
Note
Versions higher than 3.20 are only supported by "new" Daikin controllers, which are cloud-only and don't have local API (at least full implementation) Therefore information, specific to online controller's HTTP API is not available any more. BRP069C41 controller is used for testing those.
- "F8" result: "G8 0200"
- "FY00" result: "GY00 0430
-
Reported BRP069B41 protocol version:
pv=3.40,cpv=3,cpv_minor=20 - Known models: FTXM**R, CVXM20A3V1B
- BRP069C41 startup sequence: F2 F1 F3 F4 F5 F8 F9 F6 F7 FB FG FK FM FN FP FQ FS FT FC FY00 FY10 FY20 FU00 FU02 FU05 FU15 FU25 FU35 FU45 FR VS000M Rd RL RH RN RI Ra RX FX00 FX10 FX20 FX30 FX40 FX50 FX60 FX70 FX80 Rz52 Rz72 FX90 FXA0 FXB0 FXC0 FXD0 FXE0 FXF0 FX01 FX11 FX21 FX31 FX41 FX51 FX61 FX71 FX81 DY10 DY20
- BRP069C41 polling loop: F2 F1 F3 F4 F5 F8 F9 F6 F7 FB FG FK FM FN FP FQ FS FT FR FU02 FU04
- BRP069C41 sensor polling sequence: Rd RL RH RN RI Ra RX FX00 FX10 FX20 FX30 FX40 FX50 FX60 FX70 FX80 Rz52 Rz72 FX90 FXA0 FXB0 FXC0 FXD0 FXE0 FXF0 FX01 FX11 FX21 FX31 FX41 FX51 FX61 FX71 FX81
"BRP069" column indicates whether a command is used by original Daikin controller. "Optional" means that the command is not mandatory, and the A/C is allowed to respond NAK. "Rudimentary" means that the command is supported, but the response looks like a stub and most likely not used.
| Command | Response | Payload length | Semantics | v1 | v2 | v3 | BRP069 | Notes |
|---|---|---|---|---|---|---|---|---|
| M | M | 4 | Supposedly model code for protocol v1 | Yes | Rudimentary | Rudimentary | <=v2 | Reports 'FFFF' (ASCII) for v2+ conditioners. BRP069B41 queries but appears to ignore it. |
| V | V | 4 | Yes | Yes | No | 4 digit hex, seems constant per unit | ||
| VS000M | VS | 14 | Indoor unit firmware version | Yes | Yes | >=v3.01 | Only polled once | |
| D3 | N/A | 3 | Set on/off timer | Yes | Yes | No | ||
| D7 | N/A | 4 | Unknown | Yes | Yes | >= v3 | Payload: '0000' ASCII | |
| DJ | N/A | 4 | Unknown | Yes | Yes | Yes | ||
| DL | N/A | 4 | Unknown | Yes | Yes | Payload: '0000' ASCII | ||
| DR | N/A | 4 | Set louver angle | ? | Yes | Yes | No | |
| DY10 | N/A | 8 | Unknown | Yes | >=v3.00 | Payload: 41 38 44 33 36 36 36 46 | ||
| DY20 | N/A | 4 | Unknown | Yes | >=v3.00 | Payload: | ||
| F1 | G1 | 4 | Power, mode, setpoint, fan speed setting | Yes | Yes | Yes | Yes | |
| F2 | G2 | 4 | Optional features | Yes | Yes | Yes | Yes | |
| F3 | G3 | 4 | On/off timer. "Powerful" mode on some models (?) | Yes | Yes | Yes | Yes | |
| F4 | G4 | 4 | Error status(?) | Yes | Yes | Yes | Yes | |
| F5 | G5 | 4 | Swing, humidity setting | Yes | Yes | Yes | Yes | Supported values depend on optional features, see F2 |
| F6 | G6 | 4 | Powerful, Comfort, Quiet, Streamer, Sensor modes' LED control | Partial | Yes | Yes | See description for protocol version details. | |
| F7 | G7 | 4 | Demand mode, econo mode | No | Yes | Yes | Yes | |
| F8 | G8 | 4 | Protocol version (up to v2) | Yes | Yes | Rudimentary | Yes | |
| F9 | G9 | 4 | Coarse indoor, outdoor temperature (℃), humidity (%) | No | Yes | Yes | Yes | |
| FA | GA | 4 | No | Yes | Yes | No | ||
| FB | GB | 4 | Timer lamp (orange) | No | Yes | Yes | Yes | |
| FC | GC | 4 | Model code | No | Yes | Yes | Yes | Only polled once. First byte appears to indicate unit size/capacity |
| FG | GG | 4 | IR remote counter | No | Yes | Yes | Yes | increments on each IR command |
| FK | GK | 4 | Optional features (v2+) | No | Yes | Yes | Yes | |
| FL | GL | 4 | No | Yes | ||||
| FM | GM | 4 | Energy consumption of all units on same outdoor unit | No | Yes | Yes | Yes | 4 digit ASCII encoded hex number in reverse- indicates total Wh/100. Use FU04 for energy consumption per indoor unit. |
| FN | GN | 4 | ITELC. Some Daikin internal code. | No | Yes | Yes | Yes | |
| FP | GP | 4 | No | Yes | Yes | Yes | ||
| FQ | GQ | 4 | No | Yes | Yes | Yes | ||
| FR | GR | 4 | Louver position setting | No | Yes | Yes | Yes | |
| FS | GS | 4 | Wifi module operation mode | No | Yes | Yes | Yes | |
| FT | GT | 4 | Outdoor capacity, unconfirmed | No | Yes | Yes | Yes | |
| FU00 | GU00 | 32 | Optional v3+ features | No | No | >=v3.20 | >= v3.20 | Only polled once. |
| FU02 | GU02 | 32 | Allowed temperature ranges | No | No | Yes | >= v3.01 | |
| FU04 | GU04 | 32 | Lifetime energy consumption (cool/heat) of indoor unit | No | No | >=v3.20 | >=v3.20 | 16 digit ASCII encoded hex number in reverse- indicates total Wh/100 for cooling (8 first bytes) and heating (next 8 bytes) |
| FU05 | GU05 | 32 | Model name | No | No | >=v3.40 | ||
| FU15 | GU05 | 32 | Production information | No | No | >=v3.40 | ||
| FU25 | GU05 | 32 | Production order | No | No | >=v3.40 | ||
| FU35 | GU05 | 32 | Indoor unit production information | No | No | >=v3.40 | ||
| FU45 | GU05 | 32 | Outdoor unit production information | No | No | >=v3.40 | ||
| FV | GV | Unknown | No ? | No ? | >= v3.01 ? | No | Found by brute-forcing; tested on FTXF20D5V1B (v3.20), S22ZTES-W (v3.00) and ATX20K2V1B (v2) | |
| FX00 | GX00 | 2 | Unknown | No | No | Optional | Yes | |
| FX10 | GX10 | 2 | Unknown | No | No | Optional | Yes | |
| FX20 | GX20 | 4 | Unknown | No | No | Optional | Yes | |
| FX30 | GX30 | 2 | Unknown | No | No | Optional | Yes | |
| FX40 | GX40 | 2 | Unknown | No | No | Optional | Yes | |
| FX50 | GX50 | 2 | Unknown | No | No | Optional | Yes | |
| FX60 | GX60 | 4 | Power consumption of indoor unit | No | No | Optional | Yes | 4 digit ASCII encoded hex number in reverse- indicates total W/10. |
| FX70 | GX70 | 4 | Unknown | No | No | Optional | Yes | |
| FX80 | GX80 | 4 | Unknown | No | No | Optional | Yes | |
| FX90 | GX90 | 4 | Unknown | No | No | Optional | Yes | |
| FXA0 | GXA0 | 4 | Unknown | No | No | Optional | Yes | |
| FXB0 | GXB0 | 2 | Unknown | No | No | Optional | Yes | |
| FXC0 | GXC0 | 2 | Unknown | No | No | Optional | Yes | |
| FXD0 | GXD0 | 8 | Unknown | No | No | Optional (>=3.40) | Yes | |
| FXE0 | GXE0 | 8 | Unknown | No | No | Optional (>=3.40) | Yes | |
| FXF0 | GXF0 | 8 | Unknown | No | No | Optional (>=3.40) | Yes | |
| FX01 | GX01 | 8 | Unknown | No | No | Optional (>=3.40) | Yes | |
| FX11 | GX11 | 8 | Unknown | No | No | Optional (>=3.40) | Yes | |
| FX21 | GX21 | 2 | Unknown | No | No | Optional (>=3.40) | Yes | |
| FX31 | GX31 | 8 | Unknown | No | No | Optional (>=3.40) | Yes | |
| FX41 | GX41 | 8 | Unknown | No | No | Optional (>=3.40) | Yes | |
| FX51 | GX51 | 4 | Unknown | No | No | Optional (>=3.40) | Yes | |
| FX61 | GX61 | 2 | Unknown | No | No | Optional (>=3.40) | Yes | |
| FX71 | GX71 | 2 | Unknown | No | No | Optional (>=3.40) | Yes | |
| FX81 | GX81 | 2 | Unknown | No | No | Optional (>=3.40) | Yes | |
| FY00 | GY00 | 4 | Protocol version (v3+) | No | No | Yes | Yes | Only polled once |
| FY10 | GY10 | 8 | Unknown | No | No | Yes | Yes | Only polled once |
| FY20 | GY20 | 4 | Unknown | No | No | Yes | Yes | Only polled once |
All multi-byte values are transmitted in little-endian rather than in wire order. That means a command with a result of "GM 1D00" is to be interpreted as having returned "00D1".
A number of data formats are used more than once.
Example value: Wire "300", Rectified "003", Interpreted 3
Just a regular ASCII number string. Used for things like fan speed and humidity sensors. There's never any actual decimal separator, but one may be implied.
Example value: Wire "720+", Rectified "+027", interpreted +27
ASCII number string with the sign always present. Used for things like temperature sensors and fin angle.
Example value: Wire "B233", Rectified "332B", Interpreted 13099
Hexadecimal number as an ASCII string, more compact than the decimal one. Used for things like lifetime power consumption.
ASCII character representing a setpoint temperature that a remote can send to the unit.
| Value | Meaning |
|---|---|
| "@" | 18.0C |
| "A" | 18.5C |
| … | |
| "W" | 29.5C |
| "X" | 30.0C |
| 0x80 | N/A (for dry mode) |
Commands that start with F are mostly used for reading the settings of the indoor unit. They are sent without a payload. They respond with a message starting with G and the field "name", followed by the current configured value. Commands starting with D are usually for writing those same configuration values, with the same format. These do not get a reply, and are only acknowledged.
| Link | Write | Read | Resp. | Semantics | Note | Protocol support |
|---|---|---|---|---|---|---|
| link | D1 | F1 | G1 | Mode and temperature control | v0, v2, v3.0, v3.2 | |
| link | F2 | G2 | Optional features support | D2 is probably a stub. | v0, v2, v3.0, v3.2 | |
| link | D3 | F3 | G3 | On/Off timer | F3 reads 4 bytes. D3 will accept 3 or 4 bytes, but the 4th value does not seem to get written. Purpose of the 4th byte unclear. | v0, v2, v3.0, v3.2 |
| link | F4 | G4 | Unknown | v0, v2, v3.0, v3.2 | ||
| link | D5 | F5 | G5 | Swing, humidity setting | Supported values depend on optional features, see G2 | v0, v2, v3.0, v3.2 |
| link | D6 | F6 | G6 | Special modes (Powerful, Streamer, etc.) | v2, v3.0, v3.2 | |
| link | D7 | F7 | G7 | Demand control, econo mode | BRP069B41 initializes D7 on startup to known value "0000" | v2, v3.0, v3.2 |
| link | F8 | G8 | Protocol version (legacy) | Units with protocol v3+ still report as v2 here. V3 compatible controllers also use "FY00". | v0, v2, v3.0, v3.2 | |
| link | F9 | G9 | Coarse temperatures, humidity (%) | v2, v3.0, v3.2 |
| Link | Write | Read | Resp. | Semantics | Note | Protocol support |
|---|---|---|---|---|---|---|
| FA | GA | Unknown | Unused by BRP069B41, but units reply to it. | v2, v3.0, v3.2 | ||
| FB | GB | Timer lamp (orange) | Byte 0 set to "1" sets Timer lamp to ON, "0" sets it to OFF (seen on FTXA20A2V1BW, used by Wifi module to signal weekly schedule is ON). | v2, v3.0, v3.2 | ||
| link | FC | GC | Model code? | Only polled once. First byte appears to indicate unit size/capacity | v2, v3.0, v3.2 | |
| FG | GG | IR remote counter | Increments on each IR command | v2, v3.0, v3.2 | ||
| link | DH | Unknown | ||||
| link | DJ | Unknown | Sent by BRP069B41 controller on startup, may be clock-related. | v2, v3.0, v3.2 | ||
| link | FK | GK | Optionnal features support (v2+) | v2, v3.0, v3.2 | ||
| DL | FL | GL | Unknown | v2, v3.0, v3.2 | ||
| FM | GM | Energy consumption of all units on same outdoor unit | 4 digit ASCII encoded hex number in reverse- indicates total Wh/100 | v2, v3.0, v3.2 | ||
| link | FN | GN | Unknown | Reported as "itelc" by get_monitordata, might be internal model code. | v2, v3.0, v3.2 | |
| FP | GP | Unknown | v2, v3.0, v3.2 | |||
| FQ | GQ | Unknown | v2, v3.0, v3.2 | |||
| link | DR | FR | GR | Louver angle setting | v2, v3.0, v3.2 | |
| link | FS | GS | Wifi module operation mode | v2, v3.0, v3.2 | ||
| link | FT | GT | Unknown | Potentially related to outdoor unit capacity | v2, v3.0, v3.2 | |
| FV | GV | Unknown | Found by brute-forcing, unused by BRP069B41 | v2, v3.0, v3.2 |
Power, mode, temperature setpoint, and fan speeds.
Payload length: 4 characters
| Byte | Description | Values |
|---|---|---|
| 0 | Power | 0: 0ff 1: On |
| 1 | Mode | 0: Auto, Cooling 1: Auto 2: Dry 3: Cool 4: Heat 6: Fan 7: Auto, Heating |
| 2 | Setpoint | @: 18.0C A: 18.5C … W: 29.5C X: 30.0C 0x80: N/A (for dry mode) |
| 3 | Fan | 3: Low 4: Mid-low 5: Medium 6: Mid-high 7: High A: Auto B: Quiet or Q for Night? |
Writes of values "0", "1" and "7" are treated as identical by D1, and all apply auto mode. When reading back with F1 (also RB), the unit will return "0" in G1 if auto mode is cooling or "7" if auto mode is heating (or was last cooling/heating if idle or off).
Command D1 will acknowledge and apply the quiet ("B") fan setting, but command F1 will read it back as if the fan was set to "Auto" mode. Command RG, however, which uses the same mode values as D1 and F1, will correctly return "B" if the fan is in quiet mode.
Unknown. Since G2 data is read only it is probably a stub.
Payload length: 1 character, even though G2 has a 4 character payload.
| Byte | Description | Values |
|---|---|---|
| 0 | Unknown | 0 |
Optional features support
Payload length: 4 bytes
The G2 response are bitmasks. Omitted bits are always zero.
| Byte | Bit | Description |
|---|---|---|
| 0 | 0 | Unknown. Set to 1 on CTXMxxRVMA, ignored by BRP069B41 |
| 2 | Swing (any kind) is available | |
| 3 | Horizontal swing is available | |
| 4 | Shield bit (0x30) | |
| 5 | Shield bit (0x30) | |
| 1 | 0 | Unknown. Set to 1 on CTXMxxRVMA, ignored by BRP069B41 |
| 1 | Always 1, unknown | |
| 3 | Unknown. Reflected by BRP069B41 in aircon/model_info. 0: type=C, 1: type=N | |
| 4 | Shield bit (0x30) | |
| 5 | Shield bit (0x30) | |
| 2 | 7 | Set to 1 by DJ command. Purpose is unknown. |
| 3 | 1 | "Humidity" operation mode is available |
| 4 | "Humidity" setting is available for additional operation modes (see matrix below) | |
| 7 | Always 1. Perhaps shield? |
Humidity settings availability matrix (reverse engineered from Daikin online controller app) s_humd is a per-mode bitmask, presented by BRP069B41 in /aircon/model_info
| Byte 3, Bit 1 | Byte 3, Bit 4 | s_humd |
|---|---|---|
| 0 | 0 | 0 (0x00) |
| 0 | 1 | 146 (0x92) |
| 1 | 0 | 165 (0xA5) |
| 1 | 1 | 183 (0xB7) |
Support for humidity settings in D5 when an s_humd bit is set:
| s_humd Bit | Mode | Off | Low | Standard | High | Continuous |
|---|---|---|---|---|---|---|
| 0 | Auto | y | y | y | y | y |
| 1 | Cool | y | y | y | y | y |
| 2 | Heat | y | y | y | y | y |
| 3 | Fan | - | - | - | - | - |
| 4 | Dry | y | - | y | y | y |
| 5 | Humidify | y | - | y | y | y |
On/off timers
Payload length: 4 bytes
| Byte | Description | Values |
|---|---|---|
| 0 | Enable | 0: Disabled 1: On timer set 2: Off timer set 3: Both timers set |
| 1 | On Timer |
0x31: Timer expires 1 period from time set0xC0: Timer expires 144 periods from time set0xFE: Disabled |
| 2 | Off Timer |
0x31: Timer expires 1 period from time set0xC0: Timer expires 144 periods from time set0xFE: Disabled |
| 3 | Unknown | Reports 0x00 after bootup, changed to 0x30 by DJ on v2+ units. Meaning unknown. Value seems unchanged when writing. |
When written with D3, the payload length can be 3 or 4 bytes. The value of byte 4 is ignored when writing.
The timer granularity is in 10 minute periods. Previous documentation efforts indicate that some units may only support a range of 12 hours or multiples of whole hours. The unit's timer evaluation appears internal. When the values are set it seems to record an internal reference time and actions the timers when they expire based on it because the timer value visible in G3 does not decrement as time elapses. When the timers expire, they keep their value but the corresponding enable bit is cleared. Therefore it does not seem possible to recover the wall clock time of expiry by just reading G3.
On majority of units if the timer is disabled, the respective setting bytes are set to 0xFE. However, on ATX20K2V1B and S22ZTES-W they read as 0x30 in this case. Experiments with BRP069B41 controller on simulator show that it doesn't accept response of 0x30 0x30 0x30 0x00; it sends NAK and keeps retrying, falling into "SERIAL IF FALURE" state. It's also known that this controller actually works with ATX20K2V1B. I tried snooping boot process on my ATX20K2V1B; that yielded no clue. The response on bootup is 0x30 0x30 0x30 0x00; and everything works just fine.
According to commit 0c5f769894365d344012b36846f0b91023840d94 on some models bit 1 of byte 3 is used as "Powerful" mode flag. Those models apparently do not support F6 command, but unfortunately there's no information on which exact model that is, and the v0 units tested so far exhibit no such behaviour. Given the above, that could be a mistake.
Unknown, serial interface error information.
Payload length: 4 bytes
- byte0: 0x30 ('0')
- byte1: 0x00
- byte2
- Bit 5: Conditioner internal error flag. If reported as 1, BRP069B41 only polls 4 commands F1-F3-F4-F2 and reports in /aircon/model_info:
ret=SERIAL IF FAILURE,err=252
Experiments show, that once read, the bit resets to 0. It's not known which actions cause it to raise.
- Bit 5: Conditioner internal error flag. If reported as 1, BRP069B41 only polls 4 commands F1-F3-F4-F2 and reports in /aircon/model_info:
- byte3: 0x30 ('0') or 0x00, probably depending on the protocol version
Values observed (FTXS**L, protocol v0):
-
G4 0\x00\x80\x00(usual, both on and off) -
G4 0\x00\xA0\x00(after resuming from power outage; reading immediately resets the value from\xA0to\x80)
Louver swing settings.
Payload length: 4 characters
| Byte | Description | Values |
|---|---|---|
| 0 | Swing | 0: Off 1: Vertical 2: Horizontal 7: Both |
| 1 | Swing Enabled | 0: Off ?: Any Swing On |
| 2 | Humidity setting | 0: Off0x3A: Low0x3B: Standard0x3C: High0xFF: Continuous |
| 3 | Unknown | 0 0x80 in response |
There's a sixth "Moisturising" humidity setting that is reported as identical to the Off setting on units that have it. Also, the service manual states that the humidity setting corresponds to "Humidifies or dehumidifies with high/moderate/low power", the meaning of that byte changes based on operation mode. See G2 for supported mode combinations.
Special modes settings.
Payload length: 4 characters
The G2 response are bitmasks. Omitted bits are always zero.
| Byte | Bit | Description |
|---|---|---|
| 0 | 1 | "Powerful" mode on |
| 4 | Shield bit (0x30) | |
| 5 | Shield bit (0x30) | |
| 6 | "Comfort" mode on | |
| 7 | "Quiet" mode on | |
| 1 | 4 | Shield bit (0x30) |
| 5 | Shield bit (0x30) | |
| 7 | "Streamer" mode on | |
| 2 | 4 | Shield bit (0x30) |
| 5 | Shield bit (0x30) | |
| 3 | 2 | Sensor LED |
| 3 | Sensor mode on (if supported, otherwise LED control) LED masks ( (value & mask) == mask): 0x0C: Off, 0x08: Dark, 0x04: On |
|
| 4 | Shield bit (0x30) | |
| 5 | Shield bit (0x30) |
D6 is optional for v2 conditioners. ATX20K2V1B does not support it, returning NAK.
Set of available special modes is determined by FU00, which is only supported in protocol v3. Early protocol versions (tested on ATX20K2V1B, also reported for FTXS series, which is known to be v0) support F6 too, but only report "Powerful" mode. Those conditioners also appear not to support respective D6 control command. Therefore Faikout currently has no way of controlling special modes on those models. It's unknown whether they can be controlled over S21 protocol in principle.
Commit 0c5f769894365d344012b36846f0b91023840d94 suggests that some models don't support this command at all. If such a case "Powerful" mode flag would come from F3. Unfortunately information on which A/C model exhibits this behavior has been lost.
“Econo” and demand settings.
Payload length: 4 characters
| Byte | Description | Values |
|---|---|---|
| 0 | Demand | 0x30 + (100 - value) |
| 1 | Econo | 0: Off 2: On |
| 2 | Unknown | 0 A in response? |
| 3 | Unknown | 0 G in response? |
Demand control limits a system's power consumption to a given percentage. On units that don't support demand control, a value of 100% should be assumed so the resulting ASCII value is '0'.
Protocol version (old, prior to v3). See Protocol Versions for more details.
Payload length: 4 characters
Null-padded string (reverse order) indicating the "protocol version" supported by the unit.
Below is a table of known responses for F8:
| Raw Response | Rectified | Meaning | Example |
|---|---|---|---|
G 8 0 0x00 0x00 0x00
|
0 |
"0" (protocol v0) | 1, 2 |
G 8 0 1 0x00 0x00
|
10 |
"10" (protocol v1) | 1 |
G 8 0 2 0x00 0x00
|
20 |
"20" (protocol v2) | 1, 2 |
| G 8 0 2 0 0 | 0020 |
"20" (protocol v2+) | 1 |
This command only reports real version number up to version 2.0. Protocol v3.0 froze F8 response to '0200' ASCII (which stands for v2) and superseded this command by FY00. Daikin controller distinguishes between protocol versions by probing for FY00 command, which is not recognized and returns NAK prior to v3.
Coarse indoor, outdoor temperature (℃), humidity (%)
Payload length: 4 characters
| Byte | Description | Values |
|---|---|---|
| 0 | Indoor Temperature |
0x80 + (value / 2) degrees C |
| 1 | Outdoor Temperature |
0x80 + (value / 2) degrees C |
| 2 | Humidity |
0x30 + value %. 5% granularity |
| 3 | Unknown | 0 |
The values are all rounded down to their granularity. Unsupported values can return 0xFF.
e.g. 0xac:aa:4e:30 22C inside, 21C outside, 30% humidity 1
Model code
Payload length: 4 characters
Tip
Return values for this command are tracked on the Model Codes page.
Payload is 4 ASCII characters in reverse order (byte3 to byte0), which are reported as model= in /aircon/get_model_info. The controller by itself does not seem to do anything with this string, just pass.
The model code always looks like a hexadecimal number, nothing like customer-visible model code, printed on the label. This is most likely Daikin's internal model number, which can be looked up in some proprietary database.
Unknown.
Payload length: 4 characters
| Byte | Description | Values |
|---|---|---|
| 0 | Unknown | 1 |
| 1 | Unknown | 1 |
| 2 | Unknown | 0 |
| 3 | Unknown | 0 |
Unknown
Payload length: 4 characters
This command is send by BRP069B41 controller on startup. The payload is sometimes '2010' and sometimes '2030' ASCII. The effect is the following:
Query extra optional features [protocol v2]
Payload length: 4 bytes
The GK response are bitmasks. Omitted bits are always zero.
| Byte | Bit | Description |
|---|---|---|
| 0 | 0 | Unknown. Reported as acled= value by BRP069B41 in /aircon/get_model_info |
| 2 | ||
| 3 | "Laundry" operation mode is available. | |
| 4 | Shield bit (0x30) | |
| 5 | Shield bit (0x30) | |
| 6 | ||
| 7 | ||
| 1 | 0 | Unknown. Reported as elec= value by BRP069B41 in /aircon/get_model_info |
| 2 | Reported as temp_rng= value by BRP069B41 in /aircon/get_model_info. Used by Japanese controllers to select between the two temperature ranges. | |
| 3 | Reported as m_dtct= value by BRP069B41 in /aircon/get_model_info. Probably "motion detector" AKA "Intelligent eye" availability | |
| 4 | Shield bit (0x30) | |
| 5 | Shield bit (0x30) | |
| 6 | Unknown. Reported 1 by FTXF20D5V1B | |
| 2 | 0 | Unknown. Reported as "ac_dst=" value by BRP069B41 in /aircon/get_model_info: 0 => ac_dst=jp; 1 => ac_dst=-- |
| 1 | Indicates simple "Humidify" mode implementation. When set to 1, humidity setting is not available regardless of respective F2 bits; and BRP069B41 reports s_humd=16 in /aircon/get_model_info | |
| 2 | Fan controls available. When set to 0, swing bits in F2 response are ignored; and neither fan speed nor swing controls are available in "Daikin online controller" app. | |
| 3 | Unknown. Reported as "disp_dry=" value by BRP069B41 in /aircon/get_model_info | |
| 4 | Shield bit (0x30) | |
| 5 | Shield bit (0x30) | |
| 3 | 0 | Demand mode if available. Daikin controller ignores this bit in protocol <= 2 |
| 1 | ||
| 2 | ||
| 3 | ||
| 4 | Shield bit (0x30) | |
| 5 | Shield bit (0x30) | |
| 6 | ||
| 7 |
ITELC, whatever that means. Perhaps related to the Intelligent Touch Controller product.
Payload length: 4 characters
4 bytes of the payload are displayed by BRP069B41 in reversed order as itelc= parameter in /aircon/get_monitordata. The exact meaning of this isn't known; "ITELC" isn't mentioned anywhere on the web and in service manuals. Some internal code.
Louver angle setting
Payload length: 4 characters
| Byte | Description | Values |
|---|---|---|
| 0 | Louver angle | ?: Swing is active, no constant setting 0: Default position. Also reported on units which don't support manual angle setting. 1: Upper position 2: high middle position 3: Middle position 4: Low middle position 5: Lower position |
| 1 | Unknown | 0 |
| 2 | Unknown | 0 |
| 3 | Unknown | 0 |
Writing '?' turns on vertical swing (at least on my ATX20K2V1B, equivalent to sending D5 with respective value); and writing '0' stops the swing (it it was on), leaving the louver where it was (equivalent to disabling swing via D5).
Wifi module operation mode. Changed by IR remote control to enable AP mode or WPS. Seems to be '3' (AP mode) by default at power on.
Payload length: 4 characters
| Byte | Description | Values |
|---|---|---|
| 0 | Wifi mode | '1': - '2': WPS '3': AP mode '4': RUN (SSID+KEY) |
| 1 | Counter | From '0' to 'F' (loop), increment each time Wifi mode is changed. (Wifi module can ignore byte 0 if counter is not increased) |
| 2 | Unknown | |
| 3 | Unknown |
Thought to be outdoor unit capacity in indoor head units, though unconfirmed.
Payload length: 4 characters
Number in reverse ASCII. As this is read in the Daikin controller polling loop it could be dynamic and reflect the number of currently communicating indoor units. Does not seem to correspond to capacity in tons/BTUs/kWs.
Special modes availability flags
Payload length: 32 bytes
| Byte | Description | Values |
|---|---|---|
| 0 | Powerful |
0x33: "Powerful" mode available0x30: Unavailable |
| 1 | Econo |
0x33: "Econo" mode available0x30: Unavailable |
| 2 | Unknown | Set to 0x33 (enabled) on FTXF20D5V1B, but no mode_info flag was found. |
| 3 | Unknown | |
| 4 | Unknown | Set to 0x33 (enabled) on FTXF20D5V1B, but no mode_info flag was found. |
| 5 | Streamer |
0x33: "Streamer" mode available0x30: Unavailable |
| 6-31 | Unknown | Could be reserved |
Bytes 0, 1 and 5 effectively make up a bitmask, which is reported by Daikin controller as en_spmode= value in /aircon/get_model_info.
Apparently 0x30 and 0x33 are the only legitimate byte values for this command. Experiments show that 0x31 and 0x32 are treated no differently from 0x30; and value of 0x34 causes the controller to report "SERIAL IF FAILURE" error with code 252. I didn't try any other values because it's very time-consuming process. Since the command is only polled once; after every change the controller has to be rebooted.
Example payload from FTXF20D5V1B: 0x33 0x33 0x33 0x30 0x33 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30
Temperature limits
Response code: GU00, payload length: 32 bytes
- byte 0 - unknown
- byte 1 - Unknown, also looks like temperature limit. Reported by Daikin controller as atlmt_l= in /aircon/get_model_info if enabled by byte 0 bit 6 of FK command. Valid values are from 0xA0 to 0xB0 (inclusive); the value is determined as: atlmt_l = (byte - 0xA0) / 2.0. This yields range from 0 to 8.0. If byte value is outside of the valid range, the atlmt_l parameter value defaults to zero.
- byte 2 - Minimum allowed temperature for Heat mode. Reported by Daikin controller as hmlmt_l= in /aircon/get_model_info. Valid values are from 0x30 to 0x6F (inclusive); the value is determined as: hmlmt_l = (byte - 0x30) / 2.0 + 10.0. This yields range from 10.0 to 41.5 . If byte value is outside of the valid range, the hmlmt_l parameter is not present.
- byte 3 - unknown
- byte 4 - unknown
- the rest - looks like unused. 0xFF is often default content of unwritten EEPROMs.
Example payload from FTXF20D5V1B: 0xA0, 0xA0, 0x30 ,0x31, 0x30, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF.
Lifetime energy consumption (cool/heat) of indoor unit. Seems to include consumption of indoor unit and the part of energy consumed by outddor unit for this indoor unit.
Response code: GU04, payload length: 32 bytes
- byte 0 to 7 - Lifetime energy consumption in cooling mode. 8 digit ASCII encoded hex number in reverse- indicates total Wh/100.
- byte 8 to 15 - Lifetime energy consumption in heating mode. 8 digit ASCII encoded hex number in reverse- indicates total Wh/100.
- byte 16 to 31 - 0x46 (16 bytes to value 'F')
Example payloads from FTXA20A2V1BW (ASCII representation):
"44200000B3B20000FFFFFFFFFFFFFFFF"
- "44200000" means 0x00000244 = 580 => 58,0kWh lifetime energy in cooling mode
- "B3B20000" means 0X00002B3B = 11067 => 1106,7 kWh lifetime energy in heating mode
Model name
Response code: GU05, payload length: 32 bytes
Contains conditioner model name, spelled in ASCII, not backwards. The actual string length is 22 bytes; remaining 10 bytes are filled with 0xFF
Example payload from FTXA35C2V1B:
0x46 0x54 0x58 0x41 0x33 0x35 0x43 0x32 0x56 0x31 0x42 0x53 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
On some units the response appears to be all spaces: https://github.com/revk/ESP32-Faikout/issues/408#issuecomment-2395086066
Production information
bytes 0..3 - Production site? Looks like some Daikin internal code. Reported as prod_site= by /aircon/get_model_info
bytes 4..9 - Production line? Reported as prod_line= by /aircon/get_model_info
byte 10 - Unknown
bytes 11..31 - Unused ROM space, contain 0xFF
Production order
bytes 0..14 - Production order? Reported as prod_order= by /aircon/get_model_info
bytes 15..31 - Unused ROM space, contain 0xFF
Perhaps indoor unit production information. It is known that Onecta is capable of displaying indoor and outdoor unit data, but i (Sonic-Amiga) was unable to make BRP069C41 controller working with S21 protocol; so unable to verify Onecta's interpretation of the data.
bytes 0..1 - Reported as prod_inp_site= by /aircon/get_model_info
byte 2 - Reported as prod_inp_base= by /aircon/get_model_info
byte 3 - Reported as prod_inp_dev= by /aircon/get_model_info
bytes 4..11 - Reported as prod_inp_mdate= by /aircon/get_model_info
bytes 12..23 - Reported as prod_inp_serial= by /aircon/get_model_info
bytes 24..31 - Unused ROM space, contain 0xFF
Perhaps outdoor unit production information. Similar to the above
bytes 0..1 - Reported as prod_outp_site= by /aircon/get_model_info
byte 2 - Reported as prod_outp_base= by /aircon/get_model_info
byte 3 - Reported as prod_outp_dev= by /aircon/get_model_info
bytes 4..11 - Reported as prod_outp_mdate= by /aircon/get_model_info
bytes 12..23 - Reported as prod_outp_serial= by /aircon/get_model_info
bytes 24..31 - Unused ROM space, contain 0xFF
Returned as "gx**" by /aircon/get_monitordata, purpose unknown.
Protocol version (v3 and beyond)
Response code: GY00, payload length: 4 bytes
- bytes 0,1: Protocol version minor as inverted ASCII
- bytes 2,3: Protocol version major as inverted ASCII
Example: response '0230' stands for protocol version 3.20
For protocols version below 3 this command isn't supported and returns NAK; in this case old F8 is used. In at least one instance, a v0 unit ACKed with47:00:00:00:00:00, despite seemingly unsupported.
Unknown
Response code: GY00, payload length: 8 bytes
The returned value is an 8-byte hexadecimal string of unknown purpose. Could be conditioner firmware's git hash.
BRP069B41 controller only queries this command once in lifetime. Therefore it clearly has to do with A/C identification.
Example payload from FTXF20D5V1B: {'A', '8', 'D', '3', '6', '6', '6', 'F'}
Unknown
Response code: GY20, payload length: 4 bytes
The returned value is an 4-byte hexadecimal string of unknown purpose.
BRP069B41 controller only queries this command once in lifetime. Therefore it clearly has to do with A/C identification.
Example payload from FTXF20D5V1B: {'E', '4', '0', '2'}
Commands whose return values are mostly neatly formatted, reverse ASCII, ready to display strings representing volatile sensor data.
These don't seem to change across protocol versions, so version support is omitted in the table.
| Link | Read | Resp. | Semantics | Note | Example value | Meaning |
|---|---|---|---|---|---|---|
| link | RA | SA | Power on/off | "SA1" | Online | |
| link | RB | SB | Indoor unit action | "RB4" | Heat | |
| link | RC | SC | Temperature set point | "SC120+" | 21°C | |
| link | RD | SD | On timer setting | Inferior to G3 | "441" | 144 |
| link | RE | SE | Off timer setting | Inferior to G3 | "720" | 27 |
| link | RF | SF | Louver swing settings | "SF1F" | Vertical swing | |
| link | RG | SG | Fan setting | Better information than G1 | "SGB" | Quiet |
| link | RH | SH | Indoor temperature | "SH562+" | 26.5°C | |
| link | RI | SI | Indoor heat exchanger temperature | "SI572+" | 27.5°C | |
| link | RK | SK | Fan speed set point | "SK670" | 760 rpm | |
| link | RL | SL | Fan speed (measured) | "SL570" | 750 rpm | |
| link | RM | SM | Louver angle set point | "SM780+" | 87.0° | |
| link | RN | SN | Louver angle (measured) | "SN510+" | 15.0° | |
| link | RW | SW | Unknown | "SW00" and "SW09" observed on offline units | "SW09" | |
| link | RX | SX | Real target temperature | "SX522+" | 22.5°C | |
| link | Ra | Sa | Outside temperature | "Sa520+" | 2.5°C | |
| link | Rb | Sb | Indoor unit "pull" | "Sb700" | 7/15 | |
| link | Rd | Sd | Compressor frequency (measured) | "Sd450" | 54 Hz | |
| link | Re | Se | Indoor humidity | "Se970" | 79% RH | |
| link | Rg | Sg | Compressor on/off | "Sg1" | Online |
Indoor unit power
Payload length: 1 character
| Byte | Description | Values |
|---|---|---|
| 0 | Power | 0: Off 1: On |
Follows power portion of G1.
Indoor unit climate action
Payload length: 1 character
| Byte | Description | Values |
|---|---|---|
| 0 | Mode | 0: Auto, Cooling 1: Auto 2: Dry 3: Cool 4: Heat 6: Fan 7: Auto, Heating |
Follows mode portion of G1.
Temperature setpoint (Celcius)
Payload length: 4 characters
Signed decimal number, ASCII in reverse order. Example: "120+" would stand for +21.0 C. Unlike temperature sensors, there's no scaling so this value is in whole degrees, no decimals.
On timer setting
Payload length: 3 characters
Plain decimal number, ASCII in reverse order. Example: "441" would stand for 144 10-minute periods from time of setting. Only updated when the timer is first set, doesn't provide indication if active. Follows G3.
Off timer setting
Payload length: 3 characters
Plain decimal number, ASCII in reverse order. Example: "720" would stand for 27 10-minute periods from time of setting. Only updated when the timer is first set, doesn't provide indication if active. Follows G3.
Swing settings
Payload length: 2 characters
| Byte | Description | Values |
|---|---|---|
| 0 | Swing | 0: Off 1: Vertical 2: Horizontal 7: Both |
| 1 | Swing Enabled | 0: Off F: Any Swing On |
Similar coding to swing portion of G5.
Fan mode
Payload length: 1 character
| Byte | Description | Values |
|---|---|---|
| 0 | Fan mode | 3: Low 4: Mid-low 5: Medium 6: Mid-high 7: High A: Auto B: Quiet or Q for Night? |
Unlike G1, SG will return "B" for quiet mode.
Home temperature (Celcius)
Payload length: 4 characters
Signed decimal number, ASCII in reverse order, multiplied by 10. Example: "123+" would stand for +32.1 C
Inlet temperature (Celcius)
Payload length: 4 characters
Signed decimal number, ASCII in reverse order, multiplied by 10. Example: "123+" would stand for +32.1 C
Target fan speed (RPM)
Payload length: 3 characters
Plain decimal number, ASCII in reverse order, divided by 10. Example: "560" would stand for 760 RPM. Leads SL.
Measured fan speed (RPM)
Payload length: 3 characters
Plain decimal number, ASCII in reverse order, divided by 10. Example: "570" would stand for 750 RPM. Lags SK.
Target louver angle (degrees)
Payload length: 4 characters
Signed decimal number, ASCII in reverse order. On my ATXF20 with swing on I observed the value changing from +000 to +090 and back. Can become negative when unit is off and louver is closed. Leads SN.
Measured louver angle (degrees)
Payload length: 4 characters
Signed decimal number, ASCII in reverse order. On my ATXF20 with swing on I observed the value changing from +000 to +090 and back. Can become negative when unit is off and louver is closed. Lags SM.
Target temperature (Celcius)
Payload length: 4 characters
Signed decimal number, ASCII in reverse order, multiplied by 10. Example: "123+" would stand for +32.1 C
Temperature the indoor unit is actually trying to match, accounts for any powerful mode or intelligent eye modifiers or similar (heuristics are detailled in service manuals)
Unknown
Payload length: 2 characters
"SW00" and "SW09" observed on offline units
Outside temperature (Celcius)
Payload length: 4 characters
Signed decimal number, ASCII in reverse order, multiplied by 10. Example: "123+" would stand for +32.1 C
Indoor unit "pull"
Payload length: 3 characters
Plain decimal number, ASCII in reverse order. Example: "300" would stand for 3. Represents how much this indoor unit currently "pulls" on the compressor. Ranges from 0-15 indicating no load through to maximum load, with a value determined by the difference between the target and indoor temperatures. The compressor uses this signal to determine its target operating frequency (documented in service manuals). Ressembles "Indoor frequency command signal", but always switches to "Sb900" during defrost.
Outdoor unit compressor frequency (Hz)
Payload length: 3 characters
Plain decimal number, ASCII in reverse order. Example: "560" would stand for 65 Hertz. At least one unit returned 999 when unsupported.
Indoor humidity (%)
Payload length: 3 characters
Plain decimal number, ASCII in reverse order. Example: "970" means 79% humidity. Units with no humidity sensor seem to always report 50%.
Indoor unit compressor active
Payload length: 1 character
| Byte | Description | Values |
|---|---|---|
| 0 | Active | 0: Off 1: On |
Indicates if the indoor unit is currently using the compressor. Unknown if it registers when the compressor switches off before defrosting.
The Rz command family contains a number of extra queriable data fields (with a 2 byte response payload), some of which mirror the various other R* commands, and some of which contain unique information. Input validation appear to be looser than other commands, since it can interpret the checksum byte as input if the command is shorter than expected, and seems to alias values that are invalid or unknown (including non-ASCII input bytes) using some yet unknown logic. The results of these commands appear to be fairly consistant and reliable, though.
Bit-field as ASCII string. Represents the state of the unit.
- 0x01: Powerful mode
- 0x02: Defrost in progress
- 0x04: Refrigerant valve to this unit open (meaning it is actively heating or cooling)
- 0x08: Unit online, whether or not it uses the compressor
- The unit reads as offline if it is turned off or if its current mode conflicts with the compressor (i.e: trying to use fan mode while the compressor is heating)
| RzB2 response | Rectified | Bitfield | Bitfield meaning | Meaning |
|---|---|---|---|---|
| SzB200 | 00 | 0b0000_0000 |
(Not online) | Indoor unit is turned off |
| SzB280 | 08 | 0b0000_1000 |
Online | Unit is idle (or in fan-only mode) |
| SzB2C0 | 0C | 0b0000_1100 |
Online + Active | Unit is actively heating/cooling |
| SzB2D0 | 0D | 0b0000_1101 |
Online + Active + Powerful | Unit is heating/cooling "at max capacity" |
| SzB2A0 | 0A | 0b0000_1010 |
Online + Defrost | Defrost sequence starting/ending |
| SzB2E0 | 0E | 0b0000_1110 |
Online + Active + Defrost | Defrost sequence in progress (unit is cooling) |
| SzB220 | 02 | 0b0000_0010 |
Defrost | Compressor is defrosting but unit is offline |
Bit-field as ASCII string. Represents the state of the unit with regards to the whole system.
- 0x01: Compressor locked/"3 minutes standby"
- 0x02: Unknown, not observed yet
- 0x04: Refrigerant valve to this unit open (meaning it is actively heating or cooling)
- 0x08: Defrost in progress
- 0x10: Unknown, not observed yet
- 0x20: (Multi-zone) Other units are online in a compressor-reliant mode, whether they're active or idle
- 0x40: Unknown, observed on protocol v2+ units, both single-zone and multi-zone.
Whenever the compressor stops for any reason (besides defrost) or if restarting after a power outage, it enters "3 minutes standby" and will not resume operation until a 3 minutes period has passed.
Response payload examples
| Wire order | Reverse order | Meaning |
|---|---|---|
| "SzC340" | 04 | This unit is actively heating/cooling |
| "SzC380" | 08 | Defrost sequence, unit is idle (possibly offline) |
| "SzC3C0" | 0C (0x04 + 0x08) | Defrost sequence, unit is cooling |
| "SzC302" | 20 | This unit is idle or offline, but other units are online |
| "SzC310" | 01 | Compressor just stopped and is unavailable |
| "SzC312" | 21 | Compressor unavailable, other units are online |
| "SzC300" | 00 | Unit is idle, compressor probably not in use |
Queried by BRP069C, seemingly static, meaning unknown.
Queried by BRP069C, seemingly static, meaning unknown.
Payload format: 4 digit ASCII hex characters in reverse order.
Response code: C, payload size: 5 bytes. 6 bytes total.
Example payload: A1000; Meaning (presumed): 0x0001
Example response: C100D4; Meaning (presumed): 0x*001 is 0x4D
Presumably for reading a physical memory address. Like Rz**, it performs no input validation and will interpret the checksum as part of the payload if said payload is too short. It will also accept pretty much anything as its payload, including any ASCII character that is not a hexadecimal digit or, indeed, any non-ASCII 8 bit value. The payload is returned as-is, with any non-ASCII characters intact, and with the "final" byte (believed to be the most significant byte, so the one that wouldn't change on a sequential read) truncated. Both parts of the response payload are presumed to be in reverse order. The response payload also tends to change when the same command is sent multiple time, at least for certain payloads (like "0000"), so this couldn't be some kind of ROM dump command.
The internal "conversion" from non-hexadecimal values to hex ones is unknown.
Note
Faikout may discard single-byte commands, so when testing over MQTT, something like "MM" tends to be more reliable than "M".
Tip
Return values for this command are tracked on the Model Codes page.
Response code: M, payload size: 4 bytes. 5 bytes total.
Payload format: 4 digit ASCII hex characters in unknown order.
Example payload: 0x33 0x45 0x35 0x33; ASCII (wire order) "35E3"
One of rare, perhaps very old, commands, whose code only consists of one byte (anything past the first letter is ignored so "M", "Mx" and "M12345" are treated the same). Unknown for sure, but looks like model code for protocol v0, according to https://github.com/revk/ESP32-Faikout/issues/408#issuecomment-2313256060
. Didn't differ between various indoor unit models connected to the same outdoor unit, so suspected to be related to the outdoor unit.
The command is supported in v2+ protocols, but the only seen reply is 'MFFFF' (ASCII). At least some protocol v1 units have return values for both M and GC, which don't appear similar or related so far. The official wired controller doesn't seem to do anything about the payload.
It seems clear that the returned ASCII string is to be interpreted as an hexadecimal number (akin to the output for FM/GM), though not much more can be discerned so far, including whether they are to be interpreted in forward or reverse order.
Note
Faikout may discard single-byte commands, so when testing over MQTT, something like "VV" tends to be more reliable than "V".
Tip
Return values for this command are tracked on the Model Codes page.
Response code: V, payload size: 4 bytes. 5 bytes total.
Payload format: 4 digit ASCII hex characters in unknown order.
Another old, protocol v0 command. Assumed to be some kind of version identifier but unknown. Command payload seems to be ignored, so "V", "Vx" and "V12345" should be equivalent.
Indoor unit firmware version
Response code: VS, payload size: 14 bytes
- Bytes 0 - 7 - firmware version in text format in reversed ASCII as usual. Shown as "Indoor unit software" on "Indoor unit" page by Onecta app when using new cloud-only controller. Info provided by @nikbyte using FTXA35C2V1B's integrated controller.
- Bytes 8 - 13 - Always read as '00000M' again in reversed ASCII.
Example payload: 0x31 0x30 0x31 0x37 0x31 0x30 0x32 0x32 0x4D 0x30 0x30 0x30 0x30 0x30 ; firmware version "22017101"
This command is only polled once. BRP069B41 controller also polls this command, but no HTTP API endpoints are known to expose this information.
On v0 units, this is treated like a normal "V" command.
- No method has been identified yet for reading the current error code. The protocol has to support this in some way, because Daikin's own wired S21 remote control (BRC073/BRC81A1, not to be confused with the "NAV controller"/BRC1E73, which uses P1P2) is able to detect a non-zero error code and display it. See issue #569.
- The wired remote also reports "MODE CONFLICT" for multizone systems in invalid configurations, like heating and cooling at the same time. This is not an error code, but has also not been found.
- The state of the "intelligent eye" motion sensor isn't reported by any of the identified commands. It is often possible to compare the setpoint (RC/SC) and target temperature (RX/GX) to indirectly determine whether or not movement has been detected recently, but even as a workaround, it's rather brittle. See issue #788.
- Reading the indoor unit action, to determine whether it is defrosting, idle, currently heating/cooling, etc., is not well understood enough to be implemented. Commands RzB2 and RzC3 work on some units, but not all of them, and unsupported
Rz**commands have the unfortunate quirk of returning garbage data instead of NAK. Something more robust would be desirable, but no other commands so far seem to report that kind of information, and neither the wired remote nor the wireless adapter seem to check for this (only the P1P2 controllers like the Madoka and NAV controller seem to report "Standby" during preheating or defrost). See issue #556. - Overriding the measured room temperature value with data from an external sensor is a desired feature, but whether or not that's possible is still unclear. The S21 wired remote merely displays the reported value from the indoor unit, and has no temperature sensor of its own.