Pico TeleTYpewriter — an archaic telex reborn as a networked serial console.
PICOTTY is a star-topology serial console for a fleet of Raspberry Pi Pico nodes. Each node plugs into the USB port of a headless machine and becomes a networked serial console with keyboard injection — it reads the target's serial console back over the network and types keystrokes into the machine. One hub (a Raspberry Pi Zero 2 W) coordinates the whole swarm and gives you a single browser dashboard to watch every node and drive it, over your management network.
This is a serial console, not a KVM. There is no video capture. Each node is a serial console with a USB keyboard attached: it reads what the target emits on its serial line and types back into it. Reading output therefore depends on the target actually having a serial console configured.
I built PICOTTY for my own homelab, which runs on a rack of mini PCs. Unlike a server, those boxes expose no accessible serial port — no BMC/IPMI, no DB9/RJ45 console, often not even a usable header — so there's no out-of-band way to see a stuck boot, poke a frozen box, catch a kernel panic, or drive the BIOS without walking over and plugging in a keyboard and monitor. PICOTTY gives every one of those headless machines exactly that, over the network: a tiny Pico on its USB port acts as a USB keyboard (so you can type at BIOS, GRUB, and the OS) and reads back a serial console, all surfaced in one browser dashboard. It's the lights-out management a fleet of consumer mini PCs never shipped with.
One page drives the whole swarm. Click any screenshot to open it at full resolution.

Serial console — live target output (ANSI-cleaned) with the HID ⇄ Serial input toggle

In-app help — every control, its REST endpoint, and the gotchas, built into the hub
- Networked serial consoles — read each target's serial console live and type
into its serial login (
send), all from the browser over the network. - Keyboard injection — each node is also a USB HID keyboard, so it drives the target everywhere a keyboard works: BIOS, GRUB, initramfs, the OS.
- One dashboard — live status, per-node console, keys/chords, macros, bulk commands, and an event log, over HTTP + WebSocket.
- Durable history — every node, command, result, and output chunk is recorded in SQLite on the hub.
- Headless-friendly — LED status codes, opt-in
/error.txt, and a REPL debug path make a node with no monitor diagnosable.
What's new: interactive serial write with a real terminal renderer, per-node
prompt-state badges, a machine up/dead liveness badge (is the target alive,
not just the node), a wait-for-output expect engine and YAML runbooks
(author, view, and edit in the browser), a Reboot-machine menu with three
methods (serial reboot, Ctrl+Alt+Del, Magic SysRq Alt+SysRq+B), custom
quick chords you save and replay, an offline command queue, asciicast
session replay, a raw serial bridge for minicom/PuTTY, webhook/ntfy
alerting, per-node keyboard layouts, and OTA firmware updates (chunked,
checksummed, .zip-upload bundles, canary rollout with watchdog-revert). The hub
now ships as the picotty uv package (uv tool install picotty, with a
picotty.client SDK), and an optional Telegram bot sidecar puts stats, alerts,
and a break-glass terminal on your phone. Depth in
docs/automation.md, docs/operations.md,
docs/ota.md, docs/packaging.md, and
docs/telegram.md.
Per target machine: a Raspberry Pi Pico (or Pico 2) + a WIZnet W5100S Ethernet HAT + a data-capable USB cable + an Ethernet drop. One shared Pi Zero 2 W runs the hub. (The all-in-one W5100S-EVB-Pico replaces the Pico + HAT.)
![]() WIZnet W5100S Ethernet HAT — wired Ethernet over SPI |
![]() Pico + HAT — SPI Ethernet, USB left free for the target |

A node assembled in its 3D-printed enclosure

▶ Open the interactive 3D viewer · or click the STL in the repo for GitHub's built-in 3D viewer
Files in Enclosure Cad/:
STL (slice & print) ·
STEP (neutral CAD) ·
Fusion 360 (.f3d) (editable source)
→ Full bill of materials and topology: docs/hardware.md
The hub ships as the picotty package (uv). Install it as a tool, or run
from source:
# 1. Hub — on the Pi Zero 2 W
uv tool install picotty # picotty-hub + picotty-sim on PATH (or: from source ↓)
picotty-hub # → dashboard at http://<hub-ip>:8080
# from a repo checkout instead:
bash hub/scripts/install.sh # uv sync --extra hub (+ fetches the terminal libs)
bash hub/scripts/install-service.sh # or: bash hub/scripts/run.sh (foreground)
# 2. Node — on your flashing machine (<id> = a node id under private/nodes/)
bash firmware/scripts/install-deps.sh
bash firmware/scripts/build.sh --node <id> --drive /run/media/$USER/CIRCUITPY
# …or stage an artifact to drag on later:
bash firmware/scripts/build.sh --node <id> --stage # -> firmware/build/<id>/ + .zip
# 3. Target — on each managed machine (emits its serial console to the node)
sudo bash target-setup/proxmox-serial.shTest without hardware:
uv run picotty-sim --id <id> --token <TOKEN> # a fake node against the hub
python3 firmware/tools/testhub.py --selftest # a mock hub against a real node
python3 firmware/tools/testhub.py --framecheck # offline firmware framing checks
cd hub && uv run python tests/test_db.py # offline hub db checksYour node ids, hub address, and token live only under private/ (gitignored) —
nothing machine-specific is committed.
The full technical reference lives in docs/:
| Doc | What's inside |
|---|---|
| architecture.md | Component roles, the single-loop hub, the one-cable USB design, the wire protocol, message journeys, data model, repo layout |
| hardware.md | Physical topology, bill of materials, component tree, sizing |
| deployment.md | The three build phases, the end-to-end workflow, the full script reference |
| firmware.md | Firmware lifecycle, LED codes, keyboard layout, OTA capability, hardening, CircuitPython version rules |
| operations.md | Observability, prompt-state badges, HID vs Serial input, console renderer, session recording, serial bridge, alerting |
| automation.md | Prompt-state detection, the expect (wait-for-output) engine, offline command queue, YAML runbooks |
| ota.md | Over-the-wire firmware updates: the safety model and rollout posture |
| packaging.md | The picotty uv package: import surfaces, extras, entry points, the client SDK, and building + publishing to PyPI |
| telegram.md | The Telegram bot sidecar: stats / alerts / gated terminal over chat, and the dashboard credential setup |
| considerations.md | Security boundary, target requirements, power, roadmap |
- CONTRIBUTING.md — dev setup, coding style, the "keep secrets out of the repo" rule
- SECURITY.md — reporting a vulnerability, operator hardening
- CODE_OF_CONDUCT.md — community expectations
- Licensed under the terms in LICENSE.
PICOTTY — a teletype for machines that forgot they had a keyboard.










