A large, slow first layer tripped AFC's buffer fault detection ("AFC NOT
FEEDING"). _FIRST_LAYER_FAULT_DETECTION ON=1 at the end of PRINT_START
sets first_layer_sensitivity (4: pause after 70 mm instead of 30 mm);
ON=0 restores the [AFC_buffer] config value. The slicer calls ON=0 at
the start of layer 2:
{if layer_num == 1}_FIRST_LAYER_FAULT_DETECTION ON=0{endif}
_PRINT_CLEANUP calls it too, for prints that end in the first layer.
ON=0 only restores what ON=1 changed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
385 lines
17 KiB
INI
385 lines
17 KiB
INI
# Print start and end: PRINT_WARMUP, PRINT_START, PRINT_END
|
|
|
|
#####################################################################
|
|
# Macros
|
|
#####################################################################
|
|
|
|
# OrcaSlicer calls PRINT_WARMUP and then PRINT_START. HEAT_SOAK at the end of
|
|
# PRINT_WARMUP pauses the print file and resumes it when the soak is done, so
|
|
# PRINT_START runs on a soaked bed:
|
|
# PRINT_WARMUP bed heats while the nozzle cools and the printer homes,
|
|
# wait for the bed, HEAT_SOAK
|
|
# PRINT_START QGL, Z, contact Z calibration, mesh, load the initial tool,
|
|
# PA for all lanes, XY reference
|
|
# The filament is loaded after the Z calibration, so the contact always
|
|
# touches a clean nozzle that has not been oozing at 150C for minutes.
|
|
# Chamber prints are the exception: PRINT_WARMUP loads and purges the first
|
|
# tool before the chamber heats (_LOAD_FOR_CHAMBER), retracted for the soak;
|
|
# BLOBIFIER_CLEAN cleans the nozzle before the contact.
|
|
|
|
[gcode_macro PRINT_WARMUP]
|
|
description: Print start before HEAT_SOAK: home while the bed heats, wait for the bed, soak
|
|
gcode:
|
|
{% set target_bed = params.BED|int %}
|
|
{% set target_chamber = params.CHAMBER|default("0")|int %}
|
|
{% set initial_tool = params.TOOL|default("0")|int %}
|
|
|
|
RESET_FAILURE
|
|
CLEAR_PAUSE
|
|
# Clear what an earlier print or error left on the display
|
|
M117
|
|
|
|
_LIGHTS_ON
|
|
|
|
UPDATE_DELAYED_GCODE ID=_delayed_motors_off DURATION=0
|
|
BED_MESH_CLEAR
|
|
SET_GCODE_OFFSET Z=0
|
|
SKEW_PROFILE LOAD=Califlower
|
|
M106 S0
|
|
|
|
# Bed first, so it heats during the rest of the warmup
|
|
{% if target_bed < 60 %}
|
|
RESPOND MSG="Using PID for <60°C bed"
|
|
SET_HEATER_TEMPERATURE HEATER=heater_bed TARGET={target_bed} PID_KP=33.606 PID_KI=0.694 PID_KD=407.058
|
|
{% else %}
|
|
RESPOND MSG="Using PID for ≥60°C bed"
|
|
SET_HEATER_TEMPERATURE HEATER=heater_bed TARGET={target_bed} pid_Kp=46.843 pid_Ki=2.002 pid_Kd=274.034
|
|
{% endif %}
|
|
|
|
# Only to get the toolhead over the bed for the soak; the Z that counts
|
|
# is the contact calibration in PRINT_START on the soaked bed
|
|
_STATUS STATE=homing
|
|
# HOME homes Z with Beacon contact: wait for a steady nozzle temperature,
|
|
# a nozzle still heating spreads the contact samples (6 restarts seen)
|
|
_NOZZLE_CONTACT_TEMP WAIT=1
|
|
RESPOND MSG="homing"
|
|
HOME
|
|
{% if target_chamber > 30 %}
|
|
_LOAD_FOR_CHAMBER TOOL={initial_tool}
|
|
{% endif %}
|
|
_GUARD_ON PHASE=warmup_park
|
|
G1 X175 Y175 Z5 F1200
|
|
_GUARD_OFF
|
|
|
|
_STATUS STATE=heating_bed
|
|
{% if target_bed > 0 %}
|
|
RESPOND MSG="heating"
|
|
M190 S{target_bed}
|
|
{% endif %}
|
|
|
|
{% if target_chamber > 30 %}
|
|
RESPOND MSG="waiting for chamber temperature"
|
|
M141 S{target_chamber}
|
|
M106 S255
|
|
# Unreachable SOAK_TEMP + CANCEL='_NOTHING': soaks until the TIMEOUT, then continues the print
|
|
HEAT_SOAK HEATER='heater_bed' TARGET={target_bed} SOAKER='temperature_sensor chamber' SOAK_TEMP={target_chamber} TIMEOUT=60 CANCEL='_NOTHING'
|
|
{% else %}
|
|
RESPOND MSG="waiting for bed core temperature"
|
|
HEAT_SOAK HEATER='heater_bed' TARGET={target_bed} SOAKER='temperature_sensor bed_core' SOAK_TEMP={target_bed - 5} TIMEOUT=30 CANCEL='_NOTHING'
|
|
{% endif %}
|
|
|
|
# With the chamber heated, filament in the toolhead path softens: a PLA lane
|
|
# still loaded softens in the PTFE tube, and the piece of PLA that a cut
|
|
# unload leaves below the cutter softens in the extruder and jams the next
|
|
# load (seen: PLA unloaded, ASA jammed after the soak). The piece stays until
|
|
# the next load, so an empty toolhead can have it too. For a chamber print,
|
|
# load the print's first tool and purge it now, before the chamber heats:
|
|
# the purge at the new filament's temperature pushes the leftover out.
|
|
# AFC unloads a loaded lane back into the Box Turtle, outside the chamber.
|
|
[gcode_macro _LOAD_FOR_CHAMBER]
|
|
description: Before a chamber soak: load and purge TOOL (the print's first tool) unless its lane is already loaded
|
|
gcode:
|
|
{% set tool = 'T' ~ params.TOOL|default(0)|int %}
|
|
{% if 'AFC' in printer %}
|
|
{% set cur = printer.AFC.current_load %}
|
|
{% set ns = namespace(lane=none) %}
|
|
{% for l in printer.AFC.lanes if ('AFC_stepper ' ~ l) in printer and printer['AFC_stepper ' ~ l].map == tool %}
|
|
{% set ns.lane = l %}
|
|
{% endfor %}
|
|
{% if ns.lane is none %}
|
|
_ERROR MSG="Chamber heating: no AFC lane is mapped to {tool}, print stopped before the chamber heats" DISPLAY="No lane for {tool}, print stopped"
|
|
{% elif cur != ns.lane %}
|
|
{% set new = printer['AFC_stepper ' ~ ns.lane] %}
|
|
# Hot enough to unload the old filament and to purge the new one
|
|
{% set temps = [new.extruder_temp|default(0, true)|int, printer.configfile.settings.extruder.min_extrude_temp|int + 20] %}
|
|
{% if cur and ('AFC_stepper ' ~ cur) in printer %}
|
|
{% set temps = temps + [printer['AFC_stepper ' ~ cur].extruder_temp|default(0, true)|int] %}
|
|
RESPOND MSG="Chamber heating: unloading {cur} ({printer['AFC_stepper ' ~ cur].material}), loading and purging {ns.lane} ({new.material}, {tool}) before the chamber heats"
|
|
{% else %}
|
|
RESPOND MSG="Chamber heating: loading and purging {ns.lane} ({new.material}, {tool}) before the chamber heats"
|
|
{% endif %}
|
|
# The Blobifier only purges with the QGL applied: without it AFC
|
|
# loaded the lane and skipped the purge. Level now (PRINT_START
|
|
# levels again on the soaked bed) and home Z after it.
|
|
{% if printer.quad_gantry_level and not printer.quad_gantry_level.applied %}
|
|
_GUARD_ON PHASE=warmup_qgl
|
|
QUAD_GANTRY_LEVEL
|
|
_GUARD_ON PHASE=warmup_z_home
|
|
G28 Z
|
|
_GUARD_OFF
|
|
{% endif %}
|
|
M109 S{temps|max}
|
|
{tool}
|
|
_LOAD_FOR_CHAMBER_VERIFY LANE={ns.lane}
|
|
{% endif %}
|
|
{% endif %}
|
|
|
|
# Separate macro: rendered after the tool change has run. AFC pauses on a
|
|
# failed load instead of raising an error, so check the result here: a
|
|
# failed load stops the print before the chamber heats.
|
|
[gcode_macro _LOAD_FOR_CHAMBER_VERIFY]
|
|
description: Helper for _LOAD_FOR_CHAMBER: stop the print when LANE is not loaded, else retract and cool the nozzle for the soak
|
|
gcode:
|
|
{% if printer.AFC.current_load != params.LANE %}
|
|
_ERROR MSG="Chamber heating: {params.LANE} could not be loaded ({printer.AFC.current_load or 'nothing'} in the toolhead), print stopped before the chamber heats" DISPLAY="Load failed, print stopped"
|
|
{% else %}
|
|
# New filament, just purged: a retract remembered for the old one is
|
|
# gone. Retract so it does not ooze during the soak; PRINT_START
|
|
# undoes it over the bucket.
|
|
SAVE_VARIABLE VARIABLE=nozzle_retract VALUE=0
|
|
_NOZZLE_RETRACT
|
|
M104 S150
|
|
{% endif %}
|
|
|
|
# Beacon contact refuses a nozzle above contact_max_hotend_temperature (180C)
|
|
[gcode_macro _NOZZLE_CONTACT_TEMP]
|
|
description: Nozzle to 150C for Beacon contact; wait only while it is above MAXIMUM (default 170C), WAIT=1 also waits until it is at least 145C
|
|
gcode:
|
|
{% set max = params.MAXIMUM|default(170)|float %}
|
|
{% set heat = params.WAIT|default(0)|int == 1 %}
|
|
M104 S150
|
|
{% if heat %}
|
|
TEMPERATURE_WAIT SENSOR=extruder MINIMUM=145 MAXIMUM={max}
|
|
{% elif printer.extruder.temperature > max %}
|
|
RESPOND MSG="Nozzle at {printer.extruder.temperature|round|int}C, waiting until it is below {max|int}C"
|
|
TEMPERATURE_WAIT SENSOR=extruder MAXIMUM={max}
|
|
{% endif %}
|
|
|
|
# AFC heats for a tool change from the print file's per-tool temperatures.
|
|
# When the loaded lane's tool number is not in that list (for example lane5 =
|
|
# T4 loaded, single-colour print with only T0), AFC does not heat at all and
|
|
# the unload fails below min_extrude_temp. Heat for the loaded filament first.
|
|
[gcode_macro _HEAT_FOR_LOADED_LANE]
|
|
description: Heat to the loaded lane's filament temperature before changing to another tool
|
|
gcode:
|
|
{% set tool = 'T' ~ params.TOOL|default(0)|int %}
|
|
{% set cur = printer.AFC.current_load %}
|
|
{% if cur and ('AFC_stepper ' ~ cur) in printer %}
|
|
{% set lane = printer['AFC_stepper ' ~ cur] %}
|
|
{% if lane.map != tool %}
|
|
{% set min_temp = printer.configfile.settings.extruder.min_extrude_temp|int + 20 %}
|
|
{% set temp = [lane.extruder_temp|default(0, true)|int, min_temp]|max %}
|
|
RESPOND MSG="Heating to {temp}C to unload {cur} ({lane.material}) before {tool}"
|
|
M109 S{temp}
|
|
{% endif %}
|
|
{% endif %}
|
|
|
|
[gcode_macro _NOTHING]
|
|
gcode:
|
|
RESUME_BASE
|
|
|
|
[gcode_macro PRINT_START]
|
|
description: Print start after HEAT_SOAK: QGL, Z calibration, mesh, load the initial tool, PA
|
|
gcode:
|
|
{% set target_extruder = params.EXTRUDER|int %}
|
|
{% set initial_tool = params.TOOL|default("0")|int %}
|
|
|
|
RESPOND MSG="print start macro continued"
|
|
_LIGHTS_ON
|
|
M106 S0
|
|
|
|
# The last homing was before the soak: check that Z still matches the
|
|
# Beacon before the gantry goes low. MAX=1.0: the soak itself drifts Z
|
|
# a few tenths; the console line shows how much.
|
|
_Z_CHECK MAX=1.0
|
|
|
|
# The bed is soaked now: level and calibrate Z on the final geometry
|
|
_STATUS STATE=leveling
|
|
RESPOND MSG="quad gantry level"
|
|
# Nozzle guard (bd_pressure) on while nothing should touch the nozzle
|
|
_GUARD_ON PHASE=qgl
|
|
QUAD_GANTRY_LEVEL
|
|
# QGL moved the Z steppers and the frame drifted during the soak: home Z
|
|
# again (proximity, on the model of the contact homing in PRINT_WARMUP)
|
|
# before the brush takes the nozzle below Z0
|
|
_GUARD_ON PHASE=z_home
|
|
G28 Z
|
|
|
|
# Own phase, log only: the bristles press on the nozzle (peak 47,
|
|
# 4 reads above 30 in a row), stop mode would shut down every start
|
|
_GUARD_ON PHASE=brush MODE=log
|
|
BLOBIFIER_CLEAN
|
|
_GUARD_OFF
|
|
_STATUS STATE=calibrating_z
|
|
_NOZZLE_CONTACT_TEMP WAIT=1
|
|
G28 Z METHOD=CONTACT CALIBRATE=1 ; calibrate z offset and beacon model hot
|
|
|
|
_STATUS STATE=meshing
|
|
RESPOND MSG="meshing"
|
|
_GUARD_ON PHASE=mesh
|
|
BED_MESH_CALIBRATE RUNS=2 ; bed mesh in scan mode
|
|
# From here on the nozzle extrudes and AFC cuts and loads: guard off
|
|
_GUARD_OFF
|
|
|
|
# Heat over the bucket, then undo the retracts of the previous PRINT_END
|
|
_STATUS STATE=heating_extruder
|
|
BLOBIFIER_PARK
|
|
M109 S{target_extruder}
|
|
_NOZZLE_UNRETRACT
|
|
|
|
# Load the initial tool (unloading another lane needs that lane's temperature)
|
|
_HEAT_FOR_LOADED_LANE TOOL={initial_tool}
|
|
T{initial_tool}
|
|
|
|
PA_CALIBRATE_ALL_LANES LANES="lane1,lane2,lane3,lane4,lane5"
|
|
# A calibration can leave another lane loaded, and every cached lane
|
|
# sets its PA: load the initial tool again and apply its PA
|
|
T{initial_tool}
|
|
_PA_APPLY_LOADED_LANE
|
|
|
|
M109 S{target_extruder}
|
|
SET_GCODE_OFFSET Z=-0.00 ; squish
|
|
BLOBIFIER_CLEAN
|
|
|
|
# XY reference for AFC_XY_SAFETY_CHECK (homes X and Y; Z stays from the
|
|
# contact calibration)
|
|
_AFC_STORE_HOME_REFERENCE
|
|
|
|
# bdwidth is not used while printing: its motion reading stays at 0 and its
|
|
# per-reading width compensation made the flow jump between 77 and 119%.
|
|
# To use it again: SET_BDWIDTH NAME=fila_width_0 COMMAND=ENABLE_WIDTH
|
|
|
|
# A large, slow first layer can trip AFC's buffer fault detection
|
|
# ("AFC NOT FEEDING"); the slicer switches it back at layer 2
|
|
_FIRST_LAYER_FAULT_DETECTION ON=1
|
|
|
|
RESPOND MSG="starting"
|
|
_STATUS STATE=printing
|
|
|
|
|
|
[gcode_macro PRINT_END]
|
|
# Use PRINT_END for the slicer ending script - please customise for your slicer of choice
|
|
gcode:
|
|
# safe anti-stringing move coords
|
|
{% set th = printer.toolhead %}
|
|
{% set x_safe = th.position.x + 20 * (1 if th.axis_maximum.x - th.position.x > 20 else -1) %}
|
|
{% set y_safe = th.position.y + 20 * (1 if th.axis_maximum.y - th.position.y > 20 else -1) %}
|
|
{% set z_safe = [th.position.z + 2, th.axis_maximum.z]|min %}
|
|
|
|
SAVE_GCODE_STATE NAME=STATE_PRINT_END
|
|
|
|
_LIGHTS_ON
|
|
|
|
M400 ; wait for buffer to clear
|
|
G92 E0 ; zero the extruder
|
|
_NOZZLE_RETRACT ; so the nozzle does not leak while cooling down
|
|
|
|
TURN_OFF_HEATERS
|
|
iHEATER_OFF ; Turns off the chamber heater
|
|
|
|
G90 ; absolute positioning
|
|
G0 X{x_safe} Y{y_safe} Z{z_safe} F20000 ; move nozzle to remove stringing
|
|
G0 X{th.axis_maximum.x//2} Y{th.axis_maximum.y - 5} F3600 ; park nozzle at rear
|
|
M107 ; turn off fan
|
|
|
|
#SET_PIN PIN=caselight VALUE=0.03
|
|
_STATUS STATE=part_ready
|
|
|
|
UPDATE_DELAYED_GCODE ID=_delayed_motors_off DURATION=3600
|
|
|
|
RESTORE_GCODE_STATE NAME=STATE_PRINT_END
|
|
# After the restore: RESTORE_GCODE_STATE also restores the flow (M221)
|
|
_PRINT_CLEANUP
|
|
|
|
# End-of-print cleanup shared by PRINT_END and CANCEL_PRINT
|
|
[gcode_macro _PRINT_CLEANUP]
|
|
description: Heaters (incl. chamber heater) and part fan off, flow back to 100%, bdwidth off, mesh and skew cleared, nozzle guard off
|
|
gcode:
|
|
_GUARD_OFF ; a print start that failed while it was armed
|
|
TURN_OFF_HEATERS
|
|
iHEATER_OFF ; stops the chamber heater control loop too
|
|
M107
|
|
{% if 'bdwidth fila_width_0' in printer %}
|
|
SET_BDWIDTH NAME=fila_width_0 COMMAND=DISABLE ; also sets M221 S100
|
|
{% endif %}
|
|
M221 S100
|
|
BED_MESH_CLEAR
|
|
SET_SKEW CLEAR=1
|
|
{% if 'gcode_macro _PURGE_CHECK_CFG' in printer %}
|
|
SET_GCODE_VARIABLE MACRO=_PURGE_CHECK_CFG VARIABLE=ignore_missing_sensor VALUE=False
|
|
{% endif %}
|
|
_FIRST_LAYER_FAULT_DETECTION ON=0 ; a print that ended during the first layer
|
|
|
|
# AFC buffer fault detection (filament_error_sensitivity, 0-10, higher =
|
|
# pauses sooner) with its own value during the first layer.
|
|
# ON=1 at the end of PRINT_START, ON=0 from the slicer's layer change G-code
|
|
# at the start of layer 2 (OrcaSlicer, layer_num counts from 0):
|
|
# {if layer_num == 1}_FIRST_LAYER_FAULT_DETECTION ON=0{endif}
|
|
# _PRINT_CLEANUP also calls ON=0. The normal value is read from the
|
|
# [AFC_buffer] config; ON=0 only restores it if ON=1 changed it.
|
|
[gcode_macro _FIRST_LAYER_FAULT_DETECTION]
|
|
description: AFC filament fault detection with first_layer_sensitivity during the first layer (ON=1), back to the config value (ON=0)
|
|
variable_buffer: "Turtle_1"
|
|
variable_first_layer_sensitivity: 4 # 0-10: 0 = off, 4 = pause after 70 mm, 8 = after 30 mm
|
|
variable_lowered: False
|
|
gcode:
|
|
{% set key = "afc_buffer " ~ buffer|lower %}
|
|
{% set normal = printer.configfile.settings[key].filament_error_sensitivity|default(0)|float if key in printer.configfile.settings else 0 %}
|
|
{% if params.ON|default(1)|int %}
|
|
{% if normal > 0 and first_layer_sensitivity|float != normal %}
|
|
AFC_SET_ERROR_SENSITIVITY BUFFER={buffer} SENSITIVITY={first_layer_sensitivity}
|
|
SET_GCODE_VARIABLE MACRO=_FIRST_LAYER_FAULT_DETECTION VARIABLE=lowered VALUE=True
|
|
RESPOND MSG="AFC fault detection {first_layer_sensitivity} for the first layer (normally {normal})"
|
|
{% endif %}
|
|
{% elif lowered %}
|
|
AFC_SET_ERROR_SENSITIVITY BUFFER={buffer} SENSITIVITY={normal}
|
|
SET_GCODE_VARIABLE MACRO=_FIRST_LAYER_FAULT_DETECTION VARIABLE=lowered VALUE=False
|
|
RESPOND MSG="AFC fault detection back to {normal}"
|
|
{% endif %}
|
|
|
|
# Retract/unretract that is remembered across prints and restarts.
|
|
# Klipper's G10 only retracts once until a G11, and nothing sends G11 here,
|
|
# so G10 in PRINT_WARMUP used up the retract and PRINT_END's G10 did nothing.
|
|
# _NOZZLE_RETRACT adds to the saved variable nozzle_retract, _NOZZLE_UNRETRACT
|
|
# extrudes that amount again (over the bucket, in PRINT_START) and clears it.
|
|
[gcode_macro _NOZZLE_RETRACT]
|
|
description: Retract LENGTH mm (default variable_length) and remember it for _NOZZLE_UNRETRACT; EXTRA=mm already retracted elsewhere, only remembered
|
|
variable_length: 5.0 # end-of-print retract; more stops leaking, too much can jam (PLA/heat creep)
|
|
variable_max_total: 15.0 # never remember more than this in total
|
|
gcode:
|
|
{% set length = params.LENGTH|default(length)|float %}
|
|
{% set extra = params.EXTRA|default(0)|float %}
|
|
{% set done = printer.save_variables.variables.nozzle_retract|default(0)|float %}
|
|
{% set length = [length, max_total - done - extra]|min %}
|
|
{% if length > 0 and printer.extruder.can_extrude %}
|
|
SAVE_GCODE_STATE NAME=_nozzle_retract
|
|
M83
|
|
G1 E-{length} F1800
|
|
RESTORE_GCODE_STATE NAME=_nozzle_retract
|
|
{% else %}
|
|
{% set length = 0 %}
|
|
{% endif %}
|
|
{% if length + extra > 0 %}
|
|
SAVE_VARIABLE VARIABLE=nozzle_retract VALUE={[done + extra + length, max_total]|min}
|
|
{% endif %}
|
|
|
|
[gcode_macro _NOZZLE_UNRETRACT]
|
|
description: Extrude what _NOZZLE_RETRACT retracted (call over the bucket, nozzle hot)
|
|
gcode:
|
|
{% set done = printer.save_variables.variables.nozzle_retract|default(0)|float %}
|
|
{% if done > 0 %}
|
|
{% if printer.extruder.can_extrude %}
|
|
SAVE_GCODE_STATE NAME=_nozzle_unretract
|
|
M83
|
|
G1 E{done} F300
|
|
RESTORE_GCODE_STATE NAME=_nozzle_unretract
|
|
SAVE_VARIABLE VARIABLE=nozzle_retract VALUE=0
|
|
{% else %}
|
|
_WARN MSG="_NOZZLE_UNRETRACT: extruder too cold, {done} mm still retracted"
|
|
{% endif %}
|
|
{% endif %}
|
|
|
|
[delayed_gcode _delayed_motors_off]
|
|
gcode:
|
|
M84
|