Die Snapmaker J1S ins Moonraker-Ökosystem einbinden — 3D Etplus

Bei 3D Etplus betreiben wir täglich eine Flotte von 3D-Druckern. Die meisten teilen einen gemeinsamen Arbeitsablauf: In PrusaSlicer slicen, den GCode über das Netzwerk an den Drucker senden und den Auftrag über ein Mainsail-Dashboard überwachen — alles angetrieben durch die Moonraker-API. Unsere Snapmaker J1S ist jedoch der Aussenseiter. Sie verwendet das proprietäre Snapmaker-Protokoll und ein eigenes geschlossenes Software-Ökosystem, was bedeutet, dass wir nicht in PrusaSlicer slicen und eine Datei direkt an den Drucker senden können, sie nicht vom selben Dashboard wie alles andere überwachen können und sie nicht in die Flottenmanagement-Tools einbinden können, auf die wir angewiesen sind.

Dieser Beitrag beschreibt, wie wir snapmaker_moonraker gebaut haben — eine Open-Source-Brücke, die die Snapmaker J1S wie einen Klipper-Drucker funktionieren lässt, steuerbar über Standard-Moonraker-kompatible Tools und direkt erreichbar über PrusaSlicers Netzwerkdruck-Dialog. Das Projekt ist auf GitHub verfügbar unter der MIT-Lizenz.

Der Ausgangspunkt: sm2uploader

Dieses Projekt würde ohne sm2uploader von macdylan nicht existieren — ein Go-basiertes Kommandozeilen-Tool zum Hochladen von Dateien auf Snapmaker-Drucker. sm2uploader (selbst aufbauend auf früherer Arbeit von kanocz) lieferte das entscheidende Fundament: eine funktionierende Implementierung des binären SACP-Protokolls (Snapmaker Application Communication Protocol) in Go, einschliesslich TCP-Handshake, Paket-Kodierung/-Dekodierung, CRC-Prüfsummen, Datei-Upload-Chunking und UDP-Druckererkennung.

sm2uploader bewies, dass es möglich ist, mit einem Snapmaker-Drucker von benutzerdefinierter Software aus zu kommunizieren — ohne Luban. Die Frage war: Können wir dieses Protokollwissen nehmen und in eine Moonraker-kompatible API einbetten, sodass die J1S für die Tools, die wir bereits nutzen, wie jeder andere Klipper-Drucker aussieht — angefangen bei PrusaSlicers Fähigkeit, GCode-Dateien direkt an einen Moonraker-Host zu senden?

Das Problem: Ein Drucker, zwei Welten

Unser täglicher Arbeitsablauf für jeden anderen Drucker ist unkompliziert: Ein Modell in PrusaSlicer slicen, den Netzwerk-Sende-Button drücken, um den GCode zur Moonraker-Instanz des Druckers hochzuladen, dann den Druck über Mainsail überwachen. Die Snapmaker J1S unterbricht diese Kette vollständig. Sie spricht nur SACP — ein proprietäres Binärprotokoll über TCP — und stellt keine Standard-Schnittstellen bereit. Es gibt keine Moonraker-API, keine OctoPrint-Kompatibilität und keine Möglichkeit, sie in eine Klipper-basierte Flotte einzubinden.

Das bedeutet: Kein Dateiversand aus PrusaSlicer, kein Mainsail-Dashboard, keine einheitliche Flottenübersicht und keine Möglichkeit, die J1S in dieselbe Überwachungs- und Management-Pipeline wie unsere anderen Maschinen einzubinden.

Die J1S unter ihren Klipper-sprechenden Kollegen — die eine Maschine, die nicht in den Arbeitsablauf passte.

Die Lösung: Eine protokollbasierte Brücke in Go

Aufbauend auf dem SACP-Protokollcode von sm2uploader haben wir eine Brückenanwendung in Go geschrieben, die zwischen Standard-Moonraker-Clients und dem Snapmaker-Drucker sitzt. Sie stellt eine vollständig Moonraker-kompatible HTTP- und WebSocket-API auf Port 7125 bereit — demselben Port, den echtes Moonraker nutzt — und übersetzt jede Anfrage in SACP-Binärprotokoll-Befehle, die per TCP an den Drucker gesendet werden.

Aus PrusaSlicers Sicht ist die Brücke einfach ein weiterer Moonraker-Host. Man konfiguriert ihn als Netzwerkdrucker, und der „An Drucker senden»-Button funktioniert genau wie bei jeder Klipper-Maschine. Die Brücke empfängt den hochgeladenen GCode, überträgt ihn per SACP an die J1S und kann den Druck starten — alles ohne Luban.

Alles läuft über ein einziges Protokoll:

  • SACP über TCP (Port 8888) — Die einzige Netzwerkschnittstelle der J1S. Wird verwendet für Verbindungs-Handshake, Temperaturabfragen, Datei-Uploads, Druckstatus-Abonnements, Koordinatenverfolgung, Lüfterüberwachung, GCode-Ausführung, Homing und Notstopp.
[PrusaSlicer / Mainsail / Fluidd] <--HTTP/WebSocket--> [Brücke :7125] <--SACP TCP:8888--> [J1S-Drucker]

Wir hatten zunächst angenommen, dass die J1S auch eine REST-API auf Port 8080 bereitstellen würde, wie für ältere Snapmaker-Modelle dokumentiert. Ein Portscan während der realen Tests widerlegte dies schnell — Port 8080 ist auf der J1S geschlossen. Das bedeutete, dass alles über SACP laufen musste, was sich als erhebliche ingenieurtechnische Herausforderung herausstellte, aber letztendlich eine sauberere und zuverlässigere Architektur hervorbrachte.

Die Brücke übersetzt Moonraker-API-Aufrufe in SACP-Binärprotokoll-Befehle — die einzige Netzwerkschnittstelle der J1S.

Reverse-Engineering des SACP-Protokolls

sm2uploader gab uns die Grundlagen — Verbindung, Datei-Uploads und Erkennung — aber es fragt keine Temperaturen ab, abonniert keinen Druckstatus und liest keine Koordinaten. Diese Funktionen mussten von Grund auf gebaut werden, durch Reverse-Engineering des SACP-Binärprotokolls mithilfe des Firmware-Quellcodes des SnapmakerController-IDEX und der Luban-Desktop-Anwendung.

Temperaturabfragen

Die J1S antwortet nicht auf Standard-GCode-Temperaturabfragen (M105) über SACP; sie gibt leere Antworten zurück. Wir mussten SACP-Befehlspakete direkt senden (CommandSet 0x10 für Extruder, 0x14 für das Heizbett) und die binären Antwortdaten parsen.

Die korrekte Temperaturkodierung zu finden, erforderte mehrere Reverse-Engineering-Iterationen:

  1. Erster Versuch: float32-Kodierung — Basierend auf der Snapmaker-2.0-Dokumentation. Ergebnis: 0,0°C durchgängig. Falsches Format.
  2. Zweiter Versuch: uint16 Milligrad — Basierend auf Roh-Hex-Analyse der aufgezeichneten Pakete. Teilweise funktional — eine Düse las korrekt, die andere nicht.
  3. Dritter Versuch: int32 Milligrad — Nach Studium des SnapmakerController-IDEX-Firmware-Quellcodes fanden wir die tatsächliche Kodierung: vorzeichenbehaftete 32-Bit-Ganzzahlen in Little-Endian-Byte-Reihenfolge, die Milligrad Celsius darstellen (durch 1000 teilen für °C).

Eine weitere J1S-spezifische Entdeckung: Der Dual-Extruder-Drucker sendet separate SACP-Pakete pro Düse, identifiziert durch ein HeadID-Byte im Paket-Header. Der Index pro Extruder-Datensatz ist immer null — es ist der Header, der links (T0) von rechts (T1) unterscheidet. Unser Paket-Router verschmilzt diese zu einem einheitlichen Temperaturstatus.

Drei Reverse-Engineering-Iterationen waren nötig, um das J1S-Temperaturformat korrekt zu dekodieren.

Druckstatus über SACP-Abonnements

Ohne verfügbare HTTP-API erforderte das Abrufen von Druckstatus, Fortschritt und Maschinenstatus die Implementierung des SACP-Abonnement-Mechanismus. Die Brücke abonniert periodische Datenströme von der Drucker-Firmware, indem sie Abonnementanfragen (CommandSet 0x01, CommandID 0x00) mit dem Ziel-Datentyp und einem Abfrageintervall sendet.

Wir haben Abonnements implementiert für:

  • Maschinen-Heartbeat (0x01/0xA0) — 11 verschiedene Zustände von IDLE bis COMPLETING, gemappt auf Klippers Standby/Printing/Paused-Modell.
  • Verstrichene Druckzeit (0xAC/0xA5) — Sekunden seit Druckbeginn, als uint32.
  • Aktuelle Druckzeile (0xAC/0xA0) — GCode-Zeilennummer zur Fortschrittsverfolgung.
  • Lüfterstatus (0x10/0xA3) — Lüftergeschwindigkeit pro Düse, gemappt auf Moonrakers 0,0–1,0-Skala.
  • XYZ-Koordinaten (0x01/0x30) — Achsenpositionen in Mikrometern (int32 LE, ÷1000 für mm).
  • Dateiinformationen (0xAC/0x00 und 0xAC/0x1A) — Aktuell gedruckter Dateiname, abgefragt vom Controller und dem Bildschirm-MCU.

Eine zentrale Entdeckung war, dass die J1S zwei adressierbare „Peers» auf derselben TCP-Verbindung hat: den Controller (Peer-ID 1) und den Touchscreen-MCU (Peer-ID 2). Einige Daten — wie der Dateiname des aktuellen Drucks und die Gesamtzeilenzahl — sind nur vom Bildschirm-MCU verfügbar.

Was die Brücke heute kann

Nach acht Entwicklungssitzungen und ausgiebigen Tests am realen Drucker unterstützt die Brücke einen umfassenden Satz an Moonraker-Funktionen:

  • PrusaSlicer-Netzwerkdruck — Die Brücke als Moonraker-Host in PrusaSlicer konfigurieren und GCode-Dateien direkt über das Netzwerk an die J1S senden.
  • Echtzeit-Temperaturüberwachung — Beide Extruder und Heizbett, über binäre SACP-Abfragen abgerufen und in Mainsails Temperaturdiagramm dargestellt.
  • Druckstatusverfolgung — Maschinenstatus (Leerlauf/Drucken/Pause/Abschluss), Dateiname, verstrichene Zeit und Lüftergeschwindigkeit — alles über SACP-Abonnements mit Live-Updates.
  • Live-Positionsverfolgung — XYZ-Werkzeugkopfkoordinaten und Referenzierungsstatus der Achsen, in Echtzeit aktualisiert.
  • Temperatursteuerung — Zieltemperaturen für beide Extruder und das Heizbett einstellen.
  • GCode-Ausführung — Beliebige GCode-Befehle über die Mainsail-Konsole senden.
  • Dateiverwaltung — GCode-Dateien hochladen, auflisten, herunterladen und löschen. Metadaten-Extraktion aus Slicer-Kommentaren.
  • Drucksteuerung — Drucke über die Mainsail-Oberfläche starten, pausieren, fortsetzen und abbrechen.
  • Notstopp — M112-Unterstützung für sofortigen Druckerhalt.
  • Druckererkennung — UDP-Broadcast-Erkennung zum Auffinden von Snapmaker-Druckern im lokalen Netzwerk.
  • Datenbank-API — Schlüssel-Wert-Speicher mit Namespace-Unterstützung, in JSON-Dateien persistiert.
  • Druckhistorie — Auftragsverfolgung mit Start-/Endzeiten, Dauer und kumulativen Statistiken.
  • WebSocket-Abonnements — Vollständige JSON-RPC-2.0-Unterstützung mit Live-Status-Updates, passend zum Moonraker-Abonnementmodell.
  • Obico-KI-Drucküberwachung — Vollständige Kompatibilität mit moonraker-obico, verifiziert gegen einen selbst-gehosteten Obico-Server für KI-gestützte Druckfehler-Erkennung.

Es gibt eine bekannte Einschränkung: Der Druckfortschritt in Prozent bleibt bei 0 % für Drucke, die über den Touchscreen gestartet werden. Die Bildschirm-MCU-Abfrage, die die Gesamtzeilenzahl liefert (nötig für die Prozentberechnung), läuft auf der J1S bei touchscreen-initiierten Drucken in einen Timeout. Drucke, die über Mainsail/PrusaSlicer gestartet werden, haben dieses Problem nicht, da die Brücke die Gesamtzeilenzahl der hochgeladenen Datei kennt.

Aus PrusaSlicers Sicht ist die J1S einfach ein weiterer Moonraker-Drucker im Netzwerk.
Die J1S zeigt Live-Temperaturen beider Extruder und des Heizbetts in Mainsail — wie jeder Klipper-Drucker. Vollständige Drucküberwachung und -steuerung über dieselbe Oberfläche wie bei jedem anderen Drucker in der Werkstatt.

Schlüsselfertige Bereitstellung: Raspberry-Pi-Image mit Webcam-Unterstützung

Um die Bereitstellung so einfach wie möglich zu machen, haben wir eine CI-Pipeline gebaut, die ein flashfertiges Raspberry Pi 3 SD-Karten-Image erzeugt. Die Jenkins-Pipeline cross-kompiliert das Go-Binary für ARMv7, lädt Raspberry Pi OS Lite herunter, mountet das Image in einer QEMU-emulierten ARM-Chroot-Umgebung und installiert alles:

  • nginx als Reverse-Proxy, der Mainsail auf Port 80 ausliefert und API-/WebSocket-Datenverkehr an die Brücke auf Port 7125 weiterleitet.
  • Mainsail — die neueste Version, automatisch von GitHub während des Image-Builds bezogen.
  • snapmaker_moonraker — als systemd-Dienst mit automatischem Neustart bei Fehler.
  • crowsnest — der Webcam-Daemon des Mainsail-Teams, der Kamera-Streaming für Live-Drucküberwachung direkt im Mainsail-Dashboard bereitstellt. Mit einer USB-Webcam am Pi erhalten Sie eine Live-Ansicht des Druckbetts neben Temperaturen und Fortschritt — dasselbe Setup wie auf unseren Klipper-Maschinen.
  • moonraker-obico — vorinstalliert und konfiguriert für KI-gestützte Druckfehler-Erkennung. Verbindet sich mit einem selbst-gehosteten Obico-Server mit Janus-WebRTC-Streaming für latenzarmes Video.
  • SSH aktiviert für Fernwartung, Hostname auf snapmaker gesetzt.
[Browser] → [nginx :80] → [Mainsail statische Dateien]
                        → proxy_pass → [snapmaker_moonraker :7125]
                                            ↓
                                  [Snapmaker J1S über SACP TCP:8888]

[USB-Webcam] → [crowsnest] → [MJPEG/WebRTC-Stream] → [Mainsail Webcam-Panel]
                            → [Janus WebRTC] → [Obico KI-Fehlererkennung]

Image flashen, Pi booten, USB-Webcam anschliessen, Browser auf http://snapmaker.local richten — und Sie haben ein voll funktionsfähiges Mainsail-Dashboard mit Live-Video und KI-Drucküberwachung für Ihre J1S. Der gesamte Build-Prozess ist automatisiert und erzeugt komprimierte Images, bereit für GitHub Releases.

Automatisiertes CI produziert flashbare SD-Karten-Images bei jedem getaggten Release.

Zentrale Designentscheidungen

Warum Go? Gos ausgezeichnete Cross-Compilation-Unterstützung war entscheidend. Ein einzelner GOOS=linux GOARCH=arm-Befehl erzeugt ein statisch gelinktes ARM-Binary — keine Laufzeitabhängigkeiten, kein Interpreter, keine Bibliotheks-Versionierungsprobleme auf dem Pi. Gos eingebaute Nebenläufigkeitsprimitive (Goroutinen, Channels, Mutexe) machten auch das asynchrone SACP-Paket-Routing einfach — die Brücke führt eine dedizierte Paket-Router-Goroutine aus, die eingehende SACP-Antworten über Channels, indexiert nach Sequenznummer, an wartende Aufrufer demultiplext.

Warum den SACP-Code von sm2uploader vendoren? sm2uploader ist ein eigenständiges Programm (Gos package main), was bedeutet, dass es nicht als Bibliothek von anderen Go-Projekten importiert werden kann. Statt das Projekt eines anderen zu forken und umzustrukturieren, haben wir den Protokoll-Code in ein sauberes sacp/-Paket übernommen, es erheblich um Temperatur-Parsing, Abonnement-Verwaltung, Multi-Peer-Adressierung und Status-Dekodierung erweitert und volle Namensnennung an macdylan und kanocz gegeben.

Reine SACP-Architektur: Wir hatten ursprünglich einen Dual-Protokoll-Ansatz geplant — SACP für binäre Operationen und die Snapmaker-HTTP-API für GCode-Ausführung und Status-Polling. Tests am echten Drucker zeigten, dass die J1S überhaupt keine HTTP-API hat (Port 8080 ist geschlossen). Das erzwang eine komplette Neuschreibung, um alles über SACP zu routen, einschliesslich Druckstatus über den Abonnement-Mechanismus und GCode-Ausführung über SACP-Befehlspakete. Das Ergebnis ist tatsächlich sauberer: Eine einzige TCP-Verbindung wickelt die gesamte Kommunikation ab, und die abonnement-basierten Status-Updates sind effizienter als periodisches HTTP-Polling gewesen wäre.

State/StateData-Aufteilungsmuster: Gos sync.RWMutex kann nicht sicher als Wert kopiert werden, aber wir mussten Momentaufnahmen des Druckerstatus frei herumreichen können. Die Lösung war, den mutierbaren, mutex-geschützten State-Container vom einfachen StateData-Werttyp zu trennen. Eine Snapshot()-Methode gibt eine sichere Kopie der Daten ohne den Lock zurück.

Nächste Schritte: Flottenintegration vervollständigen

Die Brücke bietet uns heute den Kern-Arbeitsablauf, den wir wollten: In PrusaSlicer slicen, über das Netzwerk an die J1S senden, den Druck in Mainsail mit Live-Webcam-Feed überwachen und KI-Fehlererkennung von Obico erhalten. Das grössere Ziel — die Netzwerkverwaltung der Snapmaker vollständig mit den restlichen 3D-Druckern zu vereinheitlichen, die 3D Etplus täglich betreibt — ist gut fortgeschritten. Das verbleibende Integrationsziel ist:

  • Spoolman für Filament-Tracking — Verfolgung der Spulennutzung, des verbleibenden Filaments und der Materialtypen über alle Drucker hinweg, einschliesslich der J1S. Dies ist essentiell für einen Multi-Drucker-Betrieb, wo das Wissen über die verbleibende Filamentmenge auf jeder Spule Zeit spart und Ausfälle mitten im Druck verhindert.

Diese Integration ist noch nicht abgeschlossen, aber sie ist der natürliche nächste Meilenstein des Projekts. Ihre Umsetzung wird bedeuten, dass die J1S ein vollwertiges Mitglied unserer Druckfarm ist — überwacht, verwaltet und nachverfolgt mit denselben Tools wie jede andere Maschine. Die Spoolman-Integration behandeln wir in einem Folgebeitrag.

Ausserdem stehen mehrere technische Verbesserungen auf der Roadmap:

  • Lösung des 0-%-Fortschrittsproblems für touchscreen-initiierte Drucke.
  • Verbesserung der SACP-Verbindungszuverlässigkeit (die initiale Verbindung läuft gelegentlich in einen Timeout, aber die automatische Wiederverbindung greift schnell).
  • Token-Aktualisierung und -Persistenz (derzeit muss das Authentifizierungstoken manuell auf dem Touchscreen des Druckers bestätigt werden und geht bei einem Stromzyklus verloren).
  • Ende-zu-Ende-Tests des vollständigen PrusaSlicer → Upload → Druck → Überwachung → Abschluss-Arbeitsablaufs.
Die Brücke identifiziert sich auf dem Display des Druckers — ein kleines, aber befriedigendes Detail.

Open Source & KI-Offenlegung

Das Projekt snapmaker_moonraker wird unter der MIT-Lizenz veröffentlicht und ist auf GitHub verfügbar. Es baut direkt auf der SACP-Protokollarbeit von sm2uploader (MIT-Lizenz) von macdylan und snapmaker-sm2uploader von kanocz auf — ohne ihr vorheriges Reverse-Engineering des Snapmaker-Protokolls wäre dieses Projekt nicht realisierbar gewesen. Beiträge und Feedback sind willkommen.

Im Sinne der Transparenz: Dieses Projekt wurde mit Unterstützung von Claude (Anthropic) entwickelt. Die Architektur, Code-Struktur und Implementierung entstanden durch Mensch-KI-Zusammenarbeit — ein Arbeitsablauf, den wir als bemerkenswert effektiv für diese Art von Protokoll-Level-Systemprogrammierung empfunden haben.


Bleiben Sie dran für Teil 2, in dem wir die Spoolman-Filament-Tracking-Integration für die Snapmaker J1S behandeln.

snapmakermoonrakermainsailklipperprusaslicercrowsnestobicoopen-sourcegolangSACPraspberry-pi

Folgen
Newest Art:
SHOPPING BAG 0
RECENTLY VIEWED 0