Skip to main content

uart-helper

PyPI PyPI Downloads

A cross-platform Python framework for UART/serial port monitoring, command sending, and data receiving. Built on pyserial.

Includes a CLI tool for quick serial port diagnostics, plug/unplug monitoring (JSONL output), text/hex command communication, and TOML-based device profile management.

Installation

% pip install uart-helper

Verify your setup:

% uart-helper --check

CLI Usage

List connected serial ports

% uart-helper

Found 2 port(s):

  /dev/ttyUSB0  aaaa:0001  "USB Serial"  serial=0001  [Temp Sensor]  (role=sensor)
  /dev/ttyS0
% uart-helper --json
{"status": true, "action": "scan", "meta": {...}, "data": [{"device": "/dev/ttyUSB0", "vid": "aaaa", "pid": "0001", ...}]}

Filter devices

% uart-helper --vid aaaa                          # single vendor ID
% uart-helper --vid aaaa --vid bbbb               # multiple vendor IDs
% uart-helper --vid aaaa --pid 0001               # vendor + product ID
% uart-helper --name "USB*"                        # description glob pattern
% uart-helper --device "/dev/ttyUSB*"              # device path glob pattern

Multiple --vid, --pid, --name, and --device values are cross-producted into match rules. For example, --vid aaaa --vid bbbb --pid 0001 creates two rules: aaaa:0001 and bbbb:0001.

Monitor plug/unplug events

% uart-helper --listen

Outputs JSONL (one JSON object per line) — designed for piping into AI agents or automation scripts:

{"status": true, "action": "init", "meta": {...}, "data": [{"device": "/dev/ttyUSB0", "vid": "aaaa", "pid": "0001", ...}]}
{"status": true, "action": "plug", "meta": {...}, "data": [{"device": "/dev/ttyUSB1", "vid": "bbbb", "pid": "0002", ...}]}
{"status": true, "action": "unplug", "meta": {...}, "data": [{"device": "/dev/ttyUSB1", "vid": "bbbb", "pid": "0002", ...}]}
{"status": true, "action": "stop", "meta": {...}, "data": []}

Events: init (startup port list), plug, unplug, error, stop (Ctrl+C).

Combine with filters:

% uart-helper --listen --vid aaaa --interval 1000
% uart-helper --listen --profile sample

Send commands

% uart-helper send --port /dev/ttyUSB0 --crlf "AT"
Sent 4 bytes to /dev/ttyUSB0
Received 4 bytes:
  TEXT: OK
  HEX:  4f 4b 0d 0a
% uart-helper send --port /dev/ttyUSB0 --hex "FF 01 02 03"
Sent 4 bytes to /dev/ttyUSB0
Received 2 bytes:
  HEX: 06 00

Hex input supports multiple formats (case-insensitive):

% uart-helper send --port /dev/ttyUSB0 --hex "FF 01 02"       # space-separated
% uart-helper send --port /dev/ttyUSB0 --hex "ff0102"          # continuous (no spaces)
% uart-helper send --port /dev/ttyUSB0 --hex "Ff:01:aB"        # colon-separated, mixed case
% uart-helper send --port /dev/ttyUSB0 --hex "0xFF 0x01 0x02"  # 0x-prefixed

Specify UART parameters:

% uart-helper send --port /dev/ttyUSB0 --baudrate 9600 --crlf "AT"
% uart-helper send --port /dev/ttyUSB0 --baudrate 9600 --parity E --stopbits 2 --crlf "AT"
% uart-helper send --port /dev/ttyUSB0 --crlf "AT" --json
{"status": true, "action": "send", "meta": {...}, "data": {"port": "/dev/ttyUSB0", "sent_bytes": 4, "received_bytes": 4, "received_hex": "4f 4b 0d 0a", "received_text": "OK\r\n"}}

Interactive serial monitor

% uart-helper monitor --port /dev/ttyUSB0
Monitoring /dev/ttyUSB0 (baud=115200) — Ctrl+C to stop
──────────────────────────────────────────────────
Hello from device...

Screen-style CLI compatibility:

% uart-helper screen -U /dev/tty.usbserial-110 115200
Monitoring /dev/tty.usbserial-110 (baud=115200) — Ctrl+C to stop
──────────────────────────────────────────────────

In screen mode, stdin is key-by-key (no Enter needed) and shortcuts are supported:

Ctrl+A, H   toggle HEX mirror output on/off
Ctrl+A, A   send a literal Ctrl+A byte (0x01)

HEX mirror can include line number and datetime:

% uart-helper screen -U /dev/tty.usbserial-110 115200 --datetime-format "%Y-%m-%d_%H:%M:%S"
# Example HEX mirror line:
[HEX #000123 2026-03-26_22:45:10] 48 65 6c 6c 6f

Single-file debug logging:

% uart-helper screen -U /dev/tty.usbserial-110 115200 --log-file /tmp/uart-debug.log
# log line format:
# ts=2026-03-26T14:45:10.123456+00:00 bytes=5 hex=48 65 6c 6c 6f text=Hello\r\n
% uart-helper monitor --port /dev/ttyUSB0 --hex
[2026-03-26T12:00:00+00:00] 48 65 6c 6c 6f
% uart-helper monitor --port /dev/ttyUSB0 --output-hex
48 65 6c 6c 6f
% uart-helper monitor --port /dev/ttyUSB0 --stdin
Monitoring /dev/ttyUSB0 (baud=115200) — Ctrl+C to stop
──────────────────────────────────────────────────
# type into terminal and press Enter to send each line to UART
% uart-helper monitor --port /dev/ttyUSB0 --stdin --output-hex
48 65 6c 6c 6f
% uart-helper monitor --port /dev/ttyUSB0 --output-merge
48 65 6c 6c 6f
Hello
% uart-helper monitor --port /dev/ttyUSB0 --stdin --output-file /tmp/output.log --output-file-hex /tmp/output.hex
# stdout keeps printing, while text/hex are also appended to files
% uart-helper monitor --port /dev/ttyUSB0 --output-file-hex-mixed /tmp/output_mixed.log
# each chunk appends:
#   line 1: hex
#   line 2: text/binary
% uart-helper monitor --port /dev/ttyUSB0 --json
{"status": true, "action": "monitor_start", "meta": {...}, "data": {"port": "/dev/ttyUSB0", "baudrate": 115200}}
{"status": true, "action": "data", "meta": {...}, "data": {"timestamp": "...", "bytes": 5, "hex": "48 65 6c 6c 6f", "text": "Hello"}}

Error output

When pyserial is missing, all modes emit a structured error:

{"status": false, "action": "error", "meta": {...}, "error": -1, "errorMessage": "pyserial is not installed. Install with: pip install pyserial"}

Device Profiles

Profiles are TOML files that define named sets of port match rules and default UART settings. Instead of typing --vid, --pid, and --baudrate every time, save your device definitions once and reference them by name.

Profile search directories (first match wins)

  1. ./uart-helper.d/ — current working directory (project-level)
  2. ~/.config/uart-helper/ — user config (shared across projects)

Create a profile

% mkdir -p ~/.config/uart-helper
% cat > ~/.config/uart-helper/sample.toml << 'EOF'
description = "My UART devices"

[defaults]
baudrate = 115200
bytesize = 8
parity = "N"
stopbits = 1
timeout = 1.0

[[rules]]
vid = "aaaa"
pid = "0001"
label = "Temp Sensor"
[rules.metadata]
role = "sensor"

[[rules]]
vid = "bbbb"
pid = "0002"
label = "Motor Controller"
[rules.metadata]
role = "controller"
EOF

Each [[rules]] entry supports these optional fields:

Field Description Example
vid USB Vendor ID (hex string) "aaaa"
pid USB Product ID (hex string) "0001"
label Human-readable rule name "Temp Sensor"
name Port description glob pattern "USB*"
serial Serial number glob pattern "SN-*"
device Device path glob pattern "/dev/ttyUSB*"
metadata Arbitrary key-value pairs {role = "sensor"}

The [defaults] section sets UART parameters for the profile:

Field Description Default
baudrate Baud rate 115200
bytesize Data bits (5, 6, 7, 8) 8
parity Parity ("N", "E", "O", "M", "S") "N"
stopbits Stop bits (1, 1.5, 2) 1
timeout Read timeout in seconds 1.0

Use a profile

% uart-helper --profile sample
% uart-helper --profile sample --listen
% uart-helper --profile sample --json
% uart-helper send --port /dev/ttyUSB0 --profile sample --crlf "AT"

Or specify a TOML file directly:

% uart-helper --config ./my-devices.toml

Profile rules and CLI flags (--vid, --pid, --name, --device) are merged — you can add extra filters on top of a profile.

List available profiles

% uart-helper profiles

Config directories (search order):
  ./uart-helper.d
  /Users/you/.config/uart-helper

Available profiles (1):

  sample
    My UART devices
    2 rule(s), baud=115200 — /Users/you/.config/uart-helper/sample.toml
% uart-helper profiles --json
{"status": true, "action": "profiles", "meta": {...}, "data": {"config_dirs": [...], "profiles": [{"name": "sample", ...}]}}

Project-level profiles

Place TOML files in uart-helper.d/ in your project root. These take priority over user-level profiles with the same name:

my-project/
  uart-helper.d/
    my-devices.toml      ← project-specific config
  src/
    main.py

Python API

Port monitoring

from uart_helper import SerialMonitor, PortMatchRule

rules = [
    PortMatchRule(vid=0xAAAA, pid=0x0001, label="Temp Sensor"),
    PortMatchRule(vid=0xBBBB, pid=0x0002, label="Motor Controller"),
]

monitor = SerialMonitor(match_rules=rules, poll_interval_ms=500)

# One-time scan
for identity, rule in monitor.scan_once():
    print(f"Found: {identity} (rule: {rule.label})")

# Continuous monitoring
monitor.on_port_event = lambda event: print(f"[{event.event_type.value}] {event.port}")
monitor.run_forever()  # Ctrl+C to stop

UART communication

from uart_helper import UARTDevice, PortIdentity, UARTConfig

identity = PortIdentity(device="/dev/ttyUSB0")
config = UARTConfig(baudrate=115200)

with UARTDevice(identity, config) as dev:
    # Send text command (e.g., AT command)
    result = dev.send_command("AT\r\n")
    print(result.text)

    # Send hex bytes
    result = dev.send_hex("FF 01 02 03")
    if result.ok:
        print(f"Received {len(result.data)} bytes: {result.data.hex()}")

    # Low-level write + read
    dev.write(b"\x01\x02\x03")
    result = dev.read(1024, timeout_ms=2000)
    print(result.data)

    # Read until terminator
    result = dev.read_until(terminator=b"\r\n")
    print(result.text)

Load profiles programmatically

from uart_helper import load_profile, load_profile_by_name, list_profiles

# By name (searches config dirs)
profile = load_profile_by_name("sample")
print(f"Default baud: {profile.defaults.baudrate}")

# By path
profile = load_profile("./my-devices.toml")

# List all
for p in list_profiles():
    print(f"{p.name}: {p.description} ({p.rule_count} rules)")

# Use profile rules with SerialMonitor
monitor = SerialMonitor(match_rules=profile.rules)

Runtime metadata

from uart_helper import get_meta

meta = get_meta()
# {"uart_helper": "1.0.0", "python": "3.12.3", "platform": "...", "os": "Darwin", "arch": "arm64", "pyserial": "3.5"}

Running Tests

Tests are fully mock-based — no real serial hardware needed.

% git clone https://github.com/changyy/py-uart-helper.git
% cd py-uart-helper
% pip install -e ".[dev]"
% python -m pytest tests/ -v

To run with coverage:

% python -m pytest tests/ --cov=uart_helper --cov-report=term-missing

Project Structure

py-uart-helper/
  src/uart_helper/
    __init__.py         Package exports
    types.py            PortIdentity, PortMatchRule, UARTConfig, TransferResult, PortEvent
    device.py           SerialDevice abstract base class
    uart_device.py      UARTDevice — pyserial wrapper with text/hex send/receive
    monitor.py          SerialMonitor — polling-based attach/detach detection
    config.py           TOML profile loader with UART defaults
    cli.py              CLI entry point (uart-helper command)
  tests/
  examples/
    sample.toml         Sample device profile
  pyproject.toml
  README.md

Requirements

  • Python 3.10+
  • pyserial >= 3.5

  • py-usb-helper — USB device monitoring and bulk/SCSI communication

License

MIT © Yuan-Yi Chang

Metadata

Release files for uart-helper 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for uart-helper 1.0.1
File Size Uploaded Provenance
uart_helper-1.0.1.tar.gz 35.6 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for uart-helper 1.0.1
File Interpreter ABI Platform
uart_helper-1.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 65.3 kB

Release files / uart_helper-1.0.1.tar.gz

Download URL uart_helper-1.0.1.tar.gz
Size 35.6 kB
Tags Source
SHA-256 checksum
How to use checksums
4f913f8f15adb034617ebe8a7f3b8d265d86b27c1972947fe556db49cd330802
BLAKE2b-256 checksum
How to use checksums
715f61b53e3fd747c949af0a404a1d7d708cd722d7f22360b33fec09077473c6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 26, 2026.

Transparency log

Release files / uart_helper-1.0.1-py3-none-any.whl

Download URL uart_helper-1.0.1-py3-none-any.whl
Size 29.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ec0619383b4a9a42600eb17f59f20f0955493e7318b9552a0ae4c5a41809bd94
BLAKE2b-256 checksum
How to use checksums
4a0f0b3a502f61fccc82f9130ce967cba3a2ee7ab1f5035732d40cf05b7a2993
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 26, 2026.

Transparency log

Security for uart-helper 1.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for uart_helper-1.0.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Mar 26, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page