gantrix
A small framework for driving GRBL-based gantry devices with swappable tool heads — laser engraver today, whatever else you bolt to the carriage later (solenoid stamp, pen plotter, spray head, ...). Grew out of a one-off laser text-engraving script; generalized so the same motion/fill planning works for any tool, without being locked to the laser.
Layout
gantrix.transport— serial connection to GRBL, command/ack handshake, homes with the tool forced off first (a previous crashed run can leave a tool physically engaged even after the host process dies). A command that gets no reply at all (dead link, controller reset) raisesGrblErrorrather than being silently swallowed; a mid-command controller reboot is detected from its boot banner instead of waiting out the full timeout.emergency_stop()sends GRBL's real-time soft-reset byte directly, bypassing the normal command queue, so it works even while another thread is blocked waiting on a stuck command.gantrix.tools— theToolplugin interface (engage/disengage+ feed rates).LaserTool(M3/M5 with power) is the only concrete implementation shipped so far; new device types are added by subclassingTool(seesrc/gantrix/tools/laser.pyas the reference).gantrix.design— turns a design intoShapes the planner can sweep.design.textis a small stroke font (rects + tapered diagonals).design.shapes.RasterShapeis a generic bitmap-gridShape, backing bothdesign.qrcode(any QR-encodable data) anddesign.image(any image — logo, silhouette, photo — thresholded to black/white at a configurable resolution). True vector/SVG import (arbitrary curves, not just bitmaps) is the natural next addition here.gantrix.planner.fill— generic boustrophedon (zigzag raster) fill: given anything exposingspans_at_y(y), sweeps scanlines and emits direction-alternating segments that minimize travel.gantrix.job— wires a design + tool + transport into a runnable job. Accepts either a port string (opens/closes its own connection — CLI usage) or an already-connected transport (keeps it open — GUI usage).gantrix.gui.app— preview a design as SVG, connect/disconnect a serial port, and run a job — all from the browser.gantrix.gui.connectionholds the one live serial connection the process manages;gantrix.gui.job_runnerruns aJobin a background thread (so the HTTP request returns immediately) and exposes progress + cancellation for the page to poll. Only one job at a time — it's a single physical device, not a queue. Two ways to stop: Cancel is cooperative (checked between fill segments — fine for "I want a different design," not fast enough to rely on if something's actually wrong); EMERGENCY STOP hits the hardware directly viaemergency_stop()and works regardless of whether a command is stuck. Home manually re-homes ($H) when connected and no job is running.
Quickstart
pip install -e ".[dev,gui,design]"
pytest # no hardware needed
# preview a design in the browser (no hardware needed)
uvicorn gantrix.gui.app:app --reload
# open http://127.0.0.1:8000
# run it for real
gantrix text HAND --port /dev/cu.usbserial-XXXXXX
gantrix qr "https://example.com" --port /dev/cu.usbserial-XXXXXX --cell-size 1.5
gantrix image logo.png --port /dev/cu.usbserial-XXXXXX --height 40 --cell-size 0.3
Adding a new device
Swapping tools without touching the design or planner is the point of the
Tool split. To support a new actuator, subclass Tool and implement
engage/disengage (see src/gantrix/tools/laser.py):
class MyTool(Tool):
def engage(self, transport):
transport.send("M62 P1") # e.g. a GRBL digital output pin
def disengage(self, transport):
transport.send("M63 P1")
Then use it exactly like LaserTool — same layout_text/layout_qr
output, same Job, just a different tool instance passed in.
Status
Early scaffold. Laser path is validated against a working engrave script.
QR fill segments have been round-trip verified (reconstructing the module
grid from planned segments reproduces the source matrix exactly) and
rendered to confirm the code is well-formed. Only one concrete Tool
(laser) ships so far — the interface is there for more, none built yet.
Vector/SVG import and shape-drawing in the GUI (not just text/QR forms) are
not built yet either. The GUI can connect/disconnect, run a job (text or
QR), Cancel, Home, and hardware-level EMERGENCY STOP. A real incident
during testing (the controller stopped responding mid-job) surfaced that
failures weren't being surfaced at all — send() used to return False
on a dead link and nothing checked it, so the job just kept retrying into
the void with no visible error. Fixed: failures now raise and show up in
the job's error field instead of hanging silently.
Release files for gantrix 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| gantrix-0.1.0.tar.gz | 24.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| gantrix-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 46.3 kB
Release files / gantrix-0.1.0.tar.gz
| Download URL | gantrix-0.1.0.tar.gz |
|---|---|
| Size | 24.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8d815fa2f470a7a98b4dc221c8b0cc9ee22f4c1d52110d922be9878812302a20
|
|
BLAKE2b-256 checksum How to use checksums |
0a2993adda2cdaac813460f8691bd82142a0f3cede88a465407a9cbd063a88cf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.0
|
Release files / gantrix-0.1.0-py3-none-any.whl
| Download URL | gantrix-0.1.0-py3-none-any.whl |
|---|---|
| Size | 21.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0ad9d275f46506d8fc9c6c66e42162b80492bb82c42ea1f6a4e58be3147018a8
|
|
BLAKE2b-256 checksum How to use checksums |
dfda3ce605feeaa41c71b252da33b89db0b28654a9b632eb49371da1a66a0064
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.0
|