diff --git a/docs/pt2-protocol.md b/docs/pt2-protocol.md index 5b47aa4..c7ebee7 100644 --- a/docs/pt2-protocol.md +++ b/docs/pt2-protocol.md @@ -611,6 +611,68 @@ This fits the real panel behavior: - Correct fake-CCU traffic can wake it to `CONNECT: OK`. - Without richer CCU state, readouts illuminate but show placeholders like `----`. +## Active-State Local-Control Watch + +Bench evidence now suggests `CONNECT: OK` is maintained by an ongoing CCU refresh stream, not by one magic wake frame. This creates a useful local-control test: keep the RCP in the active state and press/turn physical controls while logging device TX. + +Two JSON scenarios are set up for that: + +```powershell +.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\active-control-report-watch-quiet.json --parity E --log captures\active-control-report-watch-quiet.txt --result-json captures\active-control-report-watch-quiet-result.json +.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\active-control-report-watch-broad.json --parity E --log captures\active-control-report-watch-broad.txt --result-json captures\active-control-report-watch-broad-result.json +.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\active-control-report-watch-gated.json --parity E --log captures\active-control-report-watch-gated.txt --result-json captures\active-control-report-watch-gated-result.json +``` + +The quiet scenario sends the selector-zero OK seed, then refreshes `E000[0x0093]=0x9020` every 0.60 s. This is the lowest-noise active hold that has already kept `CONNECT: OK` alive. + +The broad scenario streams `E000[0x008F]=0x1800` and `E000[0x0093]=0xFFFF` every cycle. This is noisier, but may open more local-control/status gates. + +The gated scenario first seeds candidate secondary-table feature bits with command 6: + +```text +06 00 15 80 00 C9 ; E400[0x0015] OTHERS/COPY visibility candidate +06 01 0F 18 00 4A ; E400[0x008F] shutter/OTHERS bits 11+12 +06 01 13 FF FF 4E ; E400[0x0093] broad local-report/status gate candidate +``` + +Then it streams `E000[0x008F]=0x1800` and `E000[0x0093]=0x90FF`. This is the better next watch if quiet/broad E000-only refreshes do not produce local-control TX. + +After a run, summarize unexpected device frames with: + +```powershell +.\.venv\Scripts\python.exe scripts\serial_scenario_unexpected.py captures\active-control-report-watch-quiet.txt --show-all +.\.venv\Scripts\python.exe scripts\serial_scenario_unexpected.py captures\active-control-report-watch-broad.txt --show-all +.\.venv\Scripts\python.exe scripts\serial_scenario_unexpected.py captures\active-control-report-watch-gated.txt --show-all +``` + +Expected refresh responses are ignored by default: heartbeat, table readbacks, and `02 00 02 00 00 5A`. Any remaining checksum-valid frame is a candidate local-control report. This is where physical button/dial reports should appear if the RCP only talks while the CCU keeps the selector tables active. + +The unexpected-frame summarizer also labels known autonomous button frames so they are not mistaken for newly unlocked controls: + +```text +00 00 15 80 00 CF ; known CALL active report +00 00 15 00 00 4F ; known CALL inactive report +00 00 07 80 00 DD ; known CAM POWER report +``` + +First quiet/broad watch captures on 2026-05-26 showed no unexpected checksum-valid frames in the observed window: + +```text +active-control-report-watch-quiet: 114 detected frames, all expected refresh/heartbeat/readback +active-control-report-watch-broad: 133 detected frames, all expected refresh/heartbeat/readback +``` + +Those runs stopped mid-scenario, so this is not a full negative proof. The practical interpretation is narrower: E000 active-state refresh alone did not make the pressed controls emit visible TX in the watched window. The gated command-6 version is the next ROM-supported test. + +The first gated watch capture produced a new periodic active-state response: + +```text +02 00 04 00 00 5C ; gated 0x0004 transition candidate, seen twice +01 00 04 00 00 5F ; gated 0x0004 active response candidate, seen 328 times +``` + +After treating those as expected gated refresh traffic, the capture had zero novel frames. A direct search found no known CALL/CAM POWER frames in that log. So the gated setup changed the session/status response shape, but it did not yet prove that physical controls beyond the already-known autonomous buttons are reporting. + ## What Is Still Unknown - The official PT2 names for commands and selectors. @@ -635,7 +697,7 @@ This fits the real panel behavior: 5. Dump selector table state before and after CONNECT OK. 6. Seed selectors `0x003`, `0x040`, and `0x0F6` after selector-zero OK and watch lamps/readouts. 7. Mine selector dispatch handlers for known UI text terms: `IRIS`, `GAIN`, `SHUTTER`, `BARS`, `BLACK`, `CALL`, `AUTO`, `DIAG`. -8. Build a fake-CCU streamer that repeatedly writes a small selector set and logs which RCP reports appear. +8. Run the active-control watch scenarios and map any unexpected frames back to physical controls. ## Source Files And Reports diff --git a/docs/pt2-shutter-display-trace.md b/docs/pt2-shutter-display-trace.md index b29f5c5..3383d41 100644 --- a/docs/pt2-shutter-display-trace.md +++ b/docs/pt2-shutter-display-trace.md @@ -152,6 +152,14 @@ These JSON scenarios are set up for the current bench runner and default to `384 .\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\shutter-0093-blackflare-field-groups.json --parity E --log captures\shutter-0093-blackflare-field-groups.txt --result-json captures\shutter-0093-blackflare-field-groups-result.json .\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\shutter-0093-blackflare-field-confirm.json --parity E --log captures\shutter-0093-blackflare-field-confirm.txt --result-json captures\shutter-0093-blackflare-field-confirm-result.json .\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\shutter-0093-blackflare-field-stream-confirm.json --parity E --log captures\shutter-0093-blackflare-field-stream-confirm.txt --result-json captures\shutter-0093-blackflare-field-stream-confirm-result.json +.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\shutter-0093-blackflare-confirm-f020.json --parity E --log captures\shutter-0093-blackflare-confirm-f020.txt --result-json captures\shutter-0093-blackflare-confirm-f020-result.json +-Manual-auto-manual + +.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\shutter-0093-blackflare-confirm-ff20.json --parity E --log captures\shutter-0093-blackflare-confirm-ff20.txt --result-json captures\shutter-0093-blackflare-confirm-ff20-result.json +.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\shutter-0093-blackflare-confirm-9f20.json --parity E --log captures\shutter-0093-blackflare-confirm-9f20.txt --result-json captures\shutter-0093-blackflare-confirm-9f20-result.json +.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\shutter-0093-blackflare-confirm-90ff.json --parity E --log captures\shutter-0093-blackflare-confirm-90ff.txt --result-json captures\shutter-0093-blackflare-confirm-90ff-result.json +.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\shutter-0093-blackflare-confirm-9fff.json --parity E --log captures\shutter-0093-blackflare-confirm-9fff.txt --result-json captures\shutter-0093-blackflare-confirm-9fff-result.json +.\.venv\Scripts\python.exe scripts\serial_scenario.py scenarios\shutter-0093-blackflare-lowbyte-groups.json --parity E --log captures\shutter-0093-blackflare-lowbyte-groups.txt --result-json captures\shutter-0093-blackflare-lowbyte-groups-result.json ``` Run order: @@ -167,6 +175,8 @@ Run order: 9. `shutter-0093-blackflare-field-groups.json`: tests multi-bit field groups after the slow-marked run suggested black/flare AUTO is not one single extra bit over `0x9020`. 10. `shutter-0093-blackflare-field-confirm.json`: shorter, longer-hold confirmation of the broad field groups after the first field-groups run produced three MANUAL->AUTO->MANUAL swaps. 11. `shutter-0093-blackflare-field-stream-confirm.json`: repeats each candidate every 0.60 s after the longer-hold confirmation fell into `CONNECT:NOT ACT`, testing the CCU-refresh model directly. +12. `shutter-0093-blackflare-confirm-*.json`: one-candidate streamed confirmations. Each run includes a known `0xFFFF` AUTO positive control, then brackets one candidate with `0x9020` manual context. After the positive-control section, the expected candidate pattern is MANUAL -> AUTO -> MANUAL -> AUTO -> MANUAL only if that candidate drives black/flare AUTO. +13. `shutter-0093-blackflare-lowbyte-groups.json`: keeps the high byte fixed at `0x90` and varies only the low byte, after `0x90FF` and `0x9FFF` both toggled black/flare AUTO. ## Bench Observations 2026-05-26 @@ -182,7 +192,10 @@ Observed visible effects from the first adjacent-selector run set: | `shutter-0093-blackflare-slow-marked` | held `0x9020`, `0xFFFF`, then alternated `0x9020` with each candidate | white-balance stayed stable, black/flare swapped twice with large pauses; panel stayed `CONNECT: OK` throughout | likely only the manual-reference to `0xFFFF` and `0xFFFF` back to `0x9020` changed black/flare, so AUTO is probably a multi-bit field rather than one added bit | | `shutter-0093-blackflare-field-groups` | held broad grouped values such as `0xF020`, `0xFF20`, `0x9F20`, `0x90FF`, `0x9FFF` | black/flare made three MANUAL->AUTO->MANUAL swaps | broad field combinations can produce black/flare AUTO; use the shorter confirmation run to identify which grouped windows caused the swaps | | `shutter-0093-blackflare-field-confirm` | held `0xFFFF` for 2.8 s, then candidate groups | one MANUAL->AUTO transition at the start, then `CONNECT:NOT ACT`; PRESET white-balance lamp briefly flashed a few times while inactive | a silent 2.8 s hold is too long; traffic must be refreshed, and some lamp latch/update paths can still flash during inactive display cleanup | +| `shutter-0093-blackflare-field-stream-confirm` | streamed every candidate at 0.60 s and saw 148 `02 00 02 00 00 5A` OK-path responses with no resync errors | manual/auto/manual groups were visible, then `CONNECT:NOT ACT` after the stream ended | the stream itself kept OK alive; the final inactive state is likely just the refresh stopping. Candidate isolation needs streamed, one-candidate tests | +| `shutter-0093-blackflare-confirm-f020`, `ff20`, `9f20` | each completed cleanly with 137 OK-path responses and no resync errors | MANUAL->AUTO->MANUAL only | only the built-in `0xFFFF` positive control produced AUTO; these candidates did not drive black/flare AUTO by themselves | +| `shutter-0093-blackflare-confirm-90ff`, `9fff` | each completed cleanly with 137 OK-path responses and no resync errors | black/flare went back and forth a couple of times | `0x90FF` is sufficient to drive the AUTO effect, so the black/flare field is likely in the low byte of `0x0093`; `0x9FFF` is a superset rather than a separate high-byte requirement | | `shutter-008f-evs-clear-control` | `0x008F` writes acknowledged as `0x0800`, `0x0000`, `0x0800` | SHUTTER swapped between EVS and OFF while iris AUTO lamp stayed on | `0x008F` is a packed shutter/display status word; clearing it does not simply mean "blank" | | `shutter-008f-evs-timeout-control` | only `0x008F=0x0800` was sent after OK seed | EVS/iris AUTO, then `CONNECT:NOT ACT` | later blanking is timeout cleanup, not selector-clear evidence | -This changes the local naming: the "adjacent shutter" group should be treated as a broader camera-status and panel-output word cluster. `0x008F` currently covers shutter EVS/OFF plus iris AUTO side effects; `0x0093` covers at least white-balance manual/preset and black/flare manual/auto side effects, and still gates some ROM-observed shutter/clear-scan report lanes. +This changes the local naming: the "adjacent shutter" group should be treated as a broader camera-status and panel-output word cluster. `0x008F` currently covers shutter EVS/OFF plus iris AUTO side effects; `0x0093` covers at least white-balance manual/preset and black/flare manual/auto side effects, and still gates some ROM-observed shutter/clear-scan report lanes. Current black/flare evidence points to `0x0093` high byte `0x90` as enough context, with the low byte selecting the black/flare MANUAL/AUTO state. diff --git a/h8536/bench_connect_lcd.py b/h8536/bench_connect_lcd.py index 0a9d4c4..36d1165 100644 --- a/h8536/bench_connect_lcd.py +++ b/h8536/bench_connect_lcd.py @@ -148,6 +148,11 @@ def label_frame(frame: bytes) -> str: bytes.fromhex("0780C040A0FD"): "visible_40A0_family_C0", bytes.fromhex("07804020902D"): "visible_retry_0040_2090_candidate", bytes.fromhex("0780C060205D"): "visible_C0_6020_family_candidate", + bytes.fromhex("0000158000CF"): "known_call_button_active_report", + bytes.fromhex("00001500004F"): "known_call_button_inactive_report", + bytes.fromhex("0000078000DD"): "known_cam_power_button_report", + bytes.fromhex("01000400005F"): "gated_active_0004_response_candidate", + bytes.fromhex("02000400005C"): "gated_active_0004_transition_candidate", } label = labels.get(frame, "") if label: diff --git a/h8536/serial_scenario_unexpected.py b/h8536/serial_scenario_unexpected.py new file mode 100644 index 0000000..3632bce --- /dev/null +++ b/h8536/serial_scenario_unexpected.py @@ -0,0 +1,136 @@ +from __future__ import annotations + +import argparse +import re +import sys +from collections import Counter +from dataclasses import dataclass +from pathlib import Path +from typing import Iterable, TextIO + + +DETECT_RE = re.compile( + r"^(?P