printcontrol

A keyboard-driven TUI for monitoring and controlling a 3D printer over USB serial. One static Go binary, zero runtime dependencies, theme-aware via your terminal's 16-colour palette — designed to feel at home on Omarchy alongside wiremix, lazygit, lazydocker, and friends.

The model: the printer is in charge during an SD print, we observe and adjust. Start a print from the LCD, the SD card, or printcontrol's own SD picker — then live-monitor temps, progress, and ETA, and tweak feedrate, flow, fan, hotend / bed setpoints, or babystep Z without ever leaving the keyboard. Quit and come back later; the printer keeps printing and printcontrol rediscovers the in-progress job on reconnect.

┌─ printcontrol  ·  3D printer TUI ────────────────────────────────────────┐
│ ● Status                            │ ● Temperatures                     │
│ Connection: /dev/ttyUSB1 @ 115200   │ Hotend:  201.3 / 210.0 °C          │
│             · Marlin 2.1.2          │ Bed:      60.2 /  60.0 °C          │
│ File:       benchy.gcode  SD PRINT  │                                    │
│ Progress:   [██████████··········]  │   set hotend: [h]                  │
│ ETA:        1h 12m                  │   set bed:    [b]                  │
├─────────────────────────────────────┴────────────────────────────────────┤
│ ● Controls                                                                │
│   Feedrate: 110%  [-/+] [0]   Flow: 100%  [ [ / ] ]   Fan: 30%  [f/F]    │
│   Babystep Z: -0.050  [{ / }] [Z]                                         │
│   press [g] to send raw G-code                                            │
├──────────────────────────────────────────────────────────────────────────┤
│ ● Log                                                                     │
│   > M105                                                                  │
│   < ok T:201.3 /210.0 B:60.2 /60.0 @:127 B@:0                             │
├──────────────────────────────────────────────────────────────────────────┤
│ [c] connect [d] disconnect [s] sd start [p] pause [r] resume [x] cancel  │
│ [h] hotend [b] bed [g] gcode [q] quit                                    │
└──────────────────────────────────────────────────────────────────────────┘

Install

Build from source — needs Go 1.22+. The repo's canonical install target is ~/.local/bin (matches Omarchy's PATH); adjust GOBIN if you want it elsewhere.

cd go
GOBIN=$HOME/.local/bin go install ./cmd/printcontrol

You need permission to open the serial port. On Arch / Omarchy that means membership in the uucp group:

groups | grep -q uucp || sudo usermod -aG uucp "$USER"   # then log out and back in

Verify the printer is visible:

ls /dev/ttyACM* /dev/ttyUSB* 2>/dev/null

Run

printcontrol                          # auto-detect port AND baud
printcontrol -port /dev/ttyUSB1       # lock the port, still auto-baud
printcontrol -port /dev/ttyUSB1 -baud 250000
printcontrol -no-connect              # launch without touching serial

Flags

Flag Default Description
-port (auto) Serial device path. Scans /dev/ttyACM* + /dev/ttyUSB* if empty.
-baud 0 Baud rate. 0 = auto-detect from [250000, 115200, 230400, 500000, 57600].
-no-connect false Skip auto-connect on launch.

Auto-detect

With no flags, printcontrol scans every plausible serial device and probes each candidate baud until something replies with an unmistakably printer-shaped line (Marlin, FIRMWARE_NAME, RepRapFirmware, Klipper, or a T:.../... temperature report). Two-letter tokens like "ok" are not accepted as a match because they appear too easily in misaligned-baud garbage. Worst-case probe time is ~15 s; first match wins and the rest are skipped.

The connect flash at the bottom of the screen reports the chosen combination — e.g. connected: /dev/ttyUSB1 @ 115200.

Keys (top-level)

Key Action
c Connect / reconnect
d Disconnect
s List SD card and pick a file to print
p Pause SD print (M25)
r Resume (M24)
x Cancel SD print (M524)
h Focus hotend setpoint input
b Focus bed setpoint input
g Focus raw G-code input
+ / - Feedrate ±10%
0 Feedrate back to 100%
[ / ] Flow ±5% (M221)
f / F Fan ±10%
{ / } Babystep Z ∓0.05 mm (M290, Marlin)
Z Reset babystep to zero
q Quit

When a text input is focused: type the value, Enter to commit, Esc to cancel.

SD start workflow (key: s)

  1. Press s. printcontrol sends M21 (mount SD) and M20 (list files).
  2. A picker appears in place of the Log panel.
  3. / (or k/j) to highlight, Enter to start the print, Esc to dismiss without printing.
  4. Selecting a file sends M23 <filename> then M24. The print runs from the SD card, autonomously of printcontrol.
  5. You can q immediately after — the printer doesn't care. Reconnect any time; the M27 poll discovers the in-progress print within ~5 seconds and Status / Progress / ETA repopulate.

Notes:

  • The picker shows the long filename if the firmware reports one, but always passes the short 8.3 name to M23 (that's what Marlin requires).
  • Nested folders are listed but can't currently be entered. File a request if you need it.

Register with the Omarchy launcher

Once you're happy with it, drop a desktop entry so SUPER+SPACE finds it:

omarchy-tui-install printcontrol printcontrol float <icon-url>

float makes Hyprland treat it as a floating window — usually the right call for a single-pane TUI. Switch to tile if you'd rather have it in your tiling layout.

What works · what doesn't

Works today

  • Auto-detect serial port and baud rate
  • Connect / disconnect over USB serial (multi-port scan)
  • Live temperatures (hotend, bed, chamber if present) every 2 s
  • SD-card print detection, progress %, ETA
  • SD card file listing (M20) and start (M23 + M24) from the TUI
  • Pause / resume / cancel (M25 / M24 / M524)
  • Temperature setpoints (M104 / M140)
  • Fan, feedrate, flow controls (M106 / M220 / M221)
  • Babystep Z (M290 — Marlin)
  • Raw G-code entry
  • Firmware identification (M115)
  • Adaptive log panel (footer stays visible on short terminals)

Not yet

  • Host-side print streaming (sending a .gcode file from the computer). Needs the line-numbered protocol with checksum + resend handling.
  • Klipper-specific babystep (SET_GCODE_OFFSET). Sends M290 today, which Klipper rejects.
  • SD folder navigation (M20 <dir>).
  • Octoprint / Moonraker backends. Architecture supports it; see ARCHITECTURE.md.
  • Persisted profiles (per-printer last setpoints, preferred port).

Troubleshooting

"no responsive printer on [...]" — Auto-detect probed every ttyACM/ttyUSB device at every common baud and didn't find a printer signature. Most often this means another program (OctoPrint, Klipper's klippy, pronsole) already owns the port. Quit it first, or pass -port explicitly. If you have an unusual baud (e.g. 1000000), pass -baud to override the probe.

"could not auto-detect baud rate" — printcontrol opened the port but saw no recognisable reply at any candidate baud. Confirm the device is actually a printer (screen /dev/ttyUSB1 115200, then send M115); also common: the printer is mid-firmware-flash, or the USB cable is power-only.

Temps show but progress stays at 0%M27 only reports SD progress. If you started the print from a host (OctoPrint, etc.) progress won't appear here.

Babystep does nothing — You're likely on Klipper, which doesn't implement M290. Klipper support is on the roadmap.

Mangled lines like TT::57.4957.49 — This was the symptom of Marlin's auto-temperature report colliding with our explicit M105 poll. Fixed upstream: the prime block now sends M155 S0 / M27 S0 to disable auto-reports.

Layout

printcontrol/
├── go/
│   ├── cmd/printcontrol/main.go     # entry point, flag parsing
│   ├── internal/printer/            # serial driver + G-code state machine
│   │   ├── state.go                 # State / PrintState / TempPair / SDFile
│   │   ├── parse.go                 # line parsing (M105, M27, M115, M20, ...)
│   │   ├── printer.go               # Connect/Disconnect, owning goroutine, probe
│   │   └── parse_test.go
│   └── internal/tui/                # Bubble Tea model + Lipgloss styling
│       ├── model.go                 # Model, key handling, SD picker, layout
│       └── style.go
├── README.md
└── ARCHITECTURE.md                  # protocol + design notes

See ARCHITECTURE.md for the wire protocol, the state model, the auto-detect probe strategy, and why things are wired the way they are.

License

GPL-3.0-or-later (matches Printrun, which inspired the protocol layer).

Description
Omarchy Style control TUI for my printer
Readme 52 KiB
Languages
Go 100%