Skip to content

Instantly share code, notes, and snippets.

@rogerlz
Last active May 19, 2024 13:50
Show Gist options
  • Save rogerlz/03ae2b7530cb7587cf652ae4de88a4ae to your computer and use it in GitHub Desktop.
Save rogerlz/03ae2b7530cb7587cf652ae4de88a4ae to your computer and use it in GitHub Desktop.
heated_fan.py
# Support fans that are enabled when a heater is on
#
# This file may be distributed under the terms of the GNU GPLv3 license.
from . import fan
PIN_MIN_TIME = 0.100
class HeatedFan:
def __init__(self, config):
self.printer = config.get_printer()
self.reactor = self.printer.get_reactor()
self.gcode = config.get_printer().lookup_object("gcode")
try:
self.printer.lookup_object("fan")
except:
self.printer.add_object("fan", self)
else:
raise self.printer.config_error(
"Can't setup fan and heated_fan together"
)
# setup heater
pheaters = self.printer.load_object(config, "heaters")
self.heater = pheaters.setup_heater(config, "F")
self.heater_temp = config.getfloat("heater_temp", 50.0)
self.stats = self.heater.stats
# setup fan
self.fan = fan.Fan(config, default_shutdown_speed=1.0)
self.min_speed = config.getfloat(
"min_speed", 1.0, minval=0.0, maxval=1.0
)
self.idle_timeout = config.getfloat("idle_timeout", 60.0, minval=0.0)
self.set_speed = 0.0
self.timeout_timer = None
self.gcode.register_command(
"SET_HEATED_FAN_TARGET", self.cmd_SET_HEATED_FAN_TARGET
)
self.gcode.register_command("M106", self.cmd_M106)
self.gcode.register_command("M107", self.cmd_M107)
self.printer.register_event_handler("klippy:ready", self.handle_ready)
def cmd_SET_HEATED_FAN_TARGET(self, gcmd):
self.heater_temp = gcmd.get_float("TARGET", 0)
def cmd_M106(self, gcmd):
self.set_speed = gcmd.get_float("S", 255.0, minval=0.0) / 255.0
if not self.set_speed:
return self.cmd_M107(gcmd)
if self.set_speed < self.min_speed:
self.set_speed = self.min_speed
self.fan.set_speed_from_command(self.set_speed)
_, target_temp = self.heater.get_temp(self.reactor.monotonic())
# if the heater is off or has a different target temp than configured, set it
if not target_temp or target_temp != self.heater_temp:
self.heater.set_temp(self.heater_temp)
def cmd_M107(self, gcmd):
self.set_speed = 0.0
self.heater.set_temp(0)
self.start_timeout_timer()
def start_timeout_timer(self):
self.fan.set_speed_from_command(self.min_speed)
self.timeout_timer = self.reactor.register_timer(
self.handle_timeout_timer,
self.reactor.monotonic() + self.idle_timeout,
)
def handle_ready(self):
self.reactor.register_timer(
self.callback, self.reactor.monotonic() + PIN_MIN_TIME
)
def handle_timeout_timer(self, eventtime):
self.fan.set_speed_from_command(self.set_speed)
return self.reactor.NEVER
def callback(self, eventtime):
_, target_temp = self.heater.get_temp(eventtime)
# if the heater has a target and the fan is off, turn it on to min fan speed
if target_temp and not self.set_speed:
self.fan.set_speed_from_command(self.min_speed)
return eventtime + 1.0
def get_status(self, eventtime):
status = self.fan.get_status(eventtime).copy()
status.update(self.heater.get_status(eventtime))
return status
def load_config(config):
return HeatedFan(config)
@rogerlz
Copy link
Author

rogerlz commented Apr 6, 2024

[heated_fan]
heater_pin: PA5
sensor_type: Generic 3950
sensor_pin: PA0
min_temp: 0
max_temp: 130
control: pid
pid_kp: 63.350
pid_ki: 4.100
pid_kd: 244.691
pin: ebb36:PA0
heater_temp: 50
min_speed: 0.5
idle_timeout: 5

@levins-law
Copy link

levins-law commented Apr 21, 2024

heated_fan.py is working on my instance of Klipper/Mainsail (which is out of date and on old version of Python)

  • Mainsail>Temperatures>Target control field is missing, which seems to be a Mainsail bug per @rogerlz

    Click for pic

    image

  • M106 seems to control fan speed correctly, responds to GCODE fan speed commands when printing.

    • Simplify3D has a Cooling control window with layerwise control which seems to be functioning.

      Click for pic

      image

  • Currently unable to control heated_fan temperature with the slicer, however S3D has a Temperature Controller list in the Temperature tab of the process settings dialog.

    Click for pic

    image

    • S3D only has three temperature types: Extruder [M104 Tx], Heated Build Platform [M140], Heated Chamber [M141]

    • If I attempt to create a Heated Part Cooling temp controller using Extruder type on Temperature Number = T0 the slicer just sends two extruder temp commands in the GCODE and Klipper picks one it likes and sends that temp to the hotend.

    • If I set Temperature Number = T1 the slicer creates separate GCODE commands, here is a snippet of a sliced print with those settings

    • Click for GCODE (no wait for temp commands, for testing purposes)

      M82
      M106 S0 P0
      M141 S100
      M140 S205
      M104 S400 T0
      M104 S200 T1
      START_PRINT
      ; process Process 1
      ; layer 1, Z = 0.1800
      T0
      G92 E0.00000
      G1 E-0.40000 F1800
      ; feature skirt
      ; tool H0.1800 W0.480
      G1 Z0.1800 F1002
      G1 X128.665 Y147.070 F7200
      G92 E0.00000
      G1 E0.40000 F1800
      G92 E0.00000
      G1 X129.551 Y146.348 E0.04106 F750
      G1 X130.309 Y145.988 E0.07121
      G1 X130.985 Y145.795 E0.09646

    • Klipper seems to handle T0 just fine (or ignores it) but when it receives the M104 S200 T1 command it returns an Extruder not configured error.

    • rogerlz suggested a GCODE macro could be used to help Klipper and graciously put one together to try. It doesn't work yet.

    • Click for macro sample and resulting Klipper error

      [gcode_macro M104]
      rename_existing: _M104
      gcode:
      {% if rawparams %}
      {% set temp = rawparams.split('S', 1)[1] %}
      {% set extruder = rawparams.split('T', 1)[1] %}
      {% if extruder == "0" %}
      _M104 S{{ temp }}
      {% else %}
      SET_HEATER_TEMPERATURE HEATER=heated_fan TARGET={{ temp }}
      {% endif %}
      {% endif %}

      image

  • Using GCODE macro HEAT_SOAK (for bed) activates the heated_fan which I do not want, looking for obvious solutions at this time.

Features and Functions to fix or refine

  • Get Mainsail>Temperatures>Target control field working Heated Fan so I can manually adjust temperature of heater core.
  • Find a way to let the slicer's temperature controller capabilities control the heated fan temperature while normal M106 fan speed controls the fan speed like any other printer.
  • Stop HEAT_SOAK macro from activating Heated Fan.
  • Develop a Chamber Heater heat soak macro

Current klipper config 4/21/24:

Click me
# Mainsail settings
[include mainsail.cfg]

[printer]
kinematics: cartesian
max_velocity: 500
max_accel: 2500
max_accel_to_decel: 2500
max_z_velocity: 10
max_z_accel: 100

[stepper_x]
# Remember, 3/16/24 you moved the X driver to the Extruder 1 position on the MCU and had to update the TMC settings to match
step_pin: P1.15
dir_pin: P1.14
enable_pin: !P1.16
microsteps: 16
rotation_distance: 32
full_steps_per_rotation: 400
endstop_pin: tmc5160_stepper_x:virtual_endstop
position_endstop: -25
position_min: -25
position_max: 308
homing_speed: 50
homing_retract_dist: 0

[stepper_y]
step_pin: P0.19
dir_pin: P0.20
enable_pin: !P2.8
microsteps: 16
rotation_distance: 32
full_steps_per_rotation: 400
endstop_pin: tmc5160_stepper_y:virtual_endstop
position_endstop: -33
position_min: -33
position_max: 290
homing_speed: 50
homing_retract_dist: 0

[stepper_z]
step_pin: P0.22
dir_pin: !P2.11
enable_pin: !P0.21
microsteps: 16
rotation_distance: 8
full_steps_per_rotation: 200
endstop_pin: probe:z_virtual_endstop
position_min: -1.7
position_max: 200

[extruder]
step_pin: P2.13
dir_pin: P0.11
enable_pin: !P2.12
microsteps: 16
#   <16> for BMG with LDO-42sth25-1404MAH
rotation_distance: 22.67895
gear_ratio: 50:10 #for standard 10t motor
full_steps_per_rotation: 200
#   <400> for BMG with LDO-42sth25-1404MAH
nozzle_diameter: 0.400
filament_diameter: 1.750
max_extrude_cross_section: 5
pressure_advance: 0.1
heater_pin: P2.7
sensor_type: PT1000
sensor_pin: P0.24
control = pid
pid_kp = 22.466
pid_ki = 1.218
pid_kd = 103.622
min_temp: 0
max_temp: 450

[heater_fan heatbreak_fan]
pin: P2.5
max_power: 1.0
shutdown_speed: 1
cycle_time: .010
hardware_pwm: False
kick_start_time: .1
heater: extruder

[firmware_retraction]
retract_length: .4
retract_speed: 30
unretract_extra_length: 0
unretract_speed: 30

[heater_bed]
heater_pin: P2.1
sensor_type: PT1000
sensor_pin: P0.25
control: pid
control = pid
pid_kp = 49.728
pid_ki = 1.927
pid_kd = 320.746
min_temp: 0
max_temp: 230

[heated_fan]
heater_pin: P2.4
sensor_type: PT1000
sensor_pin: P0.23
min_temp: 0
max_temp: 215
control: pid
pid_kp: 31.958
pid_ki: 2.803
pid_kd: 91.079
pwm_cycle_time: 0.100
pin: !P1.0
shutdown_speed: 1
cycle_time: 0.001
hardware_pwm: False
heater_temp: 150
# This is the temp the heater will target whenever the fan is activated
min_speed: 0.45
idle_timeout: 60

[heater_generic chamber_heater]
gcode_id: C
heater_pin: P1.24
max_power: 1
sensor_type: MAX31865
sensor_pin: rpi:gpio22
rtd_nominal_r: 100
rtd_reference_r: 430
rtd_num_of_wires: 3
control: watermark
#pid_Kp:
#pid_Ki:
#pid_Kd:
#pwm_cycle_time:
min_temp: 0
max_temp: 150

[verify_heater chamber_heater]
max_error: 240
#   The maximum "cumulative temperature error" before raising an
#   error.
check_gain_time: 240
#   This controls heater verification during initial heating. Smaller
#   values result in stricter checking and larger values allow for
#   more time before an error is reported. Specifically, during
#   initial heating, as long as the heater increases in temperature
#   within this time frame (specified in seconds) then the internal
#   "error counter" is reset. The default is 20 seconds for extruders
#   and 60 seconds for heater_bed.
hysteresis: 10
#   The maximum temperature difference (in Celsius) to a target
#   temperature that is considered in range of the target. This
#   controls the max_error range check. It is rare to customize this
#   value. The default is 5.
heating_gain: 1
#   The minimum temperature (in Celsius) that the heater must increase
#   by during the check_gain_time check. It is rare to customize this
#   value. The default is 2.

[controller_fan chamber_fan]
pin: P2.3
max_power: 1.0
shutdown_speed: 1
cycle_time: .010
hardware_pwm: False
#kick_start_time: 0.100
#off_below:
fan_speed: 1.0
idle_timeout: 600
#   The amount of time (in seconds) after a stepper driver or heater
#   was active and the fan should be kept running. The default
#   is 30 seconds.
heater: chamber_heater
stepper: extruder

[mcu]
serial: /dev/serial/by-id/usb-Klipper_lpc1769_0D10010DA39869AFBE54405EC62000F5-if00

[mcu rpi]
serial: /tmp/klipper_host_mcu

[temperature_sensor raspberry_pi]
sensor_type: temperature_host
min_temp: 10
max_temp: 100

[servo probewinch]
pin: P2.0
maximum_servo_angle: 290
minimum_pulse_width: 0.001
maximum_pulse_width: 0.002
initial_angle: 290
#initial_pulse_width:

[probe]
pin: P1.27 #probe input
deactivate_on_each_sample: False
x_offset: 29
y_offset: 11
z_offset = 2.2
speed: 3.0
samples: 1
sample_retract_dist: 3.0
#lift_speed:
#samples_result: average
#samples_tolerance: 0.100
#samples_tolerance_retries: 0
activate_gcode:
  PROBE_DEPLOY
  G4 P700
  # allow time for probe to deploy before homing Z
deactivate_gcode:
  PROBE_RETRACT

[gcode_macro PROBE_DEPLOY]
gcode:
  SET_SERVO SERVO=probewinch ANGLE=0

[gcode_macro PROBE_RETRACT]
gcode:
 SET_SERVO SERVO=probewinch ANGLE=290


[safe_z_home]
home_xy_position: 130, 150
speed: 175
z_hop: 10
z_hop_speed: 10


[screws_tilt_adjust]
screw1: 70, 270
screw1_name: backleft
#screw1_fine_adjust:
screw2: 270, 270
screw2_name: backright
#screw2_fine_adjust:
screw3: 270, 43
screw3_name: frontright
#screw3_fine_adjust:
screw4: 70, 45
screw4_name: frontleft
#screw4_fine_adjust:
speed: 140
horizontal_move_z: 5
#   The height (in mm) that the head should be commanded to move to
#   just prior to starting a probe operation. The default is 5.
screw_thread: CW-M4

[bed_mesh]
speed: 120
#   The speed (in mm/s) of non-probing moves during the calibration.
#   The default is 50.
horizontal_move_z: 5
#   The height (in mm) that the head should be commanded to move to
#   just prior to starting a probe operation. The default is 5.
mesh_min: 50, 40
#   Defines the minimum X, Y coordinate of the mesh for rectangular
#   beds. This coordinate is relative to the probe's location. This
#   will be the first point probed, nearest to the origin. This
#   parameter must be provided for rectangular beds.
mesh_max: 270, 275
#   Defines the maximum X, Y coordinate of the mesh for rectangular
#   beds. Adheres to the same principle as mesh_min, however this will
#   be the furthest point probed from the bed's origin. This parameter
#   must be provided for rectangular beds.
probe_count: 5

########################################
# Resonance test and compensation
########################################

#[adxl345]
#cs_pin: rpi:None

#[resonance_tester]
#accel_chip: adxl345
#probe_points:
 #   150,150,20  # an example

#[input_shaper]
#shaper_freq_x: 72.0
#shaper_freq_y: 28.0
#shaper_type: mzv

########################################
# TMC5160 configuration
########################################

[tmc5160 stepper_x]
cs_pin: P1.1
spi_software_miso_pin: P0.5
spi_software_mosi_pin: P1.17
spi_software_sclk_pin: P0.4
interpolate: False
run_current: 0.730
#stealthchop_threshold: 999999
diag1_pin: ^!P1.25
driver_SGT: 1

[tmc5160 stepper_y]
cs_pin: P1.9
spi_software_miso_pin: P0.5
spi_software_mosi_pin: P1.17
spi_software_sclk_pin: P0.4
interpolate: False
run_current: .700
#stealthchop_threshold: 999999
diag1_pin: ^!P1.28
driver_SGT: 0

[tmc5160 stepper_z]
cs_pin: P1.8
spi_software_miso_pin: P0.5
spi_software_mosi_pin: P1.17
spi_software_sclk_pin: P0.4
interpolate: False
run_current: 0.850
stealthchop_threshold: 999999
#diag1_pin: P1.xx

[tmc5160 extruder]
cs_pin: P1.4
spi_software_miso_pin: P0.5
spi_software_mosi_pin: P1.17
spi_software_sclk_pin: P0.4
interpolate: False
run_current: 0.300
stealthchop_threshold: 999999
#diag1_pin: P1.xx


########################################
# EXP1 / EXP2 (display) pins
########################################
[board_pins]
aliases:
    # EXP1 header
    EXP1_1=P1.30, EXP1_3=P1.18, EXP1_5=P1.20, EXP1_7=P1.22, EXP1_9=<GND>,
    EXP1_2=P0.28, EXP1_4=P1.19, EXP1_6=P1.21, EXP1_8=P1.23, EXP1_10=<5V>,
    # EXP2 header
    EXP2_1=P0.17, EXP2_3=P3.26, EXP2_5=P3.25, EXP2_7=P1.31, EXP2_9=<GND>,
    EXP2_2=P0.15, EXP2_4=P0.16, EXP2_6=P0.18, EXP2_8=<RST>, EXP2_10=<NC>
    # Pins EXP2_1, EXP2_6, EXP2_2 are also MISO, MOSI, SCK of bus "ssp0"
# See the sample-lcd.cfg file for definitions of common LCD displays.


######################################################################
# 128x64 Full Graphic Creality CR10 / ENDER 3 stockdisplay
######################################################################
# This section is used for a Creality "12864" display with a single
# ribbon cable between the display's EXP3 plug and the
# micro-controller board's EXP1 connector.
[display]
lcd_type: st7920
cs_pin: EXP1_7
sclk_pin: EXP1_6
sid_pin: EXP1_8
encoder_pins: ^EXP1_5, ^EXP1_3
click_pin: ^!EXP1_2

[output_pin beeper]
pin: EXP1_1


######################################################################
# MACROS
######################################################################
[gcode_macro START_PRINT]
gcode:
    #{% set BED_TEMP = params.BED_TEMP|default(0)|float %}
    #{% set EXTRUDER_TEMP = params.EXTRUDER_TEMP|default(0)|float %}
    # Start bed heating
    #M140 S{BED_TEMP}
    # Use absolute coordinates
    G90
    # Reset the G-Code Z offset (adjust Z offset if needed)
    SET_GCODE_OFFSET Z=0.0
    # Home the printer
    G28
    PROBE_RETRACT
    # Move the nozzle near the bed    
    G1 Z5 F3000
    # Move the nozzle very close to the bed
    G1 Z3 F300
    # Wait for bed to reach temperature
    #M190 S{BED_TEMP}
    # Set and wait for nozzle to reach temperature
    #M109 S{EXTRUDER_TEMP}
    G0 X0 Y15 F9000 ; Go to front
    G0 Z0.15 ; Drop to bed
    G92 E0 ; zero the extruded length
    G1 X100 E15 F400 ; Extrude 15mm of filament in a 10cm line
    G92 E0 ; zero the extruded length
    G1 E-1 F500 ; Retract a little
    G1 X80 F8000 ; Quickly wipe away from the filament line
    G1 Z0.3 ; Raise and begin printing.

[gcode_macro END_PRINT]
gcode:
    # Turn off bed, extruder, and fan
    M140 S0
    M104 S0
    M106 S0
    # Move nozzle away from print while retracting
    G91
    G1 X-50 Y-50 E-3 F300
    # Raise nozzle by 10mm
    PROBE_RETRACT
    G1 Z15 F3000
    G90
    G1 X0 Y310
    # Disable steppers
    M84

[gcode_macro SCREW_ADJUST]
gcode:
    G28  
    SCREWS_TILT_CALCULATE
    PROBE_RETRACT

[gcode_macro PROBE_CALIB]
gcode:
    G28
    PROBE_CALIBRATE
    PROBE_RETRACT

[gcode_macro BED_MESH]
gcode:
    G28
    BED_MESH_CALIBRATE
    PROBE_RETRACT

[gcode_macro M141]
gcode:
   SET_HEATER_TEMPERATURE HEATER=chamber_heater TARGET={params.S}
#   Taken from https://forum.makerforums.info/t/setting-up-chamber-heating-with-klipper-and-octoprint/88471

[gcode_macro M191]
gcode:
   SET_HEATER_TEMPERATURE HEATER=chamber_heater TARGET={params.S}
   TEMPERATURE_WAIT SENSOR="heater_generic chamber_heater" MINIMUM={params.S}
#   Taken from https://forum.makerforums.info/t/setting-up-chamber-heating-with-klipper-and-octoprint/88471

#[gcode_macro M104]
#rename_existing: _M104
#gcode:
# {% if rawparams %}
#   {% set temp  = rawparams.split('S', 1)[1] %}
#   {% set extruder = rawparams.split('T', 1)[1] %}
#    
#   {% if extruder == "0" %}
#     _M104 S{{ temp }}
#   {% else %}
#     SET_HEATER_TEMPERATURE HEATER=heated_fan TARGET={{ temp }}
#   {% endif %}
# {% endif %}

#[gcode_macro T1]
#gcode:
    #SET_HEATED_FAN_TARGET TARGET={params.S}             # sets the temp of the part cooling fan

#[gcode_macro M106]
#gcode:
#  {% set fanspeed = 255 %}
#  {% if params.S is defined %}
#    {% set fanspeed = params.S|int %}
#  {% endif %}
#  {% if fanspeed < 0 %}
#    {% set fanspeed = 0 %}
#  {% endif %}
#  {% if fanspeed > 255 %}
#    {% set fanspeed = 255 %}
 # {% endif %}
#  RESPOND PREFIX="info" MSG="Macro > M106 > speed {fanspeed}"
#  FANSPEED SPEED={fanspeed}

#what am I doing here, just messing with M106? This code failed to do anything. Need to look at virtual pins and see what that can do.



[gcode_macro HEAT_SOAK]
description: heats the bed for a while

variable_target_temp: 0
variable_stage: None ## heating -> soaking -> done -> None

## in seconds
variable_check_interval: 10
variable_soak_time_remaining: 0
variable_total_time_elapsed: 0

gcode:
    {% set TARGET = params.TARGET | default(0) | float %}
    {% set DURATION = (params.DURATION | default(5) | int) * 60 %} ## minutes to seconds

    SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=target_temp         VALUE={ TARGET }
    SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=stage               VALUE="'heating'"
    SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=soak_time_remaining VALUE={ DURATION }
    SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=total_time_elapsed  VALUE=0

    ;; fire up the heater
    SET_HEATER_TEMPERATURE HEATER=heater_bed TARGET={ TARGET }

    ;; run the fan to circulate air
    _SET_FAN_SPEED PERCENT=50

    ;; put the bed and nozzle where they're a safe distance apart
#    G28
    #CENTER

    M84 ;; turn off steppers

    UPDATE_DELAYED_GCODE ID=heat_soaker DURATION={ check_interval }

[gcode_macro CANCEL_HEAT_SOAK]
description: cancels an in-progress HEAT_SOAK cycle
gcode:
    SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=stage VALUE="'cancel'"
    UPDATE_DELAYED_GCODE ID=heat_soaker DURATION=1

[delayed_gcode heat_soaker]
; ## debug
; { action_respond_info( printer['gcode_macro HEAT_SOAK'] | tojson )}
gcode:
    {% set heat_soak = printer['gcode_macro HEAT_SOAK'] %}

    ## 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 stage = heat_soak.stage %}
    {% if stage == "heating" and printer.heater_bed.temperature >= heat_soak.target_temp %}
        {% set stage = "soaking" %}
    {% endif %}

    {% if stage == "soaking" %}
        ## update 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 }

        {% if soak_time_remaining == 0 %}
            {% set stage = "done" %}
        {% endif %}
    {% endif %}

    SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=stage VALUE="'{ stage }'"

    {% if stage in ("done", "cancel") %}

        {% if stage == "cancel" %}
            {% set stage = "done" %}
            TURN_OFF_HEATERS
            M107 ; turn off fan

            M117 { "soak cancelled after ~%.1fm" | format(total_time_elapsed / 60.0) }
        {% else %}
            M117 { "soak complete after %.1fm" | format(total_time_elapsed / 60.0) }
        {% endif %}

        ## reset all state vars, except stage, which may be queried via the api
        SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=target_temp         VALUE=0
        SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=soak_time_remaining VALUE=0
        SET_GCODE_VARIABLE MACRO=HEAT_SOAK VARIABLE=total_time_elapsed  VALUE=0

    {% else %}

        {% if total_time_elapsed % 90 == 0 %}
            ## output status periodically
            {% if stage == "heating" %}
                M117 { "heating -- %.1fm elapsed" | format(total_time_elapsed / 60.0) }
            {% elif stage == "soaking" %}
                M117 { "soaking -- %.1fm remaining" | format(soak_time_remaining / 60.0) }
            {% endif %}
        {% endif %}

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

[gcode_macro _SET_FAN_SPEED]
gcode:
    M106 S{ (params.PERCENT | float) * 255 / 100 }


[gcode_macro CENTER]
gcode:
    G90
    G0 X{ printer.toolhead.axis_maximum.x/2 } Y{ printer.toolhead.axis_maximum.y/2 } Z{ printer.toolhead.axis_maximum.z/2 } F7200

######################################################################
# FILAMENT MACROS

#[gcode_macro Prp_HTNCF]
#description: Adjust filament-specific settings
#gcode:
  # Sets pressure advance for given filament
#  SET_PRESSURE_ADVANCE ADVANCE=.04

#[gcode_macro Prp_PETG]
#description: Adjust filament-specific settings
#gcode:
  # Sets pressure advance for given filament
#  SET_PRESSURE_ADVANCE ADVANCE=.05

#*# <---------------------- SAVE_CONFIG ---------------------->
#*# DO NOT EDIT THIS BLOCK OR BELOW. The contents are auto-generated.
#*#
#*# [extruder]
#*#
#*# [probe]
#*# z_offset = 2.195
#*#
#*# [bed_mesh default]
#*# version = 1
#*# points =
#*# 	-0.110000, -0.097500, -0.095000, -0.160000, -0.260000
#*# 	0.007500, 0.015000, -0.010000, -0.082500, -0.200000
#*# 	0.052500, 0.035000, 0.002500, -0.065000, -0.175000
#*# 	0.027500, 0.042500, 0.007500, -0.057500, -0.155000
#*# 	-0.067500, -0.050000, -0.067500, -0.125000, -0.217500
#*# tension = 0.2
#*# min_x = 50.0
#*# algo = lagrange
#*# y_count = 5
#*# mesh_y_pps = 2
#*# min_y = 40.0
#*# x_count = 5
#*# max_y = 275.0
#*# mesh_x_pps = 2
#*# max_x = 270.0
#*#
#*# [heater_bed]
#*#
#*# [part_cooler_heater]
#*# control = pid
#*# pid_kp = 31.958
#*# pid_ki = 2.803
#*# pid_kd = 91.079

@levins-law
Copy link

levins-law commented Apr 22, 2024

Roger gave me some Macro-roni to sample.

rename_existing: M104.1
gcode:
  {% if rawparams %}
    {% set temp = params.S %}
    {% set extruder = params.T %}
    {% if extruder == "0" %}
      M104.1 S{ temp }
    {% else %}
      SET_HEATER_TEMPERATURE HEATER=heated_fan TARGET={ temp }
    {% endif %}
  {% endif %}

@levins-law
Copy link

I loaded that into config and sent the file to the printer. It did not error out, and might have worked, but I have to leave for the airport and will test an actual print next weekend when I get back.

@levins-law
Copy link

So the HEAT_SOAK macro, when activated, does turn on the heated_fan, which I manually turn off. What's really interesting is that the chamber heater does not time out while the HEAT_SOAK macro is running for a chosen period if time. I have to request a chamber temperature in Mainsail, but just like a normal hotend it will eventually turn off if I don't start a print.
I actually want that behavior, because the chamber fan should run whenever there are things heating up in the chamber (like the bed) but I don't understand why it's responding to the bed's HEAT_SOAK macro. Unexplained behavior should be explained.

@rogerlz
Copy link
Author

rogerlz commented May 2, 2024

Your HEAT_SOAK has a line _SET_FAN_SPEED, which runs M106 to turn on the fan

@levins-law
Copy link

levins-law commented May 4, 2024

I'm glad to report some success!

The rename_existing: M104.1 macro successfully responded to the Slicer's GCODE.

On the S3D Cooling tab I set the slicer to 0% fan at layer 1, 50% at layer 2, and 100% from layer 3 onward.
On the Temperature tab I then configured a new temperature controller Part Fan Heater and assigned it to toolhead T1.

What didn't work:

For some reason when the print was sent to the printer the heated fan turned on immediately at the configuration defaults (currently 150C and 45% fan speed- which is the min speed allowed above zero)
I turned that off manually, and let the print do its homing and warming up before it prints the purge line and starts printing layer 1 and that worked as expected.
Layer 1 printed fine, layer 2 started and the fan kicked on at 200C air temp, then layer 3 started at 100% fan.

The problem is that I had a fan speed set for layer 1,2, & 3 but I did not set a temperature for layer 3. I believe what happens is thus:

  • Layer 1, fan speed was zero and slicer temperature requested was zero.
  • Layer 2, fan speed was 50% and slicer temperature was 200C.
  • Layer 3, fan speed was 100% but I did not set a repeated request for 200C, so the Klipper default of 150C was used.
  • I manually set_heater_temp to 200C and the print was fine for the remaining layers.

At this point is not really a Klipper problem, as the engineer has to set fan temp and speed properly in the slicer. However, It is a failure mechanism that could be averted if the slicer understood the relationship between the cooling fan speed and the temperature control.
A simple promot letting the user know their layer number setpoints don't match might be enough. Something to think about when heated part cooling becomes more commonplace, and users demand these features.

Here is what the GCODE for the print looked like:

G90
M82
M106 S0 P0
M141 S135
M140 S217
M104 S400 T0
M104 S0 T1
START_PRINT
; process Ultem GF Thrust Block
; layer 1, Z = 0.3250
T0
G92 E0.00000
G1 E-0.40000 F1800
; feature skirt
; tool H0.2250 W0.480
G1 Z0.3250 F1002
G1 X134.537 Y134.685 F7200
G92 E0.00000
G1 E0.40000 F1800
G92 E0.00000
G1 X135.685 Y133.537 E0.07288 F750
G1 X137.168 Y132.770 E0.14788...

skipping a bunch of stuff...

...G1 X139.215 Y171.478 E68.29043
G92 E0.00000
G1 E-0.40000 F1800
; layer 2, Z = 0.5750
M106 S128 P0
M104 S200 T1
; feature inner perimeter
; tool H0.2500 W0.480
G1 Z0.5750 F1002
G1 X139.063 Y172.430 F7200
G92 E0.00000
G1 E0.40000 F1800
G92 E0.00000
G1 X138.777 Y172.385 E0.01443 F2400

skipping a bunch of stuff...

G1 X173.019 Y171.922 E78.84363
G1 X172.922 Y172.019 E78.85051
G92 E0.00000
G1 E-0.40000 F1800
; layer 3, Z = 0.8250
M106 S255 P0
; feature inner perimeter
G1 Z0.8250 F1002
G1 X172.937 Y172.680 F7200
G92 E0.00000
G1 E0.40000 F1800
G92 E0.00000
G1 X139.063 Y172.680 E1.69000 F2400
G1 X138.700 Y172.623 E1.70833

Click for Temperature settings pic

image

Click for Cooling settings pic

image

@levins-law
Copy link

I was trying to print some Extem TPI for the first time and maxing out my bed heat at 250C and hotend at 440C. I decided to try heated part cooling air with the first layer but I think it was somehow preventing the print from starting.

I figured if the filament requires 260 or 270 bed temps, and my bed can only get to 250, then hot part cooling air may help the first layer in this case.

In Simplify 3D I set the fan speed at 100% for Layer 1, and the temperature for the fan to 260C, and checked the "wait for temp to stabilize before printing" box. I haven't been doing that because it's not a CRITICAL thing to wait for and I don't want the print to pause and ooze while that comes up to temp at the start of layer 2.

I sent the GCODE and the printer seemed to get all the temps to their targets but the print just wouldn't go forward. I killed it, and went back to no 1st layer cooling so my torture with Extem could continue.

The reason this could be unrelated to heated_fan is because there were a lot of new variables. I definitely found the limit of my Chube hotend's 80W heater cartridge. I didn't know people had best results with a 120W cart so I'm waiting for that upgrade. I tried tuning the PIDs at 400C but the filament was cooling the hotend too fast and I got several ADC errors.
Then my thermo ADC board kept "erroring" but I'm pretty sure it's the jumper wires from the board to the Pi GPIO pins being janky. I'll fix that too.

The bed heater is 120vac and I think it has enough power, but it probably needs a special PID tune for this extreme filament too. I got a couple ADC errors from it because it's close to its max.

These are possibly isolated failure mechanism unrelated to heated_fan, but since I had the weird hang (no error, just inaction) I thought it prudent to examine.

@rogerlz if you would like these notes in the PR let me know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment