- Protocol specification reverse engineered from LaserPecker Design Space 2.12.1 (
docs/protocol.md). - Frame encoder/decoder, command builders and reply parsers; frames verified byte-identical to the vendor encoder.
- Serial (CH340, 460800 baud) and Bluetooth LE transports with a shared blocking interface.
- Floyd-Steinberg dithering and 1-bit/8-bit raster packing for engraving jobs.
- High-level device API: status, version, preview, focus, raster upload, engrave, progress polling.
laserpeckerCLI with ports, info, status, files, preview, stop and engrave commands.- Read path verified on an LP2 (firmware 3.16) over USB and BLE; captured replies kept as test fixtures.
- Status byte 24 identified as the active-link indicator (1 = Bluetooth, 2 = USB).
- Engraving verified end to end on hardware: dither, upload, print start, motif on the workpiece.
- Documented a device state where all file transfers are refused; cause unknown, cleared by a power cycle.
- Frame reader splits several frames arriving in one read instead of discarding the trailing ones.
- Replies are matched by function code, so unsolicited acknowledgements are no longer mistaken for answers.
- Two-package layout:
laserpecker(driver) andpyrograph(GUI), architecture inspired by MeerK40t. - Documented the Android app's alternative upload method (
0xD0packet frames) and its extra function codes. - Status mode 1 identified as "engraving"; engraving verified over Bluetooth as well as USB.
- BLE transport closes on garbage collection — a leaked connection stops the device from advertising.
- Document model in
pyrograph: geometry, objects, layers with laser parameters — millimetres, no Qt, no device. - Undo/redo through commands; the document is only ever changed through one.
- Native
.pygcontainer: standard SVG for geometry, JSON for parameters, original bitmaps kept lossless. - Job creation turns a layer into a dithered raster at its own DPI, with the origin in device pixels.
.lpbidentified as a baked USB-stick export, not a project format; the editable vendor format is.lp2.- SVG import: shapes, full path grammar including arcs, nested transforms, units and inline images.
- Brightness and contrast before dithering, so a photograph can be brought into a burnable range.
laserpecker ditherwrites the dithered result as a PNG, to judge the settings without a device.MockTransport: an in-memory device that answers queries, takes uploads and runs a job to completion.- Device abstraction in
pyrograph.devices: profile, normalised status, and the LaserPecker adapter. pyrographCLI: import an SVG, show the device, frame a document and engrave it, with--mock.- SVG import drops invisible elements, so an icon's bounding-box path is no longer engraved.
- Stroke width per layer (
line_width_mm); a one-pixel hairline burns too faintly to see on paper. - Engraving from a pyrograph document verified on hardware: SVG icon to marked workpiece.
- SVG
stroke-widthis imported per object, converted to millimetres and scaled with the transform. - The raster covers the stroked ink instead of the bare path, so wide outlines are no longer clipped.
- Round joints and caps when stroking, so icon dots drawn as zero-length segments survive.
- GUI (
pyrograph-gui): work-area canvas, layer panel with laser parameters, device panel on a worker thread. - Canvas tools: select, move, scale by handle, rubber band, line, rectangle, ellipse, polyline, polygon.
TransformObjectandCommandGroup: a transform scales the stroke with it, a multi-object edit undoes as one.- Filled paths (
DocumentObject.fill), even-odd, so glyph counters and QR modules come out right. - QR code and barcode generators producing filled geometry, not bitmaps (
pyrograph.codes). - Text tool with a family picker; the font file behind a family name is resolved by scanning the font dirs.
- Clipboard (cut/copy/paste), delete, select all, and a toolbar above the canvas.
- Align, distribute, mirror, rotate by 90° and duplicate as an n×m array.
- Millimetre rulers, a configurable grid, and snapping to the grid and to object edges.
- Stop button for framing; the device traces until told to stop, and disconnecting stops it too.
- An engraver plugged into USB is detected and preselected, by product ID rather than by bridge chip.
- Upload file IDs derive from the image, not the job name — the device kept the cached file and re-engraved it.
bleakis a plain dependency now; as an optional extra every Bluetooth path failed on the import instead.- BLE chunk size follows the negotiated ATT MTU, capped at the vendor's 179 — a longer write is rejected outright.
- Bluetooth scan in the device panel: it runs on the worker thread and offers what it found, without connecting.
- Dialogs open with the cursor in their first field; the OK button used to take the keyboard and swallow typing.
- A job waits for the machine to report itself running; the first idle reply used to count as "finished".
- A dead link is reported and the connection closed, instead of the status poll raising once a second.
- An image the platform cannot decode is marked on the canvas; it used to make every redraw, and undo, fail.
- Frame and Engrave follow the connection state, and live in a Device menu as well as on the toolbar.
- Locked objects can no longer be selected, moved, scaled or deleted.
- Shift extends a rubber-band selection instead of replacing it.
- New in the File menu, and opening or importing asks before it discards unsaved work.
- Undoing every change marks the document unmodified again; a malformed SVG reports itself as a bad import.
- Device facade serves streaming machines:
DeviceProfile.streams, andDeviceStatus.progressmay beNone. - A device that cannot report a percentage draws a busy bar instead of one stuck at zero.
- EZCAD2 / BJJCZ galvo protocol documented from balor and galvoplotter (
docs/galvo.md); no adapter yet. - New
ezcad2package: driver for BJJCZ LMC galvo boards — USB, list buffering,.corfiles. No hardware. VectorJobandbuild_vector_job: flattened outlines for devices with no raster format.- Galvo adapter (
pyrograph.devices.ezcad2): field-centred origin, flipped Y, red-light framing. - The CLI picks raster or vector output from the device profile, and takes
--galvo. - A vector job names what it could not express — bitmaps, and fills that burn as outlines only.
- Device picker in the window: machine and connection are separate choices, and only working pairs show up.
- A galvo controller plugged in selects itself, the same way a LaserPecker's serial port already did.
- Galvo settings dialog: correction file, scale and laser source, kept across sessions.
- Loading a
.corfile fills in the scale it was calibrated at, so the field size stops being a guess. - Experimental host-side field correction from a measured grid, for lenses with no correction file.
- The window warns instead of staying quiet when a job leaves geometry out or the lens data is missing.
pyrograph --galvotakes--cor-file,--galvos-per-mmand--source, and says when it is guessing.- Cloned galvo boards (
0x9980) are found and named instead of looking like nothing plugged in. - Hatching: a filled shape is swept with parallel lines, so a QR code marks solid on a vector machine.
- Fill spacing and angle per layer, next to the other laser parameters.
- Fixed: a stray
0xAAin the incoming bytes jammed the frame reader, losing every reply behind it. - Fixed: a galvo job with more than one pass uploaded each pass but only ever marked the first.
- Fixed: a hidden SVG group was imported anyway, and
style="display:none"— Inkscape's spelling — was ignored. - Fixed: SVG arc flags written without a separator (
a5 5 0 0130 0) made the import fail.