Use lightweight READ_STATUS instead of full CONFIG_READ for profile polling

Live-Sync polls read_active_profile() every 1.5s, but it was requesting
a full 740-byte config dump (~124 chunk packets) just to read one
byte out of it. The firmware handles CONFIG_READ synchronously and
blocking, which delayed its LED animation update enough to make
Pulse/Blink visibly stutter on every poll cycle -- see
VersaMCU's doc/07_serial_protocol.md ("READ_STATUS vs. CONFIG_READ
fuer Polling") for the root-cause writeup on the firmware side.

read_active_profile() now sends VersaMCU's new CMD_READ_STATUS (0x06)
and reads back a single EVT_STATUS (0x86) packet instead of driving
the chunked dump protocol. Requires the corresponding firmware update
(VersaMCU commit "Add lightweight READ_STATUS command..."); older
firmware without it just times out gracefully (last_error stays
"timeout", no crash).

desktop_viewer.py's SERIAL_POLL_S/SERIAL_IDLE_S already sit back at
their original 1.5s/3.0s (temporarily raised to 8s as a stopgap before
the firmware fix landed) since the expensive dump is gone now.

Verified end-to-end against a freshly flashed board: correct profile
returned, no more visible LED stutter with Live-Sync on.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
cjjohn 2026-08-07 15:37:40 +02:00
parent 553ecc54d8
commit 4215323f3f

View file

@ -37,6 +37,7 @@ CONFIG_SIZE = 740
MACRO_SIZE = 512
ACTIVE_PROFILE_OFFSET = 7
CMD_READ_STATUS = 0x06
CMD_CONFIG_BEGIN = 0x10
CMD_CONFIG_DATA = 0x11
CMD_CONFIG_COMMIT = 0x12
@ -46,6 +47,7 @@ CMD_MACRO_DATA = 0x21
CMD_MACRO_COMMIT = 0x22
CMD_MACRO_READ = 0x23
EVT_STATUS = 0x86
EVT_CONFIG_ACK = 0x90
EVT_CONFIG_NACK = 0x91
EVT_CONFIG_BEGIN = 0x92
@ -187,16 +189,39 @@ class VersaPadLink:
# ── Oeffentliche API ────────────────────────────────────────
def read_active_profile(self):
"""0-2 bei Erfolg, None bei Timeout/Fehler/kein Board."""
buf = self._read_dump(CMD_CONFIG_READ, EVT_CONFIG_BEGIN, EVT_CONFIG_DATA, EVT_CONFIG_END, CONFIG_SIZE)
if buf is None:
def read_active_profile(self, deadline_s=1.0):
"""0-2 bei Erfolg, None bei Timeout/Fehler/kein Board.
Nutzt CMD_READ_STATUS (1 Antwortpaket) statt eines vollen CONFIG_READ-
Dumps (124 Pakete) -- der volle Dump blockiert die Firmware lange genug,
dass laufende LED-Pulse-Animationen beim Live-Sync-Polling sichtbar
stottern (siehe VersaMCU doc/07_serial_protocol.md, "READ_STATUS vs.
CONFIG_READ fuer Polling"). Für ältere Firmware ohne CMD_READ_STATUS
faellt das Board auf keine Antwort zurueck -> Timeout, kein Absturz."""
if not self._ensure_open():
return None
profile = buf[ACTIVE_PROFILE_OFFSET]
if profile not in (0, 1, 2):
try:
self.ser.reset_input_buffer()
self.ser.write(bytes([CMD_READ_STATUS, 0, 0, 0, 0, 0, 0, 0]))
deadline = time.time() + deadline_s
while time.time() < deadline:
raw = self.ser.read(PACKET_SIZE)
if len(raw) < PACKET_SIZE:
continue
if raw[0] == EVT_STATUS:
profile = raw[1]
if profile not in (0, 1, 2):
self.last_error = "timeout"
return None
self.last_error = None
return profile
self.last_error = "timeout"
return None
return profile
except serial.SerialException:
self.close()
self.last_error = "busy"
return None
def read_full_config(self):
"""740B Rohdaten (alle 3 Profile) oder None."""