Der Bau des Starlex-Werkzeugwechslers: Eine Reise durch die Welt der Klipper-Programmierung

Was passiert, wenn man beschließt, einen 3D-Drucker mit 12 Werkzeugen zu bauen, und sich die Hardware unter den Augen ständig ändert? Man schreibt eine Menge Klipper-Makros. Dies ist die Geschichte der programmtechnischen Herausforderungen, die wir dabei gemeistert haben, wie das aktuelle System aussieht und wohin wir nun steuern, da sich die Hardware endlich stabilisiert hat.

Was ist der Starlex-Werkzeugwechsler?

Der STC – der Starlex-Werkzeugwechsler von Swiss3dc – ist ein „leichtes“ Werkzeugwechselsystem für Klipper-basierte 3D-Drucker. Das Wort „leicht“ ist der entscheidende Unterschied zu einem herkömmlichen Werkzeugwechsler.

Bei einem vollständigen Werkzeugwechsler (wie der E3D- oder Prusa-XL-Architektur) ist jedes Werkzeug ein kompletter, in sich geschlossener Druckkopf: mit eigenem Extruder-Schrittmotor, Hotend, Heizelement, Thermistor und Lüfter. Ein Werkzeugwechsel bedeutet also den Austausch der gesamten Antriebs- und Heizbaugruppe. Das sorgt zwar für maximale Unabhängigkeit zwischen den Werkzeugen, erhöht jedoch das Gewicht, die Kosten und die mechanische Komplexität jedes einzelnen Werkzeugs erheblich.

Der Starlex verfolgt einen anderen Ansatz. Jedes Werkzeug besteht lediglich aus einer Hotend-Baugruppe – einem Heizblock, einem Thermistor, einer Düse und einem Lüfter. Es gibt keinen Extruder-Schrittmotor pro Werkzeug. Stattdessen befindet sich ein einziger gemeinsamer Extruder-Schrittmotor auf dem Schlitten (auf der EBB36-Werkzeugkopfplatine), und der Nockenmechanismus koppelt ihn physisch an das jeweils angedockte Werkzeug. Wenn der Schlitten ein Werkzeug aufnimmt, rastet die Kupplung in den Filamentweg ein; wenn er das Werkzeug wieder auf das Gestell absetzt, löst sich die Kupplung. Der Extrusionsantrieb verbleibt stets auf dem Schlitten.

Dadurch bleiben Werkzeugmasse und Kosten gering – man parkt Hotends, keine Druckköpfe –, während gleichzeitig eine echte Temperaturisolierung pro Material, keine Kreuzkontamination zwischen den Materialien und unabhängige Z-Offsets pro Werkzeug gewährleistet sind. Der Kompromiss besteht darin, dass die Kupplung und die Ausrichtung des Filamentwegs über 12 Steckplätze hinweg zuverlässig funktionieren müssen – und genau hier liegt der größte technische Herausforderungspunkt.

Unser System betreibt 12 Werkzeuge. Diese Zahl war ausschlaggebend für fast jede unserer Software-Entscheidungen.

Der Hardware-Stack

Bevor wir uns mit der Programmierung befassen, ist es hilfreich zu verstehen, mit welchen Komponenten die Software kommunizieren muss. Die Maschine basiert auf einem BIGTREETECH Manta M8P V2.0 als primärer MCU, auf der Klipper-Firmware auf einem Raspberry-Pi-Host läuft. Ein CoreXY-Portal übernimmt die XY-Bewegung; drei unabhängige Z-Spindeln sorgen für die Bettnivellierung. Das Werkzeugmagazin befindet sich vorne links im Bauvolumen und wird von einem Nockenmechanismus an einem Schrittmotor mit Planetengetriebe angetrieben – durch die Drehung der Nocke wird eine Kupplungsstange angehoben oder abgesenkt, die den Werkzeugkopf am Schlitten arretiert oder freigibt.

Raspberry Pi Klipper · Moonraker · KlipperScreen USB / UART Manta M8P V2.0 · STM32H723 X / Y / Z₀ / Z₁ / Z₂ steppers · Bed heater · Tool rack cam stepper Y endstop · BDsensor Z probe (virtual endstop) CAN bus USB USB EBB36 GEN2 STM32G0B1 · CAN bus ▸ BDsensor (I²C on PA6/PA7) ▸ Shared extruder stepper ▸ X endstop ▸ Carriage cooling fan ▸ Filament path (all tools) ReHeat A0 · Board 0 RP2354B · Tools T0 – T5 ▸ 6 heater outputs (GPIO 0–5) ▸ 6 fan outputs (GPIO 6–11) ▸ 6 thermistor inputs (GPIO 40–45) ▸ 6 park detection endstops ▸ 6 filament runout sensors ReHeat A0 · Board 1 RP2354B · Tools T6 – T11 ▸ 6 heater outputs (GPIO 0–5) ▸ 6 fan outputs (GPIO 6–11) ▸ 6 thermistor inputs (GPIO 40–45) ▸ 6 park detection endstops ▸ 6 filament runout sensors

Abbildung 1 – Kommunikationshierarchie der Hardware. Die Manta M8P ist die zentrale MCU; die EBB36-Werkzeugkopfplatine kommuniziert über CAN, während zwei ReHeat A0-Erweiterungsplatinen über USB angeschlossen sind und gemeinsam alle 12 Werkzeugheizungen, Lüfter, Thermistoren und Sensoren verwalten.

Jedes Werkzeug verfügt unabhängig über eine Heizung, einen Thermistor, einen Lüfter, einen Endschalter zur Parkposition-Erkennung und einen Sensor für den Filamentmangel. Das ergibt insgesamt 60 unabhängige Signale über 12 Werkzeuge hinweg, weshalb es die ReHeat A0-Erweiterungsplatinen überhaupt gibt – und warum es sich lohnt, sie näher zu erläutern.

Das ReHeat A0 ist eine Prototyp-Platine, die wir in Zusammenarbeit mit Intelligent Agent entwickelt haben, dem norwegischen Unternehmen, das hinter der Recore-3D-Drucker-Steuerplatine steht. Das Problem, mit dem wir uns an sie wandten, betraf speziell den schnellen Werkzeugwechsel: Wenn jedes Werkzeug lediglich aus einer Hotend-Baugruppe und nicht aus einem vollständigen Druckkopf besteht, lassen sich realistisch gesehen weitaus mehr Werkzeuge betreiben, als jede bisherige Erweiterungshardware zu verwalten ausgelegt war. Standard-Werkzeugkopfplatinen gehen von einer Platine pro Werkzeug aus. Wir benötigten eine Platine für jeweils sechs Werkzeuge. Die ReHeat A0 – sechs Heizungsausgänge, sechs Lüfterausgänge, sechs Thermistor-Eingänge, zwölf Endschalter-Eingänge, alles auf einer RP2354B-MCU mit USB-C und nativer Klipper-Unterstützung – wurde von Grund auf neu entwickelt, um diese Lücke zu schließen. Sie ist ein direktes Ergebnis der Möglichkeiten, die diese Art von Architektur für den schnellen Werkzeugwechsel eröffnet.

Ad-hoc-Start: Warum zuerst benutzerdefinierte Makros

Als dieses Projekt begann, hatten wir zwei Werkzeuge. Dann vier. Dann zwölf. Das Design des Nockenmechanismus änderte sich. Der Standort der Heizelektronik änderte sich – von der Hauptplatine über die Werkzeugkopfplatine bis hin zu dedizierten Erweiterungsplatinen. Wir arbeiteten die mechanische Kopplung, die Geometrie des Filamentwegs und den Schlitzabstand aus, während wir gleichzeitig den Konfigurationscode schrieben.

Die Makro- und Konfigurationssprache von Klipper ist für eine Konfigurationsdatei überraschend ausdrucksstark: vollständige Jinja2-Templating-Unterstützung, Zugriff auf das gesamte Drucker-Status-Dictionary, benannte Variablen, verzögerte G-Code-Ausführung und beliebige Verkettung von G-Code-Befehlen. Damit lässt sich ein funktionierender Werkzeugwechsler von Grund auf neu erstellen, ohne C++ zu berühren – und, was noch wichtiger ist: Man kann ihn morgen schon wieder anpassen, wenn die Hardware etwas Unerwartetes tut.

Also haben wir alles selbst programmiert. T-Befehl-Makros, Werkzeugzustandsüberwachung, Rack-Steuerung, Lüftermanagement, Offset-Persistenz – einfach alles. Es war die richtige Entscheidung. Doch bevor wir darauf eingehen, warum, schauen wir uns zunächst an, was wir tatsächlich lösen mussten.


1 Der BDsensor-Pin-Konflikt

Der BDsensor ist ein kapazitiver Z-Sensor, der über I²C kommuniziert. Der logische Anschlusspunkt am EBB36 war derPROBEPort – Pin PB8. Es hätte fast funktioniert. Das Problem: An diesem Port befindet sich auf der SDA-Leitung ein Optokoppler, der für herkömmliche Endschaltersignale vorgesehen ist, und dieser Optokoppler klemmt das SDA-Signal so ab, dass die I²C-Kommunikation gestört wird. Der Sensor initialisierte sich zwar, lieferte aber unlesbare Messwerte.

Die Lösung bestand darin, einen echten I²C-Bus auf dem EBB36 zu finden. Die Pins PA6 und PA7 sind mit einem Hardware-I²C-Peripheriegerät verdrahtet, bei dem sich kein Optokoppler im Signalweg befindet:

# Don't use aliases for the board pins — use the raw I²C port
[BDsensor]
sda_pin: ^EBB36:PA6
scl_pin: EBB36:PA7
delay: 10
homing_cmd: G990028  # required when G28 is redefined

rename_existingDiehoming_cmd: G990028Zeile ist eine weniger offensichtliche Anforderung: Wenn man mit neu definiertG28, muss die interne Autokalibrierungssequenz des BDsensors wissen, welchen G-Code sie für die Referenzfahrt aufrufen soll. Ohne diese Angabe löst die Kalibrierung den Makro-Wrapper aus und nicht den Basisbefehl G28.1, was zu einer Endlosschleife führt.


2 Der Nockenmechanismus des Werkzeugmagazins

Das Werkzeugmagazin ist eine lineare Schiene, die sich über die Vorderseite der Maschine erstreckt. Jedes Werkzeug wird in seinem Steckplatz abgelegt; wenn sich der Schlitten einem Steckplatz nähert und sich die Nocke dreht, hebt eine Kupplungsstange das Werkzeug an und arretiert es am Schlitten (oder senkt es ab und gibt es wieder an das Magazin zurück).

manual_stepperDie Nocke wird von einem Schrittmotor mit einem 5:1-Planetengetriebe angetrieben, der in Klipper als „Manual Stepper“ konfiguriert ist. „Manual Stepper“ bedeutet, dass Klipper diese Achse nicht in die normale Bewegungsplanung einbezieht – wir steuern sie explizit mitMANUAL_STEPPERBefehlen:

[manual_stepper tool_rack]
step_pin: PG9
dir_pin: PD7
enable_pin: !PG11
rotation_distance: 2      # 1 full cam rotation = 2mm virtual travel
gear_ratio: 5:1
microsteps: 4
velocity: 5
accel: 100
endstop_pin: ^!PF3

Interessant ist, wie wir die Nocke während der Werkzeugaufnahme mit dem Extrudermotor synchronisieren. Der Kupplungsmechanismus funktioniert am besten, wenn sich der Extruder beim Einrasten der Nocke ebenfalls leicht dreht – dies hilft bei der Ausrichtung des Filamentwegs. Wir haben den nicht standardmäßigenGCODE_AXIS=R Parameter von Klipper für den manuellen Schrittmotor verwendet, um die Zahnstangen-Nocken vorübergehend einer virtuellen „R“-Achse zuzuordnen, und dann mehrachsige G1-Bewegungen ausgegeben, die gleichzeitig X (Rütteln), E (Extruder-Vorbereitung) und R (Nockenrotation) ansteuerten:

# Temporarily expose cam as R axis so G1 can move all three in sync
MANUAL_STEPPER STEPPER=tool_rack GCODE_AXIS=R LIMIT_VELOCITY=20 LIMIT_ACCEL=100
G1 X0.1  E0.667 R0.667 F100   # jiggle right, prime, cam rotate down
G1 X-0.2 E0.333 R0.333 F100
G1 X0.1  E0.500 R-0.5  F100   # jiggle back, cam back up
G1       E0.500 R0.5   F100
G1       E0.180 R0.18  F100
MANUAL_STEPPER STEPPER=tool_rack GCODE_AXIS=   # detach R axis

Diese „Jiggle-Sequenz“ wurde empirisch im Laufe zahlreicher Kupplungsversuche entwickelt. Die kleine X-Oszillation unterstützt das korrekte Einrasten der mechanischen Kupplung, während der Extruder den Filamentweg vorbelastet. Der R-Achsen-Trick durchläuft dieToolGcodeTransformKlipper-Schicht unverändert, was wichtig ist, sobald wir Werkzeugversätze hinzufügen.


3 Zwölf Heizelemente in einer Konfigurationssprache

Die Konfiguration von Klipper ist keine Programmiersprache. Aber die Jinja2-Templating-Funktion innerhalb von G-Code-Makros ist eine Programmiersprache, und die Grenze zwischen beiden führt zu Reibungsverlusten, wenn man Dinge dynamisch umsetzen muss.

Die grundlegende Herausforderung: Werkzeug 0 verwendet den[extruder]Heizelement-Abschnitt (einen speziellen Klipper-Abschnitt mit eigenem Namensraum), während die Werkzeuge 1–11 die Abschnitte nutzen[heater_generic toolN_heater]. Man kann keinen einzigen Ausdruck schreiben, der beide transparent behandelt, ohne zuvor die Werkzeugnummer zu kennen.

In Makros stellt Klipper den Heizelementstatus über dasprinter Wörterbuch bereit.printer.extruder.temperature funktioniert für T0. Für T1–T11 benötigten wir eine Suche anhand von Zeichenfolgen-Schlüsseln:

# Dynamic heater name construction in Jinja2
{% set hname = "heater_generic tool" ~ tool_num ~ "_heater" %}
{% set temp   = printer[hname].temperature | default(0) %}
{% set target = printer[hname].target     | default(0) %}

Der| default(0)Filter ist unerlässlich. Wenn ein „Heater“-Abschnitt nicht definiert ist (weil wir beispielsweise T5 noch nicht verkabelt haben),printer["heater_generic tool5_heater"]gibt er ein leeres Dictionary zurück, anstatt einen Fehler auszulösen. Der Standardfilter sorgt dafür, dass er sicher den Wert Null zurückgibt, anstatt das Makro zum Absturz zu bringen.

Dieses Muster taucht an drei verschiedenen Stellen auf: bei den M104/M109-Routing-Überschreibungen, bei der Überprüfung des Schlittenlüfters und bei der Logik zur Aktualisierung des geparkten Lüfters. Ein Fehler an einer dieser Stellen führte entweder dazu, dass ein Lüfter ausgeschaltet blieb, obwohl er eingeschaltet sein sollte, oder zu einem Klipper-Fehler, der das Makro mitten im Druckvorgang zum Absturz brachte.


4 Das Problem der Lüfterverwaltung

Ein Werkzeugwechsler verfügt über zwei unterschiedliche Lüftergruppen, die eine völlig unterschiedliche Steuerungslogik erfordern:

  • Schlittenlüfter – kühlt das aktive Hotend. Sollte immer eingeschaltet sein, wenn die Heizung des aktiven Werkzeugs über 50 °C liegt oder einen Zielwert ungleich Null hat.
  • Geparkte Lüfter – kühlen die im Rack befindlichen Werkzeugköpfe. Sie sollten eingeschaltet sein, wenn die Heizung eines geparkten Werkzeugs heiß ist, jedoch NICHT, wenn sich dieses Werkzeug gerade auf dem Schlitten befindet (da der Schlittenlüfter dies übernimmt und die Geometrie der geparkten Lüfter die aktive Position ohnehin nicht erreicht).

Zwölf Werkzeuge, ein Lüfter pro Zwei-Werkzeug-Paar, das bedeutet 6 geparkte Lüfter. Die Ein-/Aus-Logik für jeden Lüfter lautet wie folgt:

Lüfter T(N)+T(N+1) ist EIN, wenn: (T(N) heiß ist UND T(N) nicht das aktive Werkzeug ist) ODER (T(N+1) heiß ist UND T(N+1) nicht das aktive Werkzeug ist)

Wir haben dies als_UPDATE_PARKED_FANS implementiert – ein Makro, das den Zustand aller 12 Heizelemente ausliest (unter Verwendung der oben beschriebenen dynamischen Abfrage) und entsprechend Befehle ausgibtSET_FAN_SPEED. Dies läuft einmal beim Systemstart, einmal nach jedem Werkzeugwechsel und in einer 2-Sekunden-Timer-Schleife:

[delayed_gcode CARRIAGE_FAN_LOOP]
initial_duration: 2
gcode:
    _CARRIAGE_FAN_CHECK
    UPDATE_DELAYED_GCODE ID=CARRIAGE_FAN_LOOP DURATION=2

shutdown_speed: 1Ein wichtiges Sicherheitsdetail: Alle geparkten Lüfter werden mit deklariert. Wenn Klipper abstürzt oder in einem Fehlerzustand heruntergefahren wird, laufen die Lüfter automatisch auf volle Drehzahl hoch. Geparkte Werkzeugköpfe, die zum Zeitpunkt des Klipper-Absturzes heiß waren, werden gekühlt, anstatt weiter zu überhitzen.


5 Werkzeugversätze und Zustandserhaltung

Jedes Werkzeug weist eine leicht abweichende Düsengeometrie auf, und aufgrund von Montagetoleranzen befindet sich jedes Werkzeug relativ zum Maschinenursprung an einer leicht unterschiedlichen X-, Y- und Z-Position. Wir erfassen werkzeugspezifische Offsets, die Klipper nach einem Werkzeugwechsel auf alle nachfolgenden Bewegungskoordinaten anwendet.

saved_variables.cfgDie Offsets werden in gespeichert, einer Klipper-eigenen Datei, die auch nach einem Neustart erhalten bleibt:

[save_variables]
filename: ~/printer_data/config/saved_variables.cfg

# In the variables file:
[Variables]
tool_offset_t0 = [0.0, 0.0, 0.3]
tool_offset_t1 = [-1.0, -0.5, 0.1]
tool_offset_t2 = [-0.4, 0.0, 0.4]

SAVE_TOOL_Z_OFFSETDer Ablauf der Z-Kalibrierung ist bewusst einfach gehalten: Bei ausgewähltem Werkzeug bewegt der Benutzer die Z-Achse schrittweise nach unten, bis sich ein Papiertest richtig anfühlt, und ruft dann das Makro auf. Dieses Makro liestprinter.gcode_move.gcode_position.z die aktuelle Position im G-Code-Raum aus und schreibt sie zurück in die Speicherdatei.

saved_variables.cfgDie Beibehaltung des Zustands über Neustarts hinweg wurde durch einenRESTORE_TOOL_ON_BOOTverzögerten G-Code gewährleistet, der 3 Sekunden nach dem Start von Klipper ausgelöst wird, die zuletzt aktive Werkzeugnummer aus liest und deren Offsets erneut anwendet. Dies verhindert, dass die Maschine mitten in einem Druckvorgang neu startet und den Überblick darüber verliert, welche Offsets aktiv sind.


6 Weiterleitung von M104 und M109 an die rechte Heizung

M104Slicer senden StandardbefehleM104 T1 S215wie (Heiztemperatur einstellen, Werkzeug 1, 215 °C) undM109 T0 S210 (auf Temperatur warten). Diese Befehle setzen eine Firmware voraus, die mehrere Extruder-Indizes nativ versteht. DieM109 /-Implementierung von Klipper kommuniziert jedoch nur mit dem[extruder] -Abschnitt.

Die Lösung besteht darin, diese Befehle mithilfe desrename_existingKlipper-Mechanismus abzufangen:

[gcode_macro M104]
rename_existing: M104.1
gcode:
    {% set s = params.S | default(0) | float %}
    {% set t = params.T | default(-1) | int %}
    {% if t == -1 or t == 0 %}
        M104.1 S{s}                               # route to [extruder]
    {% elif t >= 1 and t <= 11 %}
        SET_HEATER_TEMPERATURE HEATER={"tool" ~ t ~ "_heater"} TARGET={s}
    {% endif %}
    _CARRIAGE_FAN_CHECK
    _UPDATE_PARKED_FANS

Die Lüfterprüfungen werden innerhalb von M104 aufgerufen, sodass sich der Lüfterstatus sofort aktualisiert, sobald eine Zieltemperatur eingestellt wird – selbst wenn dies während einer Temperaturwartezeit geschieht, die die Hauptwarteschlange blockiert. Das gleiche Muster gilt für unsereSET_HEATER_TEMPERATURE Überschreibung, sodass jeder Pfad, der eine Heizelement-Zieltemperatur ändert, auch eine Lüfteraktualisierung auslöst.

Die M109-Version (blockierendes Warten) leitet den Fall T=0 anM109.1 und den Fall T=1–11 an TEMPERATURE_WAIT SENSOR="heater_generic toolN_heater"weiter:

{% elif t >= 1 and t <= 11 %}
    SET_HEATER_TEMPERATURE HEATER={"tool" ~ t ~ "_heater"} TARGET={s}
    _CARRIAGE_FAN_CHECK
    _UPDATE_PARKED_FANS
    TEMPERATURE_WAIT SENSOR={"heater_generic tool" ~ t ~ "_heater"} MINIMUM={s - 2}

DasMINIMUM={s - 2} sorgt für ein Ankunftsfenster von 2 °C und verhindert so unbegrenzte Wartezeiten aufgrund geringfügiger Thermostat-Überschreitungen, während gleichzeitig sichergestellt wird, dass die Heizung tatsächlich die Temperatur erreicht hat, bevor der Druckvorgang beginnt.


7 Z-Neigungsausgleich mit Werkzeugwechsler

Die Maschine verwendet drei unabhängige Z-Spindeln undZ_TILT_ADJUST , um das Bett waagerecht zu halten. Die BDsensor-Sonde befindet sich am Schlitten, sodass für die Nivellierung eine Bewegung über das Bett und das Abtasten mehrerer Punkte erforderlich ist. Das Problem: Auf G-Code-Koordinaten angewendete Werkzeugversätze würden die Berechnungen der Sondenposition verfälschen.

Die Lösung besteht darin, den Befehl immer ohne aktiven Werkzeugversatz auszuführenZ_TILT_ADJUST. Wir haben dies wie folgt umgesetzt:

[gcode_macro Z_TILT_ADJUST]
rename_existing: _Z_TILT_ADJUST
gcode:
    {% set saved_tool = printer["gcode_macro TOOL_VARS"].current_tool | int %}
    {% if saved_tool != -1 %}
        UNLOAD_TOOL                       # returns tool to rack, clears offsets
    {% endif %}
    _Z_TILT_ADJUST {rawparams}
    {% if saved_tool != -1 %}
        LOAD_TOOL TOOL={saved_tool}       # reloads from rack, reapplies offsets
    {% endif %}

In der Praxis entlädt das Werkzeug ohnehin explizit,PRINT_START bevor aufgerufenZ_TILT_ADJUST wird. Der Wrapper bietet jedoch ein Sicherheitsnetz für manuelle Nivelliervorgänge über KlipperScreen, bei denen möglicherweise bereits ein Werkzeug geladen ist.


8 Filamentmangel in einem Mehrwerkzeugsystem

Jedes Werkzeug verfügt über einen eigenständigen Sensor zur Erkennung von Filamentmangel. Klippers nativepause_on_runout: TrueFunktion würde bei jedem ausgelösten Sensor eine PAUSE auslösen – einschließlich aller 11 geparkten Werkzeuge, in denen überhaupt kein Filament eingelegt ist. Das ist offensichtlich falsch.

Die Lösung ist ein Gating-Makro, das nur auf Filamentmangel-Ereignisse des aktuell aktiven Werkzeugs reagiert:

[filament_switch_sensor tool0_runout]
switch_pin: ^reheat0:GPIO24
pause_on_runout: False
runout_gcode: _FILAMENT_RUNOUT_GATE TOOL=0

[gcode_macro _FILAMENT_RUNOUT_GATE]
gcode:
    {% set sensor_tool = params.TOOL | int %}
    {% set active_tool = printer["gcode_macro TOOL_VARS"].current_tool | int %}
    {% if sensor_tool == active_tool %}
        PAUSE
    {% endif %}

Alle 12 Sensoren verfügen überpause_on_runout: False und rufen_FILAMENT_RUNOUT_GATE mit ihrer Werkzeugnummer auf. Das Makro vergleicht diese mit dem erfassten aktiven Werkzeug und pausiert nur, wenn sie übereinstimmen.


Die Werkzeugwechselsequenz

All diese Elemente fügen sich zu einer choreografierten Werkzeugwechselsequenz zusammen. JederT{N}Befehl durchläuft denselben Pfad:

T{N} command issued Pre-change Clear gcode offsets to zero · Z hop +5 mm (if tool mounted) Dropoff (only if a tool is currently mounted) ① Move carriage to slot X, approach Y · retract filament ② TOOL_RACK_DOWN — cam lowers, decouples tool from carriage ③ Move carriage to engage Y — push tool fully into rack slot ④ TOOL_RACK_UP — cam rises, tool is locked to rack ⑤ Retract to approach Y — carriage clears the rack Pickup ① Move carriage to new slot X, approach Y ② TOOL_RACK_UP — cam lifts coupling bar to receive tool ③ Move to engage Y — carriage docks with tool ④ Jiggle sequence — coordinated X ± 0.1 mm + E + R cam moves ⑤ TOOL_RACK_DOWN — cam lowers, locks tool onto carriage ⑥ Prime filament · retract to approach Y · carriage clears rack Post-change Z restore −5 mm · update all fans · apply new tool XYZ offsets Restore carriage to pre-change XY position Print resumes guarded by {% if tool %}

Abbildung 2 – Werkzeugwechselsequenz. Der Z-Hop im Schritt vor dem Wechsel ist abgesichert – er wird nur ausgelöst, wenn derzeit ein Werkzeug montiert ist, da es bei der allerersten Werkzeugauswahl zu Beginn des Druckvorgangs nichts gibt, über das gesprungen werden könnte.

Aktuelle Konfigurationsarchitektur

Die Arbeitskonfiguration ist auf mehrere Dateien verteilt, von denen jede eine klare Zuständigkeitsgrenze aufweist:

printer.cfg — MCU pins, stepper config, BDsensor, Z tilt points, printer kinematics includes: ebb36.cfg · macros.cfg · tools.cfg ebb36.cfg Extruder motor pins X endstop Carriage fan · TMC driver macros.cfg T0 – T11 gcode macros PRINT_START · PRINT_END M104 / M109 overrides Z_TILT_ADJUST wrapper MEASURE / SAVE offsets tools.cfg TOOL_VARS — state tracking macro Extruder heater / sensor (M8P pins) heater_generic tool1..11_heater Parked fans / carriage fan LOAD_TOOL / UNLOAD_TOOL HOME_TOOL_RACK · rack macros Fan update loops · runout gate saved_variables.cfg Current active tool number Per-tool X / Y / Z offsets (T0–T11) KlipperScreen.conf Custom tool-change menu (T0–T11 buttons) Load / Unload filament · Home · Reset tool state

Abbildung 3 – Aktuelle („eingefrorene“) Konfigurationsarchitektur. Die Datei „saved_variables.cfg“ dient als Persistenzschicht für den Werkzeugstatus und die Offsets; alles andere besteht aus reiner Klipper-Konfiguration und Makros.


Warum Ad-hoc die richtige Strategie war

Im Nachhinein betrachtet war es der richtige Ansatz, in dieser Projektphase alles von Grund auf neu zu schreiben. Und zwar aus folgenden Gründen.

Hardware und Software haben sich gemeinsam weiterentwickelt. Als wir begannen, hatte die Kamera am Werkzeuggestell noch ein anderes Design. Die Elektronik der Heizelemente wurde von der Hauptplatine auf die EBB36 und schließlich auf spezielle ReHeat-Erweiterungsplatinen verlagert. Die Anzahl der Werkzeuge stieg von 2 auf 12. Der Schlitzabstand änderte sich. Jedes Framework, das wir zu Beginn eingeführt hätten, hätte eine kontinuierliche Neuanpassung erfordert, da sich die physikalischen Gegebenheiten darunter veränderten – wahrscheinlich bei jeder Überarbeitung. Benutzerdefinierte Makros lassen sich leicht ändern.

Das Debuggen von Hardware ist ohne Framework-Abstraktionen einfacher. Wenn eine Kupplung nicht einrastet, ein Thermistor 400 °C anzeigt oder die Nockenvorrichtung überläuft, muss man genau sehen, was die Firmware der Hardware mitteilt. Mit benutzerdefinierten Makros kannaction_respond_info() man von überall aus temporäre Protokollierung zu jedem Schritt hinzufügen und die Abfolge ändern, ohne die Interna eines Plugins verstehen zu müssen. Das war während der mechanischen Entwicklung von unschätzbarem Wert.

Die iterative Kalibrierung erforderte volle Kontrolle. Die richtige Rüttelsequenz, die richtigen X-Positionen der Schlitze, die richtige Nockengeschwindigkeit, die richtige Filament-Rückzugsdistanz zu finden – all das wurde empirisch über viele Werkzeugwechselversuche hinweg optimiert. Eine Konfigurationssprache, bei der jeder Parameter direkt in der Konfigurationsdatei zugänglich ist (und nicht im Plugin-Code verborgen liegt), hat diese Iteration beschleunigt.

Dadurch entstand ein tiefes Verständnis. Jede Eigenart der G-Code-Transformationspipeline von Klipper, der Makroausführungsreihenfolge und der Namenskonventionen für Heizelemente wurde entdeckt, debuggt und aus erster Hand verstanden. Dieses Verständnis fließt nun direkt in die Umsetzung der Plugin-Migration ein – und zeigt uns, auf welche Randfälle wir achten müssen.

Die „frozen reference config“ (2026-06-02/config/) ist genau das: ein als fehlerfrei bekannter Momentaufnahme dessen, was bei voller Hardware-Leistung funktioniert. Sie dient als verlässliche Basis, während wir daneben die neue Architektur aufbauen.


Der Weg nach vorn: das „klipper-toolchanger“-Plugin

Nun, da sich die Hardware stabilisiert hat – der Nockenmechanismus hat sich bewährt, die Schlitzgeometrie ist festgelegt, die ReHeat-Platinen sind definiert –, wird der Wartungsaufwand des maßgeschneiderten Ansatzes zu seinem größten Nachteil. Jeder Nutzer, der diese Maschine baut, muss das Makrosystem von Grund auf verstehen. Jedes Klipper-Update birgt das Risiko, dass etwas Unauffälliges in den benutzerdefinierten T-Befehls-Makros kaputtgeht. Der Code ist von Natur aus maschinenspezifisch.

Das „klipper-toolchanger“-Plugin von Viesturz bietet ein speziell für Klipper entwickeltes Werkzeugwechsler-Framework, das dieselben Probleme löst, die wir manuell gelöst haben – jedoch auf eine Weise, die von der breiteren Community gewartet werden kann. Wichtige Funktionen, die unseren Anforderungen entsprechen:

  • Deklarative Werkzeugdefinitionen – jeder[tool T0] Abschnitt von bis[tool T11] enthält eigene Offsets, eine Heizungsreferenz, einen Erkennungsstift und die X-Position des Steckplatzes. Das Plugin registriert T0dieseT11 automatisch als G-Code-Befehle.
  • ToolGcodeTransform – das Plugin verwaltet die G-Code-Offset-Pipeline. Der G-Code für das Aufnehmen und Ablegen läuft im Offset-freien Raum (sodass die Rack-Koordinaten immer absolut sind); die Offsets werden erst nach Abschluss des Wechsels angewendet.
  • save_current_tool: True – Die Wiederherstellung des Zustands beim Systemstart ist integriert und ersetzt unserenRESTORE_TOOL_ON_BOOTverzögerten G-Code.
  • initialize_on: home — Der Werkzeugwechsler initialisiert sich automatisch bei G28; ein separater Aufruf aus PRINT_START ist nicht erforderlich.
  • t_command_restore_axis: XY — Das Plugin stellt nach jedem Werkzeugwechsel automatisch die XY-Position vor dem Wechsel wieder her; wir müssen lediglich die Z-Achse selbst über die „before“- und „after“-Hooks verwalten.

Neue Architektur

klipper-toolchanger plugin T0–T11 commands · ToolGcodeTransform · save_current_tool · initialize_on: home printer.cfg MCU config · stepper pins · BDsensor · Z tilt · reheat0 / reheat1 MCU stubs includes: ebb36.cfg · toolchanger.cfg · macros.cfg · tools.cfg toolchanger.cfg ← NEW [toolchanger] motion params · before/after/pickup/dropoff gcode [tool T0] – [tool T11] offsets · detection_pin · slot X [heater_generic tool1..11_heater] on ReHeat boards [fan_generic parked_fan_t*] on ReHeat boards · shutdown_speed: 1 [filament_switch_sensor tool*_filament] on ReHeat boards T0 extruder heater/sensor → reheat0:GPIO0 / GPIO40 T6–T11 detection pins → reheat1:GPIO18–23 tools.cfg Extruder motor (EBB36) manual_stepper tool_rack HOME_TOOL_RACK TOOL_RACK_UP/DOWN _CARRIAGE_FAN_CHECK _UPDATE_PARKED_FANS SAVE_TOOL_Z_OFFSET macros.cfg PRINT_START/END M104 / M109 Z_TILT_ADJUST No T0–T11 macros (plugin registers them automatically) Tool offsets persisted by plugin SET_TOOL_PARAMETER + SAVE_TOOL_PARAMETER write to saved_variables.cfg · save_current_tool: True handles boot-time restore No separate RESTORE_TOOL_ON_BOOT delayed_gcode needed

Abbildung 4 – Neue Architektur mit dem klipper-toolchanger-Plugin. Die Makros T0–T11 entfallen vollständig; das Plugin registriert diese Befehle. Toolchanger.cfg ist die neue Konfigurationszentrale für alle werkzeugspezifischen Daten und die Wechselsequenzen.

Der wesentliche strukturelle Unterschied besteht darin, dass die benutzerdefinierten T11T0G-Code-Makros, dasTOOL_VARS Makro zur Zustandsverfolgung sowie LOAD_TOOL,UNLOAD_TOOL , RESET_TOOL_STATE, undRESTORE_TOOL_ON_BOOT alle wegfallen. Das Plugin bietet gleichwertige Funktionalität in einem gepflegten, getesteten Framework. Was bleibt, ist das maschinenspezifische Wissen: die Steckplatzpositionen, die Rüttelsequenz, die Nocken-Geschwindigkeiten, die Offset-Werte – all das kennen wir bereits aus der Ad-hoc-Phase.

Die Migration wird parallel zur festgelegten Konfiguration in einemtest_config/Verzeichnis entwickelt. Nichts wird an die Maschine übertragen, bevor es nicht anhand der als fehlerfrei bekannten Referenz validiert wurde.


Der Wert, es zunächst falsch zu bauen

Wir haben dieses System ad-hoc aufgebaut, weil wir dazu gezwungen waren. Die Hardware war ein bewegliches Ziel, die Anzahl der Werkzeuge war ungewiss, und das mechanische Design wurde parallel zur Firmware weiterentwickelt. Kein bestehendes Framework hätte all das unbeschadet überstanden.

Doch die Ad-hoc-Phase war keine vergebliche Mühe – sie war die Forschungsphase. Jedes der oben genannten gelösten Probleme stellt eine echte Einschränkung bei jeder Werkzeugwechsler-Implementierung dar: Die Offset-Pipeline muss auf Null gesetzt werden, bevor der G-Code für die Aufnahme ausgeführt wird; die Nockenwelle benötigt eine synchronisierte Extruderbewegung, um richtig einzurasten; die Logik für geparkte Lüfter benötigt den Wärmestatus pro Werkzeug mit einem Ausschluss für aktive Werkzeuge; bei Filamentmangel ist eine Gating-Schicht erforderlich; M104 muss je nach Werkzeugindex an unterschiedliche Heizelement-Backends weitergeleitet werden. Das sind keine Meinungen. Es sind Einschränkungen, die wir beim Betrieb der Hardware entdeckt haben.

Wir wissen nun genau, was das Plugin leisten muss und warum, denn wir haben es bereits selbst umgesetzt. Das ist der Vorteil, wenn man es zunächst falsch aufbaut.

Was als Nächstes kommt

  • Installieren Sie die beiden ReHeat-A0-Platinen und verdrahten Sie die Heizelemente T0–T11, Lüfter, Thermistoren und Endschalter daran
  • Installieren Sie das klipper-toolchanger-Plugin und validieren Sie estest_config/anhand der festgelegten Referenz
  • T3–T11 (derzeit Offsets auf Null) nacheinander in Betrieb nehmen
  • Kalibrieren Sie den Filament-Anfahrweg pro Werkzeug, sobald die Materialtypen zugewiesen sind
  • Beginnen Sie mit den Multimaterial-Drucktests, wobei das gesamte 12-Werkzeug-Rack bestückt ist

Das Ziel war schon immer eine Maschine, die während des Druckvorgangs ohne Eingreifen des Bedieners und ohne Kreuzkontamination das Material wechseln kann. Die Hardware ist bereit. Die Softwarearchitektur ist bereit. Jetzt ist es an der Zeit, tatsächlich mit allen zwölf Werkzeugen zu drucken.

Folgen
Newest Art:
SHOPPING BAG 0
RECENTLY VIEWED 0