docs(series4): event download, PER-EVENT delete, and the ACH config write
Three captures with operator ground truth (Thor screenshots of the event list and
the weekly schedule). All Thor-originated.
DAY AND SCHEDULE TYPE, both settled by a weekly schedule. Thor's screen showed
"Start Monitoring 8:00 AM every day, alternating TEST1/test2, Repeat Weekly
disabled". The file is 7 x 260 + 4 = 1824 bytes and every record matches row for
row:
Day = 0 Sunday .. 6 Saturday
and [0] on record 0 read 4, against 2 and 3 in the daily schedules, so it carries
schedule TYPE as well as Repeat: 2 daily, 3 daily+repeat, 4 weekly,
5 weekly+repeat (predicted, unobserved). Bit 0 is Repeat; 2 and 4 are the bases.
Same bitmask style as [6].
In daily schedules Day reads 3 everywhere and is presumably ignored -- inference,
and the value 3 is unexplained.
EVENT DOWNLOAD. SUB 0x93 -> 0x6C arms each event before 1E/1F, with empty params
and an all-zero ack -- the Series IV analogue of Series III's 1E(token=0xFE), and
simpler. Event keys are a plain sequential counter (055D4A81..86 for six events)
at data[11:15], with the event size at data[17:19]. SUB 0x0A walks the list as
30-byte timestamped records; the dates match Thor's event list exactly.
DELETE IS PER-EVENT -- and this is the last piece a homebrew ACH receiver was
missing:
0xA8 params[0:4] = <event key> -> ack 0x57
0xAA params = zeros -> ack 0x55
The operator deleted the top row of Thor's list (the newest event) and 0xA8
carried 055D4A86, the highest key from the walk. Confirmed end to end.
Strictly safer than Series III, which can only erase everything: a receiver can
delete exactly what it has confirmed it stored. Different opcodes -- do not reach
for 0xA3/0xA2. Noted that SUB 0x06 read identically before and after, so it is
not a way to confirm a deletion landed.
ACH CONFIG. 0x2C / 0x7E / 0x7F with acks 0xD3 / 0x81 / 0x80 -- identical to
Series III. 126-byte write payload, offset 0x007E; the 0x2C read returns the same
bytes behind an 11-byte prefix. The enable flag is write[5]: 0x05 enabled, 0x04
disabled. Bit 0 is the flag, bit 2 set in both states -- do NOT test for
0x01/0x00 as Series III does. Dial string at write[6:] ("RADIO RING").
Flagged: write[118:120] changed on its own between the two sessions (43 23 ->
2e 5e) with nothing touched, and Thor writes back whatever it read. Round-trip
that field, never synthesise it. Beyond the enable byte and dial string the field
map is NOT established -- only one setting was varied, and Series III's offsets
are a hypothesis, not a transfer.
Every command on the unsafe list is now observed. None has been originated by
us, which is the line that still matters.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ru8Lg9HkkYvX9VWWo65SmL
This commit is contained in:
@@ -449,6 +449,13 @@ memory 15,000,000 total and free (no events stored).
|
||||
Series III has one config and nothing to enumerate.
|
||||
15. **`SUB 0x1C` carries the device clock** (day/month/year/h/m/s at
|
||||
`data[13:21]`). Nothing else read so far reports the unit's own time.
|
||||
18. **Deletion is per-event** (`0xA8` + a key, then `0xAA`), where Series III can
|
||||
only erase everything (`0xA3`/`0xA2`).
|
||||
19. **`SUB 0x93` arms each event** before `1E`/`1F`, replacing Series III's
|
||||
`1E(token=0xFE)` — no token, no params.
|
||||
20. **Event keys are a sequential counter** (`055D4A81…86`), not flash addresses.
|
||||
21. **The ACH enable is `0x05`/`0x04`**, not Series III's `0x01`/`0x00` — bit 0
|
||||
is the flag, bit 2 is set in both states.
|
||||
16. **There is a generic file transfer addressed by full path** — `0x94`/`0x48`
|
||||
read, `0x8D`/`0x8E` write. Series III has nothing comparable; its config is
|
||||
reachable only through dedicated commands.
|
||||
@@ -1301,6 +1308,141 @@ Worth noting the histogram (`…81`) and the loudest waveform (`…84`) report
|
||||
the histogram's single 1-minute interval spans the whole thumping session, so
|
||||
its maximum should equal the loudest event in it.
|
||||
|
||||
## Event download, per-event delete, and ACH config (2026-09-25)
|
||||
|
||||
Three captures with operator-supplied ground truth, including Thor screenshots of
|
||||
the event list and the schedule. All Thor-originated; we still send nothing.
|
||||
|
||||
### ✅ `Day` is the day of week — and `[0]` is the schedule type
|
||||
|
||||
A **weekly** schedule settles both fields. Thor's screen: *"multiday, Repeat
|
||||
Weekly: Disabled, Start Monitoring at 8:00 AM every day, alternating
|
||||
TEST1 / test2."* The file is **7 × 260 + 4 = 1,824 bytes**, and every record
|
||||
matches:
|
||||
|
||||
| rec | `1/2h` | time | `Day` | | `[6]` | name |
|
||||
|---|---|---|---|---|---|---|
|
||||
| @0 | 16 | 08:00 | **0** | Sun | 2 | `TEST1.mmb` |
|
||||
| @260 | 16 | 08:00 | **1** | Mon | 2 | `test2.mmb` |
|
||||
| @520 | 16 | 08:00 | **2** | Tue | 2 | `TEST1.mmb` |
|
||||
| @780 | 16 | 08:00 | **3** | Wed | 2 | `test2.mmb` |
|
||||
| @1040 | 16 | 08:00 | **4** | Thu | 2 | `TEST1.mmb` |
|
||||
| @1300 | 16 | 08:00 | **5** | Fri | 2 | `test2.mmb` |
|
||||
| @1560 | 16 | 08:00 | **6** | Sat | 2 | `TEST1.mmb` |
|
||||
|
||||
**`Day` = 0 Sunday … 6 Saturday**, and the alternating setup names line up with
|
||||
the screen row for row.
|
||||
|
||||
`[0]` on record 0 read **4** here, against `2` and `3` in the daily schedules —
|
||||
so it carries the schedule *type* as well as Repeat:
|
||||
|
||||
| `[0]` | meaning |
|
||||
|---|---|
|
||||
| `2` | Daily, repeat off |
|
||||
| `3` | Daily, repeat on |
|
||||
| `4` | **Weekly, repeat off** |
|
||||
| `5` | Weekly, repeat on — ⚠ predicted, not observed |
|
||||
|
||||
Bit 0 is Repeat; `2` and `4` are the Day/Week bases. Same bitmask style as
|
||||
`[6]`.
|
||||
|
||||
⚠ In *daily* schedules `Day` reads `3` in every record of every capture. With
|
||||
`[0]` already saying "daily", the field is presumably ignored there — but that is
|
||||
inference, and `3` is unexplained.
|
||||
|
||||
### Event download — `SUB 0x93` arms each event
|
||||
|
||||
Thor's download of six events:
|
||||
|
||||
```
|
||||
POLL 0x15 0x49 POLL 0x1C 0x15 0x49 0x06
|
||||
0x93 0x1E 0x0C 0x5A×4 ← event 1
|
||||
0x93 0x1F 0x0C 0x5A×11 ← event 2
|
||||
0x93 0x1F 0x0C 0x5A×12 ← event 3
|
||||
0x93 0x1F 0x0C 0x5A×14 ← event 4
|
||||
0x93 0x1F 0x0C 0x5A×9 ← event 5
|
||||
0x93 0x1F 0x0C 0x5A×6 ← event 6
|
||||
0x93 0x06 0x93 0x1E 0x0A×9 ← re-walk the list
|
||||
```
|
||||
|
||||
**`SUB 0x93` → ack `0x6C`** is sent before every event, with empty params and an
|
||||
all-zero ack. It is the Series IV analogue of Series III's `1E(token=0xFE)` arm
|
||||
step, and it is simpler: no token, no params.
|
||||
|
||||
**Event keys are a plain sequential counter.** The six events walked as
|
||||
`055D4A81 … 055D4A86`, consecutive, at `data[11:15]` of the `0x1E`/`0x1F`
|
||||
response, with the event's byte size at `data[17:19]`. Nothing like Series III's
|
||||
flash addresses.
|
||||
|
||||
`SUB 0x0A` walks the event list returning **30-byte records** carrying start and
|
||||
stop timestamps (`[11]` day, `[12]` month, `[13:15]` year BE …), terminating on
|
||||
an all-zero record. The dates match Thor's event list exactly (five events on
|
||||
09/23/2026, one on 09/24/2026).
|
||||
|
||||
### 🔑 Delete is PER-EVENT — `SUB 0xA8` + `0xAA`
|
||||
|
||||
```
|
||||
0xA8 params[0:4] = <event key> → ack 0x57
|
||||
0xAA params = zeros → ack 0x55
|
||||
```
|
||||
|
||||
The operator deleted the **top row of Thor's list** — the newest event — and
|
||||
`0xA8` carried `05 5d 4a 86`, the **highest** key from the walk. Thor lists
|
||||
newest-first, so newest = highest key. Confirmed end to end.
|
||||
|
||||
**This is the last piece a homebrew ACH receiver was missing.** The manual
|
||||
established that collection state is server-side bookkeeping and that "delete
|
||||
events from unit" is a separate server-issued action; this is that action's
|
||||
opcodes.
|
||||
|
||||
Two things worth noting against Series III:
|
||||
|
||||
- Series III erases **everything** (`0xA3 → 0x1C → 0x06 → 0xA2`). Series IV
|
||||
deletes **one named event**, which is strictly safer — a receiver can delete
|
||||
exactly what it has confirmed it stored.
|
||||
- The opcodes are different. Do not reach for `0xA3`/`0xA2` here.
|
||||
|
||||
⚠ `SUB 0x06` (storage range) read **identically before and after** the delete in
|
||||
this capture, so it is not a quick way to confirm a deletion landed. Re-walk the
|
||||
event list instead.
|
||||
|
||||
### ✅ ACH config — `0x2C` / `0x7E` / `0x7F`, exactly Series III
|
||||
|
||||
Thor enabling then disabling Auto Call Home, twice through the same sequence:
|
||||
|
||||
```
|
||||
POLL → 0x15 → 0x49 → POLL → 0x2C (read) → 0x7E (write, 126 B) → 0x7F (confirm)
|
||||
```
|
||||
|
||||
Same SUBs and the same acks as Series III — `0x2C`→`0xD3`, `0x7E`→`0x81`,
|
||||
`0x7F`→`0x80`. The write payload is **126 bytes** with `offset = 0x007E`, and
|
||||
the `0x2C` read returns those same 126 bytes behind an **11-byte prefix**, so
|
||||
`read[n + 11]` is `write[n]`.
|
||||
|
||||
**The enable flag is `write[5]`** (= `read[16]`):
|
||||
|
||||
| value | state |
|
||||
|---|---|
|
||||
| `0x05` | Auto Call Home **enabled** |
|
||||
| `0x04` | **disabled** |
|
||||
|
||||
Bit 0 is the enable — the same bit position Series III uses at `raw[5]`, but with
|
||||
bit 2 set as well in both states. Do not test for `0x01`/`0x00`.
|
||||
|
||||
`write[6:]` holds the dial string, null-padded — `"RADIO RING"` on this unit,
|
||||
matching Series III's `raw[6:46]`.
|
||||
|
||||
⚠ **`write[118:120]` changed on its own between the two sessions** (`43 23` →
|
||||
`2e 5e`) without the operator touching anything. Thor reads it and writes back
|
||||
whatever it read. Its meaning is unknown — plausibly a counter, timer or
|
||||
session value. **Round-trip this field; never synthesise it.** `write[117]` is
|
||||
`0x03`, which lines up with Series III's `num_retries` of 3, and `write[120:122]`
|
||||
is `00 3c` = 60.
|
||||
|
||||
⚠ Beyond the enable byte and the dial string, **the field map is not
|
||||
established** — only one setting was varied. Series III's offsets are a starting
|
||||
hypothesis, not a transfer.
|
||||
|
||||
## What the THOR manual settles about the ACH session (2026-09-24)
|
||||
|
||||
Source: `manuals/723U0201 THOR Operator Manual Rev 08.pdf` §6.2, §3.6.
|
||||
@@ -1776,11 +1918,19 @@ because Thor performed it while we recorded. Observed is not the same as
|
||||
exercised — **we have still never originated a write frame.** The wire format
|
||||
is known; our encoder is unwritten and unproven.
|
||||
|
||||
- **Writes** (`0x68`–`0x83`) — format now known, never sent by us
|
||||
- **Call-home write** (`0x7E` / `0x7F`) — not observed at all
|
||||
- **Erase** (`0xA3` / `0xA2`) — **the only genuinely untouched destructive path**
|
||||
- **Writes** (`0x68`–`0x83`) — format known, never sent by us
|
||||
- **Call-home write** (`0x7E` / `0x7F`) — ✅ observed 2026-09-25, acks `0x81` /
|
||||
`0x80`; enable flag isolated
|
||||
- **Per-event delete** (`0xA8` / `0xAA`) — ✅ observed 2026-09-25, acks `0x57` /
|
||||
`0x55`. **Destructive.** Series III's `0xA3`/`0xA2` erase-all has *not* been
|
||||
seen on Series IV and may not exist
|
||||
- **Start / stop monitoring** (`0x96` / `0x97`) — observed via Thor 2026-09-24,
|
||||
acks `0x69` / `0x68`; still never originated by us
|
||||
acks `0x69` / `0x68`
|
||||
- **Scheduler enable** (`0x47`) — observed; `params[7]` semantics still unclear
|
||||
|
||||
Every one of these is now *observed*. **None has ever been originated by us**,
|
||||
and that is the line that still matters: the wire format is known, our encoder is
|
||||
unwritten and unproven.
|
||||
- `0x1F` (advance event pointer) — non-destructive on Series III but it does
|
||||
move device state, so it is parked with the rest
|
||||
|
||||
|
||||
Reference in New Issue
Block a user