# Du Prototype à la Production : snapmaker_moonraker atteint la v1.1.0
En février, nous avons publié [Connecter la Snapmaker J1S à l’écosystème Moonraker](/bridging-the-snapmaker-j1s-to-the-moonraker-ecosystem/), où nous décrivions comment nous avons construit un pont protocolaire open-source qui permet à la Snapmaker J1S de se comporter comme une imprimante Klipper. Au moment de la rédaction, le pont pouvait envoyer des fichiers depuis PrusaSlicer, surveiller les températures, suivre les impressions et diffuser le flux webcam — mais il manquait encore des éléments. L’intégration Spoolman figurait sur la feuille de route. Le contrôle du double extrudeur était incomplet. Les capacités IDEX de la J1S — les modes Copy et Mirror — n’avaient pas été abordées.
Quatre semaines et quarante commits plus tard, chaque point de cette feuille de route est réalisé, et le pont a acquis des fonctionnalités que nous n’avions pas initialement prévues. Cet article couvre le parcours de la v0.0.7 à la v1.1.0 : ce qui a changé, ce que nous avons appris, et où en est le projet aujourd’hui.
### Le Problème de Token Qui N’en Était Pas Un
L’article original mentionnait le « rafraîchissement et la persistance du token » comme un défi à venir. Les imprimantes Snapmaker authentifient les connexions HTTP API avec un token qui doit être confirmé en appuyant sur l’écran tactile, et ce token est perdu à chaque coupure d’alimentation. Nous nous attendions à devoir implémenter un mécanisme de rafraîchissement de token.
Il s’avère que ce n’est pas nécessaire. Le protocole SACP de la J1S sur le port 8888 — le seul canal de communication utilisé par le pont — ne requiert aucune authentification par token. Le token n’était pertinent que pour le HTTP API sur le port 8080, dont nous avons confirmé très tôt qu’il est fermé sur la J1S. Puisque le pont fait tout transiter par SACP, le problème de confirmation sur l’écran tactile ne s’applique tout simplement pas. Le champ token existe toujours dans le fichier de configuration, mais il est vestigial — un souvenir du chemin HTTP API que nous n’avons jamais emprunté.
C’est un bon exemple de la façon dont les contraintes peuvent simplifier une conception. Être contraint à une communication exclusivement SACP semblait initialement être une limitation, mais cela a éliminé toute une catégorie de problèmes d’authentification et de gestion de sessions.
### Apprendre au Pont à Parler GCode : Le Post-Processeur
Le sous-système le plus significatif est le post-processeur GCode, introduit dans la v0.0.8. Le problème qu’il résout est subtil mais critique : l’écran tactile HMI de la Snapmaker J1S lit les métadonnées à partir d’un bloc d’en-tête personnalisé au début du fichier GCode. Sans cet en-tête, l’écran tactile n’affiche aucune information d’impression — pas de temps estimé, pas de type de matériau, pas d’aperçu miniature. Luban génère ces en-têtes automatiquement ; PrusaSlicer non.
Le post-processeur s’exécute en pipeline à deux passes lors de chaque téléversement de fichier.
**La passe 1** parcourt l’intégralité du fichier pour extraire les métadonnées : températures par extrudeur à partir des commandes M104/M109, température du plateau à partir de M140/M190, boîte englobante à partir des mouvements G0/G1, utilisation de filament par outil en millimètres (en suivant l’extrusion absolue et relative séparément pour chaque outil), hauteur de couche, temps d’impression estimé, et type de filament à partir des commentaires du slicer. Elle détecte également les commandes M605 pour la sélection du mode IDEX — nous y reviendrons.
**La passe 2** transforme le GCode ligne par ligne. Trois transformations s’y produisent :
**Renumérotation des outils.** La configuration double extrudeur de PrusaSlicer attribue les outils en tant que T2 et T3 (un par imprimante physique dans son modèle interne). La J1S attend T0 et T1. Le post-processeur remappe tous les numéros d’outils modulo 2 — T2 devient T0, T3 devient T1 — pour les commandes de changement d’outil, les commandes de température (M104/M109) et les commandes de ventilateur (M106/M107).
**Extinction de la buse inutilisée.** Lorsqu’un changement d’outil se produit et que le post-processeur sait, grâce à l’analyse de la première passe, que l’outil précédent ne sera plus utilisé dans le fichier, il injecte `M104 S0 Tx` pour éteindre ce chauffage. Sur une imprimante à double extrudeur où de nombreux travaux n’utilisent qu’une buse pour une partie de l’impression, cela évite qu’un hotend inactif reste inutilement en température — économisant de l’énergie et réduisant le risque de heat creep.
**Génération de l’en-tête Snapmaker V1.** Le post-processeur construit un en-tête de métadonnées de 25 lignes au format V1 de Snapmaker, contenant le nom de l’imprimante, le temps estimé, le nombre total de lignes, les champs par extrudeur (diamètre de la buse, matériau, température, paramètres de rétraction), la température du plateau, la boîte englobante de la zone de travail et — point crucial — le mode extrudeur IDEX.
L’en-tête inclut également une **miniature**. Le post-processeur extrait les blocs miniatures de PrusaSlicer et OrcaSlicer (données PNG encodées en base64 intégrées dans les commentaires GCode) et les convertit au format data URI attendu par l’écran tactile de la J1S. Cela signifie que les fichiers tranchés téléversés depuis Mainsail affichent des aperçus miniatures corrects sur l’écran de l’imprimante — un petit détail qui fait une vraie différence lorsqu’on gère plusieurs impressions en file d’attente.
Le post-processeur est idempotent : s’il détecte un marqueur `;Header Start` déjà présent, il retourne le fichier inchangé.
### Contrôle Natif de l’Impression : Éliminer la Couche Intermédiaire
Le pont original contrôlait les impressions en envoyant des commandes GCode (M24 pour reprendre, M25 pour mettre en pause) via le canal d’exécution GCode de SACP. Cela fonctionnait, mais c’était un chemin indirect — nous demandions au firmware d’interpréter des commandes textuelles alors que nous pouvions lui parler dans son protocole binaire natif.
Dans la v0.1.0, le contrôle d’impression a été réécrit pour utiliser les commandes SACP natives : `0xAC/0x04` pour annuler, `0xAC/0x05` pour mettre en pause, `0xAC/0x06` pour reprendre. Le pont a également acquis une séquence de démarrage d’impression appropriée utilisant `sacp.StartScreenPrint()`, qui initie l’impression via le MCU de l’écran tactile — garantissant que le HMI reste synchronisé avec l’état réel de l’impression.
Une amélioration connexe dans la v0.0.8 a été le **schéma de double déconnexion pour le téléversement**. Lors du téléversement d’un fichier, nous avons découvert que l’écran tactile de la J1S a besoin de temps pour indexer le nouveau fichier avant de pouvoir l’imprimer. La solution est une séquence spécifique : téléverser le fichier, se déconnecter de SACP, attendre trois secondes que le HMI finalise son index de fichiers, se reconnecter, puis envoyer la commande d’impression. Sans cette pause, l’écran tactile ne reconnaît pas le fichier et l’impression ne démarre pas. C’est l’un de ces comportements qu’aucune documentation ne décrit — il a fallu des tests répétés sur l’imprimante réelle pour l’identifier et le résoudre.
La séquence de téléversement a également gagné une capacité de **démarrage automatique** : lorsqu’un fichier est téléversé depuis Mainsail avec l’option « démarrer l’impression », le pont gère automatiquement le cycle complet téléversement → déconnexion → reconnexion → démarrage.
### Contrôle Double Extrudeur : Deux Buses, Deux Ventilateurs, Deux Bobines
Le pont original pouvait lire les températures des deux extrudeurs mais ne pouvait pas les contrôler indépendamment. La v0.2.0 a introduit le contrôle complet de température des deux extrudeurs, en interceptant les commandes M104/M109 (extrudeur) et M140/M190 (plateau) depuis la console Mainsail et en les acheminant via les commandes SACP `SetToolTemperature` (CommandSet `0x10`, CommandID `0x02`) et `SetBedTemperature` (`0x14/0x02`). La commande Klipper `ACTIVATE_EXTRUDER` permet de changer quel extrudeur Mainsail considère comme « actif ».
La v1.0.0 a étendu cela au **contrôle des ventilateurs**. La J1S dispose de ventilateurs de refroidissement de pièce indépendants par extrudeur, et le pont les expose désormais comme des objets ventilateur Moonraker séparés — `extruder_partfan` et `extruder1_partfan`. Les commandes M106/M107 sont dirigées vers le ventilateur physique approprié en fonction du paramètre P (avec le même remappage modulo 2 que les numéros d’outils), et la commande Klipper `SET_FAN_SPEED` fonctionne pour le contrôle direct des ventilateurs depuis l’interface Mainsail.
### Spoolman : D’une Seule Bobine au Suivi Par Extrudeur
L’article original mentionnait l’intégration Spoolman comme le principal jalon restant. La v0.0.7 a livré l’implémentation initiale : parcourir et sélectionner des bobines depuis le panneau Spoolman de Mainsail, rapporter la consommation de filament au serveur Spoolman pendant les impressions, et vérifier la connexion Spoolman.
Mais la première implémentation ne suivait qu’une seule bobine — suffisant pour les travaux mono-extrudeur, incorrect pour une imprimante à double extrudeur. La v1.0.0 a reconstruit le suivi pour fonctionner par extrudeur.
Les modifications ont touché chaque couche :
**Le stockage en base de données** est passé d’une clé unique `spoolman.spool_id` à `spoolman.spool_id.0` et `spoolman.spool_id.1`, avec migration automatique de l’ancienne clé vers l’outil 0 au premier démarrage.
**L’API** a gagné un paramètre `tool` sur les endpoints HTTP (`GET/POST /server/spoolman/spool_id`) et les handlers WebSocket RPC, correspondant à l’API Moonraker étendue que le panneau Spoolman de Mainsail utilise pour les configurations multi-extrudeurs.
**Le suivi du filament** dans le post-processeur GCode a été corrigé pour suivre l’extrusion par outil indépendamment. L’accumulateur unique `lastAbsE` a été divisé en `lastAbsE[2]`, un par extrudeur. Sans cette correction, un travail à double extrudeur rapportait toute la consommation de filament sur le dernier outil actif.
**Le rapport d’utilisation** pendant les impressions calcule les deltas par outil à partir du numéro de ligne GCode (mis en correspondance avec les tableaux cumulatifs de filament par outil extraits lors du post-traitement) et envoie des requêtes PUT indépendantes au serveur Spoolman pour la bobine de chaque outil. Les rapports ne se déclenchent que lorsque le delta dépasse 0,1 mm, évitant le bruit des mouvements de déplacement et des rétractions.
Un dernier élément est arrivé après la v1.0.0 : le pont expose désormais un `tool_spool_map` dans la réponse API Spoolman, qui est ce que le panneau Spoolman multi-extrudeurs de Mainsail lit pour afficher les assignations de bobines par outil. Sans ce champ, Mainsail revient au mode mono-bobine quel que soit le nombre de bobines configurées côté serveur.
Le résultat : Mainsail affiche la bonne bobine assignée à chaque extrudeur, la consommation de filament est suivie indépendamment, et le serveur Spoolman dispose de données de consommation précises par bobine — la même configuration que nous utilisons sur nos machines Klipper mono-extrudeur, mais étendue pour la double extrusion.
### Mode IDEX Copy et Mirror
La J1S est une imprimante IDEX (Independent Dual Extrusion), ce qui signifie que ses deux têtes d’impression peuvent se déplacer indépendamment. Au-delà de l’impression bi-matière standard, l’IDEX offre deux modes de productivité : **Copy** (les deux têtes impriment la même pièce simultanément, doublant le débit) et **Mirror** (les deux têtes impriment des copies en miroir, utile pour les pièces symétriques). Faire fonctionner ces modes à travers le pont a nécessité la coordination de trois mécanismes distincts.
**Détection GCode.** PrusaSlicer signale le mode IDEX via `M605 S2` (Copy/Duplication) ou `M605 S3` (Mirror) dans le GCode de démarrage. La première passe du post-processeur détecte ces commandes et enregistre le mode.
**En-tête V1.** Le mode détecté est écrit dans l’en-tête Snapmaker V1 sous la forme `;Extruder Mode:Duplication` ou `;Extruder Mode:Mirror`. C’était la découverte critique — le HMI de la J1S lit ce champ d’en-tête pour configurer le mode d’impression avant d’exécuter le GCode. Sans l’en-tête correct, l’imprimante ignore complètement M605 et imprime en mode par défaut.
**Commande SACP SetPrintMode.** Comme mesure de fiabilité supplémentaire, le pont envoie la commande SACP `0xAC/0x0A` avec l’octet de mode approprié (`0x02` pour Duplication, `0x03` pour Mirror) après le téléversement et avant le démarrage de l’impression. Cela garantit que le firmware est dans le bon mode même si l’analyse de l’en-tête par le HMI présente des cas limites.
La v1.1.0 est également livrée avec **douze profils d’imprimante PrusaSlicer** couvrant les trois modes (Default, Copy, Mirror) avec huit préréglages de qualité chacun (de 0,08 mm ultra-fin à 0,28 mm brouillon). Les profils Copy et Mirror utilisent des dimensions de plateau réduites de moitié (150×200 mm) pour tenir compte de la zone d’impression dupliquée/miroir, et incluent les commandes M605 correctes dans leur GCode de démarrage. Ces profils permettent à un utilisateur de passer de l’installation du pont à l’impression en mode IDEX Copy sans rien configurer manuellement dans PrusaSlicer.
### Renforcement de la Sécurité
La v0.2.1 a été une version dédiée à la sécurité. Le pont est un service réseau qui accepte des téléversements de fichiers et exécute des commandes sur une imprimante — une surface d’attaque qui mérite une attention particulière.
**Prévention de la traversée de chemins.** Tous les endpoints d’opération de fichiers (téléversement, téléchargement, suppression, déplacement, copie, création de répertoire) valident désormais que les chemins résolus restent dans le répertoire de stockage GCode. Les tentatives d’évasion via des séquences `../` sont rejetées avant toute opération sur le système de fichiers.
**Prévention de l’injection de namespace.** Le paramètre namespace de l’API de base de données est validé contre un motif strict de liste blanche, empêchant l’injection de clés arbitraires dans le stockage persistant.
**Assainissement des en-têtes.** Les en-têtes de réponse HTTP construits à partir de valeurs fournies par l’utilisateur (comme les noms de fichiers dans Content-Disposition) sont assainis pour prévenir les attaques par injection d’en-têtes.
**Limites de taille des messages WebSocket.** Les connexions WebSocket imposent une taille maximale de message pour prévenir l’épuisement de la mémoire par des charges utiles surdimensionnées.
**Validation des commandes systemctl.** L’API de gestion de services (utilisée par Mainsail pour redémarrer/arrêter les services) valide les noms de service contre une liste blanche stricte avant de les passer à systemctl.
Aucune de ces mesures n’était une réponse à des vulnérabilités découvertes — il s’agissait d’un renforcement proactif basé sur l’examen de la surface d’attaque. Pour un service fonctionnant sur un Raspberry Pi sur un réseau local, le risque est faible, mais le coût de bien faire les choses l’est également.
### Historique des Températures et le Tampon Circulaire
L’une des améliorations les plus discrètes de la v0.2.0 a été l’ajout d’un **magasin d’historique des températures** — un tampon circulaire de 1200 lectures par capteur qui alimente le graphique de température de Mainsail. Le pont original ne rapportait que les températures actuelles ; le graphique de Mainsail affichait un point unique qui sautait à chaque mise à jour. Avec le tampon circulaire, le graphique affiche un historique glissant fluide, correspondant au comportement attendu par les utilisateurs d’une véritable instance Moonraker.
### Résilience de la Connexion SACP
L’article original notait que la connexion SACP « expire occasionnellement, bien que la reconnexion automatique récupère rapidement ». La v0.1.1 a résolu cela avec un mécanisme de réessai approprié : lorsque le pont perd sa connexion SACP, il tente de se reconnecter jusqu’à cinq fois avec un délai de deux secondes entre chaque tentative. Le délai est important car le firmware de la J1S a parfois besoin de temps pour nettoyer une connexion interrompue avant d’en accepter une nouvelle — se reconnecter trop rapidement aboutit à une connexion refusée.
Le pont a également gagné des **commandes de déconnexion/connexion** accessibles depuis le panneau de gestion des services de Mainsail dans la v0.2.0, permettant une reconnexion manuelle sans redémarrer l’ensemble du processus du pont.
### Compilations Multiplateformes
À partir de la v0.1.1, le pipeline CI produit des binaires pour trois plateformes : Linux x86_64, Windows x86_64, et macOS ARM64 (Apple Silicon). Le cas d’utilisation principal reste l’image SD pour Raspberry Pi, mais les binaires multiplateformes signifient que le pont peut fonctionner sur une machine de bureau pour le développement ou les tests — ou sur n’importe quel serveur Linux du réseau, pas seulement un RPi.
### Où Nous en Sommes
L’article original se terminait par une liste de travaux futurs. Voici l’état de chaque point :
| Point de la Feuille de Route Initiale | Statut |
|—|—|
| Suivi du filament Spoolman | Fait (v0.0.7), mis à niveau vers le suivi par extrudeur (v1.0.0) |
| Progression à 0% pour les impressions lancées depuis l’écran tactile | Résolu — le contrôle d’impression natif SACP signifie que les impressions sont toujours lancées via le pont |
| Fiabilité de la connexion SACP | Fait (v0.1.1) — 5 tentatives de reconnexion avec délai configurable |
| Rafraîchissement/persistance du token | Non nécessaire — SACP ne requiert aucune authentification |
Et les fonctionnalités qui ne figuraient pas sur la feuille de route initiale mais sont arrivées tout de même :
– Post-processeur GCode avec en-têtes Snapmaker V1, renumérotation des outils, extinction des buses inutilisées et miniatures HMI
– Support des modes IDEX Copy et Mirror avec profils PrusaSlicer
– Contrôle complet de la température et des ventilateurs pour le double extrudeur
– Renforcement de la sécurité sur tous les endpoints exposés au réseau
– Binaires multiplateformes
– Éditeur de fichier de configuration Mainsail et graphiques d’historique de température
Le numéro de version raconte l’histoire. En février, la v0.0.6 était un prototype fonctionnel — elle pouvait réaliser le flux de travail principal (trancher → téléverser → imprimer → surveiller) mais avec des aspérités et des éléments manquants. Aujourd’hui, la v1.1.0 est un outil de production qui gère chaque mode d’impression supporté par la J1S, suit le filament par extrudeur, fournit des frontières de sécurité appropriées et est livré avec des profils de trancheur prêts à l’emploi.
La Snapmaker J1S est désormais un membre à part entière de notre ferme d’impression. Elle tranche depuis la même installation PrusaSlicer, téléverse vers la même interface Mainsail, suit le filament dans la même instance Spoolman, et surveille les impressions avec le même serveur Obico que toutes les autres machines de l’atelier. Le pont protocolaire qui a commencé comme un moyen d’éviter Luban est devenu une couche d’intégration complète pour la gestion de notre parc.
### Ce Qu’il Reste
Le projet n’est pas terminé — un logiciel ne l’est jamais — mais les points restants sont des raffinements plutôt que des fonctionnalités manquantes :
– **Z baby-stepping** (M290) est accepté mais pas encore implémenté via SACP. C’est une fonctionnalité de confort pour les ajustements en direct de la première couche.
– **Support élargi d’imprimantes.** Le pont est construit pour la J1S, mais le protocole SACP est partagé dans toute la gamme Snapmaker. Le post-processeur GCode génère déjà des en-têtes V0 pour les modèles A150/A250/A350/Artisan. Étendre le support complet à d’autres imprimantes Snapmaker est architecturalement faisable, bien que chaque modèle ait ses propres particularités protocolaires à découvrir.
– **Tests communautaires.** Le projet a été développé et testé avec une seule J1S. Plus d’utilisateurs signifie plus de cas limites, de versions de firmware et de configurations réseau — le type de validation en conditions réelles qu’aucune quantité de tests en solo ne peut remplacer.
### Open Source et Transparence sur l’IA
snapmaker_moonraker reste disponible sur [GitHub](https://github.com/goeland86/snapmaker_moonraker) sous la MIT License. Il s’appuie sur le travail de protocole SACP de sm2uploader par macdylan et snapmaker-sm2uploader par kanocz.
Ce projet continue d’être développé avec l’assistance de Claude (Anthropic). Chaque commit du dépôt est co-signé, reflétant un flux de travail de collaboration humain-IA qui s’est avéré efficace pour ce type de programmation système au niveau protocolaire — en particulier pour le travail fastidieux mais critique d’analyse de protocoles binaires, où un collaborateur IA qui ne perd pas le fil des offsets d’octets et de l’endianness au cours d’une session de plusieurs heures est véritablement utile.




