CC2 Control compares a spool's colour exactly with the tray's, which is the tag's colour from the display palette (e.g. #F72221 for Spoolman's #FF0000), and flags the slot or asks again when they differ. New copies take the tag's colour; copies in a slot are set to the tray's colour, others keep theirs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014GcEk5tkfFSYD6NTF66NkC
cc2-spoolman-bridge
Bridge zwischen der Canvas eines oder mehrerer Elegoo Centauri Carbon 2 (Community-Firmware, CC2 Control) und Spoolman, mit Weboberfläche und NFC-Tag-Generator.
- Liegt eine Spule mit eigenem Tag in einer Canvas, erkennt die Bridge die Spoolman-Spule am Tag, legt sie in der CC2 Control dieses Druckers an (bzw. findet sie wieder) und weist sie dem Slot zu.
- In Spoolman steht dann, wo die Rolle liegt: Ort „ Slot N“ (z. B. „Centauri Carbon 2 Links Slot 3“) und die Zusatzfelder Drucker und Slot. Entnommene Rollen kommen ins „Lager“, die Felder werden geleert.
- Den von CC2 Control gemessenen Verbrauch bucht sie nach jedem Druck auf genau diese Spoolman-Spule.
- Der Tag-Generator gibt für jede Spoolman-Spule die vollständige Befehlskette zum Beschreiben eines leeren NTAG213 aus.
Was die Bridge darf und was nicht
| System | Zugriff |
|---|---|
| Drucker | nur lesen: cat /opt/usr/canvas_record_info.json per SSH. Kein MQTT, keine Druckerbefehle, keine Registrierung als MQTT-Client. |
| CC2 Control | Spulenverwaltung: GET /api/spools, POST /api/spools/save, /assign, /adjust (Rest aus Spoolman), /delete (nur Kopien archivierter/gelöschter Spoolman-Spulen), /enable (nur nach Bestätigung); während eines Drucks zusätzlich GET /api/printer (nur für Fortschritt und Restzeit in der Oberfläche) |
| Spoolman | GET /spool, /vendor, /filament; PATCH /spool/{id} (Ort, Zusatzfelder, Rest nach Nachwiegen/Leerlaufen, Archiv), PUT /spool/{id}/use; POST /vendor, /filament, /spool nur über „Neue Spule“ im Tag-Generator |
Dry-Run ist Standard. Solange er aktiv ist, werden alle Schreibaktionen nur als „würde …“ protokolliert. Ausschalten unter Einstellungen (mit Rückfrage).
Einrichtung
1. SSH-Schlüssel auf dem Drucker einschränken
Eigenen Schlüssel erzeugen (ohne Passphrase):
ssh-keygen -t ed25519 -N "" -C cc2-spoolman-bridge -f cc2_bridge_key
Auf dem Drucker in /root/.ssh/authorized_keys (bzw. dem Pfad, den dein SSH-Server nutzt) eintragen. Jede
Ausführung liefert dann nur noch den Canvas-Datensatz, egal welcher Befehl gesendet wird, und nur von 10.10.6.10:
command="cat /opt/usr/canvas_record_info.json",from="10.10.6.10",no-pty,no-port-forwarding,no-agent-forwarding,no-X11-forwarding ssh-ed25519 AAAA… cc2-spoolman-bridge
Die Oberfläche zeigt diese Zeile mit dem passenden öffentlichen Schlüssel nach dem Hochladen an. Container auf
10.10.6.10 verbinden sich über Docker-NAT mit der Host-IP, from="10.10.6.10" passt also.
2. Container starten (Docker-Host 10.10.6.10)
mkdir -p /opt/cc2-spoolman-bridge && cd /opt/cc2-spoolman-bridge
# docker-compose.yml und .env.example aus dem Repo hierher kopieren
cp .env.example .env
mkdir -p data && sudo chown 1000:1000 data
# privates Image: einmalig mit einem Gitea-Token (package: Lesen) anmelden
docker login git.menzel.center -u menzeljonas
docker compose pull && docker compose up -d
Oberfläche: http://10.10.6.10:5066
3. In der Oberfläche
- Einstellungen → Drucker: Name, IP, SSH-Port/-Benutzer und CC2-Control-URL je Drucker; „Drucker hinzufügen“ für weitere. Speichern. Der Name erscheint so in Spoolman (Ort und Feld „Drucker“).
- Privaten Schlüssel einfügen oder hochladen und speichern. Er gilt für alle Drucker (dieselbe eingeschränkte
authorized_keys-Zeile auf jedem Drucker) und liegt mit Rechten 600 in/data/ssh/id_key; die Oberfläche zeigt nur Fingerprint und öffentlichen Schlüssel. - Je Drucker Verbindung testen: zeigt die 4 Slots und den Host-Key-Fingerprint. Vergleichen und bestätigen. Danach wird er bei jeder Verbindung geprüft; ein geänderter Host-Key wird abgelehnt.
- Status im Dry-Run beobachten. Wenn die „würde …“-Einträge stimmen, Dry-Run ausschalten.
Ein Update von v1 übernimmt Einstellungen, Zuordnungen, Buchungsstände und den Host-Key automatisch; der bisherige Drucker heißt danach „CC2“ (Ort bleibt „CC2 Slot N“) und kann in den Einstellungen umbenannt werden – Ort und Felder in Spoolman ziehen dann mit.
Neue Spule anlegen
Im Tag-Generator unter Neue Spule: Hersteller aus Spoolman wählen (oder einen neuen), dann ein vorhandenes
Filament dieses Herstellers (weitere Rolle davon) oder ein neues – Material aus den in Spoolman verwendeten (oder den
übrigen Elegoo-Typen), Name, Farbe, Dichte, Durchmesser, Düsen-/Betttemperatur, Netto- und Leergewicht, Preis,
Anzahl. Dichte und Temperaturen kommen von einem vorhandenen Filament mit demselben Material (bevorzugt desselben
Herstellers), Gewichte von den übrigen Filamenten des Herstellers. Gibt es das Filament schon (gleicher Name oder
gleiches Material und gleiche Farbe), weist das Formular darauf hin; ein gleichnamiges wird nie doppelt angelegt. Die Bridge legt Hersteller (falls neu), Filament und
Spule(n) in Spoolman an, Ort „Lager“, Label wie gewohnt Hersteller + ID dreistellig (SUNLU027), und öffnet
gleich den Tag dazu. Die ID vergibt Spoolman (fortlaufend). Im Dry-Run wird nichts angelegt.
Spule mit dem iPhone auslesen
Safari kann NFC nicht selbst lesen, das iPhone liest Tags aber im Hintergrund: Steht auf dem Tag ein Link, genügt es,
das entsperrte iPhone an die Rolle zu halten und die Mitteilung anzutippen. Dafür unter Einstellungen → Adresse
der Bridge die Adresse eintragen, unter der das iPhone die Bridge erreicht (z. B. https://cc2.menzel.center), und
im Tag-Generator als Link Spule in der Bridge wählen (die Wahl wird gemerkt). Der Tag enthält dann
<Adresse>/s/<ID>; die Seite zeigt Rest (Spoolman und CC2 Control), wo die Spule steckt, die letzte Buchung und den
Verlauf, mit Links zu Spoolman und zum Tag-Generator. Spoolman-ID und Druckerdaten auf dem Tag bleiben gleich;
ob der Drucker Tags mit einem längeren Link genauso liest, ist noch an einem Test-Tag zu prüfen.
Am iPhone als Web-App
http://10.10.6.10:5066 in Safari öffnen → Teilen → Zum Home-Bildschirm. Die Bridge startet dann ohne
Safari-Leiste mit eigenem Icon. Unten liegen die Tabs Status, Protokoll, Tags und Einstellungen; auf Status
wechselt man oben zwischen den Druckern, ein Tipp auf einen Slot zeigt Details und die Aktionen. Hell/Dunkel folgt
der Systemeinstellung. Offline-Modus und Push-Mitteilungen gibt es nicht (dafür bräuchte es HTTPS).
Umgebungsvariablen
Siehe .env.example. PRINTER_HOST, SSH_PORT, SSH_USER, CC2_URL, SPOOLMAN_URL,
ID_OFFSET, POLL_INTERVAL, DRY_RUN und PRINTER_NAME (erster Drucker) sind nur Startwerte, bis die Einstellungen einmal in der Oberfläche
gespeichert wurden (/data/settings.json). BRIDGE_USER und BRIDGE_PASSWORD schalten Basic-Auth ein
(/healthz bleibt offen). PORT (Standard 5066), LOG_LEVEL.
Funktionsweise
Tag erkennen. Die Bridge liest alle ~5 s über eine stehende SSH-Verbindung den Canvas-Datensatz.
channels0–channels3 sind Slot 1–4. Ist weight ≥ ID_OFFSET, ist weight − ID_OFFSET die Spoolman-Spulen-ID;
kleinere Werte sind echte Gewichte von Original- oder Fremdtags und werden ignoriert. Neu ist ein Tag, wenn sich die ID
im Slot gegenüber dem letzten Datensatz ändert.
Zuordnung. Die Bridge speichert Drucker/Slot ↔ Spule selbst (/data/state.json). Eine Spule liegt höchstens
in einem Slot eines Druckers. Fällt weight nach einer Bearbeitung am Display auf 1000 zurück, bleibt eine
gescannte Zuordnung bestehen; fragt CC2 Control deshalb nach der Spule, weist die Bridge sie erneut zu (höchstens
einmal pro Minute). Aufgehoben wird sie, wenn
- der Slot länger als 20 s leer ist (Tray-Status 0),
- CC2 Control die Spule herausnimmt („Take out“) oder ins Lager legt (Slot ohne Spule und ohne offene Rückfrage),
- im Slot ein Tag mit einer anderen ID gescannt wird,
- die Spule in einem anderen Slot oder im anderen Drucker gescannt wird (Umzug) oder
- nach dem Einlegen einer Rolle die Tag-Daten des Slots nicht mehr ihre ID tragen (siehe unten).
Weist du eine gespiegelte Spule in CC2 Control von Hand einem belegten Slot zu, übernimmt die Bridge das.
Rolle wieder einlegen. Der Scanner sitzt an der Seite, die Rollen werden zum Scannen abgenommen. Der Drucker behält die Tag-Daten eines Slots auch, wenn die Rolle draußen ist. Kommt eine Rolle in den Slot zurück (CC2 Control fragt „eingelegt“) und stehen dort noch unverändert die Daten der Rolle, die diesen Slot zuletzt verlassen hat, gilt sie als dieselbe Rolle: Die Bridge ordnet sie wieder zu und beantwortet die Rückfrage, mit oder ohne erneuten Scan. Wird danach am Display ein Filament gewählt (die ID verschwindet), war es eine andere Rolle ohne Tag: Die Zuordnung wird aufgehoben, die Rolle kommt ins Lager. Das gilt bis zum ersten Druck mit der Rolle; danach ist eine Display-Bearbeitung wieder eine Bearbeitung derselben Rolle.
„Take out“ in CC2 Control zählt als Entnahme: Ort „Lager“, Felder leer. Bleibt die Rolle physisch im Tray, rechnet CC2 Control ihren Verbrauch keiner Spule mehr zu, protokolliert ihn aber für den Slot. Diesen Verbrauch (Millimeter aus dem CC2-Protokoll, mit Durchmesser und Dichte der Spoolman-Spule in Gramm umgerechnet) bucht die Bridge trotzdem auf die Rolle, bis sie den Slot verlässt oder CC2 Control nach einer Spule fragt. Zum erneuten Zuordnen die Rolle herausnehmen und wieder einlegen.
Spoolman synchron halten. Nach jeder Änderung schreibt die Bridge Ort und Zusatzfelder (die Felder legt sie beim ersten Mal selbst an). Zusätzlich vergleicht sie jede Minute alle Spoolman-Spulen: Geladene Rollen bekommen ihren Ort/ihre Felder zurück, falls sie dort von Hand geändert wurden (der Drucker gewinnt). Rollen mit einem Druckerort oder gesetztem Feld „Drucker“, die in keinem Slot liegen, kommen ins Lager. Eigene Orte wie „Regal 2“ bei nicht geladenen Rollen bleiben unangetastet. Wird ein Drucker umbenannt oder entfernt, ziehen Ort und Felder mit bzw. seine Rollen kommen ins Lager.
CC2 Control. Gespiegelte Spulen erkennt die Bridge an note = spoolman:<id>. Fehlt die Spule, legt sie sie
mit name = Label (lot_nr, sonst Hersteller + ID dreistellig), brand, material, color, diameter,
density, net, remaining, tare und price aus Spoolman an und weist sie dem Slot zu. Damit ist auch die
Rückfrage beantwortet. Passt das Spoolman-Material nicht zum Tag (CC2-Regel: gleich Typ/Name oder Produktlinie wie
PLA+ zu PLA), nimmt sie den Untertyp vom Tag, damit CC2 Control nicht erneut fragt.
Verbrauch. CC2 Control zieht den gemessenen Verbrauch vom remaining der zugewiesenen Spule ab. (used ist in
CC2 Control der Zeitstempel der letzten Nutzung, keine Menge.) Die Bridge bucht für jede gespiegelte Spule die
Abnahme von remaining seit der letzten Buchung per PUT /spool/{id}/use nach Spoolman. Das geschieht erst, wenn kein
Druck läuft (job.active) und remaining 60 s unverändert ist. Der gebuchte Stand wird persistent gespeichert.
Vor jeder Buchung schreibt die Bridge die Absicht in den Zustand. Bricht eine Buchung ab, prüft sie nach dem
Neustart an used_weight in Spoolman, ob sie schon angekommen ist, statt doppelt zu buchen.
CC2 Control misst den echten Extruderweg (print_stats.filament_used, mit Spülen und Farbwechseln, mit Flow-Faktor)
und rechnet ihn dem Slot zu, der gerade fördert. Ein abgebrochener oder fehlgeschlagener Druck zählt also mit dem,
was er wirklich verbraucht hat; bei Mehrfarbdrucken bekommt jede Rolle ihren Anteil. Die Bridge nimmt Dateiname und
Ergebnis aus dem CC2-Protokoll in die Buchung auf, z. B. „2.98 g von SUNLU001 … gebucht – benchy.gcode
(abgebrochen)“.
Rolle leer. Läuft eine Rolle im Druck leer, setzt CC2 Control sie auf 0 g; die Bridge setzt den Rest in Spoolman ebenfalls fest auf 0 g („Rolle leergelaufen“), statt eine Differenz zu buchen – so stimmt Spoolman auch dann, wenn CC2 Controls Zählung vorher daneben lag. Danach archiviert sie die Spule in Spoolman (abschaltbar unter Einstellungen), und ihre Kopie verschwindet aus CC2 Control. Druckt der Drucker mit einer anderen Rolle weiter, zählt alles ab dem Wechsel für deren Slot. Hat CC2 Control fälschlich „leergelaufen“ erkannt (Filament gerissen), die Spule in Spoolman zurückholen und nachwiegen. Die ID einer archivierten Spule wird nicht wieder vergeben; ihren Tag kann man für eine neue Rolle einfach neu beschreiben. Ein Scan des alten Tags ordnet nichts zu.
Nachwiegen. Wird eine Spule in CC2 Control nachgewogen (Protokolleintrag „correction“), setzt die Bridge den
Rest in Spoolman auf denselben Wert (used_weight = Startgewicht − Rest), statt die Differenz als Verbrauch zu
buchen. Steigt remaining ohne Nachwiegen, wird nichts gebucht, nur protokolliert, und der neue Stand gilt als
Ausgangspunkt.
Abgleich Spoolman → CC2 Control. Spoolman ist die Quelle für die Stammdaten: Ändern sich dort Name, Hersteller,
Material, Farbe, Durchmesser, Dichte, Netto-/Leergewicht oder Preis, zieht die Bridge die Kopie in CC2 Control
nach (höchstens einmal pro Minute, nie während eines Drucks). Für eingelegte Spulen gilt das auch für den Rest:
Weicht er mehr als 1 g von Spoolman ab und ist aller Verbrauch gebucht, setzt die Bridge ihn per Nachwiegen
(/api/spools/adjust) auf den Spoolman-Wert. Das betrifft vor allem eine Rolle, die im anderen Drucker gedruckt
hat und zurückkommt (CC2 Control hätte sonst ihren alten Stand), und ein Nachwiegen in Spoolman. Kopien von
Spulen, die in Spoolman archiviert oder gelöscht sind und in keinem Slot stecken, löscht die Bridge aus CC2
Control (dort passen höchstens 128 Spulen).
Rolle ohne Tag. Druckt ein Slot, für den CC2 Control noch fragt, welche Spule es ist, hält CC2 Control die Millimeter fest. Die Bridge zeigt das am Slot an und schreibt nach dem Druck einen Hinweis ins Protokoll. Wird die Rolle danach getaggt und gescannt, rechnet CC2 Control die Millimeter dieser Spule an und die Bridge bucht sie.
Grenzen. Slots ohne Spoolman-Tag und mit „keine Spule“ beantwortete Rückfragen werden nicht gebucht. Bei einem Stromausfall des Druckers gehen bis zu 2 min Verbrauch verloren (CC2 Control speichert beim Drucken höchstens alle 2 min). Bekommt CC2 Control eine eigene Spoolman-Anbindung, darf nur eins von beidem buchen.
Spulenverwaltung aus. Ist sie in CC2 Control ausgeschaltet, zählt CC2 Control keinen Verbrauch. Die Oberfläche zeigt das deutlich an und bietet einen Button zum Einschalten (mit Rückfrage, im Dry-Run nur protokolliert).
Tag-Format (Canvas, NTAG213/215/216)
NFC Tools → Other → Advanced NFC commands, Befehle A2:PP:XXXXXXXX (PP = Seite hex), kommagetrennt.
Seiten 0–3 werden nie geschrieben. Nie „Lock tag“ oder „Set password“ verwenden.
| Seite | Inhalt |
|---|---|
| 04–09 | NDEF-URL (https://www.elegoo.com; der Drucker ignoriert sie) |
| 10 | 36 + Herstellercode EEEEEE |
| 11 | EE + Filament-Code (2 Byte) + 00 |
| 12 | Material (4 Byte) |
| 13 | Untertyp (2 Byte) + 0000; danach wählt der Elegoo Slicer das Profil |
| 14 | Farbe RRGGBB + Finish-Byte (FF) |
| 15 | Düse min / max (je 2 Byte, °C) |
| 16 | 00000000 |
| 17 | Durchmesser in 1/100 mm + Gewicht = Spulen-ID + 10000 |
| 18 | 0036C800 |
Die Farbe wird auf die nächste Farbe der Display-Palette vorgeschlagen (Abstand in CIELAB; gesättigtes Blau wird auf
2850DF abgebildet). Material und Untertyp werden aus dem Spoolman-Material vorgeschlagen; alles ist änderbar.
Optional kann statt der Elegoo-URL ein Link auf die Spule in Spoolman auf den Tag (noch nicht am Drucker getestet).
Entwicklung
docker build --target test . # Tests (pytest) im Container
docker build -t cc2-spoolman-bridge:dev . # Image
python tools/mock_env.py --host mock --printers 2 # Mock: 2 Drucker (SSH :2222/2223, CC2 :8081/8082), Spoolman
Einen Test-Container der Bridge gegen den Mock nur in einem Netz ohne Außenverbindung starten
(docker network create --internal …) und mit SPOOLMAN_URL=http://mock:7912. Sonst spricht er bis zum Speichern
der Einstellungen mit der voreingestellten echten Spoolman-Instanz.
Die Tests decken ab: Tag-Kodierung gegen die verifizierte Vorlage, ID-Dekodierung, Palette und Materialvorschlag,
Zuordnung bei verschwindender ID, Verbrauchs-Delta und Idempotenz (auch nach Abbruch), Dry-Run, mehrere Drucker
(Umzug, Ausfall eines Druckers, Entfernen, Umbenennen), Spoolman-Ort und Zusatzfelder inkl. Korrektur von Hand-
Änderungen, Migration von v1, Request-Format für
CC2 Control (Host-Header, kein Origin, X-CC2-Request, key=value-Body, enable als nacktes on/off) und SSH
mit Host-Key-Prüfung gegen einen lokalen SSH-Server.
CI (Gitea Actions, .gitea/workflows/ci.yml): pytest → Build linux/amd64 → auf main Push nach
git.menzel.center/menzeljonas/cc2-spoolman-bridge mit den Tags latest und sha-<commit>. Für den Push braucht
das Repo ggf. ein Actions-Secret REGISTRY_TOKEN (Token mit package: Lesen und Schreiben).
Quellen und Lizenzen
- Material- und Untertyp-Codes (
app/materials.py, Lizenztext in THIRD_PARTY_NOTICES.md) aus cjtallman/canvas-rfid-tool (index.html), MIT License, Copyright (c) cjtallman. Dort wiederum nach DnG-Crafts/ELG-RFID und Savion/elegoo-rfid-editor. - Spool-API und HTTP-Guard von CC2 Control:
Neme77/centauri-carbon-2-community
(
cc2-control/docs/API.md, Abschnitt „Spool tracking“, undsrc/spools.h, Branchdevelop).