# gcode to run on printer start [delayed_gcode STARTUP] gcode: # grab saved variables {% set svv = printer.save_variables.variables %} {% set bed_leeway_temp = 5 %} {% set extruder_leeway_temp = 20 %} {% set hotend_filament = svv.hotend_filament %} # check if stored variables make sense, and set them to default values if not {% if not(svv.heater_mode == "auto" or svv.heater_mode == "manual" or svv.heater_mode == "slicer")%} { action_respond_info("setting heater_mode to AUTO, before: %s" % (svv.heater_mode)) } SAVE_VARIABLE VARIABLE=heater_mode VALUE='"auto"' {% endif %} # set temp variables - call variables macro VARIABLES # kickstart fan control macro UPDATE_DELAYED_GCODE ID=FAN_CONTROL_LOOP DURATION=2 # check the current heater states and re enable heaters if able {% if svv.extruder_state == 'hot' %} {% if svv.extruder_temperature - extruder_leeway_temp <= printer.extruder.temperature %} { action_respond_info("re-enabling extruder after printer shutoff") } SET_HEATER_TEMPERATURE HEATER=extruder TARGET={svv.extruder_temperature} MODE=override {% else %} { action_respond_info("extruder was enabled before last shutoff. State updated to cold.") } SAVE_VARIABLE VARIABLE=extruder_state VALUE='"cold"' {% endif %} {% endif %} {% if svv.bed_state == 'hot' %} {action_respond_info(printer.heater_bed.temperature|string)} {action_respond_info(bed_leeway_temp|string)} {action_respond_info(svv.bed_temperature|string)} {% if svv.bed_temperature - bed_leeway_temp <= printer.heater_bed.temperature %} { action_respond_info("re-enabling bed after printer shutoff") } SET_HEATER_TEMPERATURE HEATER=heater_bed TARGET={svv.bed_temperature} MODE=override {% else %} { action_respond_info("bed was enabled before last shutoff. State updated to cold.") } SAVE_VARIABLE VARIABLE=bed_state VALUE='"cold"' {% endif %} {% endif %} # check the filament load state: # exit-entry-bowden # 000 unloaded # 001 unloaded # 010 invalid # 011 unloaded # 100 runout # 101 invalid # 110 near_runout # 111 loaded # store button vals {% set exit_sense = (printer["gcode_button extruder_exit"].state == "PRESSED") %} {% set entry_sense = (printer["gcode_button extruder_entry"].state == "PRESSED") %} {% set bowden_sense = (printer["gcode_button bowden_sensor"].state == "PRESSED") %} # detect unloaded {% if not(entry_sense and exit_sense) or (not(entry_sense) and bowden_sense) %} {% if svv.filament_load_state != "unloaded" %} { action_respond_info("Current filament state does not match filament state on last boot. Was " ~ (svv.filament_load_state|string) ~ ", currently unloaded.") } {% endif %} SET_FILAMENT_LOAD_STATE STATE="unloaded" SET_FILAMENT_SENSOR SENSOR=bowden_encoder_sensor ENABLE=0 # loaded {% elif entry_sense and exit_sense and bowden_sense %} {% if svv.filament_load_state not in ["loaded", "primed"] %} { action_respond_info("Current filament state does not match filament state on last boot. Was " ~ (svv.filament_load_state|string) ~ "") } {% endif %} SET_FILAMENT_LOAD_STATE STATE="loaded" SET_FILAMENT_SENSOR SENSOR=bowden_encoder_sensor ENABLE=1 # runout {% elif entry_sense and not(exit_sense) and not(bowden_sense) %} { action_respond_info("Current filament state is runout. Unable to print.") } SET_FILAMENT_SENSOR SENSOR=bowden_encoder_sensor ENABLE=0 # near_runout {% elif entry_sense and exit_sense and not(bowden_sense) %} { action_respond_info("Current filament state is runout. Unable to print.") } SET_FILAMENT_SENSOR SENSOR=bowden_encoder_sensor ENABLE=0 {% endif %} initial_duration: 2 [gcode_macro TEST] gcode: {% set svv = printer.save_variables.variables %} {break} {action_respond_info(svv.filaments|selectattr("name", "in", [svv.filament_hotend, svv.filament_extruder])|map(attribute="extruder",default='None')|max|string)} ####### Calibration [gcode_macro calibrate_full] gcode: G28 QUAD_GANTRY_LEVEL BED_MESH_CALIBRATE METHOD=rapid_scan SET_GCODE_VARIABLE MACRO=VARIABLES VARIABLE=mesh_bed_temp VALUE={printer['heater_bed'].temperature} G0 X239 Y210 F{700*60} [gcode_macro calibrate] gcode: {% set mesh_temp_leeway = printer["gcode_macro VARIABLES"].mesh_bed_temp_leeway %} {% set mesh_temp = printer["gcode_macro VARIABLES"].mesh_bed_temp %} {% set skip_mesh = params.SKIP_MESH|default(False)%} {% if "xyz" not in printer['toolhead'].homed_axes %} G28 {% endif %} {% if not printer['quad_gantry_level'].applied %} QUAD_GANTRY_LEVEL {% endif %} {action_respond_info((not printer['bed_mesh'].profile_name)|string)} {% if skip_mesh %} G0 X239 Y210 F{700*60} # if a bed mesh has not been performed, or the temperature hasn't changed by more than 15 degrees. {% elif ((not printer['bed_mesh'].profile_name) or (printer['heater_bed'].temperature <= mesh_temp - mesh_temp_leeway) or (printer['heater_bed'].temperature >= mesh_temp + mesh_temp_leeway)) %} G0 X15 Y22 F{700*60} BED_MESH_CALIBRATE METHOD=rapid_scan SET_GCODE_VARIABLE MACRO=VARIABLES VARIABLE=mesh_bed_temp VALUE={printer['heater_bed'].temperature} G0 X239 Y210 F{700*60} {% endif %} [gcode_macro calibrate_override] gcode: ####### PRINT START AND STOP ETC [gcode_macro START_PRINT] description: Runs before the start of a print. Checks print readyness, corrects if nessesary, and primes the nozzle. gcode: {% set svv = printer.save_variables.variables %} # check the state of the loaded filament {% if svv.filament_load_state in ["loaded", "primed"] and svv.filament_runout_state != "runout" %} # if in auto, set bed and extruder to loaded filament parameters. {% if svv.heater_mode == "auto" %} SET_BED_TO_FILAMENT SET_HOTEND_TO_FILAMENT POSITION=both # if in slicer or manual mode, quickly sanity check the set temperatures: {% else %} {% if printer['extruder'].target <= 0 %} {action_raise_error("Extruder temperature is set to at or below zero")} {% endif %} {% endif %} # check calibration CALIBRATE SKIP_MESH=True {% if svv.filament_extruder == svv.filament_hotend %} PRIME_FILAMENT {% else %} PURGE_FILAMENT {% endif %} {% elif svv.filament_runout_state == "runout" %} {action_raise_error("Error, filament in runout state")} {% else %} {action_raise_error("Error, filament not loaded. Filament is %s" % (svv.filament_state))} {% endif %} # final enable of heaters and wait for temperature to be reached SET_HEATERS_TO_FILAMENT_AND_WAIT CALIBRATE CLEAN_NOZZLE # enable the encoder filament sensor SET_FILAMENT_SENSOR SENSOR=bowden_encoder_sensor ENABLE=1 [gcode_macro stop_print] gcode: {% set z = params.Z|default(50)|int %} ; z hop amount SET_FILAMENT_SENSOR SENSOR=bowden_encoder_sensor ENABLE=0 # reduce temperature of extruder by 10c to reduce oozing SET_HEATER_TEMPERATURE HEATER=extruder TARGET={printer['extruder'].target - 10} MODE=auto {% if (printer.gcode_move.position.z + z) < printer.toolhead.axis_maximum.z %} ; check that zhop doesn't exceed z max G91 ; relative positioning G1 Z{z} F900 ; raise Z up by z hop amount {% else %} G0 Z{printer.toolhead.axis_maximum.z} { action_respond_info("Pause zhop exceeds maximum Z height.") } {% endif %} G90 ; absolute positioning G1 Y{258} F6000 ; park toolhead [gcode_macro PAUSE] description: Pause the actual running print rename_existing: PAUSE_MAINSAIL gcode: # Parameters {% set svv = printer.save_variables.variables %} {% set z = params.Z|default(10)|int %} ; z hop amount #{% set idle_timeout = client.idle_timeout|default({60*5}) %} {% set idle_timeout = 60*5 %} # if printer is not already paused... {% if printer['pause_resume'].is_paused|int == 0 %} # set variables for reference in resume macro SET_GCODE_VARIABLE MACRO=RESUME VARIABLE=posx VALUE={printer['gcode_move'].gcode_position.x} SET_GCODE_VARIABLE MACRO=RESUME VARIABLE=posy VALUE={printer['gcode_move'].gcode_position.y} SET_GCODE_VARIABLE MACRO=RESUME VARIABLE=posz VALUE={printer['gcode_move'].gcode_position.z} SET_GCODE_VARIABLE MACRO=RESUME VARIABLE=etemp VALUE={printer['extruder'].target} SET_GCODE_VARIABLE MACRO=RESUME VARIABLE=btemp VALUE={printer['heater_bed'].target} SAVE_GCODE_STATE NAME=PAUSE PAUSE_MAINSAIL # set a new idle_timeout value SET_IDLE_TIMEOUT TIMEOUT={idle_timeout} # {client.user_pause_macro|default("")} # reduce temperature of extruder by 10c to reduce oozing if extruder is enabled {% if svv.extruder_state == 'hot' %} SET_HEATER_TEMPERATURE HEATER=extruder TARGET={printer['extruder'].target - 10} MODE=auto {% endif %} {% if (printer.gcode_move.position.z + z) < printer.toolhead.axis_maximum.z %} ; check that zhop doesn't exceed z max G91 ; relative positioning G1 Z{z} F900 ; raise Z up by z hop amount {% else %} G0 Z{printer.toolhead.axis_maximum.z} { action_respond_info("Pause zhop exceeds maximum Z height.") } {% endif %} G90 ; absolute positioning G1 Y{258} F6000 ; park toolhead SAVE_GCODE_STATE NAME=PAUSEPARK ; save parked position in case toolhead is moved during the pause (otherwise the return zhop can error) {% endif %} [gcode_macro RESUME] description: Resume the actual running print rename_existing: RESUME_MAINSAIL variable_posx: 0 variable_posy: 0 variable_posz: 0 variable_zhop: 0 variable_etemp: 0 variable_btemp: 0 gcode: # Parameters {% set e = params.E|default(2.5)|int %} ; hotend prime amount (in mm) {% set client = printer['gcode_macro _CLIENT_VARIABLE']|default({}) %} {% set velocity = printer.configfile.settings.pause_resume.recover_velocity %} {% set sp_move = client.speed_move|default(velocity) %} {% set runout_resume = True if client.runout_sensor|default("") == "" # no runout else True if not printer[client.runout_sensor].enabled # sensor is disabled else printer[client.runout_sensor].filament_detected %} # sensor status {% set can_extrude = True if printer.toolhead.extruder == '' # no extruder defined in config else printer[printer.toolhead.extruder].can_extrude %} # status of active extruder {% set do_resume = False %} {% set prompt_txt = [] %} # if printer is paused... {% if printer['pause_resume'].is_paused|int == 1 %} SET_IDLE_TIMEOUT TIMEOUT={printer.configfile.settings.idle_timeout.timeout} ; set timeout back to configured value RESTORE_GCODE_STATE NAME=PAUSEPARK MOVE=1 MOVE_SPEED=200 ; go back to parked position in case toolhead was moved during pause (otherwise the return zhop can error) # reset temperatures {% if etemp > 0 %} SET_HEATER_TEMPERATURE HEATER=extruder TARGET={etemp} MODE=auto {% endif %} {% if btemp > 0 %} SET_HEATER_TEMPERATURE_AND_WAIT HEATER=heater_bed TARGET={btemp} MODE=auto {% endif %} {% if etemp > 0 %} SET_HEATER_TEMPERATURE_AND_WAIT HEATER=extruder TARGET={etemp} MODE=auto ; Wait for hotend temp {% endif %} G91 ; relative positioning M83 ; relative extruder positioning {% if printer[printer.toolhead.extruder].temperature >= printer.configfile.settings.extruder.min_extrude_temp %} G1 Z{zhop * -1} E{e} F900 ; prime nozzle by E, lower Z back down {% else %} G1 Z{zhop * -1} F900 ; lower Z back down without priming (just in case we are testing the macro with cold hotend) {% endif %} RESTORE_GCODE_STATE NAME=PAUSE MOVE=1 MOVE_SPEED=60 ; restore position RESUME_MAINSAIL {% endif %} [gcode_macro CANCEL_PRINT] rename_existing: CANCEL_PRINT_MAINSAIL gcode: {% set z = params.Z|default(10)|int %} ; z hop amount SET_IDLE_TIMEOUT TIMEOUT={printer.configfile.settings.idle_timeout.timeout} ; set timeout back to configured value CLEAR_PAUSE CANCEL_PRINT_MAINSAIL # reduce temperature of extruder by 10c to reduce oozing SET_HEATER_TEMPERATURE HEATER=extruder TARGET={printer['extruder'].target - 10} MODE=auto {% if (printer.gcode_move.position.z + z) < printer.toolhead.axis_maximum.z %} ; check that zhop doesn't exceed z max G91 ; relative positioning G1 Z{z} F900 ; raise Z up by z hop amount {% else %} G0 Z{printer.toolhead.axis_maximum.z} { action_respond_info("Pause zhop exceeds maximum Z height.") } {% endif %} G90 ; absolute positioning G1 Y{258} F6000 ; park toolhead # clear pause_next_layer and pause_at_layer as preparation for next print SET_PAUSE_NEXT_LAYER ENABLE=0 SET_PAUSE_AT_LAYER ENABLE=0 LAYER=0 ###### HEATER AUTO HANDLING [gcode_macro SET_HEATERS_TO_FILAMENT_AND_WAIT] description: gcode: {% set svv = printer.save_variables.variables %} SET_HEATER_TEMPERATURE HEATER=extruder TARGET={svv.filaments|selectattr("name", "equalto", svv.filament_hotend)|map(attribute="extruder",default='None')|first|string} MODE=auto SET_HEATER_TEMPERATURE_AND_WAIT HEATER=heater_bed TARGET={svv.filaments|selectattr("name", "equalto", svv.filament_hotend)|map(attribute="bed",default='None')|first|string} MODE=auto SET_HEATER_TEMPERATURE_AND_WAIT HEATER=extruder TARGET={svv.filaments|selectattr("name", "equalto", svv.filament_hotend)|map(attribute="extruder",default='None')|first|string} MODE=auto [gcode_macro SET_HOTEND_TO_FILAMENT] description: sets the temperature of the hotend to the temperature required by the stored filament gcode: {% set wait = params.WAIT|default(False) %} ; z hop amount {% set svv = printer.save_variables.variables %} {% set position = params.POSITION|default('extruder')|string|lower %} {% if wait %} {% if position == 'extruder' %} SET_HEATER_TEMPERATURE_AND_WAIT HEATER=extruder TARGET={svv.filaments|selectattr("name", "in", [svv.filament_extruder])|map(attribute="extruder",default='None')|max|string} MODE=auto {% elif position == 'hotend' %} SET_HEATER_TEMPERATURE_AND_WAIT HEATER=extruder TARGET={svv.filaments|selectattr("name", "in", [svv.filament_hotend])|map(attribute="extruder",default='None')|max|string} MODE=auto {% elif position == 'both' %} SET_HEATER_TEMPERATURE_AND_WAIT HEATER=extruder TARGET={svv.filaments|selectattr("name", "in", [svv.filament_hotend, svv.filament_extruder])|map(attribute="extruder",default='None')|max|string} MODE=auto {% endif %} {% else %} {% if position == 'extruder' %} SET_HEATER_TEMPERATURE HEATER=extruder TARGET={svv.filaments|selectattr("name", "in", [svv.filament_extruder])|map(attribute="extruder",default='None')|max|string} MODE=auto {% elif position == 'hotend' %} SET_HEATER_TEMPERATURE HEATER=extruder TARGET={svv.filaments|selectattr("name", "in", [svv.filament_hotend])|map(attribute="extruder",default='None')|max|string} MODE=auto {% elif position == 'both' %} SET_HEATER_TEMPERATURE HEATER=extruder TARGET={svv.filaments|selectattr("name", "in", [svv.filament_hotend, svv.filament_extruder])|map(attribute="extruder",default='None')|max|string} MODE=auto {% endif %} {% endif %} [gcode_macro SET_BED_TO_FILAMENT] description: sets the temperature of the bed to the temperature required by the stored filament gcode: {% set svv = printer.save_variables.variables %} {% set position = params.POSITION|default('extruder')|string|lower %} {% if position == 'extruder' %} SET_HEATER_TEMPERATURE HEATER=heater_bed TARGET={svv.filaments|selectattr("name", "in", [svv.filament_extruder])|map(attribute="bed",default='None')|max|string} MODE=auto {% elif position == 'hotend' %} SET_HEATER_TEMPERATURE HEATER=heater_bed TARGET={svv.filaments|selectattr("name", "in", [svv.filament_hotend])|map(attribute="bed",default='None')|max|string} MODE=auto {% elif position == 'both' %} SET_HEATER_TEMPERATURE HEATER=heater_bed TARGET={svv.filaments|selectattr("name", "in", [svv.filament_hotend, svv.filament_extruder])|map(attribute="bed",default='None')|max|string} MODE=auto {% endif %} ## MISC MACROS [gcode_macro DUMP_VARIABLES] gcode: {% set filter_name = params.NAME|default('')|string|lower %} {% set filter_value = params.VALUE|default('')|string|lower %} {% set show_cfg = params.SHOW_CFG|default(0)|int %} {% set out = [] %} {% for key1 in printer %} {% for key2 in printer[key1] %} {% if (show_cfg or not (key1|lower == 'configfile' and key2|lower in ['config', 'settings'])) and (filter_name in key1|lower or filter_name in key2|lower) and filter_value in printer[key1][key2]|string|lower %} {% set dummy = out.append("printer['%s'].%s = %s" % (key1, key2, printer[key1][key2])) %} {% endif %} {% else %} {% if filter_name in key1|lower and filter_value in printer[key1]|string|lower %} {% set dummy = out.append("printer['%s'] = %s" % (key1, printer[key1])) %} {% endif %} {% endfor %} {% endfor %} {action_respond_info(out|join("\n"))} [gcode_macro TEST_SPEED] # Home, get position, throw around toolhead, home again. # If MCU stepper positions (first line in GET_POSITION) are greater than a full step different (your number of microsteps), then skipping occured. # We only measure to a full step to accomodate for endstop variance. # Example: TEST_SPEED SPEED=300 ACCEL=5000 ITERATIONS=10 description: Test for max speed and acceleration parameters for the printer. Procedure: Home -> ReadPositionFromMCU -> MovesToolhead@Vel&Accel -> Home -> ReadPositionfromMCU gcode: # Speed {% set speed = params.SPEED|default(printer.configfile.settings.printer.max_velocity)|int %} # Iterations {% set iterations = params.ITERATIONS|default(5)|int %} # Acceleration {% set accel = params.ACCEL|default(printer.configfile.settings.printer.max_accel)|int %} # Minimum Cruise Ratio {% set min_cruise_ratio = params.MIN_CRUISE_RATIO|default(0.5)|float %} # Bounding inset for large pattern (helps prevent slamming the toolhead into the sides after small skips, and helps to account for machines with imperfectly set dimensions) {% set bound = params.BOUND|default(20)|int %} # Size for small pattern box {% set smallpatternsize = SMALLPATTERNSIZE|default(20)|int %} # Large pattern # Max positions, inset by BOUND {% set x_min = printer.toolhead.axis_minimum.x %} {% if x_min < 0 %} {% set x_min = 0 %} {% endif %} {% set y_min = printer.toolhead.axis_minimum.y %} {% if y_min < 0 %} {% set y_min = 0 %} {% endif %} {% set x_min = x_min + bound %} {% set x_max = printer.toolhead.axis_maximum.x - bound %} {% set y_min = y_min + bound %} {% set y_max = printer.toolhead.axis_maximum.y - bound %} # Small pattern at center # Find X/Y center point {% set x_center = (printer.toolhead.axis_minimum.x|float + printer.toolhead.axis_maximum.x|float ) / 2 %} {% set y_center = (printer.toolhead.axis_minimum.y|float + printer.toolhead.axis_maximum.y|float ) / 2 %} # Set small pattern box around center point {% set x_center_min = x_center - (smallpatternsize/2) %} {% set x_center_max = x_center + (smallpatternsize/2) %} {% set y_center_min = y_center - (smallpatternsize/2) %} {% set y_center_max = y_center + (smallpatternsize/2) %} # Save current gcode state (absolute/relative, etc) SAVE_GCODE_STATE NAME=TEST_SPEED # Output parameters to g-code terminal { action_respond_info("TEST_SPEED: starting %d iterations at speed %d, accel %d" % (iterations, speed, accel)) } # Home and get position for comparison later: M400 # Finish moves - https://github.com/AndrewEllis93/Print-Tuning-Guide/issues/66 # QGL if not already QGLd (only if QGL section exists in config) {% if printer.configfile.settings.quad_gantry_level %} {% if printer.quad_gantry_level.applied == False %} G28 QUAD_GANTRY_LEVEL G28 Z {% endif %} {% endif %} # Move 50mm away from max position and home again (to help with hall effect endstop accuracy - https://github.com/AndrewEllis93/Print-Tuning-Guide/issues/24) G90 G1 X{printer.toolhead.axis_maximum.x-50} Y{printer.toolhead.axis_maximum.y-50} F{100*60} M400 # Finish moves - https://github.com/AndrewEllis93/Print-Tuning-Guide/issues/66 G28 X Y G0 X{printer.toolhead.axis_maximum.x-1} Y{printer.toolhead.axis_maximum.y-1} F{100*60} G4 P1000 GET_POSITION # Go to starting position G0 X{x_min} Y{y_min} Z{bound + 10} F{speed*60} # Set new limits {% if printer.configfile.settings.printer.minimum_cruise_ratio is defined %} SET_VELOCITY_LIMIT VELOCITY={speed} ACCEL={accel} MINIMUM_CRUISE_RATIO={min_cruise_ratio} {% else %} SET_VELOCITY_LIMIT VELOCITY={speed} ACCEL={accel} ACCEL_TO_DECEL={accel / 2} {% endif %} {% for i in range(iterations) %} # Large pattern diagonals G0 X{x_min} Y{y_min} F{speed*60} G0 X{x_max} Y{y_max} F{speed*60} G0 X{x_min} Y{y_min} F{speed*60} G0 X{x_max} Y{y_min} F{speed*60} G0 X{x_min} Y{y_max} F{speed*60} G0 X{x_max} Y{y_min} F{speed*60} # Large pattern box G0 X{x_min} Y{y_min} F{speed*60} G0 X{x_min} Y{y_max} F{speed*60} G0 X{x_max} Y{y_max} F{speed*60} G0 X{x_max} Y{y_min} F{speed*60} # Small pattern diagonals G0 X{x_center_min} Y{y_center_min} F{speed*60} G0 X{x_center_max} Y{y_center_max} F{speed*60} G0 X{x_center_min} Y{y_center_min} F{speed*60} G0 X{x_center_max} Y{y_center_min} F{speed*60} G0 X{x_center_min} Y{y_center_max} F{speed*60} G0 X{x_center_max} Y{y_center_min} F{speed*60} # Small pattern box G0 X{x_center_min} Y{y_center_min} F{speed*60} G0 X{x_center_min} Y{y_center_max} F{speed*60} G0 X{x_center_max} Y{y_center_max} F{speed*60} G0 X{x_center_max} Y{y_center_min} F{speed*60} {% endfor %} # Restore max speed/accel/accel_to_decel to their configured values {% if printer.configfile.settings.printer.minimum_cruise_ratio is defined %} SET_VELOCITY_LIMIT VELOCITY={printer.configfile.settings.printer.max_velocity} ACCEL={printer.configfile.settings.printer.max_accel} MINIMUM_CRUISE_RATIO={printer.configfile.settings.printer.minimum_cruise_ratio} {% else %} SET_VELOCITY_LIMIT VELOCITY={printer.configfile.settings.printer.max_velocity} ACCEL={printer.configfile.settings.printer.max_accel} ACCEL_TO_DECEL={printer.configfile.settings.printer.max_accel_to_decel} {% endif %} # Re-home and get position again for comparison: M400 # Finish moves - https://github.com/AndrewEllis93/Print-Tuning-Guide/issues/66 # G28 # This is a full G28 to fix an issue with CoreXZ - https://github.com/AndrewEllis93/Print-Tuning-Guide/issues/12 # Go to XY home positions (in case your homing override leaves it elsewhere) G90motors G0 X{printer.toolhead.axis_maximum.x-1} Y{printer.toolhead.axis_maximum.y-1} F{100*60} G4 P1000 GET_POSITION # Restore previous gcode state (absolute/relative, etc) RESTORE_GCODE_STATE NAME=TEST_SPEED