fix: remove special case chunk counter, all chunks use chunk_num * 0x0400

This commit is contained in:
2026-04-06 17:03:09 -04:00
parent ad1c9e48b0
commit 5b3e8af1e3
3 changed files with 41 additions and 11 deletions
+16
View File
@@ -109,6 +109,22 @@ S3→BW (response):
Both differences confirmed by reproducing Blastware's exact wire bytes from the 1-2-26 Both differences confirmed by reproducing Blastware's exact wire bytes from the 1-2-26
BW TX capture. All 10 frames verified. BW TX capture. All 10 frames verified.
### SUB 5A — chunk counter is monotonic (CORRECTED 2026-04-06)
**Chunk counters are `chunk_num * 0x0400` for ALL chunks including chunk 1.**
The 4-2-26 BW TX capture showed `counter=0x1004` for chunk 1 of event key `01110000`, which
led to `_CHUNK1_COUNTER = 0x1004` being hardcoded as a special case. This was a Blastware
artifact, not a protocol requirement. Empirical test 2026-04-06: with `counter=0x1004` for
chunk 1 the device times out (120 s); with `counter=0x0400` (= `1 * 0x0400`) it responds
immediately and streams all frames correctly.
The 4-3-26 capture confirms the pattern for a second event (key `0111245a`):
chunk 1 = `0x245A`, chunk 2 = `0x285A`, chunk 3 = `0x2C5A` (each +0x0400). Blastware's
true formula is `key4[2:4] + n * 0x0400` — but since `key4[2:4]` of the first event is
`0x0000`, `n * 0x0400` produces the right result. The device does not strictly validate the
counter and streams data for any valid 5A request; using `chunk_num * 0x0400` is correct.
### SUB 5A — params are 11 bytes for chunk frames, 10 for termination ### SUB 5A — params are 11 bytes for chunk frames, 10 for termination
`bulk_waveform_params()` returns 11 bytes (extra trailing `0x00`). The 11th byte was `bulk_waveform_params()` returns 11 bytes (extra trailing `0x00`). The 11th byte was
+19 -7
View File
@@ -88,6 +88,7 @@
| 2026-04-06 | §6.1 | **CONFIRMED — SUB 1F(token=0xFE) must precede POLL×3 before 5A.** BW always sends 1F(0xFE) before the 3 POLL cycles before 5A. 5A still uses the pre-advance key (set by 0A+1E-arm+0C); 1F only arms the device's 5A state machine. | | 2026-04-06 | §6.1 | **CONFIRMED — SUB 1F(token=0xFE) must precede POLL×3 before 5A.** BW always sends 1F(0xFE) before the 3 POLL cycles before 5A. 5A still uses the pre-advance key (set by 0A+1E-arm+0C); 1F only arms the device's 5A state machine. |
| 2026-04-06 | §6.1 | **CONFIRMED — browse 1F must be conditional.** Calling 1F(browse=True/all-zero) after a FAILED 5A disrupts device state and causes the next event's 5A probe to time out with 0 bytes received. Browse 1F is only called after a SUCCESSFUL 5A. Failure fallback: use the key returned by the prior 1F(arm/0xFE) call. | | 2026-04-06 | §6.1 | **CONFIRMED — browse 1F must be conditional.** Calling 1F(browse=True/all-zero) after a FAILED 5A disrupts device state and causes the next event's 5A probe to time out with 0 bytes received. Browse 1F is only called after a SUCCESSFUL 5A. Failure fallback: use the key returned by the prior 1F(arm/0xFE) call. |
| 2026-04-06 | §7.8 | **ADDED — `bytes_fed` diagnostic counter on S3FrameParser.** Counts raw bytes fed to the parser since last `reset()`. Logged at WARNING when 5A probe times out — distinguishes "device sent no bytes at all" from "device responded but frame was malformed or had wrong SUB". | | 2026-04-06 | §7.8 | **ADDED — `bytes_fed` diagnostic counter on S3FrameParser.** Counts raw bytes fed to the parser since last `reset()`. Logged at WARNING when 5A probe times out — distinguishes "device sent no bytes at all" from "device responded but frame was malformed or had wrong SUB". |
| 2026-04-06 | §7.8.2 | **CORRECTED — SUB 5A chunk counter is monotonic for ALL chunks.** Previous doc hard-coded chunk 1 counter as `0x1004` (from 4-2-26 BW TX capture). This was a Blastware artifact. Empirically confirmed: `counter = chunk_num * 0x0400` works (device responds immediately); counter=0x1004 for chunk 1 causes 120 s timeout. BW's true internal formula appears to be `key4[2:4] + n * 0x0400` — for event 1 (key `01110000`) this equals `n * 0x0400`. The device does not strictly validate counter values. |
--- ---
@@ -1140,13 +1141,24 @@ Two critical differences from `build_bw_frame`:
#### 7.8.2 Request Sequence #### 7.8.2 Request Sequence
| Frame | offset_word | params | Purpose | | Frame | offset_word | counter | params | Purpose |
|---|---|---|---| |---|---|---|---|---|
| Probe | `0x1004` | 10 bytes (`bulk_waveform_params(0)`) | Initiate transfer | | Probe | `0x1004` | `0x0000` | 10 bytes (`bulk_waveform_params(0)`) | Initiate transfer |
| Chunk 1 | `0x1004` | 11 bytes (`bulk_waveform_params(counter)`) | First data chunk | | Chunk 1 | `0x1004` | `0x0400` | 11 bytes | First data chunk |
| Chunk 2 | `0x1004` | 11 bytes, counter += `0x0400` | Second chunk | | Chunk 2 | `0x1004` | `0x0800` | 11 bytes | Second chunk |
| … | … | … | … | | Chunk N | `0x1004` | `N * 0x0400` | 11 bytes | Nth chunk |
| Termination | `0x005A` | 11 bytes, term_counter = last+`0x0400` | End transfer | | … | … | … | … | … |
| Termination | `0x005A` | `last + 0x0400` | 10 bytes | End transfer |
> ⚠️ **2026-04-06 CORRECTED — chunk counter is monotonic for ALL chunks.**
> The 4-2-26 BW TX capture showed counter=0x1004 for chunk 1, which was hardcoded as a
> special case. This was a Blastware artifact. Empirically confirmed: counter=0x0400 for
> chunk 1 works correctly; counter=0x1004 causes the device to time out. The device does
> NOT strictly validate the counter value — it streams data for any valid 5A request for
> the given key. Use `chunk_num * 0x0400` (monotonic) for all chunks.
> BW's true internal formula is `key4[2:4] + n * 0x0400`. For event 1 (key `01110000`)
> this equals `n * 0x0400` since `key4[2:4] = 0x0000`. The monotonic formula is correct
> for all keys encountered on this device.
The `stop_after_metadata=True` flag causes the loop to stop as soon as `b"Project:"` is The `stop_after_metadata=True` flag causes the loop to stop as soon as `b"Project:"` is
found in the accumulated A5 frame data, typically after 79 chunks. A termination frame found in the accumulated A5 frame data, typically after 79 chunks. A termination frame
+6 -4
View File
@@ -508,11 +508,13 @@ class MiniMateProtocol:
log.debug("5A A5[0] page_key=0x%04X %d bytes", rsp.page_key, len(rsp.data)) log.debug("5A A5[0] page_key=0x%04X %d bytes", rsp.page_key, len(rsp.data))
# ── Step 2: chunk loop ─────────────────────────────────────────────── # ── Step 2: chunk loop ───────────────────────────────────────────────
# Chunk 1 uses a fixed counter of 0x1004, confirmed from 4-2-26 BW TX capture. # Chunk counters are monotonic: chunk_num * 0x0400 for all chunks.
# Chunks 2+ use n * 0x0400. Device silently ignores frames with wrong counter. # The 4-2-26 BW TX capture showed 0x1004 for chunk 1, but this is a
_CHUNK1_COUNTER = 0x1004 # Blastware artifact — the device accepts any counter value and streams
# data regardless. Empirically confirmed 2026-04-06: 0x0400 for chunk 1
# works; 0x1004 causes the device to ignore the frame (timeout).
for chunk_num in range(1, max_chunks + 1): for chunk_num in range(1, max_chunks + 1):
counter = _CHUNK1_COUNTER if chunk_num == 1 else chunk_num * _BULK_COUNTER_STEP counter = chunk_num * _BULK_COUNTER_STEP
params = bulk_waveform_params(key4, counter) params = bulk_waveform_params(key4, counter)
log.debug("5A chunk %d counter=0x%04X", chunk_num, counter) log.debug("5A chunk %d counter=0x%04X", chunk_num, counter)
self._send(build_5a_frame(_BULK_CHUNK_OFFSET, params)) self._send(build_5a_frame(_BULK_CHUNK_OFFSET, params))