Realizzazione del cambio utensili Starlex: un percorso di programmazione con Klipper

Cosa succede quando si decide di costruire una stampante 3D a 12 utensili e l’hardware continua a cambiare sotto i propri piedi? Si scrivono un sacco di macro Klipper. Questa è la storia delle sfide di programmazione che abbiamo superato lungo il percorso, di come si presenta il sistema attuale e di dove stiamo andando ora che l’hardware si è finalmente stabilizzato.

Cos’è il cambio utensili Starlex?

L’STC — il cambio utensili Starlex di Swiss3dc — è un sistema di cambio utensili “leggero” per stampanti 3D basate su Klipper. Il termine “leggero” rappresenta la differenza fondamentale rispetto a un cambio utensili convenzionale.

In un cambio utensili completo (come l’architettura E3D o Prusa XL), ogni utensile è una testina di stampa completa e autonoma: è dotato di un proprio motore passo-passo per l’estrusore, hotend, riscaldatore, termistore e ventola di raffreddamento. Cambiare gli utensili significa sostituire l’intero gruppo di azionamento e riscaldamento. Ciò garantisce la massima indipendenza tra gli utensili, ma aggiunge massa, costo e complessità meccanica significativi a ciascuno di essi.

Lo Starlex adotta un approccio diverso. Ogni utensile è semplicemente un gruppo hotend — composto da blocco riscaldante, termistore, ugello e ventola di raffreddamento. Non c’è un motore passo-passo dell’estrusore per ogni utensile. Al suo posto, un unico motore passo-passo dell’estrusore condiviso è montato sul carrello (sulla scheda della testina EBB36) e il meccanismo a camme lo accoppia fisicamente a qualunque utensile sia attualmente agganciato. Quando il carrello preleva un utensile, l’accoppiamento si innesta nel percorso del filamento; quando riporta l’utensile sul rack, l’accoppiamento si sgancia. Il motore di estrusione rimane sempre sul carrello.

Ciò mantiene bassi il peso e il costo degli utensili — si parcheggiano gli hotend, non le testine di stampa — garantendo al contempo un vero isolamento termico per ogni materiale, l’assenza di contaminazione incrociata tra i materiali e offset Z indipendenti per ogni utensile. Il compromesso è che l’allineamento tra accoppiamento e percorso del filamento deve funzionare in modo affidabile su 12 slot: è proprio qui che si concentra la maggior parte delle sfide ingegneristiche più interessanti.

Il nostro sistema gestisce 12 utensili. Questo numero ha guidato quasi tutte le decisioni relative al software che abbiamo preso.

Lo stack hardware

Prima di addentrarci nella programmazione, è utile capire con cosa deve interagire il software. La macchina è basata su un BIGTREETECH Manta M8P V2.0 come MCU principale, che esegue il firmware Klipper su un host Raspberry Pi. Un portale CoreXY gestisce il movimento XY; tre viti Z indipendenti gestiscono il livellamento del piano di stampa. Il portautensili è posizionato lungo il lato anteriore sinistro del volume di stampa ed è azionato da un meccanismo a camma su un motore passo-passo con riduttore planetario: la rotazione della camma solleva o abbassa una barra di accoppiamento che blocca o sblocca la testina utensile sul carrello.

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

Figura 1 — Gerarchia di comunicazione hardware. Il Manta M8P è l’MCU centrale; la scheda della testina utensile EBB36 comunica tramite CAN, mentre due schede di espansione ReHeat A0 si collegano via USB e gestiscono collettivamente tutti i 12 riscaldatori degli utensili, le ventole, i termistori e i sensori.

Ogni utensile dispone in modo indipendente di un riscaldatore, un termistore, una ventola di raffreddamento, un finecorsa di rilevamento della posizione di riposo e un sensore di esaurimento del filamento. Ciò comporta un totale di 60 segnali indipendenti distribuiti su 12 utensili: ecco perché esistono le schede di espansione ReHeat A0 — ed ecco perché vale la pena spiegarne il funzionamento.

La ReHeat A0 è una scheda prototipo che abbiamo sviluppato in collaborazione con Intelligent Agent, l’azienda norvegese che ha realizzato la scheda di controllo per la stampante 3D Recore. Il problema che abbiamo sottoposto loro era specifico del cambio rapido degli utensili: quando ogni utensile è solo un gruppo hotend anziché una testina di stampa completa, è realisticamente possibile utilizzare molti più utensili di quanti ne potesse gestire qualsiasi hardware di espansione esistente. Le schede standard per testine di stampa prevedono una scheda per ogni utensile. Noi avevamo bisogno di una scheda ogni sei utensili. La ReHeat A0 — sei uscite per riscaldatori, sei uscite per ventole, sei ingressi per termistori, dodici ingressi per finecorsa, il tutto su un microcontrollore RP2354B con USB-C e supporto nativo per Klipper — è stata progettata da zero per colmare questa lacuna. È il risultato diretto delle opportunità offerte da questo tipo di architettura per il cambio rapido degli utensili.

Inizio ad hoc: perché prima le macro personalizzate

All’inizio di questo progetto avevamo due utensili. Poi quattro. Poi dodici. Il design del meccanismo a camme è cambiato. La posizione dell’elettronica di riscaldamento è cambiata: dalla scheda principale alla scheda della testina utensile, fino a schede di espansione dedicate. Stavamo definendo l’accoppiamento meccanico, la geometria del percorso del filamento e la spaziatura degli slot proprio mentre scrivevamo il codice di configurazione.

Il linguaggio di macro e configurazione di Klipper è sorprendentemente espressivo per un file di configurazione: templating Jinja2 completo, accesso al dizionario completo dello stato della stampante, variabili con nome, esecuzione differita del G-code e concatenamento arbitrario di comandi G-code. Ti permette di costruire da zero un cambio utensili funzionante senza toccare il C++ — e, cosa ancora più importante, ti permette di modificarlo di nuovo domani stesso se l’hardware dovesse comportarsi in modo inaspettato.

Quindi abbiamo scritto tutto su misura. Macro T-command, monitoraggio dello stato degli utensili, controllo del rack, gestione delle ventole, persistenza degli offset — tutto quanto. È stata la scelta giusta. Ma prima di arrivare al perché, esaminiamo cosa dovevamo effettivamente risolvere.


1 Il conflitto di pin del BDsensor

Il BDsensor è una sonda Z capacitiva che comunica tramite I²C. Il punto logico per collegarlo sull’EBB36 era laPROBEporta — pin PB8. Ha quasi funzionato. Il problema: quella porta presenta un optoaccoppiatore sulla linea SDA, destinato ai tradizionali segnali di fine corsa, e l’optoaccoppiatore limita il segnale SDA in modo tale da compromettere la comunicazione I²C. Il sensore si inizializzava, ma restituiva letture errate.

La soluzione è stata quella di trovare un vero e proprio bus I²C sull’EBB36. I pin PA6 e PA7 sono collegati a una periferica I²C hardware senza optoaccoppiatore nel percorso del segnale:

# 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

Lahoming_cmd: G990028riga è un requisito meno ovvio: quando si ridefinisceG28 con rename_existing, la sequenza di autocalibrazione interna del BDsensor deve sapere quale G-code richiamare per il ritorno in origine. Senza di essa, la calibrazione attiva il wrapper della macro anziché il comando di base G28.1, causando un loop.


2 Il meccanismo a camma del portautensili

Il portautensili è una barra lineare che attraversa la parte anteriore della macchina. Ogni utensile è alloggiato nel proprio alloggiamento; quando il carrello si avvicina a un alloggiamento e la camma ruota, una barra di accoppiamento solleva e blocca l’utensile sul carrello (oppure lo abbassa e lo rilascia riportandolo sul portautensili).

manual_stepperLa camma è azionata da un motore passo-passo con un riduttore planetario 5:1, configurato in Klipper come un «motore passo-passo manuale»: ciò significa che Klipper non include questo asse nella normale pianificazione del movimento — lo azioniamo esplicitamente conMANUAL_STEPPERi comandi:

[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

La parte interessante è come sincronizziamo la camma con il motore dell’estrusore durante il prelievo. Il meccanismo di accoppiamento funziona al meglio quando anche l’estrusore ruota leggermente mentre la camma si posiziona: ciò aiuta ad allineare il percorso del filamento. Abbiamo utilizzato il parametro non standardGCODE_AXIS=R di Klipper sull’asse passo-passo manuale per mappare temporaneamente la camma a cremagliera su un asse “R” virtuale, quindi abbiamo emesso movimenti G1 multiasse che azionavano simultaneamente X (oscillazione), E (preparazione dell’estrusore) e R (rotazione della camma):

# 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

Questa “sequenza di oscillazione” è stata sviluppata empiricamente nel corso di numerosi tentativi di accoppiamento. La piccola oscillazione sull’asse X aiuta l’accoppiamento meccanico a posizionarsi correttamente mentre l’estrusore precarica il percorso del filamento. Il trucco dell’asse R non viene modificato durante la stampa dello strato inToolGcodeTransform Klipper, il che diventa importante una volta aggiunti gli offset degli utensili al quadro generale.


3 Dodici riscaldatori in un unico linguaggio di configurazione

La configurazione di Klipper non è un linguaggio di programmazione. Ma il sistema di template Jinja2 all’interno delle macro G-code è un linguaggio di programmazione, e il confine tra i due crea attrito quando è necessario eseguire operazioni in modo dinamico.

La sfida fondamentale: l’utensile 0 utilizza il[extruder]riscaldatore (una sezione speciale di Klipper con un proprio spazio dei nomi), mentre gli utensili da 1 a 11 utilizzano[heater_generic toolN_heater]le sezioni. Non è possibile scrivere una singola espressione che gestisca in modo trasparente entrambi senza conoscere prima il numero dell’utensile.

Nelle macro, Klipper espone lo stato del riscaldatore tramite ilprinter dizionario.printer.extruder.temperature funziona per T0. Per T1–T11 era necessaria una ricerca per chiave stringa:

# 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) %}

Il| default(0)filtro è essenziale. Se una sezione heater non è definita (ad esempio perché non abbiamo ancora collegato T5),printer["heater_generic tool5_heater"]restituisce un dizionario vuoto anziché generare un errore. Il filtro predefinito fa sì che restituisca in modo sicuro zero invece di causare il crash della macro.

Questo schema ricorre in tre punti diversi: le sostituzioni di instradamento M104/M109, il controllo della ventola del carrello e la logica di aggiornamento della ventola in pausa. Un errore in uno qualsiasi di questi punti causava o una ventola che rimaneva spenta quando avrebbe dovuto essere accesa, oppure un errore di Klipper che causava il crash della macro durante la stampa.


4 Il problema della gestione delle ventole

Un cambiautensili dispone di due gruppi distinti di ventole che richiedono una logica di controllo completamente diversa:

  • Ventola del carrello — raffredda l’hotend attivo. Dovrebbe essere accesa ogni volta che il riscaldatore dell’utensile attivo supera i 50 °C o ha un valore target diverso da zero.
  • Ventilatori inattivi — raffreddano le testine degli utensili riposte nel rack. Devono essere accesi quando il riscaldatore di un utensile inattivo è caldo, ma NON quando quell’utensile si trova attualmente sul carrello (perché se ne occupa la ventola del carrello e la geometria del ventilatore inattivo non raggiunge comunque la posizione attiva).

Dodici utensili, una ventola per ogni coppia di utensili, significa 6 ventole per gli utensili parcheggiati. La logica di accensione/spegnimento di ciascuna ventola diventa:

La ventola T(N)+T(N+1) è accesa se: (T(N) è caldo E T(N) non è l’utensile attivo) OPPURE (T(N+1) è caldo E T(N+1) non è l’utensile attivo)

Abbiamo implementato questo comportamento tramite_UPDATE_PARKED_FANS — una macro che legge lo stato di tutti e 12 i riscaldatori (utilizzando la ricerca dinamica descritta sopra) ed emetteSET_FAN_SPEEDi comandi di conseguenza. Questa macro viene eseguita una volta all’avvio, una volta dopo ogni cambio di utensile e in un ciclo con timer di 2 secondi:

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

Un dettaglio importante per la sicurezza: tutte le ventole inattive sono dichiarate con shutdown_speed: 1. Se Klipper va in crash o si spegne in uno stato di errore, le ventole accelerano automaticamente alla massima velocità. Le testine inattive che erano calde al momento del crash di Klipper vengono raffreddate anziché surriscaldarsi.


5 Offset degli utensili e persistenza dello stato

Ogni utensile presenta una geometria dell’ugello leggermente diversa e, a causa delle tolleranze di montaggio, ciascuno si trova in una posizione X, Y e Z leggermente diversa rispetto all’origine della macchina. Teniamo traccia degli offset specifici per ogni utensile che Klipper applica a tutte le coordinate di movimento successive dopo un cambio utensile.

Gli offset vengono memorizzati in saved_variables.cfg, un file nativo di Klipper che persiste anche dopo i riavvii:

[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]

Il flusso di lavoro per la calibrazione dell’asse Z è volutamente semplice: con un utensile selezionato, l’utente sposta manualmente l’asse Z verso il basso fino a quando un test su carta non risulta corretto, quindi richiama la macro SAVE_TOOL_Z_OFFSET. Tale macro leggeprinter.gcode_move.gcode_position.z — la posizione corrente nello spazio G-code — e la riscrive nel file di salvataggio.

La persistenza dello stato tra i riavvii è stata gestita da unRESTORE_TOOL_ON_BOOT gcode ritardato che si attiva 3 secondi dopo l’avvio di Klipper, legge l’ultimo numero di utensile attivo da saved_variables.cfg, e riapplica i relativi offset. Ciò impedisce che, in caso di riavvio della macchina nel bel mezzo di una stampa, si perda traccia di quali offset siano attivi.


6 Indirizzamento dei comandi M104 e M109 al riscaldatore destro

M104Gli slicer emettono comandi standardM104 T1 S215 (imposta la temperatura del riscaldatore, utensile 1, 215 °C) eM109 T0 S210 (attendi che la temperatura raggiunga il valore impostato). Questi comandi presuppongono un firmware in grado di gestire nativamente indici multipli per gli estrusori. L’implementazioneM109 di Klipper comunica solo con la[extruder]sezione .

La soluzione consiste nell’intercettare tali comandi utilizzando il meccanismo direname_existing Klipper:

[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

I controlli della ventola vengono chiamati all’interno di M104 in modo che lo stato della ventola si aggiorni immediatamente quando viene impostata una temperatura di destinazione — anche se ciò avviene durante un’attesa di temperatura che sta bloccando la coda principale. Lo stesso schema si applica alla nostraSET_HEATER_TEMPERATURE sovrascrittura, in modo che qualsiasi percorso che modifichi la temperatura di destinazione di un riscaldatore inneschi anche un aggiornamento della ventola.

La versione M109 (attesa bloccante) indirizza il caso T=0 aM109.1 e il caso T=1–11 a TEMPERATURE_WAIT SENSOR="heater_generic toolN_heater":

{% 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}

IlMINIMUM={s - 2} fornisce una finestra di arrivo di 2 °C, evitando attese indefinite dovute a lievi superamenti del termostato, pur garantendo che il riscaldatore sia effettivamente alla temperatura desiderata prima dell’inizio della stampa.


7 Livellamento dell’inclinazione Z con un cambiautensili

La macchina utilizza tre viti Z indipendenti eZ_TILT_ADJUST per mantenere il piano di stampa livellato. La sonda BDsensor si trova sul carrello, quindi il livellamento richiede lo spostamento lungo il piano e la rilevazione di più punti. Il problema: gli offset degli utensili applicati alle coordinate G-code comprometterebbero i calcoli della posizione della sonda.

La soluzione consiste nell’eseguireZ_TILT_ADJUST sempre senza alcun offset utensile attivo. Abbiamo implementato una funzione wrapper:

[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 pratica,PRINT_START scarica comunque esplicitamente l’utensile prima di richiamareZ_TILT_ADJUST . Tuttavia, il wrapper fornisce una rete di sicurezza per le operazioni di livellamento manuale da KlipperScreen, dove un utensile potrebbe essere già caricato.


8 Esaurimento del filamento in un sistema multi-utensile

Ogni utensile dispone di un sensore di esaurimento del filamento indipendente. Il sistema nativopause_on_runout: True di Klipper attiverebbe una PAUSA per qualsiasi sensore che si attiva — compresi tutti gli 11 utensili parcheggiati che non hanno alcun filamento caricato. Questo è ovviamente errato.

La soluzione è una macro di controllo che agisce solo sugli eventi di esaurimento del filamento provenienti dall’utensile attualmente attivo:

[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 %}

Tutti e 12 i sensori hannopause_on_runout: False e chiamano_FILAMENT_RUNOUT_GATE con il proprio numero di utensile. La macro confronta tale numero con quello dell’utensile attivo monitorato e mette in pausa solo quando corrispondono.


La sequenza di cambio utensile

Tutti questi elementi si combinano in una sequenza coordinata di cambio utensile. OgniT{N}comando segue lo stesso percorso:

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 %}

Figura 2 — Sequenza di cambio utensile. Il salto sull’asse Z nella fase precedente al cambio è protetto: viene attivato solo se è attualmente montato un utensile, poiché alla primissima selezione dell’utensile all’avvio della stampa non c’è nulla da scavalcare.

Architettura della configurazione attuale

La configurazione operativa è suddivisa in diversi file, ciascuno con una chiara delimitazione delle responsabilità:

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

Figura 3 — Architettura di configurazione attuale (“congelata”). Il file saved_variables.cfg costituisce il livello di persistenza per lo stato degli utensili e gli offset; tutto il resto è costituito esclusivamente da configurazione e macro Klipper.


Perché l’approccio ad hoc è stata la strategia giusta

Con il senno di poi, scrivere tutto da zero è stato l’approccio corretto per questa fase del progetto. Ecco perché.

L'hardware e il software si sono evoluti di pari passo. Quando abbiamo iniziato, la telecamera del rack degli utensili aveva un design diverso. L'elettronica del riscaldatore è stata spostata dalla scheda madre all'EBB36 e poi a schede di espansione ReHeat dedicate. Il numero di utensili è passato da 2 a 12. Il passo degli slot è cambiato. Qualsiasi framework adottato all’inizio avrebbe richiesto un continuo riadattamento man mano che la realtà fisica cambiava al di sotto di esso — probabilmente ad ogni revisione. Le macro personalizzate sono facili da modificare.

Il debug dell’hardware è più semplice senza le astrazioni dei framework. Quando un innesto non si innesta, o un termistore rileva 400 °C, o la camma va in overshoot, è necessario vedere esattamente cosa il firmware sta comunicando all’hardware. Con le macro personalizzate è possibileaction_respond_info(), da qualsiasi punto, aggiungere una registrazione temporanea a qualsiasi fase e modificare la sequenza senza dover comprendere il funzionamento interno di un plugin. Ciò si è rivelato inestimabile durante lo sviluppo meccanico.

La calibrazione iterativa richiedeva il controllo totale. Trovare la giusta sequenza di jiggle, le giuste posizioni X dello slot, la giusta velocità della camma, la giusta distanza di retrazione del filamento — tutto ciò è stato messo a punto empiricamente nel corso di numerosi tentativi di cambio utensile. Un linguaggio di configurazione in cui ogni parametro è esposto direttamente nel file di configurazione (non nascosto nel codice del plugin) ha reso veloce tale iterazione.

Ciò ha permesso di acquisire una comprensione approfondita. Ogni peculiarità della pipeline di trasformazione del G-code di Klipper, dell’ordine di esecuzione delle macro e delle convenzioni di denominazione dei riscaldatori è stata individuata, sottoposta a debug e compresa in prima persona. Tale comprensione ora guida direttamente il modo in cui stiamo implementando la migrazione dei plugin — e ci indica a quali casi limite prestare attenzione.

La configurazione di riferimento congelata (2026-06-02/config/) è esattamente questo: un’istantanea verificata di ciò che funziona al massimo delle capacità hardware. Funge da base di riferimento mentre costruiamo la nuova architettura parallelamente ad essa.


La strada da seguire: il plugin klipper-toolchanger

Ora che l’hardware si è stabilizzato — il meccanismo a camme è collaudato, la geometria degli slot è definita, le schede ReHeat sono definite — l’onere di manutenzione dell’approccio personalizzato diventa il suo principale svantaggio. Ogni utente che costruisce questa macchina deve comprendere il sistema delle macro partendo da zero. Ogni aggiornamento di Klipper rischia di compromettere qualche aspetto sottile nelle macro personalizzate dei comandi T. Il codice è intrinsecamente specifico per questa macchina.

Il plugin klipper-toolchanger di Viesturz fornisce un framework per il cambio utensili appositamente progettato per Klipper che risolve gli stessi problemi che abbiamo risolto manualmente, ma in un modo che la comunità più ampia possa gestire. Funzionalità chiave in linea con le nostre esigenze:

  • T0Definizioni dichiarative degli utensili — ogni[tool T0] sezione da[tool T11] a contiene i propri offset, il riferimento del riscaldatore, il pin di rilevamento e la posizione X dello slot. Il plugin registra automaticamente questi datiT11 come comandi G-code.
  • ToolGcodeTransform — il plugin gestisce la pipeline degli offset G-code. Il G-code di prelievo e rilascio viene eseguito in uno spazio senza offset (quindi le coordinate del rack sono sempre assolute), quindi gli offset vengono applicati una volta completato il cambio.
  • save_current_tool: True — il ripristino dello stato all’avvio è integrato, sostituendo il nostroRESTORE_TOOL_ON_BOOT G-code ritardato.
  • initialize_on: home — il cambiutensili si inizializza automaticamente con G28, senza necessità di una chiamata separata da PRINT_START.
  • t_command_restore_axis: XY — il plugin ripristina automaticamente la posizione XY precedente al cambio dopo ogni cambio utensile; dobbiamo gestire autonomamente solo l’asse Z tramite gli hook before/after.

Nuova architettura

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

Figura 4 — Nuova architettura con il plugin klipper-toolchanger. Le macro da T0 a T11 scompaiono completamente; il plugin registra tali comandi. Toolchanger.cfg è il nuovo hub di configurazione per tutti i dati relativi ai singoli utensili e le sequenze di cambio.

T0La differenza strutturale fondamentale è che le macro G-codeT11 personalizzate, laTOOL_VARS macro di tracciamento dello stato, LOAD_TOOL, UNLOAD_TOOL, RESET_TOOL_STATE, eRESTORE_TOOL_ON_BOOT scompaiono tutte. Il plugin fornisce funzionalità equivalenti in un framework mantenuto e testato. Ciò che rimane è la conoscenza specifica della macchina: le posizioni degli slot, la sequenza di jiggle, le velocità delle camme, i valori di offset — tutti elementi che conosciamo già dalla fase ad hoc.

La migrazione viene sviluppata parallelamente alla configurazione definitiva in unatest_config/directory. Nulla viene inviato alla macchina finché non viene convalidato rispetto al riferimento noto e funzionante.


Il valore di costruire prima in modo "sbagliato"

Abbiamo costruito questo sistema ad hoc perché dovevamo farlo. L’hardware era un bersaglio mobile, il numero di utensili era incerto e la progettazione meccanica veniva iterata in parallelo con il firmware. Nessun framework esistente sarebbe sopravvissuto intatto a tutto ciò.

Ma la fase ad hoc non è stata uno sforzo sprecato: è stata la fase di ricerca. Ogni problema risolto sopra rappresenta un vincolo reale per qualsiasi implementazione del cambio utensili: la pipeline di offset deve azzerarsi prima che venga eseguito il G-code di prelievo; la camma necessita di un movimento sincronizzato dell’estrusore per posizionarsi correttamente; la logica della ventola in pausa richiede lo stato termico per ogni singolo utensile con l’esclusione di quelli attivi; l’esaurimento del filamento richiede uno strato di chiusura; M104 deve indirizzarsi a diversi backend di riscaldamento a seconda dell’indice dell’utensile. Queste non sono opinioni. Sono vincoli che abbiamo scoperto facendo funzionare l’hardware.

Ora sappiamo esattamente cosa deve fare il plugin e perché, perché l’abbiamo già fatto noi stessi. Questo è il valore di averlo costruito male all’inizio.

Prossimi passi

  • Installare le due schede ReHeat A0 e collegarvi i riscaldatori T0–T11, le ventole, i termistori e i finecorsa
  • Installare il plugin klipper-toolchanger e verificarne il funzionamentotest_config/rispetto al riferimento congelato
  • Mettiamo in funzione T3–T11 (attualmente con offset tutti a zero) uno strumento alla volta
  • Calibrare la distanza di avvio del filamento per ogni utensile man mano che vengono assegnati i tipi di materiale
  • Avviare i test di stampa multimateriale con il rack completo da 12 utensili popolato

L’obiettivo è sempre stato quello di realizzare una macchina in grado di cambiare materiale durante la stampa senza l’intervento dell’operatore e senza contaminazione incrociata. L’hardware è pronto. L’architettura software è pronta. Ora è il momento di stampare davvero con tutti e dodici gli utensili.

Segui
Newest Art:
SHOPPING BAG 0
RECENTLY VIEWED 0