Changing the protocol¶
Edit wire definitions in paintress-protocol/schema/protocol.yaml and head
definitions in profiles/*.yaml. These generate the C and Python bindings
copied into the firmware, daemon and encoder. Update the sources and
regenerate; do not edit generated copies.
The protocol source lives in paintress-team/paintress-protocol.
The workflow¶
- Edit the schema:
schema/protocol.yaml, which holds the enums, commands, events, error codes and fixed-offset payload structs. - Bump the version. Additive, backward-compatible changes bump MINOR;
breaking ones bump MAJOR. The handshake constant follows,
wire_id = (MAJOR << 8) | MINOR. The daemon refuses a firmware whose major differs, so a MAJOR bump means both sides ship together. - Regenerate with
make gen. It writes four files: the wire bindings (generated/c/paintress_protocol.h,generated/python/paintress_protocol.py) and the head bindings (generated/c/head_config.gen.h,generated/python/head_profiles.py). - Verify.
make checkconfirms the generated files are in sync with the sources.make testruns the round-trip, job and profile tests plus the cross-language golden vectors, where Python packs the expected bytes and Cmemcmps them.make compile-checkbuilds the header under the real ARM toolchain so that every_Static_assertfires. - Distribute.
make synccopies the canonical bindings into the repos that vendor them, found as../paintress-*next topaintress-protocol, andmake sync-checkfails if any vendored copy has drifted. Run it before a release. - Update the consumers, meaning firmware dispatch and usb_link, daemon handlers and parsers, and the command tables in their READMEs.
- Record it in
CHANGELOG.md.
Changing a head profile¶
Use the same generation and verification steps for head profiles. The field being changed determines which compatibility check is affected:
| Field changed | Fingerprint moved | Consequence |
|---|---|---|
nozzle_count, channels, name |
head | re-RIP the jobs, no reflash |
bytes_per_line, edges, clocks, packing, psram_line_size |
frame | reflash and re-RIP |
slot_inks, gaps, base_dpi, window_enable_map |
neither | nothing; host-side reference data |
A new head also needs a per-bus nozzle mapping in encoder/head_packer.py and
a layout in rip/head_layout.py. These describe how the head is packed and
arranged.
Rules of thumb¶
Keep retired codes reserved. For example, command 0x0A must not be
reassigned.
Payload structs use flat, little-endian fields at fixed offsets, with no optional fields or nested length prefixes. The protocol treats raster data as opaque bytes.
The profile defines the DATA line size, recorded as bytes_per_line in the
job header and included in the frame fingerprint. The protocol transports each
line without interpreting its contents.