A minimal tkinter app for sending and receiving text lines over a serial port.
-
Install uv (one command, works on Windows, macOS and Linux).
-
From this folder:
uv run serial-ui
The first run downloads Python 3.13 and the dependencies into
.venv/. You don't need to install Python or create a venv yourself. -
Leave the port set to
loop://, click Connect, type something and press Enter.loop://is a fake device built into pyserial that sends back whatever you send it.
Plug it in, click Refresh, choose its port (COM3 on Windows, /dev/ttyUSB0 or /dev/tty.usbserial-* on Linux and macOS), set the baud rate and click Connect. You don't need to change any code.
On Linux, if you get "permission denied", add yourself to the serial group and then log out and back in: sudo usermod -aG dialout $USER.
uv run ruff check --fix . && uv run ruff format . && uv run pytestCoding agents: see AGENTS.md.
src/serial_ui/
__main__.py entry point (`uv run serial-ui` or `uv run python -m serial_ui`)
serial_client.py serial I/O and the background reader thread (no UI code)
controller.py SerialController: the UI's single way to talk to the device
ui/
app.py main window: creates the controller and lays out components
connection_bar.py port/baud/connect controls, or status + disconnect when connected
main_panel.py the app's main content area: device features go here
ping_panel.py example feature: send "ping", show the reply and a count
serial_log.py log of sent and received lines
command_bar.py text input and Send button
style.py shared spacing and colours
tests/
test_serial_client.py
test_ui.py
Adding a feature: copy ui/ping_panel.py, which is commented step by step, and add your new component to ui/main_panel.py. A component sends with controller.send(...) and hears back by subscribing to controller.line_received. Replies always arrive as a separate callback, never inside the button's click handler.