Interruptible heat soak

ooh, that’s really clever!

hello, just built a voron 2.4r2, i’m having problems with parts staying stuck when initially printing, so I thought heat soak would be a good idea. I ran across your post. I added the macro to my printer.cfg.

I run heat_soak, my toolhead homes and moves to the center, but the heater_bed never turn on.

I’m assuming I need to add the temperature I want to the variable_target_temp: 0. I set it to 110, but it does not turn on, any ideas?

thank you!

It’s a parameterized macro. You need to specify the temperature and duration, like HEAT_SOAK TARGET=110 DURATION=30 to soak for 30m at 110°C.

Thanks @blalor and @mwu for this script! I’ve wanted a way to do this so badly I hacked up klippy to do it: Add MAX_SLOPE capability to TEMPERATURE_WAIT by garethky · Pull Request #4796 · Klipper3d/klipper · GitHub But I gave up on getting the PR merged because Kevin has not so much time for this kind of thing. Bit I still wanted this functionality!

My version of the script is here: klipper-voron2.4-config/heatsoak.cfg at mainline · garethky/klipper-voron2.4-config · GitHub
It uses a second temperature sensor and stops the heat soak when the rate of temperature change on that sensor drops below a target. I have the default set to a rate of change of less than 0.75 degrees C per minute. This means that if the bed is already hot the soak time will be shorter. You call it like this:

HEAT_SOAK HEATER='heater_bed' TARGET=100 SOAKER='temperature_sensor top_bed' RATE=0.75

You also have to split your print start script where you call HEAT_SOAK. The call to PAUSE will not stop the print start script from running, it just stops the next line of GCode in the file being printed from running. So now I have:

PRINT_PREPARE  [...]     ; ends with a call to HEAT_SOAK
PRINT_START  [...]       ; does mesh bed leveling after heat soak

The only real gap now is that we cant configure a smooth_time on temperature sensors so the data we get from the sensor is noisy. I might do something about that but honestly this is working fine for my needs.

Hello guys,

I’m trying to use your macro @garethky but klipper is giving me this erro os starting:

Error loading template ‘delayed_gcode heat_soaker:gcode’: TemplateSyntaxError: unexpected ‘end of print statement’

Any idea?

I found the problem:

Must change line 98 from

{action_respond_info("Heat soak timed out after ~%.1fm" | format(total_time_elapsed / 60.0)) %}

to

{action_respond_info("Heat soak timed out after ~%.1fm" % (format(total_time_elapsed / 60.0)))}
1 Like

Ahh, sorry about that, updated on github.

Is there a way to do a callback in a macro? I could add an argument to HEAT_SOAK that calls another macro when it reaches the done condition.

Oh yeah, callbacks work:

[gcode_macro MACRO_A]
gcode:
    {action_respond_info("Macro A")}
    {% set CALLBACK = (params.CALLBACK | string) %}
    {CALLBACK}

[gcode_macro MACRO_B]
gcode:
    {action_respond_info("Macro B")}
09:38:19  $ macro_a CALLBACK=MACRO_B
09:38:19  // Macro A
09:38:19  // Macro B

Ok I had yet another bug with that action_respond_info statement that I fixed. No more format() calls in the code now.

I implemented a callback in heat_soak. My print_start passes a callback to heat_soak. heat_soak then calls that macro when it reaches the done state. This means the 2 parts of the print start process are chained together and I only have to call print_start in my slicer.

I also added a callback to print_start itself, so it can start running a test print macro after the entire startup process finishes. Running the pressure advance test macro after print start looks like this:

{% set PA_CAL_CALL -%}
    PA_CAL BED={BED_TEMP} EXTRUDER={EXTRUDER_TEMP} EXTRUSION_FACTOR={ER}
{%- endset %}
PRINT_START BED_TEMP={BED_TEMP} EXTRUDER_TEMP={EXTRUDER_TEMP} CALLBACK='{PA_CAL_CALL}'

The -%} and {%- are how you remove whitespace around the value, so the result is 1 line, this is required for this trick to work.

I didn’t find a way to nest the callbacks, with the way jinja and klipper handle string escaping I don’t see how to keep the quotes in the strings intact. I can think of more devious ways of achieving :speak_no_evil: this but you probably wont like them.

Programming macros with callbacks destroys the intended simplicity of the language. Some sort of barrier statement that blocks further evaluation of a macro is probably a better solution. Like the await keyword.

1 Like

Hy @garethky , I made some changes in your macro. The most important are:
mintemp variable: my chamber temp sensor takes too long to react upon the bed heating, so I need to define a minimum temperature to finish the heatsoak, otherwise it finishes in the beginning;
check_interval: defined two check_interval, one for heating stage, which is fast and need more frequent update and another to soak stage, where longer updates helps with delta temperature calculations.

I hope you enjoy the suggestions.

1 Like

I like it!

The MIN_TEMP idea makes sense. I could also see calling the macro with a formula, like MIN_TEMP=(TARGET_TEMP * 0.85) so it scales with the target temp. I can also make it so that its optional, so if you don’t provide a value it is not checked.

Would smoothing the temperature reading have made a difference? I’ve been thinking about making the interval 1 second, gathering a reading in an array and then computing an average over 5 seconds. Basically doing the smoothing in the macro. I’m not exactly sure there are enough array operators to do this in the language though, you would ideally want something like push() and pop() or slice().

This would uncouple the interval that the temp gets polled and the interval that the messages are posted, so that would add the same kind of interval variables that you added.

I’m using the min_temp =0.5*target_temp -6, just because it works for me, But 0,85 is to high, considering that the target temperature is the bed temperature and the heatsoak temperature is the chamber one.

Using a array is a solution, but I really think that is too over for this problem. The heat soak process is long. Update faster than 30s really don`t make too much sense for me. And the moving average (the technique that you mentioned) has a small problem in the begining, since you don’t have measurements in t-1. Any way, I really think that just adjusting the sampling rate is enought to solve the problem (it solved for me).

Ok @SinisterRj, you are getting everything on your wish list, plus some more:

  • Smoothing of both the soak temp and soak temp rate are in. Each with a separately tunable smoothing factor in seconds. I’ve tested tested down to 0.1C/m and it seemed to work in my printer. 0.3C/m is the default, at that rate it would take an hour to got up 18 degrees C. The rate is smoothed using least squares to find the slope. I found this to be much better than a simple slope between 2 points in time. Default is a sample of 20 points.
  • Smoothing waits for the requested number of data points before computing a temp or rate. The script is not fooled by initial temperature sensor noise and wont quit early.
  • SOAK_TEMP lets you prolong the soak until the soak temp reaches at least that value.
  • M117 calls and message formats have been combined with logging. Soaking messages now show the temp, rate and timeout information.
  • Both heating and soaking phases have separately customizable reporting intervals.
  • The loop now evaluates the temps every 1 second. More samples is better for noise suppression and the uncoupled reporting intervals means you wont see any additional messages.
  • renamed CALLBACK to CONTINUE so its less “programmer-speak”
  • Added CANCEL which allows you to pass the macro to call when the heat soak is canceled. Default on cancel is to take no action. This prevents potential errors when heat soaking before a print and saves you from having to customize the macro.
  • mwu’s Pause/Resume functionality now only activates if there is a print running. This stops Klipper Screen from thinking a print is paused when heat soaking without a print actually running.
  • Apply rounding to temperatures so that small fractions don’t make it look like 90.0 < 90.0 is true.
  • Wrote up a readme file that explains how to use it: klipper-voron2.4-config/heatsoak.readme.md at mainline · garethky/klipper-voron2.4-config · GitHub

The CONTINUE functionality has issues I don’t think I can fix: [heatsoak] PRINT_START_CONTINUE errors out but print continues · Issue #3 · garethky/klipper-voron2.4-config · GitHub

This topic is also very relevant: Save/restore gcode state in updated pause/resume macros

If we RESUME before the CONTINUE macro, things work “as expected”, but if anything goes wrong in the CONTINUE macro it wont stop the print as it should. Not stopping in a crashed state is a deal breaker for me, clearly that’s not safe.

If we RESUME after the CONTINUE macro any state that you built up in the CONTINUE macro is lost, including any probed Z Offset and the current position of the toolhead. RESUME will reset all of this and move the toolhead back to the position it was in at the start of the HEAT_SOAK. That’s also a deal breaker for me. The whole point of CONTINUE was to do probing after the heat soak.

So option 3 is to remove the CONTINUE functionality and recommend that people split their PRINT_START up into 2 calls in their slicer like this:

# Bed heat-up, Nozzle pre-warming, Homing/QGL, park in center, HEAT_SOAK:
PRINT_WARMUP=[first_layer_temperature] BED_TEMP=[first_layer_bed_temperature]
# bed mesh, auto calibrate Z, final nozzle heat up and prime line
PRINT_START=[first_layer_temperature] BED_TEMP=[first_layer_bed_temperature] 

I think this will makes the most sense for most users. The Pause state only covers the heat_soak time, nothing more. No weird syntax for passing a callback etc.

If you have a strong opinion on the CONTINUE functionality staying in or an alternative idea for making it safe to use, I’d love to hear it.

(I was using CONTINUE for macros that printed a test pattern, but I think I can find another way of doing that, like a small .gcode file)

Made some updates:

  • CONTINUE was renamed to COMPLETE and the documentation was updated to reflect my view that most people would be better off splitting their PRINT_START macro in 2 and calling both parts from their slicer’s gocde.
  • Added overrides for CANCEL_PRINT and RESUME. These wrap any macros you may have and add functionality. Particularly RESUME allows you to skip the soaking phase but wont resume the print while heating. This makes buttons in front ends safe to press without weird side effects.

@blalor an off-topic question. Your macro is great bit I would also like the notification in HA. Do you have an example on how to set this in HA

I’m doing that with node-red and the moonNode node. I monitor the stage variable of the HEAT_SOAK macro and send a notification when that changes to done. I’m in the middle of a move and my HA setup is offline, otherwise I’d share the full details. You could use HA’s native RESTful integration to poll moonraker’s API and create an entity with that same information, which would allow for creating the notification directly in HA.

@blalor thanks. I will look into the moonNode

i want to ask why gcode heat_soak can no longer be used? earlier every time i run heat_soak TARGET=100 DURATION=5 100c for 5 minutes then the heater turns off but now it keeps turning on. i didn’t change any gcode macros. is it related to me updating klipper and fluidd?

I run HEAT_SOAK TARGET=105 DURATION=5 my toolhead goes home and moves to the middle, but the heater_bed continues to turn on and doesn’t turn off. Usually after 5 minutes the heater automatically turns off?