fix: update MiniMateClient to correctly handle 1F calls for event iteration and 5A arming

This commit is contained in:
2026-04-06 14:37:36 -04:00
parent 33de4239f4
commit 227c481022
2 changed files with 35 additions and 18 deletions
+11 -3
View File
@@ -192,18 +192,26 @@ there is only one event.
0A(key0) ← REQUIRED: establishes device context 0A(key0) ← REQUIRED: establishes device context
1E(token=0xFE) ← REQUIRED: arms device for 5A; CONFIRMED 4-2-26 + 4-3-26 1E(token=0xFE) ← REQUIRED: arms device for 5A; CONFIRMED 4-2-26 + 4-3-26
0C(key0) ← read waveform record 0C(key0) ← read waveform record
1F(all zeros / browse=False) → key1 ← MUST come BEFORE 5A (BW sequence, confirmed 4-2-26) 1F(token=0xFE) → [discard key] ← REQUIRED: arms 5A bulk stream state machine
POLL × 3 ← REQUIRED: 3 full POLL cycles before 5A (BW frames 68-73) POLL × 3 ← REQUIRED: 3 full POLL cycles before 5A (BW frames 68-73)
5A(key0) ← bulk stream; key0 used even though 1F already advanced 5A(key0) ← bulk stream; key0 used even though 1F already advanced
1F(all zeros / browse=True) → key1 ← USE THIS for loop iteration (browse=True returns correct key)
0A(key1) 0A(key1)
1E(token=0xFE) ← re-arm for next event's 5A 1E(token=0xFE) ← re-arm for next event's 5A
0C(key1) 0C(key1)
1F(browse=False) → key2 1F(token=0xFE) → [discard key] ← arm 5A
POLL × 3 POLL × 3
5A(key1) 5A(key1)
1F(browse=False) → null ← done 1F(browse=True) → null ← done
``` ```
**IMPORTANT — two separate 1F calls per event (CONFIRMED 2026-04-06):**
`1F(token=0xFE)` (browse=False) BEFORE POLL+5A arms the device's bulk stream state machine
but returns inconsistent iteration keys after the first event — do NOT use its returned key
for loop control. `1F(browse=True)` AFTER 5A returns the correct next event key for
iteration. The reason is device-side: `1F(0xFE)` appears to arm 5A and peek the next key
without reliably advancing the internal iteration pointer; `1F(all-zero)` advances it.
**The 1E(token=0xFE) arm step is required (FIXED 2026-04-06):** **The 1E(token=0xFE) arm step is required (FIXED 2026-04-06):**
The device silently ignores all 5A probe frames unless a second SUB 1E with token=0xFE The device silently ignores all 5A probe frames unless a second SUB 1E with token=0xFE
has been issued between 0A and 0C. This step is present in EVERY download cycle in both has been issued between 0A and 0C. This step is present in EVERY download cycle in both
+24 -15
View File
@@ -325,22 +325,17 @@ class MiniMateClient:
"get_events: 0C failed for key=%s: %s", cur_key.hex(), exc "get_events: 0C failed for key=%s: %s", cur_key.hex(), exc
) )
# SUB 1F — advance BEFORE 5A (matches BW sequence from 4-2-26 capture). # SUB 1F (download-arm) — send token=0xFE BEFORE POLL+5A to arm the
# MUST use browse=False (token=0xFE) — BW always sends 1F(token=0xFE) in # device's bulk stream state machine. The key returned by this call
# download mode. The device's 5A state machine requires the download-mode # is NOT used for loop iteration — 1F(download) returns inconsistent
# token here to arm the bulk stream; browse-mode (all-zero) token causes # keys after the first event (device-side state issue). A second
# the device to ignore the subsequent 5A probe. # browse-mode 1F call after 5A handles actual key advancement.
# Confirmed from 4-2-26 capture frames 66-67 vs frame 74. # Confirmed from 4-2-26 capture frames 66-67 (1F before frames 68-73 POLL).
# Save next key now; 5A will still use cur_key.
try: try:
key4, data8 = proto.advance_event(browse=False) proto.advance_event(browse=False) # arm 5A — discard returned key
log.info( log.info("get_events: 1F(download) — 5A armed")
"get_events: 1F(download) → key=%s trailing=%s",
key4.hex(), data8[4:8].hex(),
)
except ProtocolError as exc: except ProtocolError as exc:
log.warning("get_events: 1F failed: %s — stopping after this event", exc) log.warning("get_events: 1F(download) arm failed: %s", exc)
key4, data8 = b"\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00"
# POLL × 3 — BW sends 3 full POLL cycles between 1F and 5A. # POLL × 3 — BW sends 3 full POLL cycles between 1F and 5A.
# Confirmed from 4-2-26 BW TX capture (frames 68-73 before 5A at 74). # Confirmed from 4-2-26 BW TX capture (frames 68-73 before 5A at 74).
@@ -351,7 +346,7 @@ class MiniMateClient:
except ProtocolError as exc: except ProtocolError as exc:
log.warning("get_events: POLL %d failed: %s", _p, exc) log.warning("get_events: POLL %d failed: %s", _p, exc)
# SUB 5A — bulk waveform stream (uses cur_key, NOT the advanced key4). # SUB 5A — bulk waveform stream (uses cur_key, the event set up by 0A+1E+0C).
# By default (full_waveform=False): stop after frame 7 for metadata only. # By default (full_waveform=False): stop after frame 7 for metadata only.
# When full_waveform=True: fetch all chunks and decode raw ADC samples. # When full_waveform=True: fetch all chunks and decode raw ADC samples.
try: try:
@@ -389,6 +384,20 @@ class MiniMateClient:
cur_key.hex(), exc, cur_key.hex(), exc,
) )
# SUB 1F (browse) — advance event pointer for loop iteration.
# browse=True (all-zero params) confirmed correct for multi-event
# iteration from 4-3-26 browse-mode S3 captures. Must come AFTER
# 5A — the download-mode 1F above is for 5A arming only.
try:
key4, data8 = proto.advance_event(browse=True)
log.info(
"get_events: 1F(browse) → key=%s trailing=%s",
key4.hex(), data8[4:8].hex(),
)
except ProtocolError as exc:
log.warning("get_events: 1F(browse) failed: %s — stopping", exc)
key4, data8 = b"\x00\x00\x00\x00", b"\x00\x00\x00\x00\x00\x00\x00\x00"
events.append(ev) events.append(ev)
idx += 1 idx += 1