Skip to content

Double buffering

The daemon prepares swaths ahead of printing, using two firmware slots so data transfer can overlap with the current pass.

The firmware slots

The board divides its 8 MB of external PSRAM into two slots, each holding one complete swath. The host sends BEGIN_SWATH, one DATA frame per 147-byte column, then END_SWATH. On arrival, the firmware expands each line to 196 bytes (49 32-bit words), ready for DMA. No unpacking is needed during firing.

A slot moves through EMPTY → RECEIVING → READY → PRINTING → EMPTY. If a transfer contains fewer lines than declared, the firmware discards it. Only a complete swath can be armed. One slot can receive data while the other prints.

The daemon's pipeline

The daemon holds the full job in memory. A background worker keeps the firmware slots supplied with swaths.

On load_job, or when connecting with a job already loaded, the daemon clears stale slots and starts sending the first two swaths. A print request waits until its swath is ready, then sends ARM. Each PRINT_COMPLETE frees a slot for another transfer. If the next swath is already buffered, it can be armed without waiting for USB.

Streaming runs in the daemon because a swath transfer can take seconds. Doing that work in Klipper's reactor would block motion planning and can cause "Timer too close" errors. The plugin only moves, arms, sweeps and confirms completion. See Design rationale.

When things fail

A streaming failure, trigger timeout, firmware error or serial loss latches a fault in the daemon. The next print reports that fault immediately.

ABORT immediately disables and latches off the DAC. A running swath loop finishes without firing ink, and further ARM or PURGE requests return DAC_LATCHED. The daemon follows with RESET, which clears the latch and both slots. Once recovery succeeds, the pipeline can restart without reloading the job.

An interrupted print restarts from its first swath; partial resume is not supported. See Troubleshooting for fault recovery.