This commit is contained in:
Aiden
2026-05-13 16:54:41 +10:00
parent 8cc9bb4cd8
commit 4c97f0aae9
4 changed files with 144 additions and 1 deletions

View File

@@ -0,0 +1,39 @@
Button test on COM5 at 38400 8N1
Listening for 15.0s; respond_to_cam_power=False, respond_to_call=True, mirror_call=False
16:48:44.407 RX 001 bytes 00
16:48:44.439 RX 005 bytes 00 00 00 80 DA
16:48:45.016 RX 006 bytes 00 00 15 80 00 CF
16:48:45.016 DETECT call-on x1
16:48:45.068 TX button response frame 006 00 00 15 80 00 CF
16:48:45.118 TX button response frame 006 00 00 15 00 00 4F
16:48:45.149 RX 006 bytes 07 80 45 20 D0 68
16:48:45.149 DETECT watch-frame 07 80 45 20 D0 68 x1
16:48:45.200 TX watch follow-up frame 006 00 00 00 00 80 DA
16:48:45.202 TX watch follow-up frame 006 00 00 B5 00 80 6F
16:48:47.216 RX 001 bytes 00
16:48:47.246 RX 005 bytes 00 00 00 80 DA
16:48:47.916 RX 001 bytes 00
16:48:47.946 RX 005 bytes 00 00 00 80 DA
16:48:48.619 RX 001 bytes 00
16:48:48.650 RX 005 bytes 00 00 00 80 DA
16:48:49.323 RX 006 bytes 00 00 00 00 80 DA
16:48:49.323 DETECT heartbeat x1
16:48:50.024 RX 006 bytes 00 00 00 00 80 DA
16:48:50.024 DETECT heartbeat x1
16:48:50.725 RX 006 bytes 00 00 00 00 80 DA
16:48:50.725 DETECT heartbeat x1
16:48:51.429 RX 006 bytes 00 00 00 00 80 DA
16:48:51.429 DETECT heartbeat x1
16:48:52.130 RX 006 bytes 00 00 00 00 80 DA
16:48:52.130 DETECT heartbeat x1
16:48:52.831 RX 006 bytes 00 00 00 00 80 DA
16:48:52.831 DETECT heartbeat x1
16:48:53.530 RX 006 bytes 00 00 00 00 80 DA
16:48:53.530 DETECT heartbeat x1
16:48:54.233 RX 006 bytes 00 00 00 00 80 DA
16:48:54.233 DETECT heartbeat x1
16:48:54.935 RX 006 bytes 00 00 00 00 80 DA
16:48:54.935 DETECT heartbeat x1
16:48:55.607 RX 001 bytes 00
16:48:55.637 RX 005 bytes 00 00 00 80 DA
Stopped.

View File

@@ -3196,3 +3196,83 @@ python scripts/serial_button_response_test.py --port COM5 --duration 15 --prompt
If this produces the known `B5` response `07 80 6D 20 D8 48`, then the CALL If this produces the known `B5` response `07 80 6D 20 D8 48`, then the CALL
path does not consume the one-shot discovery response. If it returns heartbeat path does not consume the one-shot discovery response. If it returns heartbeat
only, CALL/`0x45` may put the RCP into a similar one-shot consumed state. only, CALL/`0x45` may put the RCP into a similar one-shot consumed state.
### 2026-05-13 CALL `0x45` Then Discovery Result
Capture:
- `captures/rcp-buttons-call-45-followup-discovery-b5.txt`
Observed sequence:
```text
RCP CALL high: 00 00 15 80 00 CF
Host CALL high echo: 00 00 15 80 00 CF
Host CALL low echo: 00 00 15 00 00 4F
RCP CALL response: 07 80 45 20 D0 68
Host primer: 00 00 00 00 80 DA
Host B5 query: 00 00 B5 00 80 6F
```
Result:
- After the follow-up `00 -> B5` query, the RCP returned heartbeat-compatible
traffic only.
- The known `B5` response `07 80 6D 20 D8 48` did not appear.
Interpretation:
- The CALL/`0x45` path does not unlock the known discovery query.
- It may consume or bypass the same cold one-shot discovery window, or the RCP
may simply ignore discovery-style queries once the CALL event path has been
exercised.
- This pushes the CALL path into the "useful diagnostic but probably not the
activation handshake" bucket.
### Cold No-Button CALL Injection Tests
Question: have we tried sending the CALL response frames without first pressing
the `CALL` button?
Answer: partially, but not in the exact form that now matters.
- Earlier command `0x15` matrix tests sent individual `0x15` frames from a cold
panel and saw `CONNECT NOT ACT`, but no non-heartbeat serial response.
- The newer reproducible `0x45` result depends on sending the CALL-high and
CALL-low frames as a pair with a gap. That exact cold/no-button pair has not
been tested yet.
Tooling note:
- `scripts/serial_button_response_test.py` now supports `--startup-frame`. These
frames are sent automatically after the listen window begins, without waiting
for a physical button event.
Test C1: cold CALL pair, 50 ms gap, no physical button press.
```powershell
python scripts/serial_button_response_test.py --port COM5 --duration 12 --prompt --startup-delay 1.0 --startup-frame-interval 0.05 --startup-frame "00 00 15 80 00 CF" --startup-frame "00 00 15 00 00 4F" --watch-frame "07 80 45 20 D0 68" --watch-frame "07 80 45 30 D0 78" --log captures/rcp-buttons-cold-call-pair-gap-50ms.txt
```
Test C2: cold CALL pair, 80 ms gap, no physical button press.
```powershell
python scripts/serial_button_response_test.py --port COM5 --duration 12 --prompt --startup-delay 1.0 --startup-frame-interval 0.08 --startup-frame "00 00 15 80 00 CF" --startup-frame "00 00 15 00 00 4F" --watch-frame "07 80 45 20 D0 68" --watch-frame "07 80 45 30 D0 78" --log captures/rcp-buttons-cold-call-pair-gap-80ms.txt
```
Test C3: cold CALL high only, no physical button press.
```powershell
python scripts/serial_button_response_test.py --port COM5 --duration 12 --prompt --startup-delay 1.0 --startup-frame "00 00 15 80 00 CF" --watch-frame "07 80 45 20 D0 68" --watch-frame "07 80 45 30 D0 78" --log captures/rcp-buttons-cold-call-high-only.txt
```
Test C4: cold CALL low only, no physical button press.
```powershell
python scripts/serial_button_response_test.py --port COM5 --duration 12 --prompt --startup-delay 1.0 --startup-frame "00 00 15 00 00 4F" --watch-frame "07 80 45 20 D0 68" --watch-frame "07 80 45 30 D0 78" --log captures/rcp-buttons-cold-call-low-only.txt
```
For each test, power-cycle first and do not press any panel buttons. If C1/C2
produce `0x45`, the host can synthesize the CALL event path. If they do not,
the RCP's own physical CALL transition is required before the echo pair has
meaning.

View File

@@ -8,6 +8,7 @@ This helper can:
3. Optionally transmit response frames when CAM POWER or CALL is observed. 3. Optionally transmit response frames when CAM POWER or CALL is observed.
4. Optionally mirror CALL high/low events with matching host responses. 4. Optionally mirror CALL high/low events with matching host responses.
5. Optionally transmit follow-up frames when a watched response frame appears. 5. Optionally transmit follow-up frames when a watched response frame appears.
6. Optionally transmit startup frames without waiting for a button event.
Known RCP-origin button frames: Known RCP-origin button frames:
@@ -175,6 +176,19 @@ def parse_args() -> argparse.Namespace:
help="hex frame to send after a watched frame appears; can be repeated", help="hex frame to send after a watched frame appears; can be repeated",
) )
parser.add_argument("--followup-delay", type=float, default=0.05) parser.add_argument("--followup-delay", type=float, default=0.05)
parser.add_argument(
"--startup-frame",
type=parse_hex_bytes,
action="append",
help="hex frame to send after listening starts, without waiting for a button event",
)
parser.add_argument("--startup-delay", type=float, default=0.5)
parser.add_argument(
"--startup-frame-interval",
type=float,
default=0.05,
help="delay between multiple startup frames",
)
parser.add_argument("--response-delay", type=float, default=0.05) parser.add_argument("--response-delay", type=float, default=0.05)
parser.add_argument( parser.add_argument(
"--response-frame-interval", "--response-frame-interval",
@@ -238,6 +252,9 @@ def main() -> int:
emit_known_counts(emit, "LATCH QUERY", read_window(ser, args.after_latch)) emit_known_counts(emit, "LATCH QUERY", read_window(ser, args.after_latch))
if args.prompt: if args.prompt:
if args.startup_frame:
input("Ready to listen. Press Enter; startup frames will be sent automatically: ")
else:
input("Ready to listen. Press Enter, then press CAM POWER/CALL on the RCP: ") input("Ready to listen. Press Enter, then press CAM POWER/CALL on the RCP: ")
ser.reset_input_buffer() ser.reset_input_buffer()
@@ -249,6 +266,13 @@ def main() -> int:
) )
stop_at = time.monotonic() + args.duration stop_at = time.monotonic() + args.duration
buffer = bytearray() buffer = bytearray()
if args.startup_frame:
time.sleep(args.startup_delay)
for frame_index, frame in enumerate(args.startup_frame):
if frame_index:
time.sleep(args.startup_frame_interval)
send_frame(ser, emit, "startup", frame)
while time.monotonic() < stop_at: while time.monotonic() < stop_at:
data = ser.read(args.chunk_size) data = ser.read(args.chunk_size)
if not data: if not data: