Collegare la Snapmaker J1S all’ecosistema Moonraker — 3D Etplus

In 3D Etplus gestiamo quotidianamente una flotta di stampanti 3D. La maggior parte condivide un flusso di lavoro comune: affettare in PrusaSlicer, inviare il GCode alla stampante tramite la rete e monitorare il lavoro dalla dashboard Mainsail — il tutto alimentato dall’API Moonraker. La nostra Snapmaker J1S, tuttavia, è l’eccezione. Utilizza il protocollo proprietario Snapmaker e il proprio ecosistema software chiuso, il che significa che non possiamo affettare in PrusaSlicer e inviare un file direttamente alla stampante, non possiamo monitorarla dalla stessa dashboard di tutto il resto e non possiamo integrarla negli strumenti di gestione della flotta su cui facciamo affidamento.

Questo articolo descrive come abbiamo costruito snapmaker_moonraker — un ponte open source che fa funzionare la Snapmaker J1S come una stampante Klipper, controllabile dagli strumenti standard compatibili con Moonraker e direttamente raggiungibile dalla finestra di stampa in rete di PrusaSlicer. Il progetto è disponibile su GitHub sotto licenza MIT.

Il punto di partenza: sm2uploader

Questo progetto non esisterebbe senza sm2uploader di macdylan, uno strumento a riga di comando scritto in Go per caricare file sulle stampanti Snapmaker. sm2uploader (a sua volta basato sul lavoro precedente di kanocz) ha fornito le fondamenta essenziali: un’implementazione funzionante del protocollo binario SACP (Snapmaker Application Communication Protocol) in Go, inclusi l’handshake TCP, la codifica/decodifica dei pacchetti, i checksum CRC, il chunking per l’upload dei file e la scoperta UDP delle stampanti.

sm2uploader ha dimostrato che era possibile comunicare con una stampante Snapmaker da software personalizzato senza Luban. La domanda era: potevamo prendere questa conoscenza del protocollo e incapsularla in un’API compatibile con Moonraker, in modo che la J1S appaia come qualsiasi altra stampante Klipper per gli strumenti che già utilizziamo — a partire dalla capacità di PrusaSlicer di inviare file GCode direttamente a un host Moonraker?

Il problema: una stampante, due mondi

Il nostro flusso di lavoro quotidiano per ogni altra stampante è semplice: affettare un modello in PrusaSlicer, premere il pulsante di invio in rete per caricare il GCode sull’istanza Moonraker della stampante, poi monitorare la stampa da Mainsail. La Snapmaker J1S interrompe completamente questa catena. Comunica solo tramite SACP — un protocollo binario proprietario su TCP — e non espone alcuna interfaccia standard. Non c’è API Moonraker, nessuna compatibilità OctoPrint e nessun modo per integrarla in una flotta basata su Klipper.

Questo significa: nessun invio di file da PrusaSlicer, nessuna dashboard Mainsail, nessuna vista unificata della flotta e nessun modo per inserire la J1S nella stessa pipeline di monitoraggio e gestione delle nostre altre macchine.

La J1S tra le sue colleghe che parlano Klipper — l’unica macchina che non si integrava nel flusso di lavoro.

La soluzione: un ponte protocollare in Go

Basandoci sul codice del protocollo SACP di sm2uploader, abbiamo scritto un’applicazione ponte in Go che si interpone tra i client Moonraker standard e la stampante Snapmaker. Espone un’API HTTP e WebSocket completamente compatibile con Moonraker sulla porta 7125 — la stessa porta del vero Moonraker — e traduce ogni richiesta in comandi del protocollo binario SACP inviati alla stampante tramite TCP.

Dal punto di vista di PrusaSlicer, il ponte è semplicemente un altro host Moonraker. Lo si configura come stampante di rete, e il pulsante «Invia alla stampante» funziona esattamente come per qualsiasi macchina Klipper. Il ponte riceve il GCode caricato, lo trasferisce alla J1S tramite SACP e può avviare la stampa — il tutto senza toccare Luban.

Tutto passa attraverso un unico protocollo:

  • SACP su TCP (porta 8888) — L’unica interfaccia di rete della J1S. Utilizzato per l’handshake di connessione, le query di temperatura, l’upload dei file, le sottoscrizioni allo stato di stampa, il tracciamento delle coordinate, il monitoraggio delle ventole, l’esecuzione GCode, l’homing e l’arresto di emergenza.
[PrusaSlicer / Mainsail / Fluidd] <--HTTP/WebSocket--> [Ponte :7125] <--SACP TCP:8888--> [Stampante J1S]

Inizialmente avevamo supposto che la J1S avrebbe esposto anche un’API REST sulla porta 8080, come documentato per i modelli Snapmaker precedenti. Una scansione delle porte durante i test reali ha rapidamente smentito questa ipotesi — la porta 8080 è chiusa sulla J1S. Ciò significava che tutto doveva passare attraverso SACP, il che si è rivelato una sfida ingegneristica significativa ma ha infine prodotto un’architettura più pulita e affidabile.

Il ponte traduce le chiamate API Moonraker in comandi del protocollo binario SACP — l’unica interfaccia di rete della J1S.

Reverse engineering del protocollo SACP

sm2uploader ci ha dato le basi — connessione, upload dei file e scoperta — ma non interroga le temperature, non si sottoscrive allo stato di stampa né legge le coordinate. Queste funzionalità hanno dovuto essere costruite da zero tramite il reverse engineering del protocollo binario SACP utilizzando il codice sorgente del firmware SnapmakerController-IDEX e l’applicazione desktop Luban.

Query di temperatura

La J1S non risponde alle query di temperatura GCode standard (M105) tramite SACP; restituisce risposte vuote. Abbiamo dovuto inviare direttamente pacchetti di comando SACP (CommandSet 0x10 per gli estrusori, 0x14 per il piano riscaldato) e analizzare i dati di risposta binari.

Trovare la codifica corretta delle temperature ha richiesto diverse iterazioni di reverse engineering:

  1. Primo tentativo: codifica float32 — Basato sulla documentazione Snapmaker 2.0. Risultato: 0,0°C ovunque. Formato sbagliato.
  2. Secondo tentativo: uint16 in milligradi — Basato sull’analisi esadecimale grezza dei pacchetti catturati. Parzialmente funzionante — un ugello leggeva correttamente, l’altro no.
  3. Terzo tentativo: int32 in milligradi — Dopo aver studiato il codice sorgente del firmware SnapmakerController-IDEX, abbiamo trovato la codifica effettiva: interi con segno a 32 bit in ordine little-endian, che rappresentano milligradi Celsius (dividere per 1000 per ottenere °C).

Un’altra scoperta specifica della J1S: la stampante a doppio estrusore invia pacchetti SACP separati per ugello, identificati da un byte HeadID nell’intestazione del pacchetto. L’indice per record dell’estrusore è sempre zero — è l’intestazione che distingue il sinistro (T0) dal destro (T1). Il nostro router di pacchetti unisce questi dati in uno stato di temperatura unificato.

Tre iterazioni di reverse engineering sono state necessarie per decodificare correttamente il formato di temperatura della J1S.

Stato di stampa tramite sottoscrizioni SACP

Senza API HTTP disponibile, ottenere lo stato di stampa, l’avanzamento e lo stato della macchina ha richiesto l’implementazione del meccanismo di sottoscrizione SACP. Il ponte si sottoscrive a flussi di dati periodici dal firmware della stampante inviando richieste di sottoscrizione (CommandSet 0x01, CommandID 0x00) con il tipo di dato target e un intervallo di polling.

Abbiamo implementato sottoscrizioni per:

  • Heartbeat della macchina (0x01/0xA0) — 11 stati distinti da IDLE a COMPLETING, mappati al modello standby/printing/paused di Klipper.
  • Tempo di stampa trascorso (0xAC/0xA5) — Secondi dall’inizio della stampa, come uint32.
  • Riga di stampa corrente (0xAC/0xA0) — Numero di riga GCode per il tracciamento dell’avanzamento.
  • Stato ventole (0x10/0xA3) — Velocità della ventola per ugello, mappata alla scala 0,0–1,0 di Moonraker.
  • Coordinate XYZ (0x01/0x30) — Posizioni degli assi in micrometri (int32 LE, ÷1000 per mm).
  • Informazioni file (0xAC/0x00 e 0xAC/0x1A) — Nome del file attualmente in stampa, interrogato sia dal controller che dal MCU dello schermo.

Una scoperta fondamentale è che la J1S ha due «peer» indirizzabili sulla stessa connessione TCP: il controller (Peer ID 1) e il MCU del touchscreen (Peer ID 2). Alcuni dati — come il nome del file in stampa e il conteggio totale delle righe — sono disponibili solo dal MCU dello schermo.

Cosa può fare il ponte oggi

Dopo otto sessioni di sviluppo e test approfonditi sulla stampante reale, il ponte supporta un set completo di funzionalità Moonraker:

  • Stampa di rete PrusaSlicer — Configurare il ponte come host Moonraker in PrusaSlicer e inviare file GCode direttamente alla J1S tramite la rete.
  • Monitoraggio delle temperature in tempo reale — Doppio estrusore e piano riscaldato, interrogati tramite query binarie SACP e visualizzati nel grafico delle temperature di Mainsail.
  • Tracciamento dello stato di stampa — Stato della macchina (inattivo/stampa/pausa/completamento), nome del file, tempo trascorso e velocità della ventola — tutto tramite sottoscrizioni SACP con aggiornamenti in diretta.
  • Tracciamento della posizione in tempo reale — Coordinate XYZ della testina e stato degli assi referenziati, aggiornati in tempo reale.
  • Controllo della temperatura — Impostare le temperature target per entrambi gli estrusori e il piano riscaldato.
  • Esecuzione GCode — Inviare comandi GCode arbitrari tramite la console Mainsail.
  • Gestione file — Caricare, elencare, scaricare ed eliminare file GCode. Estrazione dei metadati dai commenti dello slicer.
  • Controllo della stampa — Avviare, mettere in pausa, riprendere e annullare stampe dall’interfaccia Mainsail.
  • Arresto di emergenza — Supporto M112 per l’arresto immediato della stampante.
  • Scoperta delle stampanti — Scoperta tramite broadcast UDP per trovare stampanti Snapmaker sulla rete locale.
  • API database — Archiviazione chiave-valore con supporto namespace, persistita in file JSON.
  • Cronologia di stampa — Tracciamento dei lavori con orari di inizio/fine, durata e statistiche cumulative.
  • Sottoscrizioni WebSocket — Supporto completo JSON-RPC 2.0 con aggiornamenti di stato in diretta, corrispondente al modello di sottoscrizione di Moonraker.
  • Monitoraggio IA Obico — Compatibilità completa con moonraker-obico, verificata funzionante contro un server Obico self-hosted per il rilevamento dei guasti di stampa tramite IA.

Esiste una limitazione nota: la percentuale di avanzamento della stampa rimane allo 0% per le stampe avviate dal touchscreen. La query al MCU dello schermo che fornisce il conteggio totale delle righe (necessario per il calcolo della percentuale) va in timeout sulla J1S per le stampe iniziate dal touchscreen. Le stampe avviate tramite Mainsail/PrusaSlicer non hanno questo problema, poiché il ponte conosce il conteggio totale delle righe dal file caricato.

Dal punto di vista di PrusaSlicer, la J1S è semplicemente un’altra stampante Moonraker sulla rete.
La J1S che mostra le temperature in diretta di entrambi gli estrusori e del piano riscaldato in Mainsail — come qualsiasi stampante Klipper. Monitoraggio e controllo completi della stampa dalla stessa interfaccia utilizzata per ogni altra stampante in officina.

Distribuzione chiavi in mano: immagine Raspberry Pi con supporto webcam

Per rendere la distribuzione il più semplice possibile, abbiamo costruito una pipeline CI che produce un’immagine per scheda SD Raspberry Pi 3 pronta da flashare. La pipeline Jenkins cross-compila il binario Go per ARMv7, scarica Raspberry Pi OS Lite, monta l’immagine in un chroot ARM emulato da QEMU e installa tutto:

  • nginx come reverse proxy, che serve Mainsail sulla porta 80 e inoltra il traffico API/WebSocket al ponte sulla porta 7125.
  • Mainsail — l’ultima versione, scaricata automaticamente da GitHub durante la costruzione dell’immagine.
  • snapmaker_moonraker — in esecuzione come servizio systemd con riavvio automatico in caso di errore.
  • crowsnest — il demone webcam del team Mainsail, che fornisce lo streaming della telecamera per il monitoraggio in diretta delle stampe direttamente nella dashboard Mainsail. Con una webcam USB collegata al Pi, si ottiene una vista in diretta del piano di stampa accanto a temperature e avanzamento — la stessa configurazione usata sulle nostre macchine Klipper.
  • moonraker-obico — preinstallato e configurato per il rilevamento dei guasti di stampa tramite IA. Si connette a un server Obico self-hosted con streaming Janus WebRTC per video a bassa latenza.
  • SSH abilitato per la gestione remota, hostname impostato su snapmaker.
[Browser] → [nginx :80] → [file statici Mainsail]
                        → proxy_pass → [snapmaker_moonraker :7125]
                                            ↓
                                  [Snapmaker J1S via SACP TCP:8888]

[Webcam USB] → [crowsnest] → [stream MJPEG/WebRTC] → [pannello webcam Mainsail]
                            → [Janus WebRTC] → [rilevamento guasti IA Obico]

Flashare l’immagine, avviare il Pi, collegare una webcam USB, puntare il browser su http://snapmaker.local, e avrete una dashboard Mainsail completamente funzionale con video in diretta e monitoraggio IA della stampa per la vostra J1S. L’intero processo di costruzione è automatizzato e produce immagini compresse pronte per i GitHub Releases.

La CI automatizzata produce immagini per scheda SD flashabili ad ogni rilascio taggato.

Decisioni di progettazione chiave

Perché Go? L’eccellente supporto alla cross-compilazione di Go è stato fondamentale. Un singolo comando GOOS=linux GOARCH=arm produce un binario ARM collegato staticamente — nessuna dipendenza runtime, nessun interprete, nessun problema di versioning delle librerie sul Pi. Le primitive di concorrenza integrate di Go (goroutine, canali, mutex) hanno anche reso semplice il routing asincrono dei pacchetti SACP — il ponte esegue una goroutine dedicata al routing dei pacchetti che demultiplexa le risposte SACP in arrivo verso i chiamanti in attesa tramite canali indicizzati per numero di sequenza.

Perché vendorizzare il codice SACP di sm2uploader? sm2uploader è un programma standalone (package main in Go), il che significa che non può essere importato come libreria da altri progetti Go. Piuttosto che forkare e ristrutturare il progetto di qualcun altro, abbiamo vendorizzato il codice a livello di protocollo in un pacchetto sacp/ pulito, lo abbiamo esteso significativamente con il parsing delle temperature, la gestione delle sottoscrizioni, l’indirizzamento multi-peer e la decodifica dello stato, e abbiamo dato piena attribuzione a macdylan e kanocz.

Architettura esclusivamente SACP: Inizialmente avevamo pianificato un approccio a doppio protocollo — SACP per le operazioni binarie e l’API HTTP Snapmaker per l’esecuzione GCode e il polling dello stato. I test nel mondo reale hanno rivelato che la J1S non ha alcuna API HTTP (la porta 8080 è chiusa). Ciò ha imposto una riscrittura completa per instradare tutto attraverso SACP, incluso lo stato di stampa tramite il meccanismo di sottoscrizione e l’esecuzione GCode tramite pacchetti di comando SACP. Il risultato è in realtà più pulito: un’unica connessione TCP gestisce tutta la comunicazione, e gli aggiornamenti di stato basati sulle sottoscrizioni sono più efficienti di quanto sarebbe stato il polling HTTP periodico.

Pattern State/StateData: Il sync.RWMutex di Go non può essere copiato per valore in modo sicuro, ma avevamo bisogno di passare liberamente snapshot dello stato della stampante. La soluzione è stata separare il contenitore mutabile protetto da mutex State dal tipo valore semplice StateData. Un metodo Snapshot() restituisce una copia sicura dei dati senza il lock.

Prossimi passi: completare l’integrazione della flotta

Il ponte oggi ci offre il flusso di lavoro principale che volevamo: affettare in PrusaSlicer, inviare alla J1S tramite la rete, monitorare la stampa in Mainsail con un feed webcam in diretta e ottenere il rilevamento dei guasti IA da Obico. L’obiettivo più ampio — unificare completamente la gestione di rete della Snapmaker con il resto delle stampanti 3D che 3D Etplus gestisce quotidianamente — è ben avviato. L’obiettivo di integrazione rimanente è:

  • Spoolman per il tracciamento dei filamenti — Tracciare l’utilizzo delle bobine, il filamento rimanente e i tipi di materiale su tutte le stampanti, inclusa la J1S. Questo è essenziale per un’operazione multi-stampante dove sapere quanto filamento rimane su ogni bobina fa risparmiare tempo e previene gli esaurimenti a metà stampa.

Questa integrazione non è ancora completa, ma è la prossima tappa naturale del progetto. Realizzarla significherà che la J1S è un membro a pieno titolo della nostra farm di stampa — monitorata, gestita e tracciata con gli stessi strumenti di ogni altra macchina. Tratteremo il lavoro di integrazione di Spoolman in un articolo successivo.

Ci sono anche diversi miglioramenti tecnici nella roadmap:

  • Risoluzione del problema dell’avanzamento allo 0% per le stampe avviate dal touchscreen.
  • Miglioramento dell’affidabilità della connessione SACP (la connessione iniziale occasionalmente va in timeout, ma la riconnessione automatica recupera rapidamente).
  • Aggiornamento e persistenza del token (attualmente, il token di autenticazione deve essere confermato manualmente sul touchscreen della stampante e viene perso al ciclo di alimentazione).
  • Test end-to-end del flusso completo PrusaSlicer → upload → stampa → monitoraggio → completamento.
Il ponte si identifica sul display della stampante — un piccolo dettaglio, ma soddisfacente.

Open Source e dichiarazione sull’IA

Il progetto snapmaker_moonraker è rilasciato sotto licenza MIT ed è disponibile su GitHub. Si basa direttamente sul lavoro protocollare SACP di sm2uploader (licenza MIT) di macdylan e snapmaker-sm2uploader di kanocz — senza il loro precedente reverse engineering del protocollo Snapmaker, questo progetto non sarebbe stato realizzabile. Contributi e feedback sono benvenuti.

In spirito di trasparenza: questo progetto è stato sviluppato con l’assistenza di Claude (Anthropic). L’architettura, la struttura del codice e l’implementazione sono state prodotte attraverso la collaborazione uomo-IA — un flusso di lavoro che abbiamo trovato notevolmente efficace per questo tipo di programmazione di sistemi a livello di protocollo.


Restate sintonizzati per la Parte 2, dove affronteremo l’integrazione del tracciamento dei filamenti Spoolman per la Snapmaker J1S.

snapmakermoonrakermainsailklipperprusaslicercrowsnestobicoopen-sourcegolangSACPraspberry-pi

Segui
Newest Art:
SHOPPING BAG 0
RECENTLY VIEWED 0