open4neptune-new/KNCM/nozzle_clean.cfg
root e22df9b5ab Feat: Replace custom KNCM with official Open-Elegoo-Community version
Replaced our custom nozzle cleaning macros with the official
Klipper Nozzle Clean Macro (KNCM) from Open-Elegoo-Community.

Changes:
- Added KNCM/nozzle_clean.cfg (official macro code)
- Updated KNCM_settings.cfg with official variable-based config
- Updated KNCM/KNCM_settings.cfg (copy for include path)
- Removed custom CLEAN_NOZZLE, SMART_PARK, LINE_PURGE macros

Benefits of official KNCM:
- Proper wiper position calculation (not hardcoded X0 Y0!)
- Variable-based configuration (easy to customize)
- Filament-specific cleaning temperatures
- Sinusoidal wiping pattern for better cleaning
- PurgeShake to remove strings before wiping
- Actively maintained by Open-Elegoo-Community

Neptune 4 Plus specific settings:
- printer_bed_y_max: 330
- wiping_axis: Y (bed moves)
- cleaning_height: 30 (CALIBRATE THIS!)
- bed_corner_to_wiper_offset: 40.5 (MEASURE THIS!)

Reference: https://github.com/Open-Elegoo-Community/klipper-nozzle-clean-macro
2026-03-13 16:38:54 +00:00

473 lines
No EOL
20 KiB
INI

#############################################################################
# NOZZLE CLEAN CODE
#############################################################################
# ------------------------------------------------------------------------
# !!! DO NOT EDIT THIS FILE !!! - Changes below are unsupported
# ------------------------------------------------------------------------
[gcode_macro CLEAN_NOZZLE]
description: Nozzle cleaning using a brush
gcode:
# ------------------------------------------------------------------------
# LOAD SETTINGS
# ------------------------------------------------------------------------
# Load variables from KNCM_settings.cfg
{% set KNCM_SETTINGS = printer['gcode_macro _KNCM_SETTINGS'] %}
# cleaningHeight
{% set cleaningHeight = KNCM_SETTINGS.cleaning_height %}
# wipingAxis
{% set wipingAxis = KNCM_SETTINGS.wiping_axis %}
# printerBedYMax
{% set printerBedYMax = KNCM_SETTINGS.printer_bed_y_max %}
# cleaningLoops
{% set cleaningLoops = KNCM_SETTINGS.cleaning_loops %}
# purgeSHake
{% set purgeShake = KNCM_SETTINGS.purge_shake %}
# waitNozzleTempRestore
{% set waitNozzleTempRestore = KNCM_SETTINGS.wait_nozzle_temp_restore %}
# debug
{% set debug = KNCM_SETTINGS.debug %}
# acceleration
{% set acceleration = KNCM_SETTINGS.acceleration %}
# speed
{% set travelSpeed = KNCM_SETTINGS.travel_speed %}
{% set slowSpeed = KNCM_SETTINGS.slow_speed %}
{% set fastSpeed = KNCM_SETTINGS.fast_speed %}
# cleaningTemperatures
{% set cleaningTemperatures = KNCM_SETTINGS.cleaning_temperatures %}
# wiperWidth
{% set wiperWidth = KNCM_SETTINGS.wiper_width %}
# wiperLength
{% set wiperLength = KNCM_SETTINGS.wiper_length %}
# wiperWallOffset
{% set wiperWallOffset = KNCM_SETTINGS.wiper_walloffset %}
# bedCornerToWiperOffset
{% set bedCornerToWiperOffset = KNCM_SETTINGS.bed_corner_to_wiper_offset %}
# waves
{% set waves = KNCM_SETTINGS.waves %}
# ------------------------------------------------------------------------
# DEBUG MESSAGE - LOADED VARIABLES
# ------------------------------------------------------------------------
{% if debug == 1 %}
RESPOND MSG="INFO: loaded Variables"
RESPOND MSG=" - cleaningHeight : { cleaningHeight }"
RESPOND MSG=" - wipingAxis : { wipingAxis }"
RESPOND MSG=" - printerBedYMax : { printerBedYMax }"
RESPOND MSG=" - cleaningLoops : { cleaningLoops }"
RESPOND MSG=" - purgeShake : { purgeShake }"
RESPOND MSG=" - waitNozzleTempRestore : { waitNozzleTempRestore }"
RESPOND MSG=" - debug : { debug }"
RESPOND MSG=" - wiperWidth : { wiperWidth }"
RESPOND MSG=" - wiperLength : { wiperLength }"
RESPOND MSG=" - wiperWallOffset : { wiperWallOffset }"
RESPOND MSG=" - bedCornerToWiperOffset : { bedCornerToWiperOffset }"
RESPOND MSG=" - waves : { waves }"
RESPOND MSG=" - acceleration : { acceleration }"
RESPOND MSG=" - travelSpeed : { travelSpeed }"
RESPOND MSG=" - slowSpeed : { slowSpeed }"
RESPOND MSG=" - fastSpeed : { fastSpeed }"
RESPOND MSG=" - cleaningTemperatures : { cleaningTemperatures }"
{% endif %}
# ------------------------------------------------------------------------
# INITIALIZE, CHECK AND PROCESS VARIABLES
# ------------------------------------------------------------------------
{% set filamentType = params.FILAMENT_TYPE|default("PLA")|string|upper %} # Set filamentType to the provided variable (fallback to PLA)
{% set error = 0 %} # Initialize error variable
{% if wipingAxis not in ["x", "X", "y", "Y"] %} # Check for a valid wipingAxis
RESPOND MSG="ERROR: Wiping axis configuration error. Please check your wipingAxis setting!"
{% set error = 1 %}
{% endif %}
{% if wipingAxis in ["x", "X"] %} # Check clearances for cleaning with head movements
{% if ((printer.toolhead.axis_maximum.y - printerBedYMax) < 3.2) %}
RESPOND MSG="ERROR: Insufficient vertical clearance between printerBedYMax ({printerBedYMax}) and nozzle max Y ({printer.toolhead.axis_maximum.y}). Minimum required: 3.2mm."
{% set error = 1 %}
{% endif %}
{% if ((printerBedYMax < 1) or ((printerBedYMax > 1000))) %}
RESPOND MSG="ERROR: Printer bed Y-axis size ({printerBedYMax}) out of expected range. Please verify configuration."
{% set error = 1 %}
{% endif %}
{% elif ((printer.configfile.config.stepper_x.position_endstop|float + 3.2) > 0) %} # Check clearances for cleaning with bed movements
RESPOND MSG="ERROR: X homing sensor position ({printer.configfile.config.stepper_x.position_endstop}) is too close to the bed. Required: ≤ -3.2mm."
{% set error = 1 %}
{% endif %}
{% if cleaningLoops < 1 %} # Check if cleaningLoops is set to a minimum of 1 loop
RESPOND MSG="WARNING: cleaningLoops set to false value:{cleaningLoops} - changed to default 1 "
{% set cleaningLoops = 1 %}
{% endif %}
{% if wipingAxis in ["y", "Y"] %} # Cleaning using bed movement
{% set wiperYmax = printerBedYMax %}
{% if wiperWidth + wiperWallOffset + printer.configfile.config.stepper_x.position_endstop|float > 0 %} # limited clearance
{% set wiperXmin = printer.configfile.config.stepper_x.position_endstop|float %}
{% set wiperWidth = 0 - wiperWallOffset - printer.configfile.config.stepper_x.position_endstop|float %}
{% else %}
{% set wiperXmin = 0 - wiperWallOffset - wiperWidth %}
{% endif %}
{% else %} # Cleaning using head movement
{% set wiperXmin = bedCornerToWiperOffset %}
{% if printer.toolhead.axis_maximum.y - printerBedYMax - wiperWallOffset - wiperWidth < 0 %}
{% set wiperYmax = printer.toolhead.axis_maximum.y %}
{% set wiperWidth = printer.toolhead.axis_maximum.y - printerBedYMax - wiperWallOffset %}
{% else %}
{% set wiperYmax = printerBedYMax + wiperWallOffset + wiperWidth %}
{% endif %}
{% endif %}
{% if acceleration != 0 %}
{% if acceleration > printer.configfile.config['printer']['max_accel']|float %}
{% set accelerationValidated = printer.configfile.config['printer']['max_accel']|float %}
{% else %}
{% set accelerationValidated = acceleration %}
{% endif %}
{% endif %}
{% if travelSpeed > printer.configfile.config['printer']['max_velocity'] | float %}
{% set travelSpeed = printer.configfile.config['printer']['max_velocity'] | float %}
{% endif %}
{% set travelFeedrate = travelSpeed * 60 %} # Convert travelSpeed to travelFeedrate
{% if slowSpeed > printer.configfile.config['printer']['max_velocity'] | float %}
{% set slowSpeed = printer.configfile.config['printer']['max_velocity'] | float / 2 %}
{% endif %}
{% set slowFeedrate = slowSpeed * 60 %} # Convert slowSpeed to slowFeedrate
{% if fastSpeed > printer.configfile.config['printer']['max_velocity'] | float %}
{% set fastSpeed = printer.configfile.config['printer']['max_velocity'] | float %}
{% endif %}
{% set fastFeedrate = fastSpeed * 60 %} # Convert fastSpeed to fastFeedrate
{% if debug == 1 %}
RESPOND MSG="INFO: processed Variables"
RESPOND MSG=" - wiperYmax : { wiperYmax }"
RESPOND MSG=" - wiperXmin : { wiperXmin }"
RESPOND MSG=" - wiperWidth : { wiperWidth }"
RESPOND MSG=" - acceleration : { accelerationValidated }"
RESPOND MSG=" - travelFeedrate : F{ travelFeedrate }"
RESPOND MSG=" - slowFeedrate : F{ slowFeedrate }"
RESPOND MSG=" - fastFeedrate : F{ fastFeedrate }"
{% endif %}
# ------------------------------------------------------------------------
# ERROR CHECK
# ------------------------------------------------------------------------
{% if error == 0 %} # Continue only if error is zero
# ------------------------------------------------------------------------
# SET CLEANING ACCELERATION
# ------------------------------------------------------------------------
{% if acceleration != 0 %}
SET_VELOCITY_LIMIT ACCEL={accelerationValidated}
{% endif %}
# ------------------------------------------------------------------------
# HOMING PROCESS
# ------------------------------------------------------------------------
{% if "x" not in printer.toolhead.homed_axes and "y" not in printer.toolhead.homed_axes and "z" not in printer.toolhead.homed_axes %}
G28
{% elif "x" not in printer.toolhead.homed_axes %}
G28 X
{% elif "y" not in printer.toolhead.homed_axes %}
G28 Y
{% elif "z" not in printer.toolhead.homed_axes %}
G28 Z
{% endif %}
# ------------------------------------------------------------------------
# SAVE Z AXIS POSITION
# ------------------------------------------------------------------------
{% set zHeight = printer.toolhead.position.z %}
{% if zHeight == 0 %}
{% set zHeight = 10 %}
{% set travelHeight = zHeight %}
{% else %}
{% set travelHeight = zHeight + 10 %}
{% if travelHeight > printer.toolhead.axis_maximum.z|float %}
{% set travelHeight = printer.toolhead.axis_maximum.z|float -10 %}
{% endif %}
{% endif %}
{% if debug == 1 %}
RESPOND MSG="INFO: travelHeight set to {travelHeight}"
{% endif %}
# ------------------------------------------------------------------------
# CHECK FOR BLENDED FILAMENTS
# ------------------------------------------------------------------------
{% if "-" in filamentType %}
{% set baseFilament = filamentType.split("-")[0] %}
{% set stringSplitted = 1 %}
{% else %}
{% set baseFilament = filamentType %}
{% set stringSplitted = 0 %}
{% endif %}
# ------------------------------------------------------------------------
# EVALUATE CLEANING TEMPERATURE
# ------------------------------------------------------------------------
{% if baseFilament in cleaningTemperatures %}
{% set cleaningTemperature = cleaningTemperatures[baseFilament] %}
{% if debug == 1 %}
{% if stringSplitted == 0 %}
RESPOND MSG="INFO: Detected filament type {filamentType}. Cleaning temperature set to {cleaningTemperature}c."
{% else %}
RESPOND MSG="INFO: Detected filament type {filamentType} (base: {baseFilament}). Cleaning temperature set to {cleaningTemperature}c."
{% endif %}
{% endif %}
{% else %}
{% set cleaningTemperature = 140 %}
RESPOND MSG="WARNING: Unknown filament type {filamentType}. Default cleaning temperature of {cleaningTemperature}c will be used."
{% endif %}
# ------------------------------------------------------------------------
# SAVE NOZZLE TARGET TEMPERATURE
# ------------------------------------------------------------------------
{% set nozzleTargetTemperature = printer['extruder'].target %}
# ------------------------------------------------------------------------
# HEAT EXTRUDER
# ------------------------------------------------------------------------
{% if cleaningTemperature != nozzleTargetTemperature %}
SET_HEATER_TEMPERATURE HEATER=extruder TARGET={cleaningTemperature}
{% if debug == 1 %}
RESPOND MSG="INFO: Heating extruder to {cleaningTemperature}c for cleaning..."
{% endif %}
TEMPERATURE_WAIT SENSOR=extruder MINIMUM={cleaningTemperature-4} MAXIMUM={cleaningTemperature+10}
{% endif %}
# ------------------------------------------------------------------------
# CLEANING SEQUENCE
# ------------------------------------------------------------------------
{% if debug == 1 %}
RESPOND MSG="INFO: Initiating nozzle cleaning sequence..."
{% endif %}
# ------------------------------------------------------------------------
# Purge Shake
# ------------------------------------------------------------------------
{% if purgeShake == 1 %}
# Travel to PurgeShake position
G90
G1 Z{travelHeight} F3000
{% if wipingAxis in ["y", "Y"] %}
{% if debug == 1 %}
RESPOND MSG="INFO: Shaking movement starting at X=0, Y={printerBedYMax-1} (1mm safety margin)."
{% endif %}
G1 X0 Y{printerBedYMax-1} F{travelFeedrate}
{% else %}
{% if debug == 1 %}
RESPOND MSG="INFO: Shaking movement starting at X={bedCornerToWiperOffset+wiperLength+1}, Y={printerBedYMax-1} (1mm safety margin)."
{% endif %}
G1 X{bedCornerToWiperOffset+wiperLength+1} Y{printerBedYMax-1} F{travelFeedrate}
{% endif %}
# Quick left/right movements to clear filament strings
G91
G1 X20 F{fastFeedrate}
G1 X-20
G1 X20
G1 X-20
G1 X20
G1 X-20
{% endif %}
# ------------------------------------------------------------------------
# Nozzle cleaning
# ------------------------------------------------------------------------
{% if wipingAxis in ["y", "Y"] %}
# --- BED MOVEMENT CLEANING SEQUENCE ---
{% if debug == 1 %}
RESPOND MSG="INFO: Repeating cleaning motions for {cleaningLoops} times"
{% if wiperXmin == printer.configfile.config.stepper_x.position_endstop|float %}
RESPOND MSG="INFO: Starting first cleaning passes near X endstop at X={wiperXmin+0.2}, Y={wiperYmax} (limited X clearance)."
{% else %}
RESPOND MSG="INFO: Starting first cleaning passes at X={wiperXmin+(wiperWidth/4)}, Y={wiperYmax}."
{% endif %}
{% endif %}
{% for loops in range(0, cleaningLoops) %}
G90
{% if wiperXmin == printer.configfile.config.stepper_x.position_endstop|float %}
G1 X{wiperXmin+0.2} Y{wiperYmax} F{travelFeedrate}
{% else %}
G1 X{wiperXmin+(wiperWidth/4)} Y{wiperYmax} F{travelFeedrate}
{% endif %}
# Lower nozzle and start cleaning motion
G1 Z{cleaningHeight} F1200
# Perform pseudosinusoidal cleaning motion
G91
{%set ymov=(wiperLength/(waves*4)) %}
G1 F{slowFeedrate}
{% for i in range(0,waves) %}
G1 X{wiperWidth/2} Y-{ymov}
G1 Y-{ymov}
G1 X-{wiperWidth/2} Y-{ymov}
G1 Y-{ymov}
{% endfor %}
G1 X{wiperWidth/2}
{% for i in range(0,waves) %}
G1 X-{wiperWidth/2} Y{ymov}
G1 Y{ymov}
G1 X{wiperWidth/2} Y{ymov}
G1 Y{ymov}
{% endfor %}
G1 F{fastFeedrate}
G1 X-{wiperWidth/2}
{% for i in range(0,waves) %}
G1 Y-{ymov}
G1 X{wiperWidth/2} Y-{ymov}
G1 Y-{ymov}
G1 X-{wiperWidth/2} Y-{ymov}
{% endfor %}
G1 X{wiperWidth/2}
{% for i in range(0,waves) %}
G1 Y{ymov}
G1 X-{wiperWidth/2} Y{ymov}
G1 Y{ymov}
G1 X{wiperWidth/2} Y{ymov}
{% endfor %}
G1 F{slowFeedrate}
G1 X-{wiperWidth/2}
{% for i in range(0,waves) %}
G1 X{wiperWidth/2} Y-{2*ymov}
G1 X-{wiperWidth/2} Y-{2*ymov}
{% endfor %}
G1 X{wiperWidth/2}
{% for i in range(0,waves) %}
G1 X-{wiperWidth/2} Y{2*ymov}
G1 X{wiperWidth/2} Y{2*ymov}
{% endfor %}
{% endfor %}
{% else %}
# --- HEAD MOVEMENT CLEANING SEQUENCE ---
{% if debug == 1 %}
RESPOND MSG="INFO: Repeating cleaning motions for {cleaningLoops} times"
{% if wiperYmax == printer.toolhead.axis_maximum.y %}
RESPOND MSG="INFO: Starting first cleaning passes near bed edge at X={wiperXmin+wiperLength}, Y={wiperYmax-0.1} (limited Y clearance)."
{% else %}
RESPOND MSG="INFO: Starting first cleaning passes at X={wiperXmin+wiperLength}, Y={wiperYmax-(wiperWidth/4)}."
{% endif %}
{% endif %}
{% for loops in range(0, cleaningLoops) %}
G90
{% if wiperYmax == printer.toolhead.axis_maximum.y %}
G1 X{wiperXmin+wiperLength} Y{wiperYmax-0.1} F{travelFeedrate}
{% else %}
G1 X{wiperXmin+wiperLength} Y{wiperYmax-(wiperWidth/4)} F{travelFeedrate}
{% endif %}
# Lower nozzle and start cleaning motion
G1 Z{cleaningHeight} F1200
# Perform pseudosinusoidal cleaning motion
G91
{%set xmov=(wiperLength/(waves*4)) %}
G1 F{slowFeedrate}
{% for i in range(0,waves) %}
G1 X-{xmov} Y-{wiperWidth}
G1 X-{xmov}
G1 X-{xmov} Y+{wiperWidth}
G1 X-{xmov}
{% endfor %}
G1 Y-{wiperWidth}
{% for i in range(0,waves) %}
G1 X{xmov} Y+{wiperWidth}
G1 X{xmov}
G1 X{xmov} Y-{wiperWidth}
G1 X{xmov}
{% endfor %}
G1 F{fastFeedrate}
G1 Y+{wiperWidth}
{% for i in range(0,waves) %}
G1 X-{xmov}
G1 X-{xmov} Y-{wiperWidth}
G1 X-{xmov}
G1 X-{xmov} Y+{wiperWidth}
{% endfor %}
G1 Y-{wiperWidth}
{% for i in range(0,waves) %}
G1 X{xmov}
G1 X{xmov} Y+{wiperWidth}
G1 X{xmov}
G1 X{xmov} Y-{wiperWidth}
{% endfor %}
G1 F{slowFeedrate}
G1 Y+{wiperWidth}
{% for i in range(0,waves) %}
G1 X-{xmov*2} Y-{wiperWidth}
G1 X-{xmov*2} Y+{wiperWidth}
{% endfor %}
G1 Y-{wiperWidth}
{% for i in range(0,waves) %}
G1 X{xmov*2} Y+{wiperWidth}
G1 X{xmov*2} Y-{wiperWidth}
{% endfor %}
{% endfor %}
{% endif %}
# ------------------------------------------------------------------------
# Restore saved nozze target temperature & move to middle of bed
# ------------------------------------------------------------------------
{% if waitNozzleTempRestore == 0 %}
SET_HEATER_TEMPERATURE HEATER=extruder TARGET={nozzleTargetTemperature}
G1 Z{travelHeight} F3000
{% else %}
G1 Z{travelHeight} F3000
{% if nozzleTargetTemperature <= 60 %} # Nozzle is considered cold, use fans at 100% for faster cooldown
SET_HEATER_TEMPERATURE HEATER=extruder TARGET={nozzleTargetTemperature}
M106 S255
{% if debug == 1 %}
RESPOND MSG="INFO: Nozzle was cold. Waiting for temperature to rise into 50-60c range..."
{% endif %}
TEMPERATURE_WAIT SENSOR=extruder MINIMUM={50} MAXIMUM={60}
M106 S0
{% if debug == 1 %}
RESPOND MSG="INFO: Nozzle temperature reached target range (50-60c)."
{% endif %}
{% elif nozzleTargetTemperature > 60 and nozzleTargetTemperature < cleaningTemperature %} # Nozzle is preheated, use fans at 50% for faster cooldown
SET_HEATER_TEMPERATURE HEATER=extruder TARGET={nozzleTargetTemperature}
M106 S128
{% if debug == 1 %}
RESPOND MSG="INFO: Restoring nozzle temperature to {nozzleTargetTemperature}c. Please wait for stabilization..."
{% endif %}
TEMPERATURE_WAIT SENSOR=extruder MINIMUM={nozzleTargetTemperature} MAXIMUM={nozzleTargetTemperature+10}
M106 S0
{% if debug == 1 %}
RESPOND MSG="INFO: Nozzle temperature stabilized at ~{nozzleTargetTemperature}c."
{% endif %}
{% elif nozzleTargetTemperature >= cleaningTemperature %} # Nozzle is hot, use no fans for faster heating
SET_HEATER_TEMPERATURE HEATER=extruder TARGET={nozzleTargetTemperature}
{% if debug == 1 %}
RESPOND MSG="INFO: Restoring nozzle temperature to {nozzleTargetTemperature}c. Please wait for stabilization..."
{% endif %}
TEMPERATURE_WAIT SENSOR=extruder MINIMUM={nozzleTargetTemperature} MAXIMUM={nozzleTargetTemperature+10}
{% if debug == 1 %}
RESPOND MSG="INFO: Nozzle temperature stabilized at ~{nozzleTargetTemperature}c."
{% endif %}
{% endif %}
G90
G1 X{printer.toolhead.axis_maximum.x/2} Y{printer.toolhead.axis_maximum.y/2} F{travelFeedrate}
{% endif %}
# ------------------------------------------------------------------------
# Reset Acceleration
# ------------------------------------------------------------------------
SET_VELOCITY_LIMIT ACCEL={printer.configfile.config['printer']['max_accel']|float}
# ------------------------------------------------------------------------
# Print finish message
# ------------------------------------------------------------------------
{% if debug == 1 %}
RESPOND MSG="INFO: Nozzle cleaning sequence complete."
{% endif %}
{% endif %}