responses
This commit is contained in:
118
scripts/serial_send_frame.py
Normal file
118
scripts/serial_send_frame.py
Normal file
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Send one Sony RCP-TX7 candidate serial frame.
|
||||
|
||||
Use carefully. This is for controlled experiments on the host-to-RCP line
|
||||
through an RS-232 adapter. It can either send a complete hex frame or build the
|
||||
observed 6-byte frame shape and checksum from fields.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import time
|
||||
|
||||
try:
|
||||
import serial
|
||||
except ImportError:
|
||||
print(
|
||||
"Missing dependency: pyserial\n"
|
||||
"Install it with: python -m pip install pyserial",
|
||||
file=sys.stderr,
|
||||
)
|
||||
raise SystemExit(2)
|
||||
|
||||
|
||||
def parse_hex_bytes(text: str) -> bytes:
|
||||
normalized = text.replace(",", " ").replace("0x", "").replace("0X", "")
|
||||
parts = normalized.split()
|
||||
try:
|
||||
data = bytes(int(part, 16) for part in parts)
|
||||
except ValueError as exc:
|
||||
raise argparse.ArgumentTypeError(f"invalid hex byte list: {text}") from exc
|
||||
if not data:
|
||||
raise argparse.ArgumentTypeError("hex frame cannot be empty")
|
||||
if any(part and int(part, 16) > 0xFF for part in parts):
|
||||
raise argparse.ArgumentTypeError("hex values must be bytes")
|
||||
return data
|
||||
|
||||
|
||||
def build_frame(prefix1: int, prefix2: int, command: int, state: int, value: int) -> bytes:
|
||||
body = bytes([prefix1, prefix2, command, state, value])
|
||||
checksum = 0x5A
|
||||
for byte in body:
|
||||
checksum ^= byte
|
||||
return body + bytes([checksum])
|
||||
|
||||
|
||||
def hex_preview(data: bytes) -> str:
|
||||
return " ".join(f"{byte:02X}" for byte in data)
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Send an explicit or checksum-built 6-byte RCP-TX7 frame."
|
||||
)
|
||||
parser.add_argument("--port", required=True, help="serial port, for example COM3")
|
||||
parser.add_argument("--baud", type=int, default=38400)
|
||||
parser.add_argument(
|
||||
"--frame",
|
||||
type=parse_hex_bytes,
|
||||
help="complete hex frame, for example \"00 00 00 00 80 DA\"",
|
||||
)
|
||||
parser.add_argument("--prefix1", type=lambda s: int(s, 0), default=0)
|
||||
parser.add_argument("--prefix2", type=lambda s: int(s, 0), default=0)
|
||||
parser.add_argument("--command", type=lambda s: int(s, 0), default=0)
|
||||
parser.add_argument("--state", type=lambda s: int(s, 0), default=0)
|
||||
parser.add_argument("--value", type=lambda s: int(s, 0), default=0x80)
|
||||
parser.add_argument("--repeat", type=int, default=1)
|
||||
parser.add_argument("--interval", type=float, default=0.2)
|
||||
parser.add_argument(
|
||||
"--dry-run",
|
||||
action="store_true",
|
||||
help="print the frame but do not open the serial port",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def validate_byte(name: str, value: int) -> None:
|
||||
if not 0 <= value <= 0xFF:
|
||||
raise SystemExit(f"{name} must be a byte, got {value!r}")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
if args.frame:
|
||||
frame = args.frame
|
||||
else:
|
||||
for name in ("prefix1", "prefix2", "command", "state", "value"):
|
||||
validate_byte(name, getattr(args, name))
|
||||
frame = build_frame(args.prefix1, args.prefix2, args.command, args.state, args.value)
|
||||
|
||||
print(f"Frame: {hex_preview(frame)}")
|
||||
if args.dry_run:
|
||||
return 0
|
||||
|
||||
with serial.Serial(
|
||||
port=args.port,
|
||||
baudrate=args.baud,
|
||||
bytesize=serial.EIGHTBITS,
|
||||
parity=serial.PARITY_NONE,
|
||||
stopbits=serial.STOPBITS_ONE,
|
||||
timeout=0.2,
|
||||
write_timeout=1.0,
|
||||
rtscts=False,
|
||||
dsrdtr=False,
|
||||
xonxoff=False,
|
||||
) as ser:
|
||||
for index in range(args.repeat):
|
||||
ser.write(frame)
|
||||
ser.flush()
|
||||
print(f"Sent {index + 1}/{args.repeat}")
|
||||
if index + 1 < args.repeat:
|
||||
time.sleep(args.interval)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user