######################################### ###### FLOW MULTIPLIER CALIBRATION ###### ######################################### # Written by Frix_x#0161 # # @version: 1.6 # CHANGELOG: # v1.6: directly added the [gcode_arcs] definition in this file to simplify installation # v1.5: moved the install notes into a proper markdown file in: docs > features > flow_calibration.md # v1.4: fix issue when extrude_factor is != 1 # v1.3: fix the logging # v1.2: fix for those using absolute extrusion that leads to an error # v1.1: added part cooling fan control and some speed optimizations # v1.0: first flow calibration macro # ------------------------------------------------------------------------------------------------------------------------- # If you want to use it into your own config, please install it as a standalone macro as described in the # installation section of this file: docs > features > flow_calibration.md # ------------------------------------------------------------------------------------------------------------------------- ### What is it ? ### # The main reason for this set of macros is to get a filament and slicer agnostic way to calibrate the flow extrusion multiplier. # The goal is to make it easy to set, share and use it. # This macro is parametric and most of the values can be adjusted with their respective input parameters. # It can be called without any parameters - in which case the default values would be used - or with any combination of parameters as desired. # Feel free to ping me on Discord (Frix_x#0161) if you need help or have any comments to improve it :) # =========================================================================================================== # DO NOT MODIFY THOSE VARIABLES (they are used internaly by the flow calibration macro) [gcode_macro _FLOW_CALIB_VARIABLES] variable_last_shell_thickness: 0.0 variable_last_evalue: 0.0 gcode: [gcode_arcs] resolution: 0.1 [gcode_macro FLOW_MULTIPLIER_CALIBRATION] description: Print a small tower to calibrate the extrusion flow multiplier by measuring the shell gcode: # # PARAMETERS # {% set do_raft = params.DO_RAFT|default(1)|int %} # whether to print a raft or not {% set do_retract = params.DO_RECTRACT|default(0)|int %} # whether to enable retract/unrectract on travel moves {% set print_size = params.PRINT_SIZE|default(40)|int %} # size of the printed object on the build plate {% set print_height = params.HEIGHT|default(15)|int %} # height of the printed object {% set corner_radius = params.CORNER_RADIUS|default(8)|int %} # radius of the corners to smooth the shell and toolpath {% set number_of_perimeters = params.PERIMETERS|default(2)|int %} # number of perimeters to print the shell {% set fan_speed = params.FAN_SPEED|default(20)|int %} # part cooling fan speed in percent (0-100) {% set e_multiplier = params.EXTRUSION_MULTIPLIER|default(1.00)|float %} # extrusion multiplier for the shell {% set filament_diameter = params.FILAMENT_DIAMETER|default(1.75)|float %} # filament diameter {% set extrusion_width = params.EXTRUSION_WIDTH|default(0.4)|float %} # extrusion width goal for one line {% set layer_height = params.LAYER_HEIGHT|default(0.2)|float %} # layer height for the print {% set retract_length = params.RETRACT_LENGTH|default(0.5)|float %} # how much to retract when traveling between print moves {% set initial_purge = params.PURGE_MM|default(1)|int %} # mm of filament to purge before printing. set to 0 to disable {% set z_hop_height = 2 * layer_height %} {% set feedrate_print = params.CONTROL_SPEED|default(100)|int * 60 %} # print feedrate {% set feedrate_travel = params.TRAVEL_SPEED|default(200)|int * 60 %} # travel feedrate between print moves {% set feedrate_raft = params.RAFT_SPEED|default(60)|int * 60 %} # print feedrate for printing raft {% set feedrate_z = params.Z_LIFT_SPEED|default(20)|int * 60 %} # z axis travel feedrate {% set feedrate_retract = params.RETRACT_SPEED|default(50)|int * 60 %} # retract and deretract feedrate # # COMPUTED VALUES # {% set e_per_mm = ((extrusion_width - layer_height) * layer_height + 3.14159 * (layer_height / 2)**2) / (3.14159 * (filament_diameter / 2)**2) %} # computed E factor (similar to Slic3r/PrusaSlicer/SuperSlicer) {% set spacing = extrusion_width - layer_height * (1 - 3.14159 / 4) %} # line spacing during the print {% set shell_thickness = extrusion_width + (number_of_perimeters|float - 1) * spacing %} # theoric shell thickness {% set max_x = printer.toolhead.axis_maximum.x|float %} {% set max_y = printer.toolhead.axis_maximum.y|float %} {% set x_start = max_x / 2 - print_size / 2 %} {% set y_start = max_y / 2 - print_size / 2 %} {% set x_end = x_start + print_size %} {% set y_end = y_start + print_size %} {% set num_raft_lines = ([print_size, max_x]|min / spacing)|round(0, 'floor')|int %} {% set raft_size = num_raft_lines * spacing %} # # STARTING... # {action_respond_info("")} {action_respond_info("Starting extrusion flow calibration print")} {action_respond_info("This operation can not be interrupted by normal means. Hit the \"emergency stop\" button to stop it if needed")} {action_respond_info("")} {action_respond_info("Exrusion multiplier used: %.4f" % e_multiplier)} {action_respond_info("Number of perimeters to print: %d" % number_of_perimeters)} {action_respond_info("THEORIC SHELL THICKNESS: %.4f" % shell_thickness)} {action_respond_info("")} {action_respond_info("Measure the shell thickness using a caliper or micrometer. Then call the computation macro with the measured value:")} {action_respond_info("COMPUTE_FLOW_MULTIPLIER MEASURED_THICKNESS=xxx.xxx")} {action_respond_info("")} SAVE_GCODE_STATE NAME=STATE_FLOW_MULTIPLIER_CALIBRATION # # set variables for later computation # SET_GCODE_VARIABLE MACRO=_FLOW_CALIB_VARIABLES VARIABLE=last_shell_thickness VALUE={shell_thickness} SET_GCODE_VARIABLE MACRO=_FLOW_CALIB_VARIABLES VARIABLE=last_evalue VALUE={e_multiplier} # # purging before raft # G90 M83 G92 E0.0 G0 X{x_start} Y{y_start - 5} Z{layer_height} F{feedrate_travel} # move at the start position to do a purge line G91 # use relative coordinates for the prime line G1 E{initial_purge} F{5 * 60} G1 X{raft_size} E{raft_size * e_per_mm * 1.5} F{feedrate_raft / 2} # print prime line G1 Y-{extrusion_width} E{extrusion_width * e_per_mm} F{feedrate_raft / 2} # move to next line G1 X-{raft_size} E{raft_size * e_per_mm} F{feedrate_raft / 2} # print second prime line G1 E-{retract_length} F{feedrate_retract} # retract G0 Z{z_hop_height} F{feedrate_z} # z-hop G90 # back to absolute coordinates G0 X{x_start} Y{y_start} F{feedrate_travel} # move to start position G1 Z{layer_height} F{feedrate_z} # move to print height G1 E{retract_length} F{feedrate_retract} # unretract # set extrude_factor M221 S{e_multiplier * 100} # # print the raft # {% if do_raft == 1 %} G91 # use relative coordinates for the raft {% for curr_raft_line in range(1, num_raft_lines + 2) %} # print a raft line with alternating direction G1 Y{loop.cycle(1.0, -1.0) * raft_size} E{raft_size * e_per_mm} F{feedrate_raft} # spacing move {% if not loop.last %} G1 X{spacing} E{spacing * e_per_mm} F{feedrate_raft} {% endif %} {% endfor %} G1 E-{retract_length} F{feedrate_retract} # retract G0 Z{z_hop_height} F{feedrate_z} # z-hop G90 # back to absolute coordinates {% endif %} # # print the shell # G90 M106 S{fan_speed * 255 / 100} # for each layer {% for curr_layer in range(1, (print_height / layer_height)|round|int) %} G0 X{x_start + corner_radius} Y{y_start} F{feedrate_travel} # move to XY start position G1 Z{(curr_layer * layer_height) + (layer_height if do_raft == 1 else 0)} F{feedrate_z} # move to Z start position # print one layer of the shell (in a for loop to do all the perimeters of one layer) {% for perim_num in range(number_of_perimeters) %} # compute values for the current perimeter (offset and radius) {% set perim_offset = perim_num * spacing %} {% set perim_radius = corner_radius - (perim_num * spacing) %} # start position of the current perimeter G1 X{x_start + corner_radius} Y{y_start + perim_offset} F{feedrate_travel} {% if do_retract == 1 %} G1 E{retract_length} F{feedrate_retract} # unretract {% endif %} # print the perimeter using the offset and radius computed G1 X{x_end - corner_radius} Y{y_start + perim_offset} E{(print_size - (2 * corner_radius)) * e_per_mm} F{feedrate_print} G3 X{x_end - perim_offset} Y{y_start + corner_radius} J{perim_radius} E{(3.14159 / 2) * perim_radius * e_per_mm} F{feedrate_print} G1 X{x_end - perim_offset} Y{y_end - corner_radius} E{(print_size - (2 * corner_radius)) * e_per_mm} F{feedrate_print} G3 X{x_end - corner_radius} Y{y_end - perim_offset} I-{perim_radius} E{(3.14159 / 2) * perim_radius * e_per_mm} F{feedrate_print} G1 X{x_start + corner_radius} Y{y_end - perim_offset} E{(print_size - (2 * corner_radius)) * e_per_mm} F{feedrate_print} G3 X{x_start + perim_offset} Y{y_end - corner_radius} J-{perim_radius} E{(3.14159 / 2) * perim_radius * e_per_mm} F{feedrate_print} G1 X{x_start + perim_offset} Y{y_start + corner_radius} E{(print_size - (2 * corner_radius)) * e_per_mm} F{feedrate_print} G3 X{x_start + corner_radius} Y{y_start + perim_offset} I{perim_radius} E{(3.14159 / 2) * perim_radius * e_per_mm} F{feedrate_print} {% if do_retract == 1 %} G1 E-{retract_length} F{feedrate_retract} # retract {% endif %} {% endfor %} {% if do_retract == 1 %} G91 G0 Z{z_hop_height} F{feedrate_z} G90 {% endif %} {% endfor %} # # retract and move away # G1 E-{retract_length} F{feedrate_retract} G91 G0 Z20 F{feedrate_travel} RESTORE_GCODE_STATE NAME=STATE_FLOW_MULTIPLIER_CALIBRATION [gcode_macro COMPUTE_FLOW_MULTIPLIER] description: Compute a new flow multiplier by using the measured shell thickness on the calibration print gcode: {% set evalue = params.OLD_EXTRUSION_MULTIPLIER|default(0.0)|float %} # extrusion multiplier used for the calibration print {% set theorical_thickness = params.THEORICAL_THICKNESS|default(0.0)|float %} # theorical shell thickness {% set measured_thickness = params.MEASURED_THICKNESS|default(0.0)|float %} # measured shell thickness on the calibration print # if there is no OLD_EXTRUSION_MULTIPLIER passed as param, get the one from the last print calib run {% if evalue == 0.0 %} {% set last_evalue = printer["gcode_macro _FLOW_CALIB_VARIABLES"].last_evalue %} # then, if there is also no evalue saved from the last run, alert user {% if last_evalue == 0.0 %} {action_respond_info("It seems that no calibration print was run prior to this (or a restart of Klipper occured).")} {action_respond_info("You can still manually use it by calling again this macro like that:")} {action_respond_info("COMPUTE_FLOW_MULTIPLIER OLD_EXTRUSION_MULTIPLIER=xxx.xxx THEORICAL_THICKNESS=xxx.xxx MEASURED_THICKNESS=xxx.xxx")} {action_raise_error("not enough data to perform the computation of the new flow !")} {% else %} {% set final_evalue = last_evalue %} {action_respond_info("Using OLD_EXTRUSION_MULTIPLIER: %.3f" % final_evalue)} {% endif %} {% else %} {% set final_evalue = evalue %} {action_respond_info("Using OLD_EXTRUSION_MULTIPLIER: %.3f" % final_evalue)} {% endif %} # similarly, if there is no THEORICAL_THICKNESS passed as param, get the one from the last print calib run {% if theorical_thickness == 0.0 %} {% set last_shell_thickness = printer["gcode_macro _FLOW_CALIB_VARIABLES"].last_shell_thickness %} # then, if there is also no evalue saved from the last run, alert user {% if last_shell_thickness == 0.0 %} {action_respond_info("It seems that no calibration print was run prior to this (or a restart of Klipper occured).")} {action_respond_info("You can still manually use it by calling again this macro like that:")} {action_respond_info("COMPUTE_FLOW_MULTIPLIER OLD_EXTRUSION_MULTIPLIER=xxx.xxx THEORICAL_THICKNESS=xxx.xxx MEASURED_THICKNESS=xxx.xxx")} {action_raise_error("not enough data to perform the computation of the new flow !")} {% else %} {% set final_theorical_thickness = last_shell_thickness %} {action_respond_info("Using THEORICAL_THICKNESS: %.3f" % final_theorical_thickness)} {% endif %} {% else %} {% set final_theorical_thickness = theorical_thickness %} {action_respond_info("Using THEORICAL_THICKNESS: %.3f" % final_theorical_thickness)} {% endif %} # use the measured thickness from the user to compute a new flow value {% if measured_thickness == 0.0 %} {action_respond_info("You must use a caliper or micrometer to measure the calibration print shell thickness and call this macro with the measured value !!!")} {action_respond_info("COMPUTE_FLOW_MULTIPLIER MEASURED_THICKNESS=xxx.xxx")} {action_raise_error("not enough data to perform the computation of the new flow !")} {% else %} {% set new_evalue = final_theorical_thickness * final_evalue / measured_thickness %} {action_respond_info("NEW COMPUTED FLOW VALUE: %.3f" % new_evalue)} {action_respond_info("Use this new value as extrusion multiplier in your slicer of choice")} {action_respond_info("")} {% endif %}