Speed factor is reseted when switching from a toolhead to other

Basic Information:

Printer Model: Custom
MCU / Printerboard: Manta M8P
Host / SBC CB1
klippy.log maybe i upload it tomorrow

Fill out above information and in all cases attach your klippy.log file (use zip to compress it, if too big). Pasting your printer.cfg is not needed
Be sure to check our “Knowledge Base” Category first. Most relevant items, e.g. error messages, are covered there

Describe your issue:


I have noticed that when setting an speed factor, for example of 80% in a print, when doing a toolchange, this speed factor gets reseted to 100%.

This is the code i use right now for the toolchange:

[gcode_macro T0]
# Activate main extruder (T0)
gcode:
    # These params are only added if post_process script is executed propperly   EXAMPLE OF USAGE: T0 P1 X500 Y403 Z.2
    {% set printing = params.P|default(0)|int %}
    {% set x_after_toolchange = params.X|default(-1.0)|float %}
    {% set y_after_toolchange = params.Y|default(-1.0)|float %}
    {% set z_after_toolchange = params.Z|default(-1.0)|float %}
    
    {% set print_variables = printer["gcode_macro _PRINT_VARIABLE"] %}
    
    SET_GCODE_OFFSET X=0 Y=0 # Always reset gcode_offsets
    # Proceed with the toolchange of the extruders according to changing mode and if params are defined
    # If printer isn't printing (controlled by param P), do not park and nothing, if it is parking, ensure at least to park, or if params are defined and everything, proceed with the toolchange with the needed mode
    {% if printing == 1 %}
        {% if printer.dual_carriage.carriage_1 == "PRIMARY" and print_variables.enable_dual_toolchange == 1 %} # Only perform _dual_toolchange actual extruder is T1 and if specified by variable
            _DUAL_TOOLCHANGE T=0 X_AFTER_TOOLCHANGE={x_after_toolchange} Y_AFTER_TOOLCHANGE={y_after_toolchange} Z_AFTER_TOOLCHANGE={z_after_toolchange} # Proceed with a syncronized toolchange
        {% else %} # In any other case, do a normal toolchange
            _NORMAL_TOOLCHANGE T=0 X_AFTER_TOOLCHANGE={x_after_toolchange} Y_AFTER_TOOLCHANGE={y_after_toolchange} Z_AFTER_TOOLCHANGE={z_after_toolchange} # Proceed with a no syncronized toolchange
        {% endif %}
    # If printing is not defined, but it is detected that printer is printing, means that gcode hasn't been post processed propperly, or started directly some print, so at least, park extruder in order to avoid colisions
    {% elif printer.print_stats.state == "printing" %} 
        Park_extruder1
    {% endif %}

    # If P isn't defined, and printer isn't printing there is no need to park extruders (it is desired to do in this way so user, when selecting T0 or T1 in extrusion panel, only activates the extruder) 


    # Ensure to activate extruder propperly
    ACTIVATE_EXTRUDER EXTRUDER=extruder  # Activates extruder 0
    SET_DUAL_CARRIAGE CARRIAGE=0 MODE=PRIMARY  # Selects extruder carriage 0
    SYNC_EXTRUDER_MOTION EXTRUDER=extruder1 MOTION_QUEUE="" # Ensures that extruder1 stops following extruder0 movements
    SYNC_EXTRUDER_MOTION EXTRUDER=extruder MOTION_QUEUE="extruder" # Ensures that extruder stepper follows movements commanded to extruder

    # Added for handling M106 fans
    SET_GCODE_VARIABLE MACRO=FAN_VARIABLE VARIABLE=active_fan VALUE=0  # Controls extruder 0 fan


[gcode_macro T1]
# Activate the second extruder (T1)
gcode:
    # These params are only added if post_process script is executed propperly
    {% set printing = params.P|default(0)|int %}
    {% set x_after_toolchange = params.X|default(-1.0)|float %}
    {% set y_after_toolchange = params.Y|default(-1.0)|float %}
    {% set z_after_toolchange = params.Z|default(-1.0)|float %}
    

    {% set offset_t1_x = printer.save_variables.variables.offset_t1_x %}
    {% set offset_t1_y = printer.save_variables.variables.offset_t1_y %}
    {% set print_variables = printer["gcode_macro _PRINT_VARIABLE"] %}
    
    SET_GCODE_OFFSET X=0 Y=0 # Always reset gcode_offsets
    # Proceed with the toolchange of the extruders according to changing mode and if params are defined
    # If printer isn't printing (controlled by param P), do not park and nothing, if it is parking, ensure at least to park, or if params are defined and everything, proceed with the toolchange with the needed mode
    {% if printing == 1 %}
        {% if printer.dual_carriage.carriage_0 == "PRIMARY" and print_variables.enable_dual_toolchange == 1 %} # Only perform _dual_toolchange actual extruder is T1 and if specified by variable
            _DUAL_TOOLCHANGE T=1 X_AFTER_TOOLCHANGE={x_after_toolchange} Y_AFTER_TOOLCHANGE={y_after_toolchange} Z_AFTER_TOOLCHANGE={z_after_toolchange} # Proceed with a syncronized toolchange
        {% else %} # In any other case, do a normal toolchange
            _NORMAL_TOOLCHANGE T=1 X_AFTER_TOOLCHANGE={x_after_toolchange} Y_AFTER_TOOLCHANGE={y_after_toolchange} Z_AFTER_TOOLCHANGE={z_after_toolchange} # Proceed with a no syncronized toolchange
        {% endif %}
    # If printing is not defined, but it is detected that printer is printing, means that gcode hasn't been post processed propperly, or started directly some print, so at least, park extruder in order to avoid colisions
    {% elif printer.print_stats.state == "printing" %} 
        Park_extruder
    {% endif %}

    # If P isn't defined, and printer isn't printing there is no need to park extruders (it is desired to do in this way so user, when selecting T0 or T1 in extrusion panel, only activates the extruder) 
    
    
    SET_GCODE_OFFSET X={offset_t1_x} Y={offset_t1_y}   # Offsets are set after the toolhead has been executed
    
    # Ensure to activate extruder1 propperly 
    ACTIVATE_EXTRUDER EXTRUDER=extruder1   # Activates extruder 1
    SET_DUAL_CARRIAGE CARRIAGE=1 MODE=PRIMARY  # Selects extruder carriage 1
    SYNC_EXTRUDER_MOTION EXTRUDER=extruder MOTION_QUEUE="" # Ensures that extruder stops following extruder1 movements
    SYNC_EXTRUDER_MOTION EXTRUDER=extruder1 MOTION_QUEUE="extruder1" # Ensures that extruder1 stepper follows movements commanded to extruder1

    # Added for handling M106 fans
    SET_GCODE_VARIABLE MACRO=FAN_VARIABLE VARIABLE=active_fan VALUE=1  # Controls extruder 1 fan

[gcode_macro Park_extruder]
# Parks the active extruder (used in T0 and T1)
gcode:
    {% set variables = printer["gcode_macro _PRINT_VARIABLE"] %}
    {% set toolchange_speed = params.SPEED|default(variables.toolchange_speed|default(1000))|float %}
    {% set toolchange_accel = params.ACCEL|default(variables.toolchange_accel|default(4000))|float %}
    {% set current_speed = printer.toolhead.max_velocity %}
    {% set current_accel = printer.toolhead.max_accel %}
    {% set park_pos = printer.configfile.settings.stepper_x.position_endstop %}

    SAVE_GCODE_STATE NAME=park0  # Saves the current G-code state
    {% if "x" in printer.toolhead.homed_axes %}
        SET_GCODE_OFFSET X=0 Y=0
        SET_DUAL_CARRIAGE CARRIAGE=0 MODE=PRIMARY # Selects extruder carriage 0
        SET_VELOCITY_LIMIT VELOCITY={toolchange_speed} ACCEL={toolchange_accel}
        G90
        G1 X{park_pos} F{(toolchange_speed * 60)}
        SET_VELOCITY_LIMIT VELOCITY={current_speed} ACCEL={current_accel}
    {% else %}
        RESPOND MSG="Es necesario hacer home para aparcar el cabezal"
    {% endif %}
    RESTORE_GCODE_STATE NAME=park0  # Restores the G-code state

[gcode_macro Park_extruder1]
# Parks the active extruder (used in T0 and T1)
gcode:
    {% set variables = printer["gcode_macro _PRINT_VARIABLE"] %}
    {% set toolchange_speed = params.SPEED|default(variables.toolchange_speed|default(1000))|float %}
    {% set toolchange_accel = params.ACCEL|default(variables.toolchange_accel|default(4000))|float %}
    {% set current_speed = printer.toolhead.max_velocity %}
    {% set current_accel = printer.toolhead.max_accel %}
    {% set park_pos = printer.configfile.settings.dual_carriage.position_endstop %}
    
    SAVE_GCODE_STATE NAME=park1  # Saves the current G-code state
    
    # parks the toolhead if printer is homed
    {% if "x" in printer.toolhead.homed_axes %}
        SET_GCODE_OFFSET X=0 Y=0
        SET_DUAL_CARRIAGE CARRIAGE=1 MODE=PRIMARY # Selects extruder carriage 1
        SET_VELOCITY_LIMIT VELOCITY={toolchange_speed} ACCEL={toolchange_accel}
        G90
        G1 X{park_pos} F{(toolchange_speed * 60)}
        SET_VELOCITY_LIMIT VELOCITY={current_speed} ACCEL={current_accel}
    {% else %}
        RESPOND MSG="Es necesario hacer home para aparcar el cabezal"
    {% endif %}
    RESTORE_GCODE_STATE NAME=park1  # Restores the G-code state  
[gcode_macro _NORMAL_TOOLCHANGE]
# If params are defined, it will zhop, park extruder, go to the next position, and go to z position. Else, it will only park extruder, no z hop, no travel to piece
gcode:
    # Params
    {% set new_t = params.T|default(0)|int %}
    {% set x_after_toolchange = (params.X_AFTER_TOOLCHANGE|float if 0 <= params.X_AFTER_TOOLCHANGE|default(-1.0)|float <= 1120 else -1.0) %}
    {% set y_after_toolchange = (params.Y_AFTER_TOOLCHANGE|float if 0 <= params.Y_AFTER_TOOLCHANGE|default(-1.0)|float <= 720 else -1.0) %}
    {% set z_after_toolchange = (params.Z_AFTER_TOOLCHANGE|float if 0 <= params.Z_AFTER_TOOLCHANGE|default(-1.0)|float <= 620 else -1.0) %}
    
    # Defines
    {% set z_hop_distance = 0.5 %}
    {% set z_hop_speed = 30 %}

    # Print variables:
    {% set variables = printer["gcode_macro _PRINT_VARIABLE"] %}
    {% set z_max = printer.configfile.config["stepper_z"]["position_max"]|default(625)|float %}
    {% set print_height = variables.print_height|float if variables.print_height is defined else 0.0 %}
    {% set print_sequence = variables.print_sequence if variables.print_sequence is defined else "by layer" %}
    {% set current_z = printer.toolhead.position.z|default(z_max)|float %}
    {% set enable_toolchange_purge = variables.enable_toolchange_purge|default(0)|int %}
    {% set toolchange_purge_distance = variables.toolchange_purge_distance|default(10)|int %}
    {% set toolchange_purge_velocity = variables.toolchange_purge_velocity|default(1500)|int %}
    {% set enable_toolchange_clean = variables.enable_toolchange_clean|default(0)|int %}
    {% set toolchange_speed = params.SPEED|default(variables.toolchange_speed|default(1000))|float %}
    {% set toolchange_accel = params.ACCEL|default(variables.toolchange_accel|default(4000))|float %}
    {% set current_speed = printer.toolhead.max_velocity %}
    {% set current_accel = printer.toolhead.max_accel %}

    # Offsets:
    {% set offset_t1_x = printer.save_variables.variables.offset_t1_x %}
    {% set offset_t1_y = printer.save_variables.variables.offset_t1_y %}

    # Do a z hop only if z_after_toolchange is defined:
    {% if z_after_toolchange != -0.1 %}
        # z_hop_distance if mode is by layer, or print_height if it is by object, only if current_z is lower than max_position less z_hop_distance (ensure to not exceed machine limits)
        {% if current_z < (z_max - z_hop_distance) %}
            {% if print_sequence == "by layer" %} # Z hop is only done when print sequence is by layer
                G91 # Relative movement in order to do z hop
                G1 Z{z_hop_distance} F{z_hop_speed * 60}
                G90
            {% elif print_sequence == "by object" and (print_height > z_hop_distance) and (current_z < print_height) and (print_height <= z_max ) %}
                G90
                G1 Z{print_height} F{z_hop_speed * 60}
            {% endif %}
        {% endif %}
    {% endif %}

    # Proceed to park the oposite extruder to the new extruder:
    {% if new_t == 0 %}
        Park_extruder1
    {% elif new_t == 1 %}
        Park_extruder
    {% endif %}

    # Turn off actual extruder layer fan
    M106 S0

    # If enabled, purge the desired extruder prior to exit:
    {% if enable_toolchange_purge == 1 %}
        {% if new_t == 0 %}
            Park_extruder
            PURGE_IN_BOX_T0 DISTANCE={toolchange_purge_distance} SPEED={toolchange_purge_velocity} 
        {% elif new_t == 1 %}
            Park_extruder1
            PURGE_IN_BOX_T1 DISTANCE={toolchange_purge_distance} SPEED={toolchange_purge_velocity} 
        {% endif %}
    {% endif %}

    # If enabled, clean the desired extruder priot to exit:
    {% if enable_toolchange_clean == 1 %}
        {% if new_t == 0 %}
            SET_DUAL_CARRIAGE CARRIAGE=0 MODE=PRIMARY
            CLEAN_NOOZLE_T0
        {% elif new_t == 1 %}
            SET_DUAL_CARRIAGE CARRIAGE=1 MODE=PRIMARY
            CLEAN_NOOZLE_T1
        {% endif %}
    {% endif %}

    # Proceed to activate the active toolhead, and go to xy position, then go down in z, also, apply the offset to the movement
    # Activate toolhead
    {% if new_t == 0 %}
        SET_DUAL_CARRIAGE CARRIAGE=0 MODE=PRIMARY 
    {% elif new_t == 1 %}
        SET_DUAL_CARRIAGE CARRIAGE=1 MODE=PRIMARY  
        SET_GCODE_OFFSET X={offset_t1_x} Y={offset_t1_y}
    {% endif %}

    # Ensure to travel only if these params are defined
    {% if (x_after_toolchange != -0.1 and y_after_toolchange != -0.1) and ((x_after_toolchange >= 0 and x_after_toolchange <= 1120) and (y_after_toolchange >= 0  and y_after_toolchange <=720)) %}
        SET_VELOCITY_LIMIT VELOCITY={toolchange_speed} ACCEL={toolchange_accel}
        G90
        G1 X{x_after_toolchange} Y{y_after_toolchange} F{(toolchange_speed * 60)}
        SET_VELOCITY_LIMIT VELOCITY={current_speed} ACCEL={current_accel}
    {% endif %}

    # And go to z coordinate if defined:
    {% if z_after_toolchange != -0.1 and z_after_toolchange >= 0 and z_after_toolchange <= 620 %}
        G90
        G1 Z{z_after_toolchange} F{z_hop_speed * 60}
    {% endif %}

    # Always ensure to be in absolute coordinates 
    G90

I have also seen this issue here:

Hi, did you find a solution? I’m having the same problem; the print speed resets every time I change a color.
I thought about creating a macro that captures the current speed and then uses it for the next color change.

Give the OP a bit more time, yes?

This seems a bit vague overall.
Usually, the only approach is to store away the current velocity in a macro variable and reestablish it with the SET_VELOCITY_LIMIT command.

This seems not a valid approach or at best a limited approach since slicer’s like Orca use it constantly to modify limits depending on the feature.

1 Like

From my experience, this happens because the slicer runs a M220 S100 before performing each color swap. This isn’t a Klipper bug, because it’s doing exactly what the slicer tells it to do.

Hi, I think it can’t be done using SET_VELOCITY_LIMIT, because it is more related to the mainsail speed factor, one way would be to save the “speed_factor”, and then use it in the next filament change with using M220 S{xx}.

Oh, i hadn’t seen the gcode! I have a postprocessor of gcode for my machine, so i might just delete that m220 by using the postprocessor, or i might just save it in a variable using the toolchange macro. Thanks yall!

You are right, @3dcoded and @Maguz1024

07:45 printer.gcode_move.speed_factor : 0.64
07:45 SEARCH_VARS S=speed_factor
07:45 M220 S64
07:44 printer.gcode_move.speed_factor : 1.0

What you could do, is saving printer.gcode_move.speed_factor into a variable and restore it after toolchange.

1 Like