From 4215323f3f652766d06189da67b068d477d8867e Mon Sep 17 00:00:00 2001 From: cjjohn <72096478+Grovy311@users.noreply.github.com> Date: Fri, 7 Aug 2026 15:37:40 +0200 Subject: [PATCH] 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 --- versapad_serial.py | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/versapad_serial.py b/versapad_serial.py index 2c795a1..d3eb978 100644 --- a/versapad_serial.py +++ b/versapad_serial.py @@ -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."""