Group macros by topic, add Blobifier, fix first tool change
- Split the macro files into folders by topic (print, heating, afc, pressure_advance, spoolman, leds, sensors); klipper-macros.cfg is the one file printer.cfg includes. Macros moved unchanged; the effective config is identical apart from the paths of the PA shell scripts. - Add the modified Blobifier (Turtleblobifier for AFC) and define [save_variables] once in save_variables.cfg instead of in both blobifier.cfg and the PA macros (same file, bd_pa_variables.cfg). - PRINT_WARMUP: heat to the loaded lane's temperature before the first tool change. AFC skips heating when the loaded lane's tool number is not in the print file's temperature list, and the unload then failed below min_extrude_temp. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6dbb4ca3fb
commit
eaf9c82617
27 files changed
+1366
-340
No files matched your search
@@ -0,0 +1,173 @@
|
||||
# Bed fans driven by the bed heater (based on Ellis' bed fan macros)
|
||||
|
||||
############### Config options ##################
|
||||
|
||||
[gcode_macro _BEDFANVARS]
|
||||
variable_threshold: 50 # If bed temp target is above this threshold, fans will be enabled. If temp is set to below this threshold, fans will be disabled.
|
||||
variable_fast: 1.0 # Fan speed once bed temp is reached
|
||||
variable_slow: 0.5 # Fan speed while bed is heating
|
||||
variable_cooldown_temp: 40 # NEW: fans stop below this temp
|
||||
gcode:
|
||||
|
||||
########## Aliases #########
|
||||
|
||||
[gcode_macro BEDFANSSLOW]
|
||||
gcode:
|
||||
# Vars
|
||||
{% set SLOW = printer["gcode_macro _BEDFANVARS"].slow|float %}
|
||||
|
||||
SET_FAN_SPEED FAN=BedFans SPEED={SLOW}
|
||||
|
||||
[gcode_macro BEDFANSFAST]
|
||||
gcode:
|
||||
# Vars
|
||||
{% set FAST = printer["gcode_macro _BEDFANVARS"].fast|float %}
|
||||
|
||||
SET_FAN_SPEED FAN=BedFans SPEED={FAST}
|
||||
|
||||
[gcode_macro BEDFANSOFF]
|
||||
gcode:
|
||||
SET_FAN_SPEED FAN=BedFans SPEED=0
|
||||
|
||||
############ Command overrides ############
|
||||
|
||||
# Override, set fan speeds to low and start monitoring loop.
|
||||
[gcode_macro SET_HEATER_TEMPERATURE]
|
||||
rename_existing: _SET_HEATER_TEMPERATURE
|
||||
gcode:
|
||||
# Parameters
|
||||
{% set HEATER = params.HEATER|default("None") %}
|
||||
{% set TARGET = params.TARGET|default(0)|int %}
|
||||
# Vars
|
||||
{% set THRESHOLD = printer["gcode_macro _BEDFANVARS"].threshold|int %}
|
||||
{% set COOLDOWN = printer["gcode_macro _BEDFANVARS"].cooldown_temp|int %}
|
||||
|
||||
{% if HEATER|lower == "extruder" %}
|
||||
M104 S{TARGET}
|
||||
|
||||
# {% elif HEATER|lower == "iHeater_H" %}
|
||||
# _SET_HEATER_TEMPERATURE {rawparams}
|
||||
|
||||
{% elif HEATER|lower == "heater_bed" %}
|
||||
M99140 S{TARGET}
|
||||
|
||||
# Always cancel cooldown loop first (prevents overlap)
|
||||
UPDATE_DELAYED_GCODE ID=bedfancooldown DURATION=0
|
||||
|
||||
# --- NEW LOGIC BLOCK ---
|
||||
{% if TARGET == 0 %}
|
||||
# Bed turning off → start cooldown behavior
|
||||
{% if printer.heater_bed.temperature|int > COOLDOWN %}
|
||||
BEDFANSSLOW
|
||||
UPDATE_DELAYED_GCODE ID=bedfancooldown DURATION=5
|
||||
{% else %}
|
||||
BEDFANSOFF
|
||||
{% endif %}
|
||||
|
||||
# Stop normal heating loop if running
|
||||
UPDATE_DELAYED_GCODE ID=bedfanloop DURATION=0
|
||||
|
||||
{% elif TARGET >= THRESHOLD %}
|
||||
# Heating above threshold
|
||||
BEDFANSSLOW
|
||||
UPDATE_DELAYED_GCODE ID=bedfanloop DURATION=1
|
||||
|
||||
{% else %}
|
||||
# Low temp request
|
||||
BEDFANSOFF
|
||||
UPDATE_DELAYED_GCODE ID=bedfanloop DURATION=0
|
||||
{% endif %}
|
||||
# --- END NEW LOGIC ---
|
||||
|
||||
{% else %}
|
||||
# {action_respond_info("(Bedfans macro)Heater %s not supported (trying classic _SET_HEATER_TEMPERATURE)" % HEATER)}
|
||||
_SET_HEATER_TEMPERATURE {rawparams}
|
||||
{% endif %}
|
||||
|
||||
# Override M190 (Wait for Bed Temperature)
|
||||
# As a bonus, use TEMPERATURE_WAIT so we don't have to wait for PID to level off.
|
||||
[gcode_macro M190]
|
||||
rename_existing: M99190
|
||||
gcode:
|
||||
# Parameters
|
||||
{% set S = params.S|int %}
|
||||
# Vars
|
||||
{% set THRESHOLD = printer["gcode_macro _BEDFANVARS"].threshold|int %}
|
||||
|
||||
{% if S >= THRESHOLD %}
|
||||
BEDFANSSLOW # >= Threshold temp: Low speed fans while heating
|
||||
{% else %}
|
||||
BEDFANSOFF # < Threshold temp: Turn bed fans off
|
||||
{% endif %}
|
||||
|
||||
M140 {% for p in params
|
||||
%}{'%s%s' % (p, params[p])}{%
|
||||
endfor %} # Set bed temp
|
||||
|
||||
{% if S != 0 %}
|
||||
TEMPERATURE_WAIT SENSOR=heater_bed MINIMUM={S|int} MAXIMUM={S|int + 5} # Wait for bed temp within 5 degrees
|
||||
{% endif %}
|
||||
|
||||
# Post-heating fan speeds
|
||||
{% if S >= THRESHOLD %}
|
||||
BEDFANSFAST # >= Threshold temp: Higher speed fans after heating finished
|
||||
{% endif %}
|
||||
|
||||
# Replace M140 (Set Bed Temperature) to just be an alias of SET_HEATER_TEMPERATURE (which has associated bed fan logic if enabled)
|
||||
[gcode_macro M140]
|
||||
rename_existing: M99140
|
||||
gcode:
|
||||
# Parameters
|
||||
{% set S = params.S|float %}
|
||||
|
||||
SET_HEATER_TEMPERATURE HEATER=heater_bed TARGET={S}
|
||||
|
||||
# Replace TURN_OFF_HEATERS
|
||||
# [gcode_macro TURN_OFF_HEATERS]
|
||||
# rename_existing: _TURN_OFF_HEATERS
|
||||
# gcode:
|
||||
# BEDFANSOFF
|
||||
# _TURN_OFF_HEATERS
|
||||
[gcode_macro TURN_OFF_HEATERS]
|
||||
rename_existing: _TURN_OFF_HEATERS
|
||||
gcode:
|
||||
# Vars
|
||||
{% set COOLDOWN = printer["gcode_macro _BEDFANVARS"].cooldown_temp|int %}
|
||||
|
||||
# Only keep fans running if bed is still hot
|
||||
{% if printer.heater_bed.temperature|int > COOLDOWN %}
|
||||
BEDFANSSLOW
|
||||
UPDATE_DELAYED_GCODE ID=bedfancooldown DURATION=5
|
||||
{% else %}
|
||||
BEDFANSOFF
|
||||
{% endif %}
|
||||
|
||||
_TURN_OFF_HEATERS
|
||||
|
||||
################ Monitoring loop #####################
|
||||
|
||||
# Turns bed fans to "fast" speed once target bed temp is reached.
|
||||
[delayed_gcode bedfanloop]
|
||||
gcode:
|
||||
# Vars
|
||||
{% set THRESHOLD = printer["gcode_macro _BEDFANVARS"].threshold|int %}
|
||||
|
||||
{% if printer.heater_bed.target >= THRESHOLD %} # Continue only if target temp greater than threshold.
|
||||
{% if printer.heater_bed.temperature|int >= (printer.heater_bed.target|int - 1) %}
|
||||
BEDFANSFAST # If within 1 degree of target temp: Higher speed fans
|
||||
{% else %}
|
||||
UPDATE_DELAYED_GCODE ID=bedfanloop DURATION=5 # If temp not reached yet: loop again
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
[delayed_gcode bedfancooldown]
|
||||
gcode:
|
||||
# Vars
|
||||
{% set COOLDOWN = printer["gcode_macro _BEDFANVARS"].cooldown_temp|int %}
|
||||
|
||||
{% if printer.heater_bed.temperature|int > COOLDOWN %}
|
||||
BEDFANSSLOW
|
||||
UPDATE_DELAYED_GCODE ID=bedfancooldown DURATION=10
|
||||
{% else %}
|
||||
BEDFANSOFF
|
||||
{% endif %}
|
||||
@@ -0,0 +1,261 @@
|
||||
# iHeater chamber heater: MCU, heater, sensors and M141/M191
|
||||
|
||||
#========================================================
|
||||
#=====================iHeater cfg========================
|
||||
#========================================================
|
||||
|
||||
#=====================iHeater pins=======================
|
||||
[board_pins iHeater_Aliases]
|
||||
mcu: iHeater
|
||||
aliases:
|
||||
TH0=PA3, TH1=PA0, TH2=PB1,
|
||||
HEATER=PA1, FAN=PA2,
|
||||
MODE=PA4,
|
||||
LED1=PA7, LED2=PA6, LED3=PA5,
|
||||
USART1_TX=PA9, USART1_RX=PA10
|
||||
|
||||
#=====================iHeater mcu=======================
|
||||
# [mcu]
|
||||
# serial: /dev/serial/by-id/usb-Klipper_stm32f042x6_0C0018000D53304347373020-if00
|
||||
|
||||
[mcu iHeater] #second MCU
|
||||
#is_non_critical: true: true # flag this as non critical
|
||||
serial: /dev/serial/by-id/usb-Klipper_stm32f042x6_0C0020000E43304754393320-if00
|
||||
restart_method: command
|
||||
|
||||
[temperature_sensor iHeater]
|
||||
sensor_type: temperature_mcu
|
||||
sensor_mcu: iHeater
|
||||
|
||||
#============================================================
|
||||
#======================= iHeater CONFIG ======================
|
||||
#============================================================
|
||||
|
||||
# Make sure to perform PID calibration for the chamber heater:
|
||||
#🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
|
||||
# PID_CALIBRATE HEATER=iHeater_H TARGET=70
|
||||
#🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
|
||||
# This is essential for accurate and stable chamber heating.
|
||||
# Without PID tuning, the temperature may overshoot or take too long to stabilize!
|
||||
|
||||
#===========================================================
|
||||
|
||||
[heater_generic iHeater_H]
|
||||
heater_pin: iHeater:HEATER
|
||||
max_power: 1
|
||||
sensor_type: NTC 100K MGB18-104F39050L32
|
||||
sensor_pin: iHeater:TH0
|
||||
pwm_cycle_time: 0.3
|
||||
min_temp: 0
|
||||
max_temp: 120
|
||||
control: pid
|
||||
# pid_Kp: 37.948
|
||||
# pid_Ki: 2.040
|
||||
# pid_Kd: 176.497
|
||||
pid_Kp: 26.656
|
||||
pid_Ki: 0.96
|
||||
pid_Kd: 184.971
|
||||
|
||||
[verify_heater iHeater_H]
|
||||
max_error: 240
|
||||
check_gain_time: 1800
|
||||
heating_gain: 1
|
||||
|
||||
[heater_fan chamber_heater_fan]
|
||||
pin: iHeater:FAN
|
||||
heater: iHeater_H
|
||||
heater_temp: 50.0
|
||||
fan_speed: 1.0
|
||||
shutdown_speed: 0
|
||||
kick_start_time: 0.5
|
||||
|
||||
[temperature_sensor iHeater_S]
|
||||
sensor_pin: iHeater:TH1
|
||||
sensor_type: NTC 100K MGB18-104F39050L32
|
||||
min_temp: 0
|
||||
max_temp: 140
|
||||
|
||||
# [temperature_sensor iHeater_Chamber_ext]
|
||||
# sensor_pin: iHeater:TH2
|
||||
# sensor_type: NTC 100K MGB18-104F39050L32
|
||||
# min_temp: 0
|
||||
# max_temp: 140
|
||||
|
||||
[gcode_button mode_button]
|
||||
pin: iHeater:MODE
|
||||
# debounce_delay: 50
|
||||
press_gcode: iHEATER_OFF
|
||||
|
||||
# [output_pin led_1]
|
||||
# pin: iHeater:LED1
|
||||
|
||||
# [output_pin led_2]
|
||||
# pin: iHeater:LED2
|
||||
|
||||
# [output_pin led_3]
|
||||
# pin: iHeater:LED3
|
||||
|
||||
#######################################################
|
||||
##########################SETUP########################
|
||||
#######################################################
|
||||
|
||||
[gcode_macro CHAMBER_VARS]
|
||||
variable_chamber_target: 0 # target chamber temperature, °C
|
||||
variable_start_offset: 10 # printing can start when chamber reaches (target - offset)
|
||||
variable_delta_temp: 10 # delta between chamber and heater, °C
|
||||
variable_min_heater_temp: 50 # minimum heater temperature (for cooling), °C
|
||||
variable_max_heater_temp: 100 # maximum heater temperature, °C
|
||||
variable_control_interval: 1.0 # interval for control function call, seconds
|
||||
variable_air_min_delta: 0.5 # minimum difference between desired and current chamber temperature (heater temp = desired + delta_temp), °C
|
||||
variable_air_max_delta: 5.0 # maximum difference between desired and current chamber temperature (heater temp = max_heater_temp), °C
|
||||
gcode:
|
||||
|
||||
##########################MACRO########################
|
||||
|
||||
[gcode_macro M141]
|
||||
#rename_existing: M141.1
|
||||
gcode:
|
||||
# Get desired chamber temperature:
|
||||
# - if parameter S is passed, use it
|
||||
# - otherwise use chamber_target from CHAMBER_VARS
|
||||
{% set chamber_target = params.S|default(printer["gcode_macro CHAMBER_VARS"].chamber_target)|int %}
|
||||
# Get delta temperature:
|
||||
# - if parameter D is passed, use it
|
||||
# - otherwise use delta_temp from CHAMBER_VARS
|
||||
{% set delta_temp = params.D|default(printer["gcode_macro CHAMBER_VARS"].delta_temp)|int %}
|
||||
# Get maximum heater temperature:
|
||||
# - if parameter H is passed, use it
|
||||
# - otherwise use max_heater_temp from CHAMBER_VARS
|
||||
{% set max_heater_temp = params.H|default(printer["gcode_macro CHAMBER_VARS"].max_heater_temp)|int %}
|
||||
# Get interval for control function call
|
||||
{% set control_interval = printer["gcode_macro CHAMBER_VARS"].control_interval|float %}
|
||||
|
||||
RESPOND prefix="M141" msg="S{chamber_target} D{delta_temp} H{max_heater_temp}"
|
||||
|
||||
# Save parameters to CHAMBER_VARS for delayed_gcode
|
||||
SET_GCODE_VARIABLE MACRO=CHAMBER_VARS VARIABLE=chamber_target VALUE={chamber_target}
|
||||
SET_GCODE_VARIABLE MACRO=CHAMBER_VARS VARIABLE=delta_temp VALUE={delta_temp}
|
||||
SET_GCODE_VARIABLE MACRO=CHAMBER_VARS VARIABLE=max_heater_temp VALUE={max_heater_temp}
|
||||
|
||||
# Initial calculation
|
||||
iHEATER_CONTROL
|
||||
|
||||
# Start temperature control macro
|
||||
UPDATE_DELAYED_GCODE ID=_iHEATER_CONTROL DURATION={control_interval}
|
||||
|
||||
|
||||
[gcode_macro M191]
|
||||
#rename_existing: M191.1
|
||||
gcode:
|
||||
# Get desired chamber temperature:
|
||||
# - if parameter S is passed, use it
|
||||
# - otherwise use chamber_target from CHAMBER_VARS
|
||||
{% set chamber_target = params.S|default(printer["gcode_macro CHAMBER_VARS"].chamber_target)|int %}
|
||||
# Get delta temperature:
|
||||
# - if parameter D is passed, use it
|
||||
# - otherwise use delta_temp from CHAMBER_VARS
|
||||
{% set delta_temp = params.D|default(printer["gcode_macro CHAMBER_VARS"].delta_temp)|int %}
|
||||
# Get maximum heater temperature:
|
||||
# - if parameter H is passed, use it
|
||||
# - otherwise use max_heater_temp from CHAMBER_VARS
|
||||
{% set max_heater_temp = params.H|default(printer["gcode_macro CHAMBER_VARS"].max_heater_temp)|int %}
|
||||
# Get minimum chamber temperature for print start:
|
||||
# - if parameter W is passed, use it
|
||||
# - otherwise use start_offset from CHAMBER_VARS
|
||||
{% set start_offset = params.W|default(printer["gcode_macro CHAMBER_VARS"].start_offset)|int %}
|
||||
|
||||
RESPOND prefix="M191" msg="S{chamber_target} D{delta_temp} H{max_heater_temp} W{start_offset}"
|
||||
|
||||
M141 S{chamber_target} D{delta_temp} H{max_heater_temp}
|
||||
|
||||
# Save parameter to CHAMBER_VARS
|
||||
SET_GCODE_VARIABLE MACRO=CHAMBER_VARS VARIABLE=start_offset VALUE={start_offset}
|
||||
|
||||
# Wait until chamber reaches the target temperature
|
||||
TEMPERATURE_WAIT SENSOR="temperature_sensor iHeater_S" MINIMUM={chamber_target - start_offset}
|
||||
RESPOND prefix="M191" msg="Chamber reached {chamber_target - start_offset}°C"
|
||||
|
||||
|
||||
[gcode_macro iHEATER_ON]
|
||||
description: Alternative to M141 & M191 with alternative parameter names
|
||||
gcode:
|
||||
{% set s = params.CHAMBER_TEMP|default(printer["gcode_macro CHAMBER_VARS"].chamber_target)|int %}
|
||||
{% set d = params.DELTA|default(printer["gcode_macro CHAMBER_VARS"].delta_temp)|int %}
|
||||
{% set h = params.HEATER_MAX|default(printer["gcode_macro CHAMBER_VARS"].max_heater_temp)|int %}
|
||||
{% set w = params.CHAMBER_AWAIT_TEMP|default(printer["gcode_macro CHAMBER_VARS"].start_offset)|int %}
|
||||
|
||||
{% if w > 0 %}
|
||||
# If w is greater than 0, use M191 with wait for minimum temperature
|
||||
M191 S{s} D{d} H{h} W{s - w}
|
||||
{% else %}
|
||||
# Otherwise use M141 without waiting
|
||||
M141 S{s} D{d} H{h}
|
||||
{% endif %}
|
||||
|
||||
|
||||
[gcode_macro iHEATER_OFF]
|
||||
gcode:
|
||||
SET_GCODE_VARIABLE MACRO=CHAMBER_VARS VARIABLE=chamber_target VALUE=0
|
||||
# SET_GCODE_VARIABLE MACRO=CHAMBER_VARS VARIABLE=start_offset VALUE=0
|
||||
|
||||
UPDATE_DELAYED_GCODE ID=_iHEATER_CONTROL DURATION=1
|
||||
RESPOND prefix="iHeater_off" msg="Heating disabled."
|
||||
|
||||
|
||||
[gcode_macro iHEATER_CONTROL]
|
||||
gcode:
|
||||
# Target chamber temperature
|
||||
{% set target_chamber_temp = printer["gcode_macro CHAMBER_VARS"].chamber_target|float %}
|
||||
# Delta - how much the heater temp exceeds chamber temp
|
||||
{% set delta_temp = printer["gcode_macro CHAMBER_VARS"].delta_temp|float %}
|
||||
# Minimum heater temperature
|
||||
{% set min_heater_temp = printer["gcode_macro CHAMBER_VARS"].min_heater_temp|float %}
|
||||
# Maximum heater temperature
|
||||
{% set max_heater_temp = printer["gcode_macro CHAMBER_VARS"].max_heater_temp|float %}
|
||||
# Current heater temperature
|
||||
{% set heater_temp = printer["heater_generic iHeater_H"].temperature|float %}
|
||||
# Current chamber air temperature (from sensor)
|
||||
{% set chamber_temp = printer["temperature_sensor iHeater_S"].temperature|float %}
|
||||
# Control function call interval
|
||||
{% set control_interval = printer["gcode_macro CHAMBER_VARS"].control_interval|float %}
|
||||
# Minimum difference between desired and current chamber temperature
|
||||
{% set air_min_delta = printer["gcode_macro CHAMBER_VARS"].air_min_delta|float %}
|
||||
# Maximum difference between desired and current chamber temperature
|
||||
{% set air_max_delta = printer["gcode_macro CHAMBER_VARS"].air_max_delta|float %}
|
||||
# Variable to set target heater temperature
|
||||
{% set target_heater_temp = 0.0 %}
|
||||
|
||||
# RESPOND prefix="iHeater_control" msg="Heater control..."
|
||||
|
||||
{% if target_chamber_temp > 0 %}
|
||||
# Heating is active
|
||||
{% set temp_diff = target_chamber_temp - chamber_temp %}
|
||||
|
||||
# MapToRange
|
||||
{% set value = temp_diff %}
|
||||
{% set inputLowerBound = air_min_delta %}
|
||||
{% set inputUpperBound = air_max_delta %}
|
||||
{% set outputLowerBound = delta_temp %}
|
||||
{% set outputUpperBound = [delta_temp, max_heater_temp - target_chamber_temp]|max %}
|
||||
|
||||
{% set slope = (outputUpperBound - outputLowerBound) / (inputUpperBound - inputLowerBound) %}
|
||||
{% set clamped = ([inputLowerBound, value, inputUpperBound]|sort)[1] %}
|
||||
{% set adjustment = outputLowerBound + slope * (clamped - inputLowerBound) %}
|
||||
{% set target_heater_temp = (target_chamber_temp + adjustment)|round(2) %}
|
||||
|
||||
{% elif heater_temp > min_heater_temp %}
|
||||
# Heating is off, but heater hasn't cooled down yet
|
||||
RESPOND prefix="iHeater_control" msg="Cooling down the heater..."
|
||||
{% else %}
|
||||
# Heater cooled down - end macro
|
||||
RESPOND prefix="iHeater_control" msg="Heater cooldown complete."
|
||||
{% set control_interval = 0.0 %}
|
||||
{% endif %}
|
||||
|
||||
SET_HEATER_TEMPERATURE HEATER=iHeater_H TARGET={target_heater_temp}
|
||||
UPDATE_DELAYED_GCODE ID=_iHEATER_CONTROL DURATION={control_interval}
|
||||
|
||||
|
||||
[delayed_gcode _iHEATER_CONTROL]
|
||||
gcode:
|
||||
iHEATER_CONTROL
|
||||
@@ -0,0 +1,302 @@
|
||||
# Filament drying on the heated bed: START_DRYING, STOP_DRYING, STATUS_DRYING
|
||||
|
||||
###########################################################
|
||||
# Filament Drying #
|
||||
###########################################################
|
||||
# #
|
||||
# Macro Settings #
|
||||
# Z-axis bed movement - value in mm #
|
||||
# Chamber with chamber heater - value in degrees Celsius #
|
||||
# Bed - value in degrees Celsius #
|
||||
# Drying duration - value in minutes #
|
||||
# Recirculation fan - value in % #
|
||||
# Recirculation runtime - value in minutes #
|
||||
# Recirculation wait time - value in minutes #
|
||||
# Exhaust fan - value in % #
|
||||
# Exhaust runtime - value in minutes #
|
||||
# Exhaust wait time - value in minutes #
|
||||
# Setting run and wait times to "0" will keep #
|
||||
# the corresponding fan running continuously #
|
||||
# #
|
||||
###########################################################
|
||||
[gcode_macro START_DRYING]
|
||||
description: Start the filament dryer for the heated bed.
|
||||
gcode:
|
||||
# Drying parameters
|
||||
{% set MaxChamberTemperature = 60.0 %}
|
||||
{% set MaxBedTemperature = 100.0 %}
|
||||
|
||||
BEDFANSFAST
|
||||
|
||||
# Check the chamber and bed temperatures
|
||||
{% set Chamber = params.CHAMBER|default(25.0)|float %}
|
||||
{% set Bed = params.BED|default(45.0)|float %}
|
||||
|
||||
{% if Chamber < 0 %}
|
||||
M118 Error: Chamber temperature cannot be negative.
|
||||
M117 Error: Chamber temperature negative.
|
||||
{% set SkipRemainingCommands = True %}
|
||||
{% elif Chamber > MaxChamberTemperature %}
|
||||
M118 Warning: Chamber temperature exceeds the maximum value of { MaxChamberTemperature }°C. Drying will be stopped.
|
||||
M117 Warning: Chamber temperature exceeds the maximum value of { MaxChamberTemperature }°C. Drying will be stopped.
|
||||
{% set SkipRemainingCommands = True %}
|
||||
{% else %}
|
||||
{% set SkipRemainingCommands = False %}
|
||||
{% endif %}
|
||||
|
||||
{% if not SkipRemainingCommands %}
|
||||
{% if Bed < 0 %}
|
||||
M118 Error: Bed temperature cannot be negative.
|
||||
M117 Error: Bed temperature negative.
|
||||
{% set SkipRemainingCommands = True %}
|
||||
{% elif Bed == 0 %}
|
||||
M118 Error: Bed temperature is 0 degrees. How will drying be achieved?
|
||||
M117 Error: Bed temperature is 0 degrees. How will drying be achieved?
|
||||
{% set SkipRemainingCommands = True %}
|
||||
{% elif Bed > MaxBedTemperature %}
|
||||
M118 Warning: Bed temperature exceeds the maximum value of { MaxBedTemperature }°C. Drying will be stopped.
|
||||
M117 Warning: Bed temperature exceeds the maximum value of { MaxBedTemperature }°C. Drying will be stopped.
|
||||
{% set SkipRemainingCommands = True %}
|
||||
{% else %}
|
||||
{% set SkipRemainingCommands = False %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
# Check exhaust and recirculation fan values
|
||||
{% set Exhaust = params.EXHAUST|default(100)|int %}
|
||||
{% set Recirculation = params.RECIRCULATION|default(100)|int %}
|
||||
|
||||
{% if Exhaust < 0 %}
|
||||
M118 Error: Exhaust value cannot be negative.
|
||||
M117 Error: Exhaust value negative.
|
||||
{% set SkipRemainingCommands = True %}
|
||||
{% elif Recirculation < 0 %}
|
||||
M118 Error: Recirculation value cannot be negative.
|
||||
M117 Error: Recirculation value negative.
|
||||
{% set SkipRemainingCommands = True %}
|
||||
{% elif Exhaust > 100 %}
|
||||
M118 Warning: Exhaust value exceeds the maximum value of 100%. Value will be limited to 100%.
|
||||
M117 Warning: Exhaust value exceeds the maximum value of 100%. Value will be limited to 100%.
|
||||
{% set Exhaust = 100 %}
|
||||
{% elif Recirculation > 100 %}
|
||||
M118 Warning: Recirculation value exceeds the maximum value of 100%. Value will be limited to 100%.
|
||||
M117 Warning: Recirculation value exceeds the maximum value of 100%. Value will be limited to 100%.
|
||||
{% set Recirculation = 100 %}
|
||||
{% endif %}
|
||||
|
||||
{% if not SkipRemainingCommands %}
|
||||
; Enter drying duration in minutes, limited to a maximum of 12 hours (720 minutes)
|
||||
{% set DryingDurationMinutes = params.DRYING_DURATION|default(240)|int %}
|
||||
{% if DryingDurationMinutes > 720 %}
|
||||
{% set DryingDurationMinutes = 720 %}
|
||||
M118 Warning: Drying duration exceeds 12 hours. Duration limited to 12 hours.
|
||||
M117 Warning: Drying duration exceeds 12 hours. Duration limited to 12 hours.
|
||||
{% endif %}
|
||||
{% set DryingDuration = DryingDurationMinutes * 60 %}
|
||||
|
||||
# Recirculation and exhaust fan settings
|
||||
{% set WaitTime_ExhaustMinutes = params.WAIT_TIME_EXHAUST|default(60)|int %}
|
||||
{% set Runtime_ExhaustMinutes = params.RUNTIME_EXHAUST|default(30)|int %}
|
||||
{% set WaitTime_RecirculationMinutes = params.WAIT_TIME_RECIRCULATION|default(60)|int %}
|
||||
{% set Runtime_RecirculationMinutes = params.RUNTIME_RECIRCULATION|default(30)|int %}
|
||||
|
||||
# Check wait and runtime settings
|
||||
{% if WaitTime_ExhaustMinutes < 0 or Runtime_ExhaustMinutes < 0 or WaitTime_RecirculationMinutes < 0 or Runtime_RecirculationMinutes < 0 %}
|
||||
M118 Error: Wait and runtime settings cannot be negative.
|
||||
M117 Error: One or more wait or runtime settings are negative.
|
||||
{% set SkipRemainingCommands = True %}
|
||||
{% endif %}
|
||||
|
||||
{% if not SkipRemainingCommands %}
|
||||
# Convert wait and runtime to seconds
|
||||
{% set WaitTime_Exhaust = WaitTime_ExhaustMinutes * 60 %}
|
||||
{% set Runtime_Exhaust = Runtime_ExhaustMinutes * 60 %}
|
||||
{% set WaitTime_Recirculation = WaitTime_RecirculationMinutes * 60 %}
|
||||
{% set Runtime_Recirculation = Runtime_RecirculationMinutes * 60 %}
|
||||
|
||||
# Convert percentage to PWM value (0-255)
|
||||
{% set ExhaustPWM = (Exhaust * 255 / 100)|int %}
|
||||
{% set RecirculationPWM = (Recirculation * 255 / 100)|int %}
|
||||
|
||||
# Ensure PWM values are within the range (0-255)
|
||||
{% if ExhaustPWM > 255 %}
|
||||
{% set ExhaustPWM = 255 %}
|
||||
{% elif ExhaustPWM < 0 %}
|
||||
{% set ExhaustPWM = 0 %}
|
||||
{% endif %}
|
||||
|
||||
{% if RecirculationPWM > 255 %}
|
||||
{% set RecirculationPWM = 255 %}
|
||||
{% elif RecirculationPWM < 0 %}
|
||||
{% set RecirculationPWM = 0 %}
|
||||
{% endif %}
|
||||
|
||||
# Issue fan speed warnings if speed is 0
|
||||
{% if Exhaust == 0 and (WaitTime_Exhaust > 0 or Runtime_Exhaust > 0) %}
|
||||
M118 Warning: Exhaust fan speed is 0, but wait time or runtime specified.
|
||||
M117 Warning: Exhaust fan speed is 0, but wait time or runtime specified.
|
||||
{% endif %}
|
||||
|
||||
{% if Recirculation == 0 and (WaitTime_Recirculation > 0 or Runtime_Recirculation > 0) %}
|
||||
M118 Warning: Recirculation fan speed is 0, but wait time or runtime specified.
|
||||
M117 Warning: Recirculation fan speed is 0, but wait time or runtime specified.
|
||||
{% endif %}
|
||||
|
||||
# Check if fans should run continuously
|
||||
{% set ExhaustPermanent = (WaitTime_Exhaust == 0 and Runtime_Exhaust == 0) %}
|
||||
{% set RecirculationPermanent = (WaitTime_Recirculation == 0 and Runtime_Recirculation == 0) %}
|
||||
|
||||
# Check homing status of the toolhead
|
||||
{% if printer.toolhead.homed_axes|default("") != "xyz" %}
|
||||
M118 Printer is homing!
|
||||
M117 Printer is homing!
|
||||
G28 ; Home all axes
|
||||
M118 Homing completed
|
||||
M117 Homing completed
|
||||
{% else %}
|
||||
M118 Homing already completed!
|
||||
M117 Homing already completed!
|
||||
{% endif %}
|
||||
|
||||
G1 X175 Y175 F3000 ; Move the toolhead quickly to X=1, Y=1
|
||||
|
||||
# Z-axis bed movement
|
||||
{% set Z_movement = params.Z_MOVEMENT|default(250.0)|float %}
|
||||
{% if Z_movement > 0 %}
|
||||
G1 Z+{Z_movement} F1500 ; Move the bed up in the Z-axis at the specified speed
|
||||
G1 X175 Y350 Z+{Z_movement} F1500
|
||||
M118 Bed moved by Z={Z_movement}
|
||||
M117 Bed moved by Z={Z_movement}
|
||||
{% endif %}
|
||||
|
||||
# Wait until the Z-displacement is complete
|
||||
M118 Wait for completion of Z-displacement...
|
||||
M117 Wait for completion of Z-displacement...
|
||||
|
||||
# Repeat M114 to ensure that the Z position is reached
|
||||
M114 # Query the current position
|
||||
G4 P1000 # Short pause to query the Z position
|
||||
|
||||
# When Z-movement is complete, start countdown
|
||||
M118 Z-displacement completed.
|
||||
M117 Z-displacement completed.
|
||||
# Wait 30 seconds
|
||||
M118 Please insert filament and close the door! Drying starts in 30 seconds.
|
||||
M117 Please insert filament and close the door! Drying starts in 30 seconds.
|
||||
G4 P5000 # Wait 5 seconds
|
||||
# Loop for the countdown from 25 seconds to 5 seconds
|
||||
{% for i in range(1, 6) %}
|
||||
M118 Drying in {25 - 5 * (i - 1)} seconds!
|
||||
M117 Drying in {25 - 5 * (i - 1)} seconds!
|
||||
G4 P5000 # wait 5 seconds
|
||||
{% endfor %}
|
||||
|
||||
# Turn on heating elements
|
||||
M140 S{ Bed } ; Set bed temperature without waiting.
|
||||
M141 S{ Chamber } ; [OPTIONAL] Set chamber temperature.
|
||||
|
||||
# Set dryer status and start timer
|
||||
SET_GCODE_VARIABLE MACRO=STATUS_DRYING VARIABLE=time_remaining VALUE={ DryingDuration }
|
||||
SET_GCODE_VARIABLE MACRO=STATUS_DRYING VARIABLE=bed_temperature VALUE={ Bed }
|
||||
SET_GCODE_VARIABLE MACRO=STATUS_DRYING VARIABLE=chamber_temperature VALUE={ Chamber }
|
||||
SET_GCODE_VARIABLE MACRO=STATUS_DRYING VARIABLE=exhaust VALUE={ Exhaust }
|
||||
SET_GCODE_VARIABLE MACRO=STATUS_DRYING VARIABLE=exhaust_pwm VALUE={ ExhaustPWM }
|
||||
SET_GCODE_VARIABLE MACRO=STATUS_DRYING VARIABLE=recirculation VALUE={ Recirculation }
|
||||
SET_GCODE_VARIABLE MACRO=STATUS_DRYING VARIABLE=recirculation_pwm VALUE={ RecirculationPWM }
|
||||
SET_GCODE_VARIABLE MACRO=STATUS_DRYING VARIABLE=waittime_exhaust VALUE={ WaitTime_Exhaust }
|
||||
SET_GCODE_VARIABLE MACRO=STATUS_DRYING VARIABLE=runtime_exhaust VALUE={ Runtime_Exhaust }
|
||||
SET_GCODE_VARIABLE MACRO=STATUS_DRYING VARIABLE=waittime_recirculation VALUE={ WaitTime_Recirculation }
|
||||
SET_GCODE_VARIABLE MACRO=STATUS_DRYING VARIABLE=runtime_recirculation VALUE={ Runtime_Recirculation }
|
||||
SET_GCODE_VARIABLE MACRO=STATUS_DRYING VARIABLE=exhaust_permanent VALUE={ ExhaustPermanent }
|
||||
SET_GCODE_VARIABLE MACRO=STATUS_DRYING VARIABLE=recirculation_permanent VALUE={ RecirculationPermanent }
|
||||
|
||||
# Start the delayed gcode for timer
|
||||
UPDATE_DELAYED_GCODE ID=DRYING_TIMER DURATION=1
|
||||
|
||||
M118 Drying in progress...
|
||||
M117 Drying in progress...
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
[gcode_macro STOP_DRYING]
|
||||
description: Stops the filament drying process
|
||||
gcode:
|
||||
# Turn off heating elements and fans
|
||||
M140 S0 ; Turn off heating element
|
||||
M141 S0 ; [OPTIONAL] Turn off chamber heater/fan
|
||||
M106 P2 S0 ; Turn off recirculation fan
|
||||
M106 P3 S0 ; Turn off exhaust fan
|
||||
|
||||
# Stop timer and update status
|
||||
SET_GCODE_VARIABLE MACRO=STATUS_DRYING VARIABLE=time_remaining VALUE=0
|
||||
UPDATE_DELAYED_GCODE ID=DRYING_TIMER DURATION=0
|
||||
M117 Drying stopped! Please remove filament.
|
||||
M118 Drying stopped! Please remove filament.
|
||||
|
||||
# Query print bed temperature
|
||||
{% if printer.heater_bed.temperature > 40 %}
|
||||
M117 Attention: Print bed above 40°C! Take care when removing the filament roll.
|
||||
M118 Attention: Print bed above 40°C! Caution when removing the filament roll.
|
||||
{% else %}
|
||||
M117 Print bed temperature OK.
|
||||
M118 Print bed temperature OK.
|
||||
{% endif %}
|
||||
|
||||
# Send Telegram notification
|
||||
#RESPOND PREFIX=tgnotify MSG="Filament drying has been stopped. Please remove the filament."
|
||||
|
||||
[gcode_macro STATUS_DRYING]
|
||||
variable_time_remaining: 0
|
||||
variable_bed_temperature: 0
|
||||
variable_chamber_temperature: 0
|
||||
variable_exhaust: 100
|
||||
variable_exhaust_pwm: 255
|
||||
variable_recirculation: 100
|
||||
variable_recirculation_pwm: 255
|
||||
variable_waittime_exhaust: 60
|
||||
variable_runtime_exhaust: 30
|
||||
variable_waittime_recirculation: 60
|
||||
variable_runtime_recirculation: 30
|
||||
variable_exhaust_permanent: False
|
||||
variable_recirculation_permanent: False
|
||||
gcode:
|
||||
{% set cycle_time_exhaust = waittime_exhaust + runtime_exhaust %}
|
||||
{% set cycle_time_recirculation = waittime_recirculation + runtime_recirculation %}
|
||||
{% if time_remaining > 0 %}
|
||||
{% set current_time = time_remaining - 1 %}
|
||||
|
||||
{% set hours = (current_time // 3600) %}
|
||||
{% set minutes = (current_time % 3600) // 60 %}
|
||||
{% set seconds = (current_time % 3600) % 60 %}
|
||||
|
||||
{% set time_display = "%02d:%02d:%02d" % (hours, minutes, seconds) %}
|
||||
|
||||
# Update fan states based on the current cycle
|
||||
{% if exhaust_permanent or (current_time % cycle_time_exhaust) < runtime_exhaust %}
|
||||
; Exhaust fan running
|
||||
M106 P3 S{ exhaust_pwm }
|
||||
{% else %}
|
||||
; Exhaust fan off
|
||||
M106 P3 S0
|
||||
{% endif %}
|
||||
|
||||
{% if recirculation_permanent or (current_time % cycle_time_recirculation) < runtime_recirculation %}
|
||||
# Recirculation fan running
|
||||
M106 P2 S{ recirculation_pwm }
|
||||
{% else %}
|
||||
# Recirculation fan off
|
||||
M106 P2 S0
|
||||
{% endif %}
|
||||
|
||||
M117 Remaining time: { time_display }
|
||||
SET_GCODE_VARIABLE MACRO=STATUS_DRYING VARIABLE=time_remaining VALUE={ current_time }
|
||||
{% else %}
|
||||
# Stop drying when time runs out
|
||||
STOP_DRYING
|
||||
{% endif %}
|
||||
|
||||
[delayed_gcode DRYING_TIMER]
|
||||
gcode:
|
||||
UPDATE_DELAYED_GCODE ID=DRYING_TIMER DURATION=1
|
||||
STATUS_DRYING
|
||||
@@ -0,0 +1,303 @@
|
||||
# HEAT_SOAK: wait for a sensor to reach thermal equilibrium (community macro)
|
||||
|
||||
##
|
||||
## HEAT_SOAK HEATER=<heater_name> TARGET=<target_heater_temperature> SOAKER=<heat_soak_temp_sensor_name> [RATE=<heat_soak_temp_rate_of_change>] [TIMEOUT=<time_to_abort_heat_soak>]
|
||||
##
|
||||
##
|
||||
## e.g.
|
||||
## HEAT_SOAK HEATER=heater_bed TARGET=100 SOAKER="temperature_sensor top_bed"
|
||||
##
|
||||
## Based on work by blalor: https://klipper.discourse.group/t/interruptible-heat-soak/1552
|
||||
##
|
||||
## Modified by me
|
||||
##
|
||||
|
||||
[gcode_macro HEAT_SOAK]
|
||||
description: Wait for a temperature sensor to reach thermal equilibrium
|
||||
|
||||
variable_last_temp: 0
|
||||
variable_last_soak_temp_rate: None
|
||||
variable_temp_history: None
|
||||
variable_smoothed_temp_history: None
|
||||
variable_stage: "done" ## heating -> soaking -> done -> None
|
||||
variable_total_time_elapsed: 0
|
||||
variable_soak_time_remaining: 30
|
||||
variable_check_interval: 1
|
||||
variable_heating_report_interval: 2
|
||||
variable_soaking_report_interval: 5
|
||||
variable_heater_sensor: None
|
||||
variable_soaker_sensor: None
|
||||
variable_target_temp: 0.0
|
||||
variable_min_soak_temp: 0
|
||||
variable_target_rate: 0.3
|
||||
variable_complete: None
|
||||
variable_cancel: None
|
||||
variable_temp_smooth_time: 4.0
|
||||
variable_rate_smooth_time: 20.0
|
||||
variable_resume_trigger: False
|
||||
variable_was_print_active: False
|
||||
|
||||
gcode:
|
||||
{ action_respond_info( "Heat Soak starting" )}
|
||||
|
||||
# Soaker is required
|
||||
{% set SOAKER = params.SOAKER | string %}
|
||||
{% set RATE = params.RATE | default(0.3) | float %} ## in degrees C per minute
|
||||
{% set SOAK_TEMP = params.SOAK_TEMP | default(0.0) | float %}
|
||||
{% set HEATER = (params.HEATER | string) %} # Optional heater
|
||||
{% set TARGET = params.TARGET | default(0) | float %}
|
||||
{% set TIMEOUT = (params.TIMEOUT | default(30) | int) * 60 %} ## minutes to seconds
|
||||
{% set TEMP_SMOOTH = params.TEMP_SMOOTH | default(4.0) | float %} ## seconds for temp smoothing
|
||||
{% set RATE_SMOOTH = params.RATE_SMOOTH | default(20) | float %} ## seconds for rate smoothing
|
||||
{% set COMPLETE = (params.COMPLETE | string) %}
|
||||
{% set CANCEL = (params.CANCEL | string) %}
|
||||
{% set HEATING_REPORT_INTERVAL = params.HEATING_REPORT_INTERVAL | default(2) | int %}
|
||||
{% set SOAKING_REPORT_INTERVAL = params.SOAKING_REPORT_INTERVAL | default(5) | int %}
|
||||
|
||||
# User writeable variables
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=heater_sensor VALUE="{HEATER | pprint}"
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=target_temp VALUE={TARGET}
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=min_soak_temp VALUE={SOAK_TEMP}
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=soaker_sensor VALUE="{SOAKER | pprint}"
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=target_rate VALUE={RATE}
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=soak_time_remaining VALUE={TIMEOUT}
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=complete VALUE="'{COMPLETE}'"
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=cancel VALUE="'{CANCEL}'"
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=temp_smooth_time VALUE={TEMP_SMOOTH}
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=rate_smooth_time VALUE={RATE_SMOOTH}
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=heating_report_interval VALUE={HEATING_REPORT_INTERVAL}
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=soaking_report_interval VALUE={SOAKING_REPORT_INTERVAL}
|
||||
|
||||
# Internal variables
|
||||
{% set soak_temp = printer[SOAKER].temperature %}
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=last_temp VALUE={soak_temp}
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=temp_history VALUE="{[soak_temp] | pprint}"
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=smoothed_temp_history VALUE="{[] | pprint}"
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=total_time_elapsed VALUE=0
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=last_soak_temp_rate VALUE=None
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=check_interval VALUE=1.0
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=resume_trigger VALUE=False
|
||||
{% set is_print_active = printer['virtual_sdcard'].is_active or printer['virtual_sdcard'].file_position != 0.0 %}
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=was_print_active VALUE={is_print_active}
|
||||
|
||||
# start optional heater
|
||||
{% if (HEATER and TARGET != 0.0) %}
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=stage VALUE="'heating'"
|
||||
SET_HEATER_TEMPERATURE HEATER={HEATER} TARGET={TARGET}
|
||||
{% else %}
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=stage VALUE="'soaking'"
|
||||
{% endif %}
|
||||
|
||||
# pause the print, if active
|
||||
{% if is_print_active %}
|
||||
PAUSE_BASE
|
||||
{% endif %}
|
||||
|
||||
UPDATE_DELAYED_GCODE ID=_heat_soaker DURATION={check_interval}
|
||||
|
||||
[gcode_macro STOP_HEAT_SOAK]
|
||||
description: stops heat soak activity without running any callbacks
|
||||
gcode:
|
||||
UPDATE_DELAYED_GCODE ID=_heat_soaker DURATION=0 # cancel any pending run
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=stage VALUE="'done'"
|
||||
|
||||
[gcode_macro CANCEL_HEAT_SOAK]
|
||||
description: cancels an in-progress HEAT_SOAK cycle and runs the cancel callback
|
||||
gcode:
|
||||
# check that the HEAT_SOAK macro is active before canceling
|
||||
{% set heat_soak = printer['gcode_macro HEAT_SOAK'] %}
|
||||
{% set stage = heat_soak.stage %}
|
||||
{% if stage in ("heating", "soaking") %}
|
||||
STOP_HEAT_SOAK
|
||||
{% if heat_soak.cancel %}
|
||||
{heat_soak.cancel}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
[gcode_macro HEAT_SOAK_RESUME]
|
||||
description: Resume while heat soaking results in the soaking phase being skipped, any complete callback is run
|
||||
gcode:
|
||||
# {% set ON_RESUME = (params.ON_RESUME | default("_HEAT_SOAK__BASE_RESUME")) | string %}
|
||||
{% set ON_RESUME = (params.ON_RESUME | default("_HEAT_SOAK__BASE_RESUME")) | string %}
|
||||
{% set heat_soak = printer['gcode_macro HEAT_SOAK'] %}
|
||||
{% set stage = heat_soak.stage %}
|
||||
{% if stage == "heating" %}
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=resume_trigger VALUE=True
|
||||
{% elif stage == "soaking" %}
|
||||
STOP_HEAT_SOAK
|
||||
{% if heat_soak.complete %}
|
||||
{heat_soak.complete}
|
||||
{% endif %}
|
||||
{ON_RESUME} {% for p in params %}{'%s=%s ' % (p, params[p])}{% endfor %}
|
||||
{% else %}
|
||||
{ON_RESUME} {% for p in params %}{'%s=%s ' % (p, params[p])}{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
[gcode_macro RESUME]
|
||||
description: Resume while heat soaking results in the soaking phase being skipped, any complete callback is run
|
||||
rename_existing: _HEAT_SOAK__BASE_RESUME
|
||||
gcode:
|
||||
HEAT_SOAK_RESUME ON_RESUME=_HEAT_SOAK__BASE_RESUME
|
||||
|
||||
[delayed_gcode _heat_soaker]
|
||||
# description: internal macro to handle heat soaking activity
|
||||
gcode:
|
||||
{% set heat_soak = printer['gcode_macro HEAT_SOAK'] %}
|
||||
# debug
|
||||
#{ action_respond_info( printer['gcode_macro HEAT_SOAK'] | tojson )}
|
||||
|
||||
# shut down cleanly if we detect that the user canceled or started a print while heat soaking
|
||||
{% set file_position_is_zero = (printer['virtual_sdcard'].file_position == 0.0 )%}
|
||||
{% if heat_soak.was_print_active and file_position_is_zero %}
|
||||
STOP_HEAT_SOAK
|
||||
{action_respond_info("HEAT_SOAK aborted. Detected a change in virtual_sdcard print state. Call STOP_HEAT_SOAK from CANCEL_PRINT to stop seeing this error message.")}
|
||||
{% endif %}
|
||||
|
||||
# shut down cleanly if we detect that the user resumed the print while heat soaking
|
||||
{% if not file_position_is_zero and not printer['pause_resume'].is_paused %}
|
||||
STOP_HEAT_SOAK
|
||||
{action_respond_info("HEAT_SOAK aborted. Print is no longer paused. Call STOP_HEAT_SOAK or HEAT_SOAK_RESUME from your RESUME macro to stop seeing this error message.")}
|
||||
{% endif %}
|
||||
|
||||
# update total time elapsed
|
||||
{% set total_time_elapsed = heat_soak.total_time_elapsed + heat_soak.check_interval %}
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=total_time_elapsed VALUE={ total_time_elapsed }
|
||||
|
||||
{% set soaker_sensor = printer[heat_soak.soaker_sensor] %}
|
||||
{% set soak_temp = soaker_sensor.temperature %}
|
||||
{% if heat_soak.heater_sensor %}
|
||||
{% set heater_sensor = printer[heat_soak.heater_sensor] %}
|
||||
{% set heater_temp = heater_sensor.temperature | round(1) %}
|
||||
{% endif %}
|
||||
{% set stage = heat_soak.stage %}
|
||||
|
||||
# compute the new temp difference and save history
|
||||
{% set temp_history = heat_soak.temp_history %}
|
||||
{% set smoothed_temp_history = heat_soak.smoothed_temp_history %}
|
||||
{% set temp_diff = soak_temp - (temp_history | last) %}
|
||||
{% set _ = temp_history.append(soak_temp) %}
|
||||
{% set soak_temp_smoothed = None %}
|
||||
{% set slope = None %}
|
||||
{% if (temp_history | length) > heat_soak.temp_smooth_time %}
|
||||
{% set temp_history = temp_history[1:] %}
|
||||
# compute average temp
|
||||
{% set soak_temp_smoothed = ((temp_history | sum) / (temp_history | length)) %}
|
||||
# save raw value to history
|
||||
{% set _ = smoothed_temp_history.append(soak_temp_smoothed) %}
|
||||
# round off for display and comparisons later
|
||||
{% set soak_temp_smoothed = soak_temp_smoothed | round(3) %}
|
||||
# compute the slope
|
||||
{% if (smoothed_temp_history | length) > heat_soak.rate_smooth_time %}
|
||||
{% set smoothed_temp_history = smoothed_temp_history[1:] %}
|
||||
# Least Squares
|
||||
# adapted from: https://towardsdatascience.com/linear-regression-using-least-squares-a4c3456e8570
|
||||
# X values are time in seconds
|
||||
# Y values are temperatures
|
||||
{% set count = (smoothed_temp_history | length) %}
|
||||
{% set times = range(0, count, 1) %}
|
||||
{% set x_sum = (times | sum) | float %}
|
||||
{% set y_sum = (smoothed_temp_history | sum) | float %}
|
||||
# Squares
|
||||
# Jinja oddity: += doesn't work in loops??, so collect squares and sum after
|
||||
{% set xx_sum_arr = [] %}
|
||||
{% set xy_sum_arr = [] %}
|
||||
{% for i in times %}
|
||||
{% set x = times[i] %}
|
||||
{% set y = smoothed_temp_history[i] %}
|
||||
{% set _ = xx_sum_arr.append(x * x) %}
|
||||
{% set _ = xy_sum_arr.append(x * y) %}
|
||||
{% endfor %}
|
||||
{% set xx_sum = (xx_sum_arr | sum) | float %}
|
||||
{% set xy_sum = (xy_sum_arr | sum) | float %}
|
||||
# Slope calculation. Slope is per second so * 60 for rate per minute
|
||||
{% set slope = (60.0 * ((count | float * xy_sum) - (x_sum * y_sum)) / ((count | float * xx_sum) - (x_sum * x_sum))) | round(3) %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=temp_history VALUE="{(temp_history | pprint | replace("\n", ""))}"
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=smoothed_temp_history VALUE="{(smoothed_temp_history | pprint | replace("\n", ""))}"
|
||||
|
||||
# while heating
|
||||
{% if stage == "heating" %}
|
||||
{% if heater_temp < heat_soak.target_temp %}
|
||||
{% if total_time_elapsed % heat_soak.heating_report_interval == 0 %}
|
||||
{% set message = "Heating -- %.1fC / %.1fC -- %.1fm elapsed" % (heater_temp, heat_soak.target_temp, total_time_elapsed / 60.0) %}
|
||||
M117 {message}
|
||||
{action_respond_info(message)}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{action_respond_info("Heating completed after ~%.1fm, starting heat soak phase." % (total_time_elapsed / 60.0))}
|
||||
# skip the soak phase if resume was pressed
|
||||
{% set stage = "done" if heat_soak.resume_trigger else "soaking" %}
|
||||
# reset total time to 0 so soaking time is reported from 0
|
||||
{% set total_time_elapsed = 0 %}
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=total_time_elapsed VALUE={ total_time_elapsed }
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
# while soaking
|
||||
{% if stage == "soaking" %}
|
||||
## decrement the soak countdown
|
||||
{% set soak_time_remaining = [heat_soak.soak_time_remaining - heat_soak.check_interval, 0] | max %}
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=soak_time_remaining VALUE={ soak_time_remaining }
|
||||
|
||||
## abort soaking if timeout is reached
|
||||
{% if soak_time_remaining <= 0 %}
|
||||
{action_respond_info("Heat soak timed out after ~%.1fm" % (total_time_elapsed / 60.0))}
|
||||
{% set stage = "cancel" %}
|
||||
# if the smoothing algorithm hasn't settled on these values yet, dont evaluate them
|
||||
{% elif slope is none or soak_temp_smoothed is none %}
|
||||
{% if total_time_elapsed % heat_soak.soaking_report_interval == 0 %}
|
||||
## not enough data points yet to determing the soak rate, show dashes
|
||||
{% set message = "Soaking -- gathering data -- %.1fm remaining" % (soak_time_remaining / 60.0) %}
|
||||
M117 { message }
|
||||
{action_respond_info(message)}
|
||||
{% endif %}
|
||||
# if the heat soak must continue for some reason
|
||||
{% elif (slope > heat_soak.target_rate) or (heat_soak.min_soak_temp > 0 and soak_temp_smoothed < heat_soak.min_soak_temp) %}
|
||||
{% if total_time_elapsed % heat_soak.soaking_report_interval == 0 %}
|
||||
{% set format_vars = [
|
||||
soak_temp_smoothed | float,
|
||||
(heat_soak.min_soak_temp | float | round(0) | string) if (heat_soak.min_soak_temp > 0) else "--",
|
||||
slope,
|
||||
heat_soak.target_rate,
|
||||
soak_time_remaining / 60.0
|
||||
] %}
|
||||
{% set message = "Soaking -- temp: {0:.1f}C / {1}C -- rate: {2:.3f}C/m / {3:.3f}C/m -- {4:.1f}m remaining".format(*format_vars) %}
|
||||
M117 { message }
|
||||
{action_respond_info(message)}
|
||||
{% endif %}
|
||||
## end soaking if the target rate is achieved
|
||||
{% else %}
|
||||
{action_respond_info("Heat soak complete after ~%.1fm at %.3fC/m / %.1fC" % (total_time_elapsed / 60.0, slope, soak_temp))}
|
||||
{% set stage = "done" %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
# save stage before calling any macros
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=stage VALUE="'{stage}'"
|
||||
|
||||
{% if stage in ("done", "cancel") %}
|
||||
STOP_HEAT_SOAK
|
||||
{% if stage == "cancel" %}
|
||||
{% if heat_soak.cancel %}
|
||||
{heat_soak.cancel}
|
||||
{% elif printer['virtual_sdcard'].is_active %}
|
||||
CANCEL_PRINT
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if heat_soak.complete %}
|
||||
{heat_soak.complete}
|
||||
{% endif %}
|
||||
{% if printer['pause_resume'].is_paused %}
|
||||
RESUME
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
## trigger ourselves again
|
||||
UPDATE_DELAYED_GCODE ID=_heat_soaker DURATION={ heat_soak.check_interval }
|
||||
## dwell for 1ms to prevent from going idle
|
||||
G4 P1
|
||||
{% endif %}
|
||||
|
||||
# save ending value of stage
|
||||
SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=stage VALUE="'{stage}'"
|
||||
@@ -0,0 +1,22 @@
|
||||
# SOAK_MY_BED wrapper (soak-my-bed plugin): home and QGL first
|
||||
|
||||
# [gcode_macro QUAD_GANTRY_LEVEL]
|
||||
# rename_existing: _QUAD_GANTRY_LEVEL
|
||||
# gcode:
|
||||
# _QUAD_GANTRY_LEVEL
|
||||
# _QUAD_GANTRY_LEVEL PROBE_METHOD=contact
|
||||
|
||||
### bed warp visualizer
|
||||
[gcode_macro SOAK_MY_BED]
|
||||
description: Heat soak bed and live meassure
|
||||
rename_existing: SOAK_MY_BED_BASE
|
||||
gcode:
|
||||
{% set temp = params.TEMPERATURE|default(60) %}
|
||||
{% set duration = params.DURATION|default(10) %}
|
||||
{% set heater = params.HEATER|default("heater_bed") %}
|
||||
{% set mesh = params.MESH_COMMAND|default("BED_MESH_CALIBRATE METHOD=beacon") %}
|
||||
|
||||
G28
|
||||
QUAD_GANTRY_LEVEL
|
||||
|
||||
SOAK_MY_BED_BASE TEMPERATURE={temp} DURATION={duration} HEATER={heater} MESH_COMMAND="{mesh}"
|
||||
Reference in new issue
Block a user