diff --git a/macros/calibrate_flow.cfg b/macros/calibrate_flow.cfg new file mode 100644 index 0000000..7d472b7 --- /dev/null +++ b/macros/calibrate_flow.cfg @@ -0,0 +1,281 @@ +# __ ______ ___ ____ _____ +# \ \ / / ___/ _ \| _ \| ____| +# \ \ / / | | | | | |_) | _| +# \ V /| |__| |_| | _ <| |___ +# \_/ \____\___/|_| \_\_____| 3.1 +# Standalone 300 Setup. +# +######################################### +###### FLOW MULTIPLIER CALIBRATION ###### +######################################### +# Written by Frix_x#0161 # +# https://github.com/Frix-x/klipper-voron-V2/blob/main/macros/calibration/calibrate_flow.cfg +# @version: 1.5 + +# CHANGELOG: +# v1.5: moved the install notes into a proper markdown file in: doc > 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: doc > 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_macro FLOW_MULTIPLIER_CALIBRATION] +description: Print a small tower to calibrate the extrusion flow multiplier by measuring the shell +gcode: + # + # PARAMETERS + # + + # [ROKIN ADD TEMP] + {% set EXTRUDER_TEMP = params.EXTRUDER_TEMP|default(printer[printer.toolhead.extruder].target, true)|float %} + {% set BED_TEMP = params.BED_TEMP|default(printer.heater_bed.target, true)|float %} + + {% set do_raft = params.DO_RAFT|default(0)|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(6)|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(4)|int %} # number of perimeters to print the shell + {% set fan_speed = params.FAN_SPEED|default(40)|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.24)|float %} # layer height for the print + + {% set retract_length = params.RETRACT_LENGTH|default(0.8)|float %} # how much to retract when traveling between print moves + {% set initial_purge = params.PURGE_MM|default(8)|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} + + # [ROKIN ADD START] + START_PRINT EXTRUDER_TEMP={EXTRUDER_TEMP} BED_TEMP={BED_TEMP} + + # + # 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} + + # [ROKIN ADD END] + END_PRINT + + 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 %} diff --git a/macros/klipper-base.cfg b/macros/klipper-base.cfg index f7b457c..65f37ab 100644 --- a/macros/klipper-base.cfg +++ b/macros/klipper-base.cfg @@ -25,3 +25,8 @@ enable_force_move: True # https://www.klipper3d.org/Exclude_Object.html [exclude_object] + +# For flow_calibration macro +# https://github.com/Frix-x/klipper-voron-V2/blob/main/doc/features/flow_calibration.md +[gcode_arcs] +resolution: 0.2 \ No newline at end of file