diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index d0b617a36e40..e946b8538526 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -1511,17 +1511,35 @@ allowing per-filament settings and runtime tuning. ``` [firmware_retraction] -#retract_length: 0 -# The length of filament (in mm) to retract when G10 is activated, -# and to unretract when G11 is activated (but see -# unretract_extra_length below). The default is 0 mm. -#retract_speed: 20 -# The speed of retraction, in mm/s. The default is 20 mm/s. -#unretract_extra_length: 0 -# The length (in mm) of *additional* filament to add when -# unretracting. -#unretract_speed: 10 -# The speed of unretraction, in mm/s. The default is 10 mm/s. +#retract_length: 0.0 +# The length of filament (in mm) to retract when a G10 command is +# executed. When a G11 command is executed, the unretract_length +# is the sum of the retract_length and the unretract_extra_length +# (see below). The minimum value and default are 0 mm, which +# disables firmware retraction. +#retract_speed: 20.0 +# The speed of filament retraction moves (in mm/s). +# This value is typically set relatively high (>40 mm/s), +# except for soft and/oozy filaments like TPU and PETG +# (20 to 30 mm/s). The minimum value is 1 mm/s, the default value +# is 20 mm/s. +#unretract_extra_length: 0.0 +# The *additional* length (in mm) to add or the length to subtract +# from the filament move when unretracting compared to the retract +# move length. This allows priming the nozzle (positive extra length) +# or delaying extrusion after unretracting (negative length). The +# latter may help reduce blobbing. The minimum value is -1 mm +# (2.41 mm3 volume for 1.75 mm filament), the default value is 0 mm. +#unretract_speed: 10.0 +# The speed of filament unretraction moves (in mm/s). +# This parameter is not particularly critical, although often lower +# than retract_speed. The minimum value is 1 mm/s, the default value +# is 10 mm/s. +#z_hop_height: 0.0 +# The vertical height by which the nozzle is lifted from the print to +# prevent collisions with the print during travel moves when retracted. +# The minimum value is 0 mm, the default value is 0 mm, which disables +# zhop moves. ``` ### [gcode_arcs] diff --git a/docs/G-Codes.md b/docs/G-Codes.md index 1f466dcd3eb1..cc4581620248 100644 --- a/docs/G-Codes.md +++ b/docs/G-Codes.md @@ -485,35 +485,70 @@ will be disabled, if set to 1 it is enabled. The following standard G-Code commands are available when the [firmware_retraction config section](Config_Reference.md#firmware_retraction) -is enabled. These commands allow you to utilize the firmware -retraction feature available in many slicers, to reduce stringing -during non-extrusion moves from one part of the print to another. -Appropriately configuring pressure advance reduces the length of -retraction required. -- `G10`: Retracts the extruder using the currently configured - parameters. -- `G11`: Unretracts the extruder using the currently configured - parameters. +is enabled. These commands allow utilizing the firmware +retraction feature available in many slicers. Retraction is a strategy to +reduce stringing during travel moves (non-extrusion) from one part of the +print to another. Note that pressure advance should be properly configured +before retraction parameters are tuned to ensure optimal results. +- `G10`: Retracts the filament using the currently configured + parameters. If z_hop_height is set to a value greater zero, + besides retracting the filament, the nozzle is lifted by set value. +- `G11`: Unretracts the filament using the currently configured + parameters. If z_hop_height is set to a value greater zero, + besides unretracting the filament, the nozzle is lowered back on the print + with a vertical movement. The following additional commands are also available. #### SET_RETRACTION `SET_RETRACTION [RETRACT_LENGTH=] [RETRACT_SPEED=] -[UNRETRACT_EXTRA_LENGTH=] [UNRETRACT_SPEED=]`: Adjust the -parameters used by firmware retraction. RETRACT_LENGTH determines the -length of filament to retract and unretract. The speed of retraction -is adjusted via RETRACT_SPEED, and is typically set relatively -high. The speed of unretraction is adjusted via UNRETRACT_SPEED, and -is not particularly critical, although often lower than RETRACT_SPEED. -In some cases it is useful to add a small amount of additional length -on unretraction, and this is set via UNRETRACT_EXTRA_LENGTH. -SET_RETRACTION is commonly set as part of slicer per-filament -configuration, as different filaments require different parameter -settings. +[UNRETRACT_EXTRA_LENGTH=] [UNRETRACT_SPEED=] [Z_HOP_HEIGHT=]`: +Adjust the parameters used by firmware retraction. RETRACT_LENGTH determines the +length of filament to retract (the minimum as well as standard value is 0 mm). +RETRACT_SPEED determines the speed of the filament retraction move (the minimum +value is 1 mm/s, the standard value is 20 mm/s). This value is typically set +relatively high (>40 mm/s), except for soft and/or oozy filaments like TPU and +PETG (20 to 30 mm/s). +UNRETRACT_SPEED sets the speed of the filament unretract move (the minimum value +is 1 mm/s, the standard value is 10 mm/s). This parameter is not particularly +critical, although often lower than RETRACT_SPEED. +UNRETRACT_EXTRA_LENGTH allows to add a small amount of length to the filament +unretract move to prime the nozzle or to subtract a small amount of length from +the filament unretract move to reduce blobbing at seams (the minimum value is +-1 mm (2.41 mm3 volume for 1.75 mm filament), the standard value is 0 mm). +Z_HOP_HEIGHT determines the vertical height by which the nozzle is lifted from +the print to prevent collisions with the print during travel moves (the +minimum value is 0 mm, the standard value is 0 mm, which disables Z-Hop moves). +SET_RETRACTION is commonly set as part of slicer per-filament configuration, as +different filaments require different parameter settings. The command can be +issued at runtime. #### GET_RETRACTION -`GET_RETRACTION`: Queries the current parameters used by firmware -retraction and displays them on the terminal. +`GET_RETRACTION`: Queries the current parameters used by the firmware retraction +module as well as the retract state. RETRACT_LENGTH, RETRACT_SPEED, +UNRETRACT_EXTRA_LENGTH, UNRETRACT_SPEED, Z_HOP_HEIGHT and RETRACTED (True, if +retracted) are displayed on the terminal. + +#### CLEAR_RETRACTION +`CLEAR_RETRACTION`: Clears the current retract state without extruder or +motion system movement. All flags related to the retract state are reset to +False and all changes to retraction parameters made via previous SET_RETRACTION +commands are reset to config values. +NOTE: The Module contains a lot of redundancy for safety to prevent undesired +behavior. When printing from virtual SD Card, the printer state is monitored and +retraction state is cleared if a print is started, canceled or finished or if a +virtual SD card file is reset. When printing via GCode streaming (e.g. using +OctoPrint), the retract state is cleared when the steppers are disabled (M84, +typically part of end gcode and standard behavior of OctoPrint if a print is +canceled) or the printer is homed (G28, typically part of start gcode). Hence, +upon ending or canceling a print as well as starting a new print via GCode +streaming or virtual SD card, the printer should always be in unretracted state. +Nevertheless, it is recommended to add `CLEAR_RETRACTION` to your start and end +gcode to make sure the retract state is reset before and after each print. If a +print is finished or canceled while retracted and the retract state is not +cleared, either via `CLEAR_RETRACTION` without filament or motion system +movement or G11, the nozzle will stay above the requested z coordinate by the +set z_hop_height. ### [force_move] diff --git a/docs/Status_Reference.md b/docs/Status_Reference.md index 13139dd02b73..e1be9f3d77b8 100644 --- a/docs/Status_Reference.md +++ b/docs/Status_Reference.md @@ -163,10 +163,20 @@ objects: The following information is available in the [firmware_retraction](Config_Reference.md#firmware_retraction) object: -- `retract_length`, `retract_speed`, `unretract_extra_length`, - `unretract_speed`: The current settings for the firmware_retraction - module. These settings may differ from the config file if a - `SET_RETRACTION` command alters them. +- `retract_length`: Current setting for length of filament retract moves. +- `retract_speed`: Current setting for speed of filament retract moves. +- `unretract_extra_length`: Current setting for additional length of filament + unretract moves (positive values will result in filament extrusion, while + negative values up to 1 mm (2.41 mm3 for 1.75 mm filament) will result in + lagging extrusion of filament). +- `unretract_speed`: Current setting for speed of unretract moves of filament. +- `unretract_length`: Unretract move length (sum of retract and extra unretract + length). +- `z_hop_height`: Current setting for the height of nozzle lifting move (Z-Hop). +- Above settings for the firmware_retraction module may differ from the + config file if a `SET_RETRACTION` command altered them. Additional information + available is as follows. +- `retract_state`: Returns 'True' if filament is retracted. ## gcode_button diff --git a/klippy/extras/firmware_retraction.py b/klippy/extras/firmware_retraction.py index bcecf8f2632f..afa8cd2025a0 100644 --- a/klippy/extras/firmware_retraction.py +++ b/klippy/extras/firmware_retraction.py @@ -1,74 +1,337 @@ # Support for Marlin/Smoothie/Reprap style firmware retraction via G10/G11 +# Zhop funtionality includes: +# - Standard zhop (vertical move up, travel, vertical move down) # +# Copyright (C) 2023 Florian-Patrice Nagel # Copyright (C) 2019 Len Trigg # # This file may be distributed under the terms of the GNU GPLv3 license. +import logging + +# Constants +ZHOP_MOVE_SPEED_FRACTION = 0.8 class FirmwareRetraction: + ################################################################# Class init def __init__(self, config): + self.config_ref = config self.printer = config.get_printer() - self.retract_length = config.getfloat('retract_length', 0., minval=0.) - self.retract_speed = config.getfloat('retract_speed', 20., minval=1) - self.unretract_extra_length = config.getfloat( - 'unretract_extra_length', 0., minval=0.) - self.unretract_speed = config.getfloat('unretract_speed', 10., minval=1) + # Get retraction params from config, used also in clear retraction + self._get_config_params() + # Initialize variables self.unretract_length = (self.retract_length + self.unretract_extra_length) - self.is_retracted = False + self.currentPos = [] + self.currentZ = 0.0 + self.z_hop_Z = 0.0 # Z coordinate of zhop move + + self.is_retracted = False # Retract state flag + self.vsdcard_paused = False # VSDCard pause flag + self.G1_toggle_state = False # G1 toggle state flag + + # Get maximum printer move velocity for zhop moves + printer_config = config.getsection('printer') + self.max_vel = printer_config.getfloat('max_velocity') + + self.printer.register_event_handler("klippy:ready", self._handle_ready) + + ##################### Helper method to get retraction parameters from config + def _get_config_params(self): + self.retract_length = self.config_ref.getfloat(\ + 'retract_length', 0., minval=0.) + self.retract_speed = self.config_ref.getfloat(\ + 'retract_speed', 20., minval=1) + self.unretract_extra_length = self.config_ref.getfloat( + 'unretract_extra_length', 0., minval=0.) + self.unretract_speed = self.config_ref.getfloat(\ + 'unretract_speed', 10., minval=1) + # Zero min. and stand. zhop valueto ensure compatibility with macros + self.z_hop_height = self.config_ref.getfloat(\ + 'z_hop_height', 0., minval=0.) + + ######## Helper method to register commands and instantiate required objects + def _handle_ready(self): self.gcode = self.printer.lookup_object('gcode') + self.gcode_move = self.printer.lookup_object('gcode_move') + self.toolhead = self.printer.lookup_object('toolhead') + + # Register new G-code commands for setting/retrieving retraction + # parameters as well as clearing retraction state self.gcode.register_command('SET_RETRACTION', self.cmd_SET_RETRACTION, desc=self.cmd_SET_RETRACTION_help) self.gcode.register_command('GET_RETRACTION', self.cmd_GET_RETRACTION, desc=self.cmd_GET_RETRACTION_help) + self.gcode.register_command('CLEAR_RETRACTION',\ + self.cmd_CLEAR_RETRACTION, desc=self.cmd_CLEAR_RETRACTION_help) + + # Register new G-code commands for firmware retraction/unretraction self.gcode.register_command('G10', self.cmd_G10) self.gcode.register_command('G11', self.cmd_G11) + + # Register Events to clear retraction when a new print is started, an + # ongoing print is canceled or a print is finished + ################ GCode streaming mode (most commonly done via OctoPrint) + self.printer.register_event_handler("homing:homing_move_begin", \ + self._evaluate_retraction) + self.printer.register_event_handler("stepper_enable:motor_off", \ + self._evaluate_retraction) + + #### Virtual SD card mode (Mainsail, Fluidd and DWC2-to-Klipper default) + # Only register events if Virtual SD Card enabled + if self.config_ref.has_section('virtual_sdcard'): + self.vsdcard = self.printer.lookup_object('virtual_sdcard') + self.printer.register_event_handler("virtual_sdcard:reset_file", \ + self._reset_pause_flag) + self.printer.register_event_handler("print_stats:start_printing", \ + self._evaluate_retraction) + self.printer.register_event_handler("print_stats:complete_printing"\ + , self._evaluate_retraction) + self.printer.register_event_handler("print_stats:cancelled_printing\ + ", self._reset_pause_flag) + self.printer.register_event_handler("print_stats:paused_printing", \ + self._set_pause_flag) + + ###### Helper method to evaluate to clear retraction if certain events occur + # (must accept all arguments passed from event handlers) + def _evaluate_retraction(self, *args): + if self.is_retracted: # Check if retracted + if self.vsdcard_paused: # Check if VSDCard print paused + # Reset paused flag and hence do not clear retraction on + # resume command. + self.vsdcard_paused = False + else: + # If cancel command triggered pause event, clear retraction. + self._execute_clear_retraction() + + ################## Helper method to return the current retraction parameters def get_status(self, eventtime): return { "retract_length": self.retract_length, "retract_speed": self.retract_speed, "unretract_extra_length": self.unretract_extra_length, "unretract_speed": self.unretract_speed, + 'z_hop_height': self.z_hop_height, + 'unretract_length': self.unretract_length, + 'retract_state': self.is_retracted, } + + ########### Helper method to reset pause flags and force evaluate retraction + def _reset_pause_flag(self, *args): + self.vsdcard_paused = False + self._evaluate_retraction() + + ############################################ Helper method to set pause flag + def _set_pause_flag(self, *args): + self.vsdcard_paused = True + + ########################## Command to set the firmware retraction parameters cmd_SET_RETRACTION_help = ("Set firmware retraction parameters") + def cmd_SET_RETRACTION(self, gcmd): + if not self.is_retracted: # Only execute command when unretracted + self._execute_set_retraction(gcmd) # Execute command immediately + else: + self.gcode.respond_info( + "WARNING: Printer in retract state. SET_RETRACTION will not be\ + executed!") + + ################### Helper to set retraction parameters if command is called + def _execute_set_retraction(self,gcmd): self.retract_length = gcmd.get_float('RETRACT_LENGTH', self.retract_length, minval=0.) self.retract_speed = gcmd.get_float('RETRACT_SPEED', - self.retract_speed, minval=1) + self.retract_speed, minval=1.) self.unretract_extra_length = gcmd.get_float( - 'UNRETRACT_EXTRA_LENGTH', self.unretract_extra_length, minval=0.) + 'UNRETRACT_EXTRA_LENGTH', self.unretract_extra_length, minval=-1.) self.unretract_speed = gcmd.get_float('UNRETRACT_SPEED', - self.unretract_speed, minval=1) + self.unretract_speed, minval=1.) + self.z_hop_height = gcmd.get_float('Z_HOP_HEIGHT', self.z_hop_height, \ + minval=0.) # z_hop_height with 0mm min. to prevent nozzle crash self.unretract_length = (self.retract_length + self.unretract_extra_length) - self.is_retracted = False + + ############### Command to report the current firmware retraction parameters cmd_GET_RETRACTION_help = ("Report firmware retraction paramters") + def cmd_GET_RETRACTION(self, gcmd): - gcmd.respond_info("RETRACT_LENGTH=%.5f RETRACT_SPEED=%.5f" - " UNRETRACT_EXTRA_LENGTH=%.5f UNRETRACT_SPEED=%.5f" + gcmd.respond_info('RETRACT_LENGTH=%.5f RETRACT_SPEED=%.5f ' + 'UNRETRACT_EXTRA_LENGTH=%.5f UNRETRACT_SPEED=%.5f ' + ' Z_HOP_HEIGHT=%.5f ' + ' RETRACTED=%s ' % (self.retract_length, self.retract_speed, - self.unretract_extra_length, self.unretract_speed)) + self.unretract_extra_length, self.unretract_speed, + self.z_hop_height, self.is_retracted)) + + ##### Command to clear FW retraction (add to CANCEL macros at the beginning) + cmd_CLEAR_RETRACTION_help = ('Clear retraction state without retract move \ + or zhop, if enabled') + + def cmd_CLEAR_RETRACTION(self, gcmd): + if self.is_retracted: + self._execute_clear_retraction() + gcmd.respond_info('Retraction was cleared and reset to config \ + values. zhop is undone on next move.') + else: + gcmd.respond_info('WARNING: Printer is not retracted. \ + Command has been ignored!') + + ################################################# Helper to clear retraction + def _execute_clear_retraction(self): + if self.z_hop_height > 0.0: + # Re-establish regular G1 command if zhop enabled. + # zhop will be reversed on next move with z coordinate + self._re_register_G1() + self.G1_toggle_state = False # Prevent repeat re-register + self.is_retracted = False # Reset retract flag to enable G10 command + self._get_config_params() #Reset retraction parameters to config values + ########################### Gcode Command G10 to perform firmware retraction def cmd_G10(self, gcmd): - if not self.is_retracted: - self.gcode.run_script_from_command( + retract_gcode = "" # Reset retract string + zhop_gcode = "" # Reset zhop move string + + if self.retract_length == 0.0: # Check if FW retraction enabled + gcmd.respond_info('Retraction length zero. Firmware retraction \ + disabled. Command ignored!') + elif not self.is_retracted: # If filament isn't retracted, build G-Code + # Incl move command if z_hop_height > 0 + if self.z_hop_height > 0.0: + # Determine z coordinate for zhop move + self._set_zhop_move_params() + # Set zhop gcode move + zhop_gcode = ( + "G1 Z{:.5f} F{}\n" + ).format(self.z_hop_Z, int(ZHOP_MOVE_SPEED_FRACTION *\ + self.max_vel * 60)) + + retract_gcode = ( "SAVE_GCODE_STATE NAME=_retract_state\n" "G91\n" - "G1 E-%.5f F%d\n" + "G1 E-{:.5f} F{}\n" # Retract filament at retract speed + "G90\n" # Switch to absolute mode (just in case) + "{}" "RESTORE_GCODE_STATE NAME=_retract_state" - % (self.retract_length, self.retract_speed*60)) + ).format(self.retract_length, int(self.retract_speed * 60), \ + zhop_gcode) + + self.gcode.run_script_from_command(retract_gcode) self.is_retracted = True + if self.z_hop_height > 0.0: + # Swap original G1 handlers if z_hop enabled to offset following + # moves in eiter absolute or relative mode + self._unregister_G1() + self.G1_toggle_state = True #Prevent repeat unregister with flag + + else: + gcmd.respond_info('Printer is already retracted. Command ignored!') + + ######################################## Helper to determine zhop coordinate + def _set_zhop_move_params(self): + self.currentPos = self._get_gcode_pos() + self.currentZ = self.currentPos[2] + self.z_hop_Z = self.currentZ + self.z_hop_height + + ####################################### Helper to get current gcode position + def _get_gcode_pos(self): + # Get current gcode position for z_hop move if enabled + gcodestatus = self.gcode_move.get_status() + currentPos = gcodestatus['gcode_position'] + return currentPos + + ######################### GCode Command G11 to perform filament unretraction def cmd_G11(self, gcmd): - if self.is_retracted: - self.gcode.run_script_from_command( - "SAVE_GCODE_STATE NAME=_retract_state\n" + unretract_gcode = "" # Reset unretract string + unzhop_gcode = "" # Reset un-zhop move string + + if self.retract_length == 0.0: # Check if FW retraction enabled + gcmd.respond_info('Retraction length zero. Firmware retraction \ + disabled. Command ignored!') + elif self.is_retracted: # Check if the filament is retracted + if self.z_hop_height > 0.0: + self._re_register_G1() # Restore G1 handlers if z_hop on + self.G1_toggle_state = False # Prevent repeat re-register + # Set unzhop gcode move + unzhop_gcode = ( + "G1 Z-{:.5f} F{}\n" + ).format(self.z_hop_height, \ + int(ZHOP_MOVE_SPEED_FRACTION * self.max_vel * 60)) + + unretract_gcode = ( + "SAVE_GCODE_STATE NAME=_unretract_state\n" "G91\n" - "G1 E%.5f F%d\n" - "RESTORE_GCODE_STATE NAME=_retract_state" - % (self.unretract_length, self.unretract_speed*60)) - self.is_retracted = False + "{}" + "G1 E{:.5f} F{}\n" # Unretract filament + "RESTORE_GCODE_STATE NAME=_unretract_state" + ).format(unzhop_gcode, self.unretract_length, \ + int(self.unretract_speed * 60)) + + self.gcode.run_script_from_command(unretract_gcode) + self.is_retracted = False # Set the flag to filament unretracted + else: + gcmd.respond_info('Printer is not retracted. Command ignored!') + + ############################################ Register new G1 command handler + def _unregister_G1(self): + # Change handler only if G1 command has not been toggled before + if self.G1_toggle_state == False: + self._toggle_gcode_comms('G1.20140114', 'G1', self._G1_zhop, \ + 'G1 command that accounts for z hop when retracted', \ + self.G1_toggle_state) + self._toggle_gcode_comms('G0.20140114', 'G0', self._G1_zhop, \ + 'G0 command that accounts for z hop when retracted', \ + self.G1_toggle_state) + + ##################### Helper to toggle/untoggle command handlers and methods + def _toggle_gcode_comms(self, new_cmd_name, old_cmd_name, new_cmd_func, \ + new_cmd_desc, toggle_state): + # Unregister current method from current handler and store in prev_cmd + prev_cmd = self.gcode.register_command(old_cmd_name, None) + pdesc = 'Renamed builtin of "%s"' % old_cmd_name + if not toggle_state: + # Register prev method to toggled handler, indicate built-in command + self.gcode.register_command(new_cmd_name, prev_cmd, desc=pdesc) + self.gcode.register_command(old_cmd_name, new_cmd_func, \ + desc=new_cmd_desc) # Register toggled method to command handler + else: + # Unregister toggled command from the untoggled handler + self.gcode.register_command(new_cmd_name, new_cmd_func) + self.gcode.register_command(new_cmd_name, prev_cmd, \ + desc=new_cmd_desc) # Register untoggled method to untog handler + + ############ G1 method that accounts for z-hop by altering the z-coordinates + # Offsets are not touched to prevent incompatibility issues with user macros + def _G1_zhop(self,gcmd): + params = gcmd.get_command_parameters() + is_relative = self._toolhead_is_relative() + + if 'Z' in params and not is_relative: + # In absolute, adjust Z param to account for Z-hop offset + params['Z'] = str(float(params['Z']) + self.z_hop_height) + # In relative, don't adjust as Z-hop offset considered before + + new_g1_command = 'G1.20140114' + for key, value in params.items(): + new_g1_command += ' {0}{1}'.format(key, value) + + # Run originl G1 (renamed G1.20140114) with adjusted parameters + self.gcode.run_script_from_command(new_g1_command) + + ####################################### Helper to get absolute/relative mode + def _toolhead_is_relative(self): + gcodestatus = self.gcode_move.get_status() + movemode = gcodestatus['absolute_coordinates'] + return not movemode + + ######################################### Re-register old G1 command handler + def _re_register_G1(self): + # Change handler only if G1 command has been toggled before + if self.G1_toggle_state == True: + self._toggle_gcode_comms('G1', 'G1.20140114', None, 'cmd_G1_help', \ + self.G1_toggle_state) + self._toggle_gcode_comms('G0', 'G0.20140114', None, 'cmd_G1_help', \ + self.G1_toggle_state) def load_config(config): return FirmwareRetraction(config) diff --git a/klippy/extras/print_stats.py b/klippy/extras/print_stats.py index 668cd7d0c860..b0f8d38ea6cd 100644 --- a/klippy/extras/print_stats.py +++ b/klippy/extras/print_stats.py @@ -6,7 +6,7 @@ class PrintStats: def __init__(self, config): - printer = config.get_printer() + printer = self.printer = config.get_printer() self.gcode_move = printer.load_object(config, 'gcode_move') self.reactor = printer.get_reactor() self.reset() @@ -37,6 +37,7 @@ def note_start(self): gc_status = self.gcode_move.get_status(curtime) self.last_epos = gc_status['position'].e self.state = "printing" + self.printer.send_event("print_stats:start_printing") self.error_message = "" def note_pause(self): if self.last_pause_time is None: @@ -46,12 +47,15 @@ def note_pause(self): self._update_filament_usage(curtime) if self.state != "error": self.state = "paused" + self.printer.send_event("print_stats:paused_printing") def note_complete(self): self._note_finish("complete") + self.printer.send_event("print_stats:complete_printing") def note_error(self, message): self._note_finish("error", message) def note_cancel(self): self._note_finish("cancelled") + self.printer.send_event("print_stats:cancelled_printing") def _note_finish(self, state, error_message = ""): if self.print_start_time is None: return diff --git a/test/klippy/firmware_retraction_with_VSDCard.cfg b/test/klippy/firmware_retraction_with_VSDCard.cfg new file mode 100644 index 000000000000..6bd527fa5c57 --- /dev/null +++ b/test/klippy/firmware_retraction_with_VSDCard.cfg @@ -0,0 +1,120 @@ +# Config for firmware_retraction testing with Virtual SD Card module enabled +[virtual_sdcard] +path: test/klippy/sdcard_loop + +[display_status] + +[respond] +default_type: command + +[firmware_retraction] +retract_length: 3.0 +retract_speed: 45.0 +unretract_extra_length: 0.0 +unretract_speed: 45.0 +z_hop_height: 0.4 + +[stepper_x] +step_pin: PF0 +dir_pin: PF1 +enable_pin: !PD7 +microsteps: 16 +rotation_distance: 40 +endstop_pin: ^PE5 +position_endstop: 0 +position_max: 224 +homing_speed: 50 + +[stepper_y] +step_pin: PF6 +dir_pin: !PF7 +enable_pin: !PF2 +microsteps: 16 +rotation_distance: 40 +endstop_pin: ^PJ1 +position_endstop: 0 +position_max: 220 +homing_speed: 50 + +[stepper_z] +step_pin: PL3 +dir_pin: PL1 +enable_pin: !PK0 +microsteps: 16 +rotation_distance: 8 +endstop_pin: ^PD3 +position_endstop: 0 +position_max: 200 + +[extruder] +step_pin: PA4 +dir_pin: PA6 +enable_pin: !PA2 +microsteps: 16 +rotation_distance: 33.5 +nozzle_diameter: 0.400 +filament_diameter: 1.750 +heater_pin: PB4 +sensor_type: EPCOS 100K B57560G104F +sensor_pin: PK5 +control: pid +pid_Kp: 22.2 +pid_Ki: 1.08 +pid_Kd: 114 +min_temp: 0 +max_temp: 210 +min_extrude_temp: 0 + +[heater_bed] +heater_pin: PH5 +sensor_type: EPCOS 100K B57560G104F +sensor_pin: PK6 +control: watermark +min_temp: 0 +max_temp: 110 + +[mcu] +serial: /dev/ttyACM0 + +[printer] +kinematics: cartesian +max_velocity: 300 +max_accel: 3000 +max_z_velocity: 5 +max_z_accel: 100 + +[gcode_arcs] +resolution: 0.1 + +[homing_override] +gcode: + G28 X0 + G28 Y0 + G1 X112 Y110 + G28 Z0 + G1 Z8 + +[gcode_macro VERIFY_AXIS_POSITION] +gcode: + {% set axis_name = params.AXIS %} + {% set expected_position = params.EXPECTED|float %} + {% set axisstate_verbose = False %} + + {% set current_axis_position = printer.gcode_move.position[axis_name] %} + + {% if current_axis_position != expected_position %} + {action_raise_error("Wrong axis position for %s. Expected %f, got %f" + % (axis_name, expected_position, current_axis_position))} + {% else %} + {% if axisstate_verbose %} + M118 Axis {axis_name} OK! + {% endif %} + {% endif %} + +[gcode_macro CHECK_RECTRACTION_CLEARED] +gcode: + {% set retract_state = False %} + {% set retract_state = printer.firmware_retraction.retract_state %} + {% if retract_state == True %} + {action_raise_error("Retraction should be cleared!")} + {% endif%} diff --git a/test/klippy/firmware_retraction_with_VSDCard.test b/test/klippy/firmware_retraction_with_VSDCard.test new file mode 100644 index 000000000000..45100cde49a8 --- /dev/null +++ b/test/klippy/firmware_retraction_with_VSDCard.test @@ -0,0 +1,212 @@ +# Tests Firmware retraction G10 and G11 with Virtual SD Card ENABLED + +DICTIONARY atmega2560.dict +CONFIG firmware_retraction_with_VSDCard.cfg + +CLEAR_RETRACTION +G90 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=0.0 + +################################### CHECK DISABLED RETRACTION WITH ZHOP DISABLED +G28 +SET_RETRACTION RETRACT_LENGTH=0.0 UNRETRACT_EXTRA_LENGTH=0.0 Z_HOP_HEIGHT=0.0 +G10 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=0.0 +G11 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=0.0 + +############# CHECK RETRACTION CLEAR ON STEPPER DISABLE EVENT WITH ZHOP DISABLED +SET_RETRACTION RETRACT_LENGTH=2.5 UNRETRACT_EXTRA_LENGTH=1.0 Z_HOP_HEIGHT=0.0 +G10 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-2.5 +M84 +CHECK_RECTRACTION_CLEARED + +###################### CHECK RETRACTION CLEAR ON HOMING EVENT WITH ZHOP DISABLED +G28 +SET_RETRACTION RETRACT_LENGTH=2.5 UNRETRACT_EXTRA_LENGTH=1.0 Z_HOP_HEIGHT=0.0 +G10 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-5.0 +G28 +CHECK_RECTRACTION_CLEARED + +############## CHECK RETRACTION CLEAR ON STEPPER DISABLE EVENT WITH ZHOP ENABLED +SET_RETRACTION RETRACT_LENGTH=2.5 UNRETRACT_EXTRA_LENGTH=1.0 Z_HOP_HEIGHT=2.0 +G10 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-7.5 +M84 +CHECK_RECTRACTION_CLEARED + +####################### CHECK RETRACTION CLEAR ON HOMING EVENT WITH ZHOP ENABLED +G28 +SET_RETRACTION RETRACT_LENGTH=2.5 UNRETRACT_EXTRA_LENGTH=1.0 Z_HOP_HEIGHT=2.0 +G10 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-10.0 +G28 +CHECK_RECTRACTION_CLEARED + +############################################## SET PRINTER UP FOR MOVEMENT TESTS +SET_RETRACTION RETRACT_LENGTH=2.5 UNRETRACT_EXTRA_LENGTH=1.0 Z_HOP_HEIGHT=0.0 +G1 X100.0 Y100.0 Z20.0 +G91 +G1 E10.0 +G90 + +####################################################### TESTING FW WITHOUT Z_HOP + +# Unretract in unretract state +G11 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=0.0 + +# Retract in unretract state +G10 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-2.5 + +# Repeat Retract +G10 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-2.5 + +# Unretract in retract state +G11 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=1.0 + +# Second round: Retract in unretract state +G10 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-1.5 + +# Second round: Unretract in retract state +G11 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=2.0 +# Compensate for second round +G91 +G1 E-1.0 +G90 + +###################################################### TESTING FW Z_HOP STANDARD + +SET_RETRACTION UNRETRACT_EXTRA_LENGTH=0.0 +SET_RETRACTION Z_HOP_HEIGHT=2.0 + +# Retract with Standard zhop +G10 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=22.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-1.5 + +# Repeat retract with Standard zhop +G10 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=22.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-1.5 + +# Unretract with Standard zhop in retract state +G11 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=20.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=1.0 + +# Second round: Retract with Standard zhop +G10 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=22.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-1.5 + +# Second round: Unretract with Standard zhop in retract state +G11 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=20.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=1.0 + +#################################### TESTING CHANGE Z_HOP_HEIGHT WHILE RETRACTED + +G10 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=22.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-1.5 +SET_RETRACTION Z_HOP_HEIGHT=3.0 +G11 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=20.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=1.0 + +################ TESTING MOVE Z WHILE RETRACTED WITH SWITCH TO RELATIVE AND BACK + +SET_RETRACTION Z_HOP_HEIGHT=2.0 +G10 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=22.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-1.5 + +# Switch to relative mode while retracted +G91 +G1 Z-10.0 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=12.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-1.5 + +# Switch back to absolute mode +G90 +G11 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=10.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=1.0 + +################################################ TESTING MOVE XY WHILE RETRACTED + +# Test XY Move in Standard +G1 Z20.0 +G10 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=22.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-1.5 + +G1 X90.0 Y90.0 +VERIFY_AXIS_POSITION AXIS=x EXPECTED=90.0 +VERIFY_AXIS_POSITION AXIS=y EXPECTED=90.0 + +G11 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=20.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=1.0 + +G1 X100.0 Y100.0 +VERIFY_AXIS_POSITION AXIS=x EXPECTED=100.0 +VERIFY_AXIS_POSITION AXIS=y EXPECTED=100.0 + +####################################################### TESTING CLEAR RETRACTION +G1 X90.0 Y90.0 Z20.0 + +# Test CLEAR_RETRACTION while unretracted +CLEAR_RETRACTION + +# Test CLEAR_RETRACTION while retracted +G10 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=22.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-1.5 +CLEAR_RETRACTION +VERIFY_AXIS_POSITION AXIS=z EXPECTED=22.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-1.5 + +# Unretract after clearing retraction +G11 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=22.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-1.5 + +# Test CLEAR_RETRACTION while retracted, 2nd go +SET_RETRACTION Z_HOP_HEIGHT=2.0 +G10 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=24.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-4.5 +CLEAR_RETRACTION +VERIFY_AXIS_POSITION AXIS=z EXPECTED=24.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-4.5 + +# Unretract after clearing retraction and retracting +G10 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=24.4 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-7.5 +G11 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=24.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-4.5 +G10 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=24.4 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-7.5 + +# Test first move with z coordinate after retraction was cleared +G1 X100.0 Y100.0 Z28.0 +VERIFY_AXIS_POSITION AXIS=x EXPECTED=100.0 +VERIFY_AXIS_POSITION AXIS=y EXPECTED=100.0 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=28.4 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-7.5 +G11 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=28.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-4.5 +M118 Test successful! diff --git a/test/klippy/firmware_retraction_without_VSDCard.cfg b/test/klippy/firmware_retraction_without_VSDCard.cfg new file mode 100644 index 000000000000..7364eec9b768 --- /dev/null +++ b/test/klippy/firmware_retraction_without_VSDCard.cfg @@ -0,0 +1,117 @@ +# Config for firmware_retraction testing with Virtual SD Card module disabled +[display_status] + +[respond] +default_type: command + +[firmware_retraction] +retract_length: 3.0 +retract_speed: 45.0 +unretract_extra_length: 0.0 +unretract_speed: 45.0 +z_hop_height: 0.4 + +[stepper_x] +step_pin: PF0 +dir_pin: PF1 +enable_pin: !PD7 +microsteps: 16 +rotation_distance: 40 +endstop_pin: ^PE5 +position_endstop: 0 +position_max: 224 +homing_speed: 50 + +[stepper_y] +step_pin: PF6 +dir_pin: !PF7 +enable_pin: !PF2 +microsteps: 16 +rotation_distance: 40 +endstop_pin: ^PJ1 +position_endstop: 0 +position_max: 220 +homing_speed: 50 + +[stepper_z] +step_pin: PL3 +dir_pin: PL1 +enable_pin: !PK0 +microsteps: 16 +rotation_distance: 8 +endstop_pin: ^PD3 +position_endstop: 0 +position_max: 200 + +[extruder] +step_pin: PA4 +dir_pin: PA6 +enable_pin: !PA2 +microsteps: 16 +rotation_distance: 33.5 +nozzle_diameter: 0.400 +filament_diameter: 1.750 +heater_pin: PB4 +sensor_type: EPCOS 100K B57560G104F +sensor_pin: PK5 +control: pid +pid_Kp: 22.2 +pid_Ki: 1.08 +pid_Kd: 114 +min_temp: 0 +max_temp: 210 +min_extrude_temp: 0 + +[heater_bed] +heater_pin: PH5 +sensor_type: EPCOS 100K B57560G104F +sensor_pin: PK6 +control: watermark +min_temp: 0 +max_temp: 110 + +[mcu] +serial: /dev/ttyACM0 + +[printer] +kinematics: cartesian +max_velocity: 300 +max_accel: 3000 +max_z_velocity: 5 +max_z_accel: 100 + +[gcode_arcs] +resolution: 0.1 + +[homing_override] +gcode: + G28 X0 + G28 Y0 + G1 X112 Y110 + G28 Z0 + G1 X112 Y110 Z8 + +[gcode_macro VERIFY_AXIS_POSITION] +gcode: + {% set axis_name = params.AXIS %} + {% set expected_position = params.EXPECTED|float %} + {% set axisstate_verbose = False %} + + {% set current_axis_position = printer.gcode_move.position[axis_name] %} + + {% if current_axis_position != expected_position %} + {action_raise_error("Wrong axis position for %s. Expected %f, got %f" + % (axis_name, expected_position, current_axis_position))} + {% else %} + {% if axisstate_verbose %} + M118 Axis {axis_name} OK! + {% endif %} + {% endif %} + +[gcode_macro CHECK_RECTRACTION_CLEARED] +gcode: + {% set retract_state = False %} + {% set retract_state = printer.firmware_retraction.retract_state %} + {% if retract_state == True %} + {action_raise_error("Retraction should be cleared!")} + {% endif%} diff --git a/test/klippy/firmware_retraction_without_VSDCard.test b/test/klippy/firmware_retraction_without_VSDCard.test new file mode 100644 index 000000000000..bea14403200e --- /dev/null +++ b/test/klippy/firmware_retraction_without_VSDCard.test @@ -0,0 +1,212 @@ +# Tests Firmware retraction G10 and G11 with Virtual SD Card DISABLED + +DICTIONARY atmega2560.dict +CONFIG firmware_retraction_without_VSDCard.cfg + +CLEAR_RETRACTION +G90 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=0.0 + +################################### CHECK DISABLED RETRACTION WITH ZHOP DISABLED +G28 +SET_RETRACTION RETRACT_LENGTH=0.0 UNRETRACT_EXTRA_LENGTH=0.0 Z_HOP_HEIGHT=0.0 +G10 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=0.0 +G11 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=0.0 + +############# CHECK RETRACTION CLEAR ON STEPPER DISABLE EVENT WITH ZHOP DISABLED +SET_RETRACTION RETRACT_LENGTH=2.5 UNRETRACT_EXTRA_LENGTH=1.0 Z_HOP_HEIGHT=0.0 +G10 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-2.5 +M84 +CHECK_RECTRACTION_CLEARED + +###################### CHECK RETRACTION CLEAR ON HOMING EVENT WITH ZHOP DISABLED +G28 +SET_RETRACTION RETRACT_LENGTH=2.5 UNRETRACT_EXTRA_LENGTH=1.0 Z_HOP_HEIGHT=0.0 +G10 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-5.0 +G28 +CHECK_RECTRACTION_CLEARED + +############## CHECK RETRACTION CLEAR ON STEPPER DISABLE EVENT WITH ZHOP ENABLED +SET_RETRACTION RETRACT_LENGTH=2.5 UNRETRACT_EXTRA_LENGTH=1.0 Z_HOP_HEIGHT=2.0 +G10 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-7.5 +M84 +CHECK_RECTRACTION_CLEARED + +####################### CHECK RETRACTION CLEAR ON HOMING EVENT WITH ZHOP ENABLED +G28 +SET_RETRACTION RETRACT_LENGTH=2.5 UNRETRACT_EXTRA_LENGTH=1.0 Z_HOP_HEIGHT=2.0 +G10 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-10.0 +G28 +CHECK_RECTRACTION_CLEARED + +############################################## SET PRINTER UP FOR MOVEMENT TESTS +SET_RETRACTION RETRACT_LENGTH=2.5 UNRETRACT_EXTRA_LENGTH=1.0 Z_HOP_HEIGHT=0.0 +G1 X100.0 Y100.0 Z20.0 +G91 +G1 E10.0 +G90 + +####################################################### TESTING FW WITHOUT Z_HOP + +# Unretract in unretract state +G11 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=0.0 + +# Retract in unretract state +G10 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-2.5 + +# Repeat Retract +G10 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-2.5 + +# Unretract in retract state +G11 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=1.0 + +# Second round: Retract in unretract state +G10 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-1.5 + +# Second round: Unretract in retract state +G11 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=2.0 +# Compensate for second round +G91 +G1 E-1.0 +G90 + +###################################################### TESTING FW Z_HOP STANDARD + +SET_RETRACTION UNRETRACT_EXTRA_LENGTH=0.0 +SET_RETRACTION Z_HOP_HEIGHT=2.0 + +# Retract with Standard zhop +G10 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=22.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-1.5 + +# Repeat retract with Standard zhop +G10 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=22.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-1.5 + +# Unretract with Standard zhop in retract state +G11 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=20.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=1.0 + +# Second round: Retract with Standard zhop +G10 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=22.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-1.5 + +# Second round: Unretract with Standard zhop in retract state +G11 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=20.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=1.0 + +#################################### TESTING CHANGE Z_HOP_HEIGHT WHILE RETRACTED + +G10 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=22.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-1.5 +SET_RETRACTION Z_HOP_HEIGHT=3.0 +G11 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=20.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=1.0 + +################ TESTING MOVE Z WHILE RETRACTED WITH SWITCH TO RELATIVE AND BACK + +SET_RETRACTION Z_HOP_HEIGHT=2.0 +G10 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=22.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-1.5 + +# Switch to relative mode while retracted +G91 +G1 Z-10.0 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=12.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-1.5 + +# Switch back to absolute mode +G90 +G11 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=10.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=1.0 + +################################################ TESTING MOVE XY WHILE RETRACTED + +# Test XY Move in Standard +G1 Z20.0 +G10 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=22.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-1.5 + +G1 X90.0 Y90.0 +VERIFY_AXIS_POSITION AXIS=x EXPECTED=90.0 +VERIFY_AXIS_POSITION AXIS=y EXPECTED=90.0 + +G11 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=20.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=1.0 + +G1 X100.0 Y100.0 +VERIFY_AXIS_POSITION AXIS=x EXPECTED=100.0 +VERIFY_AXIS_POSITION AXIS=y EXPECTED=100.0 + +####################################################### TESTING CLEAR RETRACTION +G1 X90.0 Y90.0 Z20.0 + +# Test CLEAR_RETRACTION while unretracted +CLEAR_RETRACTION + +# Test CLEAR_RETRACTION while retracted +G10 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=22.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-1.5 +CLEAR_RETRACTION +VERIFY_AXIS_POSITION AXIS=z EXPECTED=22.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-1.5 + +# Unretract after clearing retraction +G11 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=22.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-1.5 + +# Test CLEAR_RETRACTION while retracted, 2nd go +SET_RETRACTION Z_HOP_HEIGHT=2.0 +G10 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=24.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-4.5 +CLEAR_RETRACTION +VERIFY_AXIS_POSITION AXIS=z EXPECTED=24.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-4.5 + +# Unretract after clearing retraction and retracting +G10 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=24.4 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-7.5 +G11 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=24.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-4.5 +G10 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=24.4 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-7.5 + +# Test first move with z coordinate after retraction was cleared +G1 X100.0 Y100.0 Z28.0 +VERIFY_AXIS_POSITION AXIS=x EXPECTED=100.0 +VERIFY_AXIS_POSITION AXIS=y EXPECTED=100.0 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=28.4 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-7.5 +G11 +VERIFY_AXIS_POSITION AXIS=z EXPECTED=28.0 +VERIFY_AXIS_POSITION AXIS=e EXPECTED=-4.5 +M118 Test successful!