Intégration du Snapmaker J1S à l’écosystème Moonraker

Chez 3D Etplus, nous exploitons quotidiennement une flotte d’imprimantes 3D. La plupart suivent un flux de travail commun : découpe dans PrusaSlicer , envoi du G-code à l’imprimante via le réseau et suivi de l’impression depuis un tableau de bord Mainsail , le tout grâce à l’ API Moonraker . Notre Snapmaker J1S fait exception. Elle utilise le protocole propriétaire de Snapmaker et son propre écosystème logiciel fermé, ce qui signifie que nous ne pouvons pas découper le modèle dans PrusaSlicer et envoyer un fichier directement à l’imprimante, ni la suivre depuis le même tableau de bord que les autres imprimantes, ni l’intégrer aux outils de gestion de flotte dont nous dépendons.

Cet article explique comment nous avons créé snapmaker_moonraker , un pont open source qui permet à l’imprimante Snapmaker J1S de se comporter comme une imprimante Klipper, contrôlable depuis les outils compatibles Moonraker et directement accessible via la boîte de dialogue d’impression réseau de PrusaSlicer. Le projet est disponible sur GitHub sous licence MIT.

Point de départ : sm2uploader

Ce projet n’existerait pas sans sm2uploader de macdylan , un outil en ligne de commande basé sur Go permettant de transférer des fichiers vers les imprimantes Snapmaker. sm2uploader (lui-même basé sur un travail antérieur de kanocz ) a fourni la base essentielle : une implémentation fonctionnelle du protocole binaire SACP (Snapmaker Application Communication Protocol) de Snapmaker en Go, incluant la négociation TCP, l’encodage/décodage des paquets, les sommes de contrôle CRC, le découpage des fichiers transférés et la découverte des imprimantes UDP.

sm2uploader a prouvé qu’il était possible de communiquer avec une imprimante Snapmaker depuis un logiciel personnalisé sans Luban. La question était : pouvions-nous exploiter cette connaissance du protocole et l’intégrer dans une API compatible avec Moonraker, afin que la J1S soit reconnue comme n’importe quelle autre imprimante Klipper par les outils que nous utilisons déjà — à commencer par la capacité de PrusaSlicer à envoyer directement des fichiers GCode à un hôte Moonraker ?

Le problème : Une imprimante, deux mondes

Notre flux de travail quotidien pour toutes les autres imprimantes est simple : découper un modèle dans PrusaSlicer, cliquer sur le bouton d’envoi réseau pour transférer le G-code vers l’instance Moonraker de l’imprimante, puis surveiller l’impression depuis Mainsail. La Snapmaker J1S rompt complètement ce processus. Elle utilise uniquement le protocole SACP (un protocole binaire propriétaire sur TCP) et n’expose aucune interface standard. Il n’y a pas d’API Moonraker, pas de compatibilité OctoPrint, et aucune possibilité de l’intégrer à un parc d’imprimantes basé sur Klipper.

Cela signifie qu’il est impossible d’envoyer des fichiers depuis PrusaSlicer, d’accéder au tableau de bord Mainsail, de visualiser l’ensemble du parc de machines et d’intégrer le J1S au même système de surveillance et de gestion que nos autres machines.

Parmi ses homologues basés sur Klipper, le J1S était la seule machine qui ne correspondait pas au flux de travail.

La solution : un pont de protocole basé sur Go

En nous appuyant sur le code du protocole SACP de sm2uploader , nous avons développé une application passerelle en Go qui fait le lien entre les clients Moonraker standard et l’imprimante Snapmaker. Elle expose une API HTTP et WebSocket entièrement compatible avec Moonraker sur le port 7125 (le même port que celui utilisé par Moonraker) et traduit chaque requête en commandes binaires du protocole SACP envoyées à l’imprimante via TCP.

Du point de vue de PrusaSlicer, le pont est un simple hôte Moonraker. Il suffit de le configurer comme une imprimante réseau, et le bouton « Envoyer à l’imprimante » fonctionne exactement comme pour n’importe quelle machine Klipper. Le pont reçoit le G-code téléchargé, le transfère au J1S via SACP et peut lancer l’impression, le tout sans intervention sur Luban.

Tout passe par un protocole unique :

  • SACP sur TCP (port 8888) — Seule interface réseau du J1S. Utilisée pour l’établissement de la connexion, les requêtes de température, le chargement de fichiers, l’abonnement aux états d’impression, le suivi des coordonnées, la surveillance des ventilateurs, l’exécution du G-code, le retour à l’origine et l’arrêt d’urgence.
[PrusaSlicer / Mainsail / Fluidd] <--HTTP/WebSocket--> [Bridge :7125] <--SACP TCP:8888--> [J1S Printer]

Nous avions initialement supposé que le J1S exposerait également une API REST sur le port 8080, comme indiqué dans la documentation pour les anciens modèles Snapmaker. Un scan de ports effectué lors de tests en conditions réelles a rapidement démontré le contraire : le port 8080 est fermé sur le J1S. Par conséquent, toutes les communications ont dû transiter par SACP, ce qui a représenté un défi technique important, mais a finalement permis de concevoir une architecture plus propre et plus fiable.

Le pont traduit les appels d’API Moonraker en commandes de protocole binaire SACP — la seule interface réseau du J1S.

Rétro-ingénierie du protocole SACP

sm2uploader nous fournissait les fonctionnalités de base (connexion, chargement de fichiers et détection), mais pas la consultation des températures, l’abonnement à l’état d’impression ni la lecture des coordonnées. Ces fonctionnalités ont dû être entièrement développées par rétro-ingénierie du protocole binaire SACP à partir du code source du firmware SnapmakerController-IDEX et de l’application de bureau Luban.

Requêtes de température

L’imprimante J1S ne répond pas aux requêtes de température GCode standard (M105) via SACP ; elle renvoie des réponses vides. Nous avons donc dû envoyer directement des paquets de commandes SACP (CommandSet 0x10 pour les extrudeuses, 0x14 pour le plateau chauffant) et analyser les données binaires de la réponse.

La mise au point d’un codage de température correct a nécessité plusieurs itérations de rétro-ingénierie :

  1. Première tentative : encodage float32 — D’après la documentation de Snapmaker 2.0. Résultat : 0,0 °C partout. Format incorrect.
  2. Deuxième tentative : millidegrés uint16 — Basée sur l’analyse hexadécimale brute des paquets capturés. Fonctionnement partiel : une buse a fonctionné correctement, l’autre non.
  3. Troisième tentative : millidegrés int32 — Après avoir étudié le code source du firmware SnapmakerController-IDEX, nous avons trouvé l’encodage réel : des entiers signés de 32 bits en ordre d’octets little-endian, représentant des millidegrés Celsius (diviser par 1000 pour °C).

Autre découverte spécifique à la J1S : l’imprimante à double extrudeuse envoie des paquets SACP distincts pour chaque buse , identifiés par un octet HeadID dans l’en-tête du paquet. L’index d’enregistrement par extrudeuse est toujours nul ; c’est l’en-tête qui distingue la buse gauche (T0) de la buse droite (T1). Notre routeur de paquets fusionne ces données en un état de température unique.

Trois itérations de rétro-ingénierie ont été nécessaires pour décoder correctement le format de température J1S.

État d’impression via les abonnements SACP

En l’absence d’API HTTP, l’obtention de l’état d’impression, de la progression et de l’état de la machine nécessitait la mise en œuvre du mécanisme d’abonnement de SACP. Le pont s’abonne aux flux de données périodiques du micrologiciel de l’imprimante en envoyant des requêtes d’abonnement (CommandSet 0x01, CommandID 0x00) avec le type de données cible et un intervalle d’interrogation.

Nous avons mis en place des abonnements pour :

  • Battement de cœur de la machine (0x01/0xA0) — 11 états distincts de IDLE à COMPLETING, mappés sur le modèle de veille/impression/pause de Klipper.
  • Afficher le temps écoulé (0xAC/0xA5) — Secondes depuis le début de l’impression, sous forme de uint32.
  • Ligne d’impression actuelle (0xAC/0xA0) — Numéro de ligne GCode pour le suivi de la progression.
  • État du ventilateur (0x10/0xA3) — Vitesse du ventilateur par buse, mappée sur l’échelle 0,0–1,0 de Moonraker.
  • Coordonnées XYZ (0x01/0x30) — Positions des axes en microns (int32 LE, ÷1000 pour mm).
  • Informations sur le fichier (0xAC/0x00 et 0xAC/0x1A) — En cours d’impression du nom du fichier, interrogé à la fois par le contrôleur et le microcontrôleur de l’écran.

Une découverte majeure a été que le J1S possède deux « pairs » adressables sur la même connexion TCP : le contrôleur (ID du pair 1) et le microcontrôleur de l’écran tactile (ID du pair 2). Certaines données, comme le nom du fichier d’impression et le nombre total de lignes, ne sont disponibles que depuis le microcontrôleur de l’écran.

Ce que le pont peut faire aujourd’hui

Après huit sessions de développement et de nombreux tests sur imprimante réelle, le pont prend en charge un ensemble complet de fonctionnalités Moonraker :

  • Impression réseau PrusaSlicer — Configurez le pont comme hôte Moonraker dans PrusaSlicer et envoyez les fichiers GCode directement au J1S via le réseau.
  • Surveillance de la température en temps réel — Double extrudeuse et lit chauffant, interrogés via des requêtes binaires SACP et affichés dans le graphique de température de Mainsail.
  • Suivi de l’état d’impression — État de la machine (inactif/impression/en pause/en cours), nom du fichier, temps écoulé et vitesse du ventilateur — le tout via des abonnements SACP avec mises à jour en direct.
  • Suivi de position en direct — Coordonnées XYZ de la tête d’outil et état des axes de retour à l’origine, mis à jour en temps réel.
  • Contrôle de la température — Définir les températures cibles pour les extrudeuses et le lit chauffant.
  • Exécution de GCode — Envoi de commandes GCode arbitraires via la console Mainsail.
  • Gestion des fichiers — Chargement, liste, téléchargement et suppression des fichiers GCode. Extraction des métadonnées à partir des commentaires du découpeur.
  • Contrôle de l’impression — Démarrer, mettre en pause, reprendre et annuler les impressions depuis l’interface Mainsail.
  • Arrêt d’urgence — Prise en charge M112 pour l’arrêt immédiat de l’imprimante.
  • Détection d’imprimantes — Recherche par diffusion UDP pour trouver les imprimantes Snapmaker sur le réseau local.
  • API de base de données — Stockage clé-valeur avec prise en charge des espaces de noms, persistance dans des fichiers JSON.
  • Historique d’impression — Suivi des tâches avec heures de début/fin, durée et statistiques cumulatives.
  • Abonnements WebSocket — Prise en charge complète de JSON-RPC 2.0 avec mises à jour d’état en direct, correspondant au modèle d’abonnement de Moonraker.
  • Surveillance d’impression par IA Obico — Compatibilité totale avec moonraker-obico , fonctionnement vérifié sur un serveur Obico auto-hébergé pour la détection des pannes d’impression par IA.

Il existe une limitation connue : le pourcentage de progression d’impression reste à 0 % pour les impressions lancées depuis l’écran tactile. La requête MCU de l’écran, qui fournit le nombre total de lignes (nécessaire au calcul du pourcentage), expire sur le J1S pour les impressions initiées depuis l’écran tactile. Les impressions lancées via Mainsail/PrusaSlicer ne présentent pas ce problème, car le pont connaît le nombre total de lignes grâce au fichier chargé.

Du point de vue de PrusaSlicer, la J1S n’est qu’une imprimante Moonraker de plus sur le réseau.
La J1S affiche en temps réel les températures des deux extrudeuses et du plateau chauffant dans Mainsail, comme n’importe quelle imprimante Klipper. Le suivi et le contrôle complets de l’impression se font depuis la même interface que celle utilisée pour toutes les autres imprimantes de l’atelier.

Déploiement clé en main : image Raspberry Pi avec prise en charge de la webcam

Pour simplifier au maximum le déploiement, nous avons créé un pipeline d’intégration continue qui génère une image de carte SD Raspberry Pi 3 prête à être flashée. Ce pipeline Jenkins compile le binaire Go pour ARMv7, télécharge Raspberry Pi OS Lite, monte l’image dans un environnement chroot ARM émulé par QEMU et installe tous les composants nécessaires.

  • nginx en tant que proxy inverse, servant Mainsail sur le port 80 et redirigeant le trafic API/WebSocket vers le pont sur le port 7125.
  • Mainsail — la dernière version, téléchargée automatiquement depuis GitHub lors de la création de l’image.
  • snapmaker_moonraker — exécuté en tant que service systemd avec redémarrage automatique en cas d’échec.
  • Crowsnest , le service de webcam de l’équipe Mainsail, permet de suivre l’impression en direct depuis le tableau de bord Mainsail. Grâce à une webcam USB connectée au Raspberry Pi, vous visualisez en direct le plateau d’impression, ainsi que les températures et la progression de l’impression — la même configuration que sur nos machines Klipper.
  • moonraker-obico — préinstallé et configuré pour la détection des défauts d’impression par IA. Se connecte à un serveur Obico auto-hébergé avec streaming Janus WebRTC pour une vidéo à faible latence.
  • SSH activé pour la gestion à distance, nom d’hôte défini sur snapmaker.
[Browser] → [nginx :80] → [Mainsail static files]
                        → proxy_pass → [snapmaker_moonraker :7125]
                                            ↓
                                  [Snapmaker J1S via SACP TCP:8888]

[USB Webcam] → [crowsnest] → [MJPEG/WebRTC stream] → [Mainsail webcam panel]
                            → [Janus WebRTC] → [Obico AI failure detection]

Flashez l’image, démarrez le Raspberry Pi, branchez une webcam USB, ouvrez votre navigateur à l’adresse indiquée http://snapmaker.local, et vous obtenez un tableau de bord Mainsail entièrement fonctionnel avec surveillance vidéo en direct et impression IA pour votre J1S. L’ensemble du processus de compilation est automatisé et génère des images compressées prêtes pour les publications sur GitHub.

[Capture d’écran : Tableau de bord Mainsail avec flux webcam en direct du plateau d’impression J1S] Crowsnest diffuse une vue webcam en direct directement dans l’interface Mainsail — comme pour toute configuration Klipper.

Décisions clés en matière de conception

Pourquoi Go ? L’excellente prise en charge de la compilation croisée par Go était essentielle. Une simple GOOS=linux GOARCH=armcommande génère un binaire ARM lié statiquement : aucune dépendance d’exécution, aucun interpréteur, aucun problème de versionnage de bibliothèque sur le Raspberry Pi. Les primitives de concurrence intégrées à Go (goroutines, canaux, mutex) ont également simplifié le routage asynchrone des paquets SACP : le pont exécute une goroutine de routage de paquets dédiée qui démultiplexe les réponses SACP entrantes pour les appelants en attente via des canaux associés à chaque numéro de séquence.

Pourquoi intégrer le code SACP de sm2uploader dans un package externe ? sm2uploader est un programme autonome (de Go package main), ce qui signifie qu’il ne peut pas être importé comme bibliothèque par d’autres projets Go. Plutôt que de dupliquer et de restructurer un projet existant, nous avons intégré le code du protocole dans un sacp/package propre, l’avons considérablement enrichi avec l’analyse de la température, la gestion des abonnements, l’adressage multi-pairs et le décodage d’état, et avons pleinement crédité macdylan et kanocz .

Architecture exclusivement SACP : Initialement, nous avions prévu une approche à double protocole — SACP pour les opérations binaires et l’API HTTP Snapmaker pour l’exécution du G-code et la consultation de l’état. Les tests en conditions réelles ont révélé que le J1S ne dispose d’aucune API HTTP (le port 8080 est fermé). Cela a nécessité une réécriture complète pour acheminer toutes les communications via SACP, y compris l’affichage de l’état par le biais du mécanisme d’abonnement et l’exécution du G-code via les paquets de commandes SACP. Le résultat est en réalité plus élégant : une seule connexion TCP gère toutes les communications, et les mises à jour d’état par abonnement sont plus efficaces qu’une consultation HTTP périodique.

Modèle de séparation État/StateData : en Go, sync.RWMutexla copie par valeur est impossible en toute sécurité, or nous devions pouvoir transmettre librement des instantanés de l’état de l’imprimante. La solution a consisté à séparer le Stateconteneur mutable, protégé par un mutex, du StateDatatype valeur simple. Une Snapshot()méthode renvoie une copie sécurisée des données sans verrouillage.

Prochaines étapes : finaliser l’intégration de la flotte

Aujourd’hui, la passerelle nous offre le flux de travail essentiel souhaité : découpe dans PrusaSlicer, envoi vers la J1S via le réseau, surveillance de l’impression dans Mainsail grâce à un flux webcam en direct et détection des pannes par IA via Obico . L’objectif plus large – unification complète de la gestion réseau de la Snapmaker avec celle des autres imprimantes 3D exploitées quotidiennement par 3D Etplus – est en bonne voie. Les prochaines étapes d’intégration sont :

  • Spoolman pour le suivi du filament : Suivi de l’utilisation des bobines, du filament restant et des types de matériaux sur toutes les imprimantes, y compris la J1S. Indispensable pour une utilisation multi-imprimantes, connaître la quantité de filament restante sur chaque bobine permet un gain de temps et évite les interruptions d’impression.

Cette intégration n’est pas encore terminée, mais elle constitue la prochaine étape logique du projet. Une fois réalisée, la J1S sera pleinement intégrée à notre parc d’impression : surveillée, gérée et traçable grâce aux mêmes outils que toutes les autres machines. Nous aborderons l’intégration de Spoolman dans un prochain article.

Plusieurs améliorations techniques figurent également sur la feuille de route :

  • Résolution du problème de progression à 0 % pour les impressions lancées via l’écran tactile.
  • Amélioration de la fiabilité de la connexion SACP (la connexion initiale expire parfois, mais la reconnexion automatique se rétablit rapidement).
  • Actualisation et persistance du jeton (actuellement, le jeton d’authentification doit être confirmé manuellement sur l’écran tactile de l’imprimante et est perdu lors d’une mise hors tension).
  • Tests de bout en bout du flux de travail complet PrusaSlicer → téléchargement → impression → surveillance → flux de travail complet.
Le pont s’affiche sur l’écran de l’imprimante — un petit détail, mais ô combien satisfaisant.

Divulgation des logiciels libres et de l’IA

Le projet snapmaker_moonraker est distribué sous licence MIT et disponible sur GitHub . Il s’appuie directement sur le protocole SACP développé dans sm2uploader (licence MIT) par macdylan et snapmaker-sm2uploader par kanocz ; sans leur rétro-ingénierie préalable du protocole Snapmaker, ce projet n’aurait pas été possible. Les contributions et les commentaires sont les bienvenus.

Dans un souci de transparence : ce projet a été développé avec l’aide de Claude (Anthropic). L’architecture, la structure du code et l’implémentation ont été réalisées grâce à une collaboration homme-IA – une méthode que nous avons jugée particulièrement efficace pour ce type de programmation système au niveau protocole, tirant parti de projets open source existants qui prenaient en charge différentes parties des tâches requises.


Gardez l’oeil ouvert pour la partie 2, où nous aborderons l’intégration du suivi des filaments Spoolman pour la Snapmaker J1S.

Suivez nous
Newest Art:
SHOPPING BAG 0
RECENTLY VIEWED 0