Skip to content

fix(levoit): Core 200S display and panel reset across MCU firmware versions - #83

Merged
tuct merged 1 commit into
mainfrom
fix/core200s-mcu-fw-variants
Oct 25, 2026
Merged

tuct merged 1 commit into
mainfrom
fix/core200s-mcu-fw-variants

Conversation

@tuct

@tuct tuct commented Oct 25, 2026

Copy link
Copy Markdown
Owner

Follow-up to #71 — this commit was pushed minutes after #71 merged, so it did not make it in. It addresses the byte-level data posted in #60 by @Manichee, from a Core 200S Plus on MCU 4.0.34.

Bytes 6 and 7 swap between MCU firmware versions

Their frames do not match what my captures showed on MCU 2.0.11. The version bytes decode to exactly the firmware they report, so the indices line up and the difference is real:

display on,  2.0.11 : 0B 00 02 01 00 03 | 64 01 | 00 00 01 00
display on,  4.0.34 : 22 00 04 01 00 03 | 01 64 | 01 00 00 00
display off, both   : .. .. .. 01 00 03 | 00 00 | .. .. .. ..

The two bytes are a pair — one holds display brightness, the other a transient flag that goes 0x01 for a few seconds after any command — and which is which depends on the firmware.

This also resolves a loose end from #71. My display_on_off.txt showed both bytes moving together, while filter_from_device.txt showed byte 6 moving alone. Both are explained: toggling the display writes the brightness and trips the flag in the same frame, and the capture ended before the flag expired.

#71 as merged would misbehave on 4.0.34 — reading display from a fixed byte 6 reports the display ON for a few seconds after every command. The decoder now matches on the value 0x64 across both bytes, which is exactly what 01 05 A1 writes, so either layout works with no version detection.

Same problem in the panel-reset handler

The MCU's push carries payload 0x01 on 2.0.11 but 0x00 on 4.0.34, so the payload check added in #71 would miss it on the Plus — which is what @Manichee observed: the component acks the message and publishes nothing.

Direction alone identifies the event: our own reset leaves as a 0x22 and the MCU answers with a 0x12, so any inbound 0x22 of this type came from the unit's button. Keyed on that instead.

Independent confirmation of the CADR fixes in #71

From the same thread, Current CADR = 125 / 83 / 41 m³/h at speeds 3 / 2 / 1. That is 167 × speed ÷ 4 — confirming both the 4-speed divisor on a 3-speed unit, and that speed 1's 41 m³/h truncates to zero when divided by 60. Measured on someone else's hardware.

Docs

The device README now carries both layouts with the firmware mapping, plus a symptom-to-cause table covering every report in #60: stuck 100%, the "downward staircase", 0.0%/1.0% readings, Filter Low showing "Problem" on a new filter, panel reset doing nothing in HA, and the red light not clearing.

Testing

All three levoit compile tests pass on top of current main. Not verified on hardware — @Manichee (4.0.34) and @Scags104 (1.1.23) are the right people to confirm, since my own unit is 2.0.11.

🤖 Generated with Claude Code

…rsions

Byte-level data from issue #60 on a Core 200S Plus (lap-c201s-musw, MCU 4.0.34)
does not match what my captures showed on MCU 2.0.11. The version bytes decode
to exactly the firmware the reporter names, so the indices line up and the
difference is real:

  display on,  2.0.11 : 0B 00 02 01 00 03 | 64 01 | 00 00 01 00
  display on,  4.0.34 : 22 00 04 01 00 03 | 01 64 | 01 00 00 00
  display off, both   : .. .. .. 01 00 03 | 00 00 | .. .. .. ..

Bytes 6 and 7 are a pair - one holds the display brightness, the other a
transient flag that goes 0x01 for a few seconds after any command - and the two
swap between firmware versions. That also explains why they appeared locked
together in my display capture: toggling the display writes the brightness and
trips the flag in the same frame, and the capture ended before the flag
expired.

Reading display from a fixed byte 6 would have reported the display ON for a
few seconds after every command on 4.0.34. The decoder now matches on the value
0x64 across both bytes, since that is exactly what 01 05 A1 writes, which
handles either layout without sniffing the version.

Same class of problem on the panel filter reset: the MCU's push carries payload
0x01 on 2.0.11 and 0x00 on 4.0.34, so my payload check would have missed it on
the Plus - which is what the reporter saw, the component acking the message and
publishing nothing. Direction alone identifies the event, because our own reset
leaves as a 0x22 and the MCU answers 0x12, so any inbound 0x22 of this type is
from the unit's button. Keyed on that instead.

Also confirms the 0.0%/1.0% filter readings several people hit on 1.5.0: byte 6
on 4.0.34 is the transient flag, so publishing it as a percentage produced
exactly 0 or 1. The revert in this branch removes that.

The thread independently confirms both CADR fixes too - Current CADR read
125/83/41 m3/h at speeds 3/2/1, which is 167 x speed / 4, so a 3-speed unit was
using a 4-speed divisor and speed 1's 41 m3/h truncated to zero per minute.

Documented both layouts, the firmware mapping and a symptom-to-cause table in
the device README. Compile tests pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@tuct
tuct merged commit cd55a8d into main Oct 25, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant