GOOBFACE
← Back to Config Store

📄 timelapse.cfg

FILE: timelapse.cfg
LINES: 428
SIZE: 21.96 KB
SYNTAX: INI / Klipper G-Code
1 # Timelapse klipper macro definition
2 #
3 # Copyright (C) 2021 Christoph Frei <fryakatkop@gmail.com>
4 # Copyright (C) 2021 Alex Zellner <alexander.zellner@googlemail.com>
5 #
6 # This file may be distributed under the terms of the GNU GPLv3 license
7 #
8 # Macro version 1.15
9 #
10
11 ##### DO NOT CHANGE ANY MACRO!!! #####
12
13 ##########################################################################
14 # #
15 # GET_TIMELAPSE_SETUP: Print the Timelapse setup to console #
16 # #
17 ##########################################################################
18
19 [gcode_macro GET_TIMELAPSE_SETUP]
20 description: Print the Timelapse setup
21 gcode:
22 {% set tl = printer['gcode_macro TIMELAPSE_TAKE_FRAME'] %}
23 {% set output_txt = ["Timelapse Setup:"] %}
24 {% set _dummy = output_txt.append("enable: %s" % tl.enable) %}
25 {% set _dummy = output_txt.append("park: %s" % tl.park.enable) %}
26 {% if tl.park.enable %}
27 {% set _dummy = output_txt.append("park position: %s time: %s s" % (tl.park.pos, tl.park.time)) %}
28 {% set _dummy = output_txt.append("park cord x:%s y:%s dz:%s" % (tl.park.coord.x, tl.park.coord.y, tl.park.coord.dz)) %}
29 {% set _dummy = output_txt.append("travel speed: %s mm/s" % tl.speed.travel) %}
30 {% endif %}
31 {% set _dummy = output_txt.append("fw_retract: %s" % tl.extruder.fw_retract) %}
32 {% if not tl.extruder.fw_retract %}
33 {% set _dummy = output_txt.append("retract: %s mm speed: %s mm/s" % (tl.extruder.retract, tl.speed.retract)) %}
34 {% set _dummy = output_txt.append("extrude: %s mm speed: %s mm/s" % (tl.extruder.extrude, tl.speed.extrude)) %}
35 {% endif %}
36 {% set _dummy = output_txt.append("verbose: %s" % tl.verbose) %}
37 {action_respond_info(output_txt|join("\n"))}
38
39 ################################################################################################
40 # #
41 # Use _SET_TIMELAPSE_SETUP [ENABLE=value] [VERBOSE=value] [PARK_ENABLE=value] [PARK_POS=value] #
42 # [PARK_TIME=value] [CUSTOM_POS_X=value] [CUSTOM_POS_Y=value] #
43 # [CUSTOM_POS_DZ=value][TRAVEL_SPEED=value] [RETRACT_SPEED=value] #
44 # [EXTRUDE_SPEED=value] [EXTRUDE_DISTANCE=value] #
45 # [RETRACT_DISTANCE=value] [FW_RETRACT=value] #
46 # #
47 ################################################################################################
48
49 [gcode_macro _SET_TIMELAPSE_SETUP]
50 description: Set user parameters for timelapse
51 gcode:
52 {% set tl = printer['gcode_macro TIMELAPSE_TAKE_FRAME'] %}
53 ##### get min and max bed size #####
54 {% set min = printer.toolhead.axis_minimum %}
55 {% set max = printer.toolhead.axis_maximum %}
56 {% set round_bed = True if printer.configfile.settings.printer.kinematics is in ['delta','polar','rotary_delta','winch']
57 else False %}
58 {% set park = {'min' : {'x': (min.x / 1.42)|round(3) if round_bed else min.x|round(3),
59 'y': (min.y / 1.42)|round(3) if round_bed else min.y|round(3)},
60 'max' : {'x': (max.x / 1.42)|round(3) if round_bed else max.x|round(3),
61 'y': (max.y / 1.42)|round(3) if round_bed else max.y|round(3)},
62 'center': {'x': (max.x-(max.x-min.x)/2)|round(3),
63 'y': (max.y-(max.y-min.y)/2)|round(3)}} %}
64 ##### set new values #####
65 {% if params.ENABLE %}
66 {% if params.ENABLE|lower is in ['true', 'false'] %}
67 SET_GCODE_VARIABLE MACRO=TIMELAPSE_TAKE_FRAME VARIABLE=enable VALUE={True if params.ENABLE|lower == 'true' else False}
68 {% else %}
69 {action_raise_error("ENABLE=%s not supported. Allowed values are [True, False]" % params.ENABLE|capitalize)}
70 {% endif %}
71 {% endif %}
72 {% if params.VERBOSE %}
73 {% if params.VERBOSE|lower is in ['true', 'false'] %}
74 SET_GCODE_VARIABLE MACRO=TIMELAPSE_TAKE_FRAME VARIABLE=verbose VALUE={True if params.VERBOSE|lower == 'true' else False}
75 {% else %}
76 {action_raise_error("VERBOSE=%s not supported. Allowed values are [True, False]" % params.VERBOSE|capitalize)}
77 {% endif %}
78 {% endif %}
79 {% if params.CUSTOM_POS_X %}
80 {% if params.CUSTOM_POS_X|float >= min.x and params.CUSTOM_POS_X|float <= max.x %}
81 {% set _dummy = tl.park.custom.update({'x':params.CUSTOM_POS_X|float|round(3)}) %}
82 {% else %}
83 {action_raise_error("CUSTOM_POS_X=%s must be within [%s - %s]" % (params.CUSTOM_POS_X, min.x, max.x))}
84 {% endif %}
85 {% endif %}
86 {% if params.CUSTOM_POS_Y %}
87 {% if params.CUSTOM_POS_Y|float >= min.y and params.CUSTOM_POS_Y|float <= max.y %}
88 {% set _dummy = tl.park.custom.update({'y':params.CUSTOM_POS_Y|float|round(3)}) %}
89 {% else %}
90 {action_raise_error("CUSTOM_POS_Y=%s must be within [%s - %s]" % (params.CUSTOM_POS_Y, min.y, max.y))}
91 {% endif %}
92 {% endif %}
93 {% if params.CUSTOM_POS_DZ %}
94 {% if params.CUSTOM_POS_DZ|float >= min.z and params.CUSTOM_POS_DZ|float <= max.z %}
95 {% set _dummy = tl.park.custom.update({'dz':params.CUSTOM_POS_DZ|float|round(3)}) %}
96 {% else %}
97 {action_raise_error("CUSTOM_POS_DZ=%s must be within [%s - %s]" % (params.CUSTOM_POS_DZ, min.z, max.z))}
98 {% endif %}
99 {% endif %}
100 {% if params.PARK_ENABLE %}
101 {% if params.PARK_ENABLE|lower is in ['true', 'false'] %}
102 {% set _dummy = tl.park.update({'enable':True if params.PARK_ENABLE|lower == 'true' else False}) %}
103 {% else %}
104 {action_raise_error("PARK_ENABLE=%s not supported. Allowed values are [True, False]" % params.PARK_ENABLE|capitalize)}
105 {% endif %}
106 {% endif %}
107 {% if params.PARK_POS %}
108 {% if params.PARK_POS|lower is in ['center','front_left','front_right','back_left','back_right','custom','x_only','y_only'] %}
109 {% set dic = {'center' : {'x': park.center.x , 'y': park.center.y , 'dz': 1 },
110 'front_left' : {'x': park.min.x , 'y': park.min.y , 'dz': 0 },
111 'front_right' : {'x': park.max.x , 'y': park.min.y , 'dz': 0 },
112 'back_left' : {'x': park.min.x , 'y': park.max.y , 'dz': 0 },
113 'back_right' : {'x': park.max.x , 'y': park.max.y , 'dz': 0 },
114 'custom' : {'x': tl.park.custom.x, 'y': tl.park.custom.y, 'dz': tl.park.custom.dz},
115 'x_only' : {'x': tl.park.custom.x, 'y': 'none' , 'dz': tl.park.custom.dz},
116 'y_only' : {'x': 'none' , 'y': tl.park.custom.y, 'dz': tl.park.custom.dz}} %}
117 {% set _dummy = tl.park.update({'pos':params.PARK_POS|lower}) %}
118 {% set _dummy = tl.park.update({'coord':dic[tl.park.pos]}) %}
119 {% else %}
120 {action_raise_error("PARK_POS=%s not supported. Allowed values are [CENTER, FRONT_LEFT, FRONT_RIGHT, BACK_LEFT, BACK_RIGHT, CUSTOM, X_ONLY, Y_ONLY]"
121 % params.PARK_POS|upper)}
122 {% endif %}
123 {% endif %}
124 {% if params.PARK_TIME %}
125 {% if params.PARK_TIME|float >= 0.0 %}
126 {% set _dummy = tl.park.update({'time':params.PARK_TIME|float|round(3)}) %}
127 {% else %}
128 {action_raise_error("PARK_TIME=%s must be a positive number" % params.PARK_TIME)}
129 {% endif %}
130 {% endif %}
131 SET_GCODE_VARIABLE MACRO=TIMELAPSE_TAKE_FRAME VARIABLE=park VALUE="{tl.park}"
132 {% if params.TRAVEL_SPEED %}
133 {% if params.TRAVEL_SPEED|float > 0.0 %}
134 {% set _dummy = tl.speed.update({'travel':params.TRAVEL_SPEED|float|round(3)}) %}
135 {% else %}
136 {action_raise_error("TRAVEL_SPEED=%s must be larger than 0" % params.TRAVEL_SPEED)}
137 {% endif %}
138 {% endif %}
139 {% if params.RETRACT_SPEED %}
140 {% if params.RETRACT_SPEED|float > 0.0 %}
141 {% set _dummy = tl.speed.update({'retract':params.RETRACT_SPEED|float|round(3)}) %}
142 {% else %}
143 {action_raise_error("RETRACT_SPEED=%s must be larger than 0" % params.RETRACT_SPEED)}
144 {% endif %}
145 {% endif %}
146 {% if params.EXTRUDE_SPEED %}
147 {% if params.EXTRUDE_SPEED|float > 0.0 %}
148 {% set _dummy = tl.speed.update({'extrude':params.EXTRUDE_SPEED|float|round(3)}) %}
149 {% else %}
150 {action_raise_error("EXTRUDE_SPEED=%s must be larger than 0" % params.EXTRUDE_SPEED)}
151 {% endif %}
152 {% endif %}
153 SET_GCODE_VARIABLE MACRO=TIMELAPSE_TAKE_FRAME VARIABLE=speed VALUE="{tl.speed}"
154 {% if params.EXTRUDE_DISTANCE %}
155 {% if params.EXTRUDE_DISTANCE|float >= 0.0 %}
156 {% set _dummy = tl.extruder.update({'extrude':params.EXTRUDE_DISTANCE|float|round(3)}) %}
157 {% else %}
158 {action_raise_error("EXTRUDE_DISTANCE=%s must be specified as positiv number" % params.EXTRUDE_DISTANCE)}
159 {% endif %}
160 {% endif %}
161 {% if params.RETRACT_DISTANCE %}
162 {% if params.RETRACT_DISTANCE|float >= 0.0 %}
163 {% set _dummy = tl.extruder.update({'retract':params.RETRACT_DISTANCE|float|round(3)}) %}
164 {% else %}
165 {action_raise_error("RETRACT_DISTANCE=%s must be specified as positiv number" % params.RETRACT_DISTANCE)}
166 {% endif %}
167 {% endif %}
168 {% if params.FW_RETRACT %}
169 {% if params.FW_RETRACT|lower is in ['true', 'false'] %}
170 {% if 'firmware_retraction' in printer.configfile.settings %}
171 {% set _dummy = tl.extruder.update({'fw_retract': True if params.FW_RETRACT|lower == 'true' else False}) %}
172 {% else %}
173 {% set _dummy = tl.extruder.update({'fw_retract':False}) %}
174 {% if params.FW_RETRACT|capitalize == 'True' %}
175 {action_raise_error("[firmware_retraction] not defined in printer.cfg. Can not enable fw_retract")}
176 {% endif %}
177 {% endif %}
178 {% else %}
179 {action_raise_error("FW_RETRACT=%s not supported. Allowed values are [True, False]" % params.FW_RETRACT|capitalize)}
180 {% endif %}
181 {% endif %}
182 SET_GCODE_VARIABLE MACRO=TIMELAPSE_TAKE_FRAME VARIABLE=extruder VALUE="{tl.extruder}"
183 {% if printer.configfile.settings['gcode_macro pause'] is defined %}
184 {% set _dummy = tl.macro.update({'pause': printer.configfile.settings['gcode_macro pause'].rename_existing}) %}
185 {% endif %}
186 {% if printer.configfile.settings['gcode_macro resume'] is defined %}
187 {% set _dummy = tl.macro.update({'resume': printer.configfile.settings['gcode_macro resume'].rename_existing}) %}
188 {% endif %}
189 SET_GCODE_VARIABLE MACRO=TIMELAPSE_TAKE_FRAME VARIABLE=macro VALUE="{tl.macro}"
190
191 ##########################################################################
192 # #
193 # TIMELAPSE_TAKE_FRAME: take the next picture #
194 # #
195 ##########################################################################
196
197 ######################### definition #########################
198 ## enable: enable or disable the next frame. Valid inputs: [True, False]
199 ## takingframe: internal use. Valid inputs: [True, False]
200 ##
201 ## park.enable: enable or disable to park the head while taking a picture. Valid inputs: [True, False]
202 ## park.pos : used position for parking. Valid inputs: [center, front_left, front_right, back_left, back_right, custom, x_only, y_only]
203 ## park.time : used for the debug macro. Time in s
204 ## park.custom.x, park.custom.y: coordinates of the custom parkposition. Unit [mm]
205 ## park.custom.dz : custom z hop for the picture. Unit [mm]
206 ## park.coord : internal use
207 ##
208 ## extruder.fw_retract: enable disable fw retraction [True,False]
209 ## extruder.extrude : filament extruded at the end of park. Unit [mm]
210 ## extruder.retract : filament retract at the start of park. Unit [mm]
211 ##
212 ## speed.travel : used speed for travel from and to the park positon. Unit: [mm/min]
213 ## speed.retract: used speed for retract [mm/min]
214 ## speed.extrude: used speed for extrude [mm/min]
215 ##
216 ## verbose: Enable mesage output of TIMELAPSE_TAKE_FRAME
217 ##
218 ## check_time: time when the status of the taken picture is checked. Default 0.5 sec
219 ##
220 ## restore.absolute.coordinates: internal use
221 ## restore.absolute.extrude : internal use
222 ## restore.speed : internal use
223 ## restore.e : internal use
224 ## restore.factor.speed : internal use
225 ## restore.factor.extrude : internal use
226 ##
227 ## macro.pause : internal use
228 ## macro.resume : internal use
229 ##
230 ## is_paused: internal use
231 ###############################################################
232 [gcode_macro TIMELAPSE_TAKE_FRAME]
233 description: Take Timelapse shoot
234 variable_enable: False
235 variable_takingframe: False
236 variable_park: {'enable': False,
237 'pos' : 'center',
238 'time' : 0.1,
239 'custom': {'x': 0, 'y': 0, 'dz': 0},
240 'coord' : {'x': 0, 'y': 0, 'dz': 0}}
241 variable_extruder: {'fw_retract': False,
242 'retract': 1.0,
243 'extrude': 1.0}
244 variable_speed: {'travel': 100,
245 'retract': 15,
246 'extrude': 15}
247 variable_verbose: True
248 variable_check_time: 0.5
249 variable_restore: {'absolute': {'coordinates': True, 'extrude': True}, 'speed': 1500, 'e':0, 'factor': {'speed': 1.0, 'extrude': 1.0}}
250 variable_macro: {'pause': 'PAUSE', 'resume': 'RESUME'}
251 variable_is_paused: False
252 gcode:
253 {% set hyperlapse = True if params.HYPERLAPSE and params.HYPERLAPSE|lower =='true' else False %}
254 {% if enable %}
255 {% if (hyperlapse and printer['gcode_macro HYPERLAPSE'].run) or
256 (not hyperlapse and not printer['gcode_macro HYPERLAPSE'].run) %}
257 {% if park.enable %}
258 {% set pos = {'x': 'X' + park.coord.x|string if park.pos != 'y_only' else '',
259 'y': 'Y' + park.coord.y|string if park.pos != 'x_only' else '',
260 'z': 'Z'+ [printer.gcode_move.gcode_position.z + park.coord.dz, printer.toolhead.axis_maximum.z]|min|string} %}
261 {% set restore = {'absolute': {'coordinates': printer.gcode_move.absolute_coordinates,
262 'extrude' : printer.gcode_move.absolute_extrude},
263 'speed' : printer.gcode_move.speed,
264 'e' : printer.gcode_move.gcode_position.e,
265 'factor' : {'speed' : printer.gcode_move.speed_factor,
266 'extrude': printer.gcode_move.extrude_factor}} %}
267 SET_GCODE_VARIABLE MACRO=TIMELAPSE_TAKE_FRAME VARIABLE=restore VALUE="{restore}"
268 {% if not printer[printer.toolhead.extruder].can_extrude %}
269 {% if verbose %}{action_respond_info("Timelapse: Warning, minimum extruder temperature not reached!")}{% endif %}
270 {% else %}
271 {% if extruder.fw_retract %}
272 G10
273 {% else %}
274 M83 ; insure relative extrusion
275 G0 E-{extruder.retract} F{speed.retract * 60}
276 {% endif %}
277 {% endif %}
278 SET_GCODE_VARIABLE MACRO=TIMELAPSE_TAKE_FRAME VARIABLE=is_paused VALUE=True
279 {macro.pause} ; execute the klipper PAUSE command
280 SET_GCODE_OFFSET X=0 Y=0 ; this will insure that the head parks always at the same position in a multi setup
281 G90 ; insure absolute move
282 {% if "xyz" not in printer.toolhead.homed_axes %}
283 {% if verbose %}{action_respond_info("Timelapse: Warning, axis not homed yet!")}{% endif %}
284 {% else %}
285 G0 {pos.x} {pos.y} {pos.z} F{speed.travel * 60}
286 {% endif %}
287 SET_GCODE_VARIABLE MACRO=TIMELAPSE_TAKE_FRAME VARIABLE=takingframe VALUE=True
288 UPDATE_DELAYED_GCODE ID=_WAIT_TIMELAPSE_TAKE_FRAME DURATION={check_time}
289 M400
290 {% endif %}
291 _TIMELAPSE_NEW_FRAME HYPERLAPSE={hyperlapse}
292 {% endif %}
293 {% else %}
294 {% if verbose %}{action_respond_info("Timelapse: disabled, take frame ignored")}{% endif %}
295 {% endif %}
296
297 [gcode_macro _TIMELAPSE_NEW_FRAME]
298 description: action call for timelapse shoot. must be a seperate macro
299 gcode:
300 {action_call_remote_method("timelapse_newframe",
301 macropark=printer['gcode_macro TIMELAPSE_TAKE_FRAME'].park,
302 hyperlapse=params.HYPERLAPSE)}
303
304 [delayed_gcode _WAIT_TIMELAPSE_TAKE_FRAME]
305 gcode:
306 {% set tl = printer['gcode_macro TIMELAPSE_TAKE_FRAME'] %}
307 {% set factor = {'speed': printer.gcode_move.speed_factor, 'extrude': printer.gcode_move.extrude_factor} %}
308 {% if tl.takingframe %}
309 UPDATE_DELAYED_GCODE ID=_WAIT_TIMELAPSE_TAKE_FRAME DURATION={tl.check_time}
310 {% else %}
311 {tl.macro.resume} VELOCITY={tl.speed.travel} ; execute the klipper RESUME command
312 SET_GCODE_VARIABLE MACRO=TIMELAPSE_TAKE_FRAME VARIABLE=is_paused VALUE=False
313 {% if not printer[printer.toolhead.extruder].can_extrude %}
314 {action_respond_info("Timelapse: Warning minimum extruder temperature not reached!")}
315 {% else %}
316 {% if tl.extruder.fw_retract %}
317 G11
318 {% else %}
319 G0 E{tl.extruder.extrude} F{tl.speed.extrude * 60}
320 G0 F{tl.restore.speed}
321 {% if tl.restore.absolute.extrude %}
322 M82
323 G92 E{tl.restore.e}
324 {% endif %}
325 {% endif %}
326 {% endif %}
327 {% if tl.restore.factor.speed != factor.speed %} M220 S{(factor.speed*100)|round(0)} {% endif %}
328 {% if tl.restore.factor.extrude != factor.extrude %} M221 S{(factor.extrude*100)|round(0)} {% endif %}
329 {% if not tl.restore.absolute.coordinates %} G91 {% endif %}
330 {% endif %}
331
332 ####################################################################################################
333 # #
334 # HYPERLAPSE: Starts or stops a Hyperlapse video #
335 # Usage: HYPERLAPSE ACTION=START [CYCLE=time] starts a hyperlapse with cycle time (default 30 sec) #
336 # HYPERLAPSE ACTION=STOP stops the hyperlapse recording #
337 # #
338 ####################################################################################################
339
340 ######################### definition #########################
341 ## cycle: cycle time in seconds
342 ## run: internal use [True/False]
343 ###############################################################
344 [gcode_macro HYPERLAPSE]
345 description: Start/Stop a hyperlapse recording
346 variable_cycle: 0
347 variable_run: False
348 gcode:
349 {% set cycle = params.CYCLE|default(30)|int %}
350 {% if params.ACTION and params.ACTION|lower == 'start' %}
351 {action_respond_info("Hyperlapse: frames started (Cycle %d sec)" % cycle)}
352 SET_GCODE_VARIABLE MACRO=HYPERLAPSE VARIABLE=run VALUE=True
353 SET_GCODE_VARIABLE MACRO=HYPERLAPSE VARIABLE=cycle VALUE={cycle}
354 UPDATE_DELAYED_GCODE ID=_HYPERLAPSE_LOOP DURATION={cycle}
355 TIMELAPSE_TAKE_FRAME HYPERLAPSE=True
356 {% elif params.ACTION and params.ACTION|lower == 'stop' %}
357 {% if run %}{action_respond_info("Hyperlapse: frames stopped")}{% endif %}
358 SET_GCODE_VARIABLE MACRO=HYPERLAPSE VARIABLE=run VALUE=False
359 UPDATE_DELAYED_GCODE ID=_HYPERLAPSE_LOOP DURATION=0
360 {% else %}
361 {action_raise_error("Hyperlapse: No valid input parameter
362 Use:
363 - HYPERLAPSE ACTION=START [CYCLE=time]
364 - HYPERLAPSE ACTION=STOP")}
365 {% endif %}
366
367 [delayed_gcode _HYPERLAPSE_LOOP]
368 gcode:
369 UPDATE_DELAYED_GCODE ID=_HYPERLAPSE_LOOP DURATION={printer["gcode_macro HYPERLAPSE"].cycle}
370 TIMELAPSE_TAKE_FRAME HYPERLAPSE=True
371
372 ##########################################################################
373 # #
374 # TIMELAPSE_RENDER: Render the video at print end #
375 # #
376 ##########################################################################
377
378 ######################### definition #########################
379 ## render: internal use. Valid inputs: [True, False]
380 ## run_identifier: internal use. Valid input [0 .. 3]
381 ###############################################################
382 [gcode_macro TIMELAPSE_RENDER]
383 description: Render Timelapse video and wait for the result
384 variable_render: False
385 variable_run_identifier: 0
386 gcode:
387 {action_respond_info("Timelapse: Rendering started")}
388 {action_call_remote_method("timelapse_render", byrendermacro="True")}
389 SET_GCODE_VARIABLE MACRO=TIMELAPSE_RENDER VARIABLE=render VALUE=True
390 {printer.configfile.settings['gcode_macro pause'].rename_existing} ; execute the klipper PAUSE command
391 UPDATE_DELAYED_GCODE ID=_WAIT_TIMELAPSE_RENDER DURATION=0.5
392
393 [delayed_gcode _WAIT_TIMELAPSE_RENDER]
394 gcode:
395 {% set ri = printer['gcode_macro TIMELAPSE_RENDER'].run_identifier % 4 %}
396 SET_GCODE_VARIABLE MACRO=TIMELAPSE_RENDER VARIABLE=run_identifier VALUE={ri + 1}
397 {% if printer['gcode_macro TIMELAPSE_RENDER'].render %}
398 M117 Rendering {['-','\\','|','/'][ri]}
399 UPDATE_DELAYED_GCODE ID=_WAIT_TIMELAPSE_RENDER DURATION=0.5
400 {% else %}
401 {action_respond_info("Timelapse: Rendering finished")}
402 M117
403 {printer.configfile.settings['gcode_macro resume'].rename_existing} ; execute the klipper RESUME command
404 {% endif %}
405
406 ##########################################################################
407 # #
408 # TEST_STREAM_DELAY: Helper macro to find stream and park delay #
409 # #
410 ##########################################################################
411
412 [gcode_macro TEST_STREAM_DELAY]
413 description: Helper macro to find stream and park delay
414 gcode:
415 {% set min = printer.toolhead.axis_minimum %}
416 {% set max = printer.toolhead.axis_maximum %}
417 {% set act = printer.toolhead.position %}
418 {% set tl = printer['gcode_macro TIMELAPSE_TAKE_FRAME'] %}
419 {% if act.z > 5.0 %}
420 G0 X{min.x + 5.0} F{tl.speed.travel|int * 60}
421 G0 X{(max.x-min.x)/2}
422 G4 P{tl.park.time|float * 1000}
423 _TIMELAPSE_NEW_FRAME HYPERLAPSE=FALSE
424 G0 X{max.x - 5.0}
425 {% else %}
426 {action_raise_error("Toolhead z %.3f to low. Please place head above z = 5.0" % act.z)}
427 {% endif %}
428