Carefree, observable plotting of HP-GL files on HP pen plotters over RS-232.
Project description
hpgl-buddy
Carefree, observable plotting of HP-GL files on HP pen plotters over RS-232.
It does not just shove a file at the plotter: it validates the file, splits it to fit the device buffer, feeds it so the buffer never overflows and an inked pen never stalls mid-stroke, watches the device for faults the whole time, and logs every exchange so a run can be understood and troubleshooted from the log alone.
Supported devices:
- HP 7475A (RS-232)
New devices are added as declarative profiles (see Extending); HP-IB is planned.
Install
pip install hpgl-buddy
Requires Python 3.11+. pyserial is the only runtime dependency. To work on hpgl-buddy itself, see Development.
Quick start
# 1. Generate a built-in demo to a file (no plotter needed)
hpgl-buddy demo --pens 6 --out demo.hpgl
# 2. Validate it offline (no plotter needed)
hpgl-buddy check demo.hpgl
# 3. Check the plotter is alive and interpret its status
hpgl-buddy status --port /dev/cu.usbserial-XXXX
# 4. Plot it (verbose shows every byte/ESC exchange)
hpgl-buddy -v plot demo.hpgl --port /dev/cu.usbserial-XXXX
Sample HP-GL files live in examples/.
Serial defaults: 9600 8N1, no flow control (configurable). Flow control is off by
default; enable it with --xonxoff if your cabling needs it.
Commands
| Command | What it does |
|---|---|
status |
Ad-hoc healthcheck: identification, buffer, status byte, errors, limits - all interpreted. |
check FILE |
Offline HP-GL syntax check. No device. Exit non-zero on errors. |
plot FILE |
Safe, buffer-aware plotting with progress + end-of-run report. |
monitor enable|disable|watch |
Enable/disable monitor mode (computer port) or stream the echoed bytes (terminal port). |
demo |
Generate demo HP-GL: --scene card (shapes/fills/labels/colours grid) or --scene house (a one-line drawing emitted as a single giant PD instruction - the >1024-byte oversized-instruction case, streamed in sub-blocks). |
Global: -v/--verbose (DEBUG, incl. raw ASCII+hex wire dumps), -V/--version.
Serial options (on status/plot/monitor): --port, --model (default hp7475a),
--baud, --framing (e.g. 8N1), --timeout, --xonxoff, --rtscts.
plot options
--on-error abort|prompt|continue- on a reported error: stop and park the pen (default), ask interactively, or auto-recover (ESC.Kdiscard ->IN-> replay the tracked state preamble -> carry on).--live-hpgl-verify off|chunk|pu- optional on-device HP-GL error checking (see Verification). Defaultoff.--ignore-syntax-errors- plot even if the offline check found errors.--stats-json PATH- write run statistics as JSON (-for stdout).
monitor (two ports)
The 7475A enables monitor mode via an ESC sequence on the computer (data) port, then echoes the bytes it receives out a separate terminal port - so it's two single-purpose commands, one per port:
hpgl-buddy monitor enable --port /dev/cu.COMPUTER # turn monitor mode on (data port)
hpgl-buddy monitor watch --port /dev/cu.TERMINAL # stream the echo (terminal port)
hpgl-buddy monitor disable --port /dev/cu.COMPUTER # turn it off when done
There are two monitor modes (enable --mode received|parsed): received (default, mode 1 in the documentstion)
echoes every byte as it arrives, including ESC device-control sequences; parsed (mode 0 in the documentation)
echoes only HP-GL as the plotter parses it from the buffer. watch prints every byte as
binary, hex, decimal, and ASCII/control-name.
How plotting works
file -> parse -> offline syntax check -> plan into chunks -> stream to device -> report
- Parse the bytes into instructions, each tagged with its byte offset, source line and column, and sequence index - so any error names the exact command and can be located even in a single-line file (where the line number is always 1).
- Syntax check offline;
plotrefuses a file with errors unless--ignore-syntax-errors. - Plan into chunks of <=256 bytes, split only at instruction boundaries (never inside a command), each tagged whether it ends with the pen up.
- Stream: a chunk is sent only when
ESC.Breports enough free buffer space. This one gate both prevents overflow and keeps the buffer fed, so a long pen-down stroke spanning several chunks never underruns (an underrun would park an inked pen and blot). An instruction larger than the buffer (e.g. a single hugePDpolyline) is streamed across severalESC.B-gated sub-blocks - never split as HP-GL, since the plotter reassembles the byte stream as it parses - so it plots like any other. - Watch (always on, after each chunk):
ESC.Efor I/O faults (overflow / framing / data loss) andESC.Ofor environmental faults (paper lever or pinch wheels raised -> abort; VIEW pressed -> warn). Both are immediate and never stall the pen. - Confirm: a final
OS;OE;OI;tailgate waits for the pen to physically finish and reports the end status / any HP-GL error. - Report instructions/chunks/bytes sent, elapsed time, recovered errors, and warnings
(and the same as JSON via
--stats-json).
Verification
HP-GL/syntax errors are a property of the file (already validated offline), so on-device
HP-GL error checking is optional and off by default. When enabled it never stalls the
pen - it uses a one-deep tailgate: the OS;OE;OI; query is prefixed to the chunk after a
pen-up, so its verdict reports the previous chunk while the current one is already drawing.
chunk- check at pen-up chunk boundaries.pu- break a chunk at every pen-up so each completed stroke is checked (more, smaller chunks).
Either way you learn a chunk is clean within ~1 chunk, and a reported error names the span of chunks it could belong to with all candidate instructions.
Limits to know
- No pen sensing on the 7475A: a missing or fallen pen plots dry and is not detectable
by any status query. Pre-load the pens your file uses. (
plotwarns about this.) - Throughput is bound by the baud rate: a huge pen-down instruction with very short segments can outrun a 9600-baud link no matter how we feed it (the pen draws faster than bytes arrive). hpgl-buddy always feeds as fast as the buffer allows; raise the baud if a dense drawing dwells.
Architecture
Thoroughly separated layers, each replaceable on its own:
hpgl/ parse HP-GL bytes into a Program; offline syntax check
devices/ declarative TOML profiles + abstract Device base (registry)
interface/ Transport abstraction + pyserial RS-232 implementation
status/ ESC + HP-GL command builders, response parsers, status interpretation, monitor
execution/ planner (Program -> chunks) + flow control + executor + progress/report
demo/ demo HP-GL generators
The full design rationale, with HP manual citations, lives in
DESIGN.md. To embed
hpgl-buddy as a library (e.g. a GUI), see
INTEGRATION.md.
Extending
Add a simple device by dropping a <model>.toml into devices/profiles/ (buffer size,
pen count, serial defaults, capabilities, pen_sensing). A device with unusual behavior
can subclass Device and register it. The interface layer is transport-agnostic, so HP-IB
can be added as another Transport without touching the rest.
Development
pip install -e . -r requirements-dev.txt # editable install + pinned dev tooling
tox # run the test suite (your local Python)
tox -e build # build the sdist + wheel
To run the suite across every supported Python (3.11-3.13) without installing those interpreters yourself, use the per-version container matrix:
docker compose -f docker-compose.test.yml up --abort-on-container-exit
Dependencies follow a loose-rough -> pinned pattern, frozen in clean environments by the
dependencies_update GitHub Actions workflow:
- runtime:
requirements-rough.txt(hand-maintained, loose; kept in sync with[project.dependencies]) ->requirements.txt(fully pinned). - dev / CI tooling:
requirements-dev-rough.txt(loose: pytest, tox, build, twine) ->requirements-dev.txt(fully pinned), needed only in CI and dev environments.
A Dockerfile provides a reproducible build/test image (not for serial I/O - hardware is
not reachable from a container).
Conventions: extensive logging (no print), ASCII-only output, descriptive names, and
errors that state what happened, where, and why.
References and Attributions
- Logo is rendered out of a picture from Ed Nisley's Blog: https://softsolder.com/2015/04/21/hp-7475a-plotter-oem-pen-body-model/
Vibe-code disclosure
This library is written heavily with Claude. The design is decided upon by the initial human-author. All hands-on tests are conducted by a human-operator.
The human-conducted code review (HCCR) is an optimisation of a sort. It will be done in the right time.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hpgl_buddy-1.8.0.tar.gz.
File metadata
- Download URL: hpgl_buddy-1.8.0.tar.gz
- Upload date:
- Size: 79.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4dfcbb55e0bfb757aa927c7ead7ad495a279acd92bc10a9a78b637f7c80d1d16
|
|
| MD5 |
75a63ca5925a4d8ce152389ce98cf675
|
|
| BLAKE2b-256 |
f75ab5db73fa37f56836d630d7a544f73d7bb8fcdfb7d6995ebdaa259d7ffce3
|
Provenance
The following attestation bundles were made for hpgl_buddy-1.8.0.tar.gz:
Publisher:
release.yml on hpgl-buddy/hpgl-buddy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hpgl_buddy-1.8.0.tar.gz -
Subject digest:
4dfcbb55e0bfb757aa927c7ead7ad495a279acd92bc10a9a78b637f7c80d1d16 - Sigstore transparency entry: 2051930711
- Sigstore integration time:
-
Permalink:
hpgl-buddy/hpgl-buddy@1ee10f47ac6a26638bb185b2b2d11676ab421a1e -
Branch / Tag:
refs/heads/master - Owner: https://github.com/hpgl-buddy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1ee10f47ac6a26638bb185b2b2d11676ab421a1e -
Trigger Event:
push
-
Statement type:
File details
Details for the file hpgl_buddy-1.8.0-py3-none-any.whl.
File metadata
- Download URL: hpgl_buddy-1.8.0-py3-none-any.whl
- Upload date:
- Size: 76.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ca7a93f4c3a0f7492d3d3906d1bc347349a3adb14155ec8a5f16d3b61f4adde1
|
|
| MD5 |
9f4f2bf7b1f1884de4da4fc56236703e
|
|
| BLAKE2b-256 |
dcddd2c4d64eb5eeb2277904eab84c686fd6bda79989f8b34b0ddaf5c777df91
|
Provenance
The following attestation bundles were made for hpgl_buddy-1.8.0-py3-none-any.whl:
Publisher:
release.yml on hpgl-buddy/hpgl-buddy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hpgl_buddy-1.8.0-py3-none-any.whl -
Subject digest:
ca7a93f4c3a0f7492d3d3906d1bc347349a3adb14155ec8a5f16d3b61f4adde1 - Sigstore transparency entry: 2051930980
- Sigstore integration time:
-
Permalink:
hpgl-buddy/hpgl-buddy@1ee10f47ac6a26638bb185b2b2d11676ab421a1e -
Branch / Tag:
refs/heads/master - Owner: https://github.com/hpgl-buddy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1ee10f47ac6a26638bb185b2b2d11676ab421a1e -
Trigger Event:
push
-
Statement type: