Fleet overview¶
A mix of Klipper printers and one Snapmaker J1S pretending to be one
from the operator's perspective. The shared layer is everything above
the printer's TCP socket: Mainsail UI, NFC spool selection, Spoolman
tracking, and consistent save_variables for print-start macros.
Block diagram¶
flowchart LR
subgraph Operator["๐ค Operator"]
Browser["Browser<br/>(Mainsail / Fluidd)"]
Phone["Phone<br/>(Mainsail PWA)"]
end
subgraph Klipper["๐จ๏ธ Klipper printers (each host runs its own klipper-nfc-daemon)"]
V24["Voron 2.4 โ StealthChanger<br/>Recore A7 ยท 5 toolheads (RP2040)"]
Trident["Voron Trident 300<br/>Recore A8"]
V0["Voron V0<br/>Recore A6"]
CR30["CR-30 (Klipper mod)<br/>Recore A8"]
Alcheman["Elyarchi Alcheman<br/>(vendor controller)"]
NFCDaemonK["klipper-nfc-daemon<br/>(per host)"]
V24 --- NFCDaemonK
Trident --- NFCDaemonK
V0 --- NFCDaemonK
CR30 --- NFCDaemonK
Alcheman --- NFCDaemonK
end
subgraph J1S["๐จ๏ธ Snapmaker J1S"]
Pi["Raspberry Pi<br/>(custom image)"]
Bridge["snapmaker_moonraker<br/>(Go bridge)"]
NFCDaemonJ["klipper-nfc-daemon<br/>(on the Pi)"]
J1SFw["J1S stock firmware<br/>(SACP)"]
Pi --> Bridge --> J1SFw
Pi --- NFCDaemonJ
end
subgraph Readers["๐ PN532 readers (one per printer, USB-UART)"]
R1[reader]
end
subgraph Shared["๐ Shared services"]
Spoolman["Spoolman<br/>(goeland86/Spoolman<br/>pr/nfc-support)"]
Mainsail["Mainsail / Fluidd<br/>(static, served per host)"]
end
Browser --> Mainsail
Phone --> Mainsail
Mainsail -->|"Moonraker<br/>JSON-RPC"| V24
Mainsail -->|"Moonraker<br/>JSON-RPC"| Trident
Mainsail -->|"Moonraker<br/>JSON-RPC"| V0
Mainsail -->|"Moonraker<br/>JSON-RPC"| CR30
Mainsail -->|"Moonraker<br/>JSON-RPC"| Alcheman
Mainsail -->|"Moonraker<br/>JSON-RPC"| Bridge
Readers -.->|USB-UART| NFCDaemonK
Readers -.->|USB-UART| NFCDaemonJ
NFCDaemonK -->|REST| Spoolman
NFCDaemonJ -->|REST| Spoolman
V24 -->|REST| Spoolman
Trident -->|REST| Spoolman
V0 -->|REST| Spoolman
CR30 -->|REST| Spoolman
Alcheman -->|REST| Spoolman
Bridge -->|REST| Spoolman
classDef fork fill:#ffeaa7,stroke:#fdcb6e,color:#000
classDef custom fill:#a29bfe,stroke:#6c5ce7,color:#fff
classDef vanilla fill:#dfe6e9,stroke:#b2bec3,color:#000
class Spoolman fork
class Bridge,NFCDaemonK,NFCDaemonJ custom
class V24,Trident,V0,CR30,Alcheman,J1SFw,Pi,Mainsail,R1 vanilla
Legend
Vanilla โ upstream, unmodified
Fork โ patched, see Repo map
Custom โ written from scratch in this stack
The fleet, side by side¶
| Aspect | Klipper printers (Voron 2.4 / Trident / V0 / CR-30 / Alcheman) | Snapmaker J1S |
|---|---|---|
| Firmware | Klipper (upstream) | Snapmaker stock โ proprietary, SACP over TCP |
| Host | Recore A6/A7/A8 (Vorons) or operator's host of choice (CR-30, Alcheman) | Raspberry Pi 3 with the custom image |
| Klipper-side API | Real Moonraker | snapmaker_moonraker bridge (Go) on :7125 |
| Toolheads | 5 (Voron 2.4 / StealthChanger) or 1 (all others) | 2 ร built-in extruders |
Macros / [respond] / [save_variables] |
Native (real Klipper) | Intercepted by bridge |
| Mainsail prompt dialogs | Yes (real notify_gcode_response) |
Yes (broadcast by bridge from intercepted RESPOND) |
| NFC reader | PN532 on USB-UART (one per printer) | PN532 on USB-UART (built into image) |
The point of the bridge is that from Mainsail's perspective there is no
difference between the J1S and any Klipper printer. The same macros,
the same save_variables queries, the same prompt dialogs work. That's
what makes a single fleet-wide NFC daemon possible.
Where the NFC stack plugs in¶
Every printer in the fleet runs an identical klipper-nfc-daemon
instance. The daemon doesn't know or care whether it's talking to real
Klipper or the bridge โ it speaks Moonraker JSON-RPC + emits Klipper
macros, and the underlying host (real Klipper or bridge) does the right
thing.
flowchart LR
Tag[("NFC tag<br/>NTAG213 / NFC-V")] -.->|RF| Reader[PN532 reader]
Reader -->|USB-UART| Daemon["klipper-nfc-daemon<br/>(Python)"]
Daemon -->|"POST /api/v1/nfc/lookup"| Spoolman
Daemon -->|"WS notify_gcode_response<br/>SAVE_VARIABLE<br/>RESPOND TYPE=command"| Host["Moonraker<br/>(real or bridge)"]
Spoolman -->|"spool_id"| Daemon
Host -->|"notify_gcode_response"| UI[Mainsail prompt dialog]
UI -->|"NFC_ASSIGN_TOOL T=N"| Host
The single daemon handles both single-tool ("J1S left extruder") and multi-tool ("Voron T2") spool assignments โ see NFC spool selection workflow.
Read next¶
- Data flow diagrams โ every cross-process message labelled
- Repo map โ what's upstream, what's a fork, what's bespoke, and why