@@ -184,13 +184,13 @@ Runs as `uart_ctl_process_main` (`0x9b6f8dd8`) — **called repeatedly** by the
184184
185185** S4 — MODEL FINALISE** : allocates + copies the model capability struct, keyed on device-type. Full map, struct layout, and the per-model feature tables: ** [ ` 11-model-capability-map.md ` ] ( 11-model-capability-map.md ) ** . In brief: ` {0,1,0x36,0x37} ` → TEMPLATE_A ` 0x9b807e20 ` + ` malloc(0x33c)→[0x1000b8a8] ` (A/C); ` 0x15 ` → TEMPLATE_B ` 0x9b808144 ` + ` malloc(0xe4)→[0x1000b8b4] ` (** dehumidifier** ); anything else allocates ** nothing** (` 0x9b6f9638 ` ). Next pass short-circuits S3 via ` [0x1000bcc2] ` → steady state = repeated verified num30/0x66 polls.
186186
187- > ** Corrections to an earlier revision of this line (2026-07-16, all [ PROVEN] — see doc 11 §2):**
187+ > ** Corrections to an earlier revision of this line (2026-07-16, all [ PROVEN] , see doc 11 §2):**
188188> 1 . ** ` 0x9b6f9126 ` is NOT the dispatch head** — it is ` bl 0x9b6f307c ` , the devtype * getter*
189189> (` ldrb r0,[0x10009687]; cmp r0,#1; it lo; movlo r0,#1 ` — maps 0→1).
190190> 2 . ** ` 0x36 ` /` 0x37 ` do NOT get a distinct branch.** ` 0x9b6f9130 ` * is* the TEMPLATE_A/` 0x33c `
191191> branch (` mov.w r0,#0x33c ` @ ` 0x9b6f9130 ` ), shared by all four A/C codes
192192> (` beq.w #0x9b6f9130 ` @ ` 0x9b6f9728 ` /` 0x9b6f9732 ` ).
193- > 3 . ** TEMPLATE_A is reached by an explicit ` ==1 ` test, not an "else."** The real else
193+ > 3 . ** TEMPLATE_A is reached by an explicit ` ==1 ` test rather than an "else."** The real else
194194> (` 0x9b6f9638 ` ) allocates no struct at all.
195195
196196(Off main path: ` 0x9b6f3480 ` enumerates slaves 1..8 via ` 0x9b6f3120 ` , transaction timeout ** 3000 ms, 5 retries** ; ` 0x9b6f3418 ` resets 8×0x48-byte slave slots.)
@@ -239,21 +239,21 @@ Two source threads appeared to conflict; they describe different byte origins:
239239> session tok envel [9]/[10]: 00 00 [captured] <- what v10207 stamped
240240> ```
241241>
242- > The DevType reply's envelope bytes[9]/[10] is **`00 00`** — * not* `01 01`. The claim above
243- > that "the A/C reply seeds the token to `01 01`" is therefore **wrong for the `0x0A` frame**:
244- > that `01 01` comes only from `handle_devType_cmd_result`'s inner `[3]/[4]`. ( A **`0x66` status**
245- > reply *does* carry `01 01` at [9]/[10] — measured separately — which is why an observe-only
246- > capture that re-reads every reply always looked benign and seemed to confirm the token story.)
242+ > The DevType reply's envelope bytes[9]/[10] is **`00 00`**, not `01 01`. The claim above that
243+ > "the A/C reply seeds the token to `01 01`" is **wrong for the `0x0A` frame**: that `01 01` comes
244+ > only from `handle_devType_cmd_result`'s inner `[3]/[4]`. A **`0x66` status** reply does carry
245+ > `01 01` at [9]/[10], measured separately, which is why an observe-only capture that re-reads
246+ > every reply looked benign and seemed to confirm the token story.
247247>
248- > **This is the exact v10207 failure: ** it sampled first-reply-only (stock's `globals==0` gate =
249- > the `0x0A` reply), got `00 00`, rejected it as "not yet seeded", and so stamped its `00 00`
250- > seed onto every post-handshake frame instead of `01 01`. The A/C rejected all of them.
248+ > **This is the v10207 failure. ** It sampled first-reply-only (stock's `globals==0` gate = the
249+ > `0x0A` reply), read `00 00`, rejected that as "not yet seeded", and stamped its `00 00` seed onto
250+ > every post-handshake frame in place of `01 01`. The A/C rejected all of them.
251251>
252- > **Consequence:** bytes[7]/[8] post-handshake are the **device-type/sub-type**, a *static
253- > per-model identifier* — the `01 01` in every captured stock DI frame is this model's device
254- > type. Anything reading these bytes must take `handle_devType_cmd_result`'s inner `[3]/[4]`
255- > from the class-`0x0A` reply (frame bytes `[16]/[17]`), **never** that reply's envelope
256- > `[9]/[10]`; see ` firmware/src/rs485-driver/hisense_rs485.cpp` (`hisense_devtype_from_reply`).
252+ > **Consequence:** bytes[7]/[8] post-handshake carry the **device-type/sub-type**, a *static
253+ > per-model identifier*. The `01 01` in the captured stock DI frames is this model's device type.
254+ > Read these bytes from `handle_devType_cmd_result`'s inner `[3]/[4]` on the class-`0x0A` reply
255+ > (frame bytes `[16]/[17]`), **never** from that reply's envelope `[9]/[10]`. See
256+ > `firmware/src/rs485-driver/hisense_rs485.cpp` (`hisense_devtype_from_reply`).
257257
258258### 4.6 Timing / retry summary **[PROVEN]**
259259- Per-transaction listen: **500 ms**. Timeout → status 7 → transaction returns 0.
@@ -293,21 +293,20 @@ Guard: `payload[0]==0x66` (`0x9b6f0c6a`) **and** `payload[1]==0x40` (`0x9b6f0c74
293293
294294Also: `payload[3]` (frame 16) → raw global `0x100096c9` (bits1,3 feed a state machine); `payload[4]` (frame 17) bit3 → state machine; **`payload[5]` (frame 18) read *signed* (`ldrsb` @ `0x9b6f0c86`)** into `sp+0x64`, branched `≥0/<0` @ `0x9b6f0f10` — a signed selector byte, **not** a plain temperature. The partial overlap with ESPHome (frame16≈"fan", frame18≈"mode") is coincidental — semantics are **bit-flags, not byte values**. **[PROVEN]**
295295
296- > **Correction (2026-07-16, [PROVEN] — see [doc 11](11-model-capability-map.md) §4):** an earlier
297- > revision said *"Whole commit gated on `[0x10009687]==0x15`"*. **Wrong.** The `==0x15` test only
298- > selects the **printf format string**; it does not gate the commit. The real gate is
299- > **`devtype <= 1`** (`ldrb r3,[0x10009687]; cmp r3,#1; bhi.w #0x9b6f16c4` @ `0x9b6f0d84`) —
300- > i.e. the commit runs for **our** unit (devtype 1), not only for the dehumidifier. What it
301- > commits: `supported` bytes written into the S4-allocated capability struct at `[0x1000b8a8]`,
302- > plus a 3-digit **profile code** strcpy'd to `0x100096dc` (getter `0x9b6f308c`).
303- > `ac_trans_102_64` set → profile **`199`** (the generic/transparent profile,
304- > `beq.w` @ `0x9b6f0ddc` → `'199'` @ `0x9b6f0de4`).
305-
306- > ⚠️ **The table above is authoritative and our driver disagrees with it.**
307- > `hisense_parse_features()` reads the right *bytes* but mislabels two: its `q_display` is
308- > `[0x0A]&0x08` = **`ac_purify`**, and its `purify` is `[0x0D]&0x80` = **`ac_8heat`**. True
309- > `ac_q_display` (`[0x1A]&0x40`) is not parsed at all. Independently confirmed against the stock
310- > printf arg order. Tracked for a rename (behaviour is unchanged — the byte reads are correct).
296+ > **Correction (2026-07-16, [PROVEN], see [doc 11](11-model-capability-map.md) §4):** an earlier
297+ > revision said *"Whole commit gated on `[0x10009687]==0x15"*. That is wrong. The `==0x15` test
298+ > selects the **printf format string** and does not gate the commit. The real gate is
299+ > **`devtype <= 1`** (`ldrb r3,[0x10009687]; cmp r3,#1; bhi.w #0x9b6f16c4` @ `0x9b6f0d84`), so the
300+ > commit runs for **our** unit (devtype 1) and not for the dehumidifier alone. It commits
301+ > `supported` bytes into the S4-allocated capability struct at `[0x1000b8a8]`, plus a 3-digit
302+ > **profile code** strcpy'd to `0x100096dc` (getter `0x9b6f308c`). `ac_trans_102_64` set → profile
303+ > **`199`** (the generic/transparent profile, `beq.w` @ `0x9b6f0ddc` → `'199'` @ `0x9b6f0de4`).
304+
305+ > ⚠️ **The table above is authoritative. Our driver disagreed with it until 2026-07-16.**
306+ > `hisense_parse_features()` read the right *bytes* and mislabeled two: its `q_display` held
307+ > `[0x0A]&0x08` = **`ac_purify`**, and its `purify` held `[0x0D]&0x80` = **`ac_8heat`**. True
308+ > `ac_q_display` (`[0x1A]&0x40`) still goes unparsed. Confirmed against the stock printf arg order
309+ > and renamed (`heat_8c` + `purify`); the byte reads were correct, so behaviour did not change.
311310
312311### 5b. Command `0x65` — `matter_pack_devType_cmd` `0x9b6f2c60` **[PROVEN]**
313312
0 commit comments