mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-13 07:48:30 +00:00
changes for serial monitoring
This commit is contained in:
parent
00807ae986
commit
d20a0f172a
3 changed files with 129 additions and 10 deletions
|
|
@ -51,11 +51,17 @@ def main():
|
|||
|
||||
# monitor
|
||||
m = sub.add_parser("monitor", help="Open a serial monitor")
|
||||
m.add_argument("-p", "--port", required=True)
|
||||
m.add_argument("-b", "--baud", type=int, default=115200)
|
||||
m.add_argument("--ts", action="store_true", help="Show timestamps")
|
||||
m.add_argument("-p", "--port", required=True, help="Serial port (e.g., COM5, /dev/ttyUSB0)")
|
||||
m.add_argument("-b", "--baud", type=int, default=115200, help="Baud rate")
|
||||
m.add_argument("--ts", action="store_true", help="Show timestamps on received lines")
|
||||
m.add_argument("--save", help="Append output to a file")
|
||||
m.add_argument("--reset", action="store_true", help="Toggle DTR/RTS on open")
|
||||
m.add_argument("--crlf", action="store_true", help="Use CRLF as TX line ending in line mode (default LF)")
|
||||
m.add_argument("--raw", action="store_true", help="Send keystrokes immediately (raw mode)")
|
||||
m.add_argument("--no-echo", action="store_true", help="Do not locally echo typed input")
|
||||
m.add_argument("--rtscts", action="store_true", help="Enable RTS/CTS hardware flow control")
|
||||
m.add_argument("--xonxoff", action="store_true", help="Enable XON/XOFF software flow control")
|
||||
m.add_argument("--dsrdtr", action="store_true", help="Enable DSR/DTR hardware flow control")
|
||||
m.set_defaults(func=cmd_monitor)
|
||||
|
||||
# build (Xous via cargo xtask)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,78 @@
|
|||
import sys
|
||||
import time
|
||||
import logging
|
||||
import threading
|
||||
from serial.serialutil import SerialException
|
||||
from utils.serial_utils import open_serial, safe_close
|
||||
|
||||
def _stdin_to_serial(ser, args, stop_event: threading.Event):
|
||||
"""
|
||||
Writer thread: read from stdin and write to serial.
|
||||
- Line mode (default): blocks waiting for a line, appends LF or CRLF.
|
||||
- Raw mode (--raw): sends characters immediately without waiting for newline.
|
||||
"""
|
||||
try:
|
||||
if getattr(args, "raw", False):
|
||||
# Raw mode: read larger chunks for smooth pastes when available
|
||||
while not stop_event.is_set():
|
||||
chunk = (
|
||||
sys.stdin.buffer.read1(4096)
|
||||
if hasattr(sys.stdin.buffer, "read1")
|
||||
else sys.stdin.buffer.read(1)
|
||||
)
|
||||
if not chunk:
|
||||
break # EOF
|
||||
try:
|
||||
ser.write(chunk)
|
||||
ser.flush()
|
||||
except SerialException:
|
||||
break
|
||||
if not getattr(args, "no_echo", False):
|
||||
# Local echo (avoid double-echo if target already echoes)
|
||||
try:
|
||||
sys.stdout.write(chunk.decode(errors="replace"))
|
||||
sys.stdout.flush()
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
# Line mode: read a full line, normalize line ending
|
||||
tx_eol = b"\r\n" if getattr(args, "crlf", False) else b"\n"
|
||||
while not stop_event.is_set():
|
||||
line = sys.stdin.buffer.readline()
|
||||
if not line:
|
||||
break # EOF
|
||||
# Strip any trailing \r or \n to avoid doubling endings
|
||||
line = line.rstrip(b"\r\n")
|
||||
payload = line + tx_eol
|
||||
try:
|
||||
ser.write(payload)
|
||||
ser.flush()
|
||||
except SerialException:
|
||||
break
|
||||
if not getattr(args, "no_echo", False):
|
||||
try:
|
||||
# Echo what we sent, as a single line locally
|
||||
sys.stdout.write(line.decode(errors="replace") + ("\r\n" if tx_eol == b"\r\n" else "\n"))
|
||||
sys.stdout.flush()
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logging.debug(f"[bao] stdin writer thread ended: {e}")
|
||||
finally:
|
||||
stop_event.set()
|
||||
|
||||
def cmd_monitor(args) -> None:
|
||||
ser = open_serial(args.port, args.baud, timeout=0.1, reset=getattr(args, "reset", False))
|
||||
# Open with flow-control / write-timeout if provided
|
||||
ser = open_serial(
|
||||
args.port,
|
||||
args.baud,
|
||||
timeout=0.1,
|
||||
reset=getattr(args, "reset", False),
|
||||
rtscts=getattr(args, "rtscts", False),
|
||||
xonxoff=getattr(args, "xonxoff", False),
|
||||
dsrdtr=getattr(args, "dsrdtr", False),
|
||||
write_timeout=getattr(args, "write_timeout", 1.0),
|
||||
)
|
||||
outf = None
|
||||
if getattr(args, "save", None):
|
||||
try:
|
||||
|
|
@ -14,18 +80,60 @@ def cmd_monitor(args) -> None:
|
|||
except Exception as e:
|
||||
logging.error(f"[bao] cannot open --save file: {e}")
|
||||
safe_close(ser)
|
||||
|
||||
return
|
||||
print(f"[bao] Monitor {args.port} @ {args.baud} (Ctrl+C to exit)")
|
||||
|
||||
# Initial line states (optional)
|
||||
if getattr(args, "dtr", None) is not None:
|
||||
try:
|
||||
ser.dtr = bool(args.dtr)
|
||||
except Exception:
|
||||
pass
|
||||
if getattr(args, "rts", None) is not None:
|
||||
try:
|
||||
ser.rts = bool(args.rts)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Optional flush on connect
|
||||
if not getattr(args, "no_flush", False):
|
||||
try:
|
||||
ser.reset_input_buffer()
|
||||
ser.reset_output_buffer()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Optional BREAK at start
|
||||
if getattr(args, "break_ms", 0) > 0:
|
||||
try:
|
||||
ser.send_break(duration=args.break_ms / 1000.0)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
print(f"[bao] Monitor {args.port} @ {args.baud} — interactive (Ctrl+C to exit)")
|
||||
mode = "RAW" if getattr(args, "raw", False) else ("LINE CRLF" if getattr(args, "crlf", False) else "LINE LF")
|
||||
echo = "OFF" if getattr(args, "no_echo", False) else "ON"
|
||||
ts = "ON" if getattr(args, "ts", False) else "OFF"
|
||||
fc = ",".join(n for n, on in [
|
||||
("RTS/CTS", getattr(args, "rtscts", False)),
|
||||
("XON/XOFF", getattr(args, "xonxoff", False)),
|
||||
("DSR/DTR", getattr(args, "dsrdtr", False)),
|
||||
] if on) or "none"
|
||||
print(f"[bao] RX ts:{ts} TX:{mode} Echo:{echo} Flow:{fc}")
|
||||
|
||||
consecutive_errors = 0
|
||||
stop_event = threading.Event()
|
||||
|
||||
# Start stdin→serial writer thread
|
||||
writer = threading.Thread(target=_stdin_to_serial, args=(ser, args, stop_event), daemon=True)
|
||||
writer.start()
|
||||
|
||||
try:
|
||||
while True:
|
||||
while not stop_event.is_set():
|
||||
try:
|
||||
data = ser.read(4096)
|
||||
if data:
|
||||
s = data.decode(errors="replace")
|
||||
if args.ts:
|
||||
if getattr(args, "ts", False):
|
||||
ts = time.strftime("%H:%M:%S")
|
||||
s = "".join(f"[{ts}] {line}\n" for line in s.splitlines())
|
||||
sys.stdout.write(s)
|
||||
|
|
@ -44,6 +152,11 @@ def cmd_monitor(args) -> None:
|
|||
except KeyboardInterrupt:
|
||||
pass
|
||||
finally:
|
||||
try:
|
||||
stop_event.set()
|
||||
writer.join(timeout=0.5)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
if outf:
|
||||
outf.flush()
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import serial
|
|||
from serial.serialutil import SerialException
|
||||
|
||||
|
||||
def open_serial(port: str, baud: int, *, timeout: float = 0.1, reset: bool = False) -> serial.Serial:
|
||||
def open_serial(port: str, baud: int, *, timeout: float = 0.1, reset: bool = False, **kwargs) -> serial.Serial:
|
||||
try:
|
||||
ser = serial.Serial(port, baud, timeout=timeout)
|
||||
ser = serial.Serial(port, baud, timeout=timeout, **kwargs)
|
||||
except Exception as e:
|
||||
raise SerialException(f"cannot open {port}: {e}")
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue