# so3_4.cfg
############################################################################ ##################### SmartOrbiter v3 - Print Head 4 ####################### ############################################################################
#config file version v1.4.1 #release date: 15.10.2026
[mcu so3_4] serial: /dev/serial/by-id/usb-Klipper_stm32f042x6_PH5-if00
################################################################################################################# ################################ CHANGE HERE MACRO CONFIGURABLES ################################################ #################################################################################################################
[gcode_macro _SENSOR_VARIABLES_4] # change here macro configurables, enable disable functions!************************* variable_filament_load_temp :220 # temperature to heat up hotend for filament loading, default is 235 variable_filament_unload_temp :185 # temperature to heat up hotend for filament un-loading, default is 185 variable_filament_load_min_temp :190 # minimum hotend set temperature allowed in filament load macro, default is 190 variable_nozzle_purge_length :100 # filament extrude amount during load sequenc, hotend purge from old filament, default is 100 variable_nozzle_purge_speed :300 # filament extrude speed in mm/min adjust this value lower if flow is too high and extruder skips during loading, default is 300 variable_disable_autoload :False # disable filament autoload feature by setting this variable True variable_autoload_min_absence :1.0 # seconds the switch must have read empty before an insert starts autoload, ignores wiggles and blips variable_disable_runnout :False # disable runnout by setting this variable True variable_disable_autounload :False # disable auto unload filament by setting this varia
:False # disable runnout by setting this variable True variable_disable_autounload :False # disable auto unload filament by setting this variable to True variable_enable_beep :False # uses M300 sound feature, set it True to enable variable_SENSOR_RGB_LED :False # enable and configure RGB LED pin and type for filament status indication variable_park_position_x :100 # edit your X parking position here for pause macro trigerred by runnout variable_park_position_y :10 # edit your Y parking position here for pause macro trigerred by runnout variable_park_lift_z :10 # edit your Z lift amount for parking position here, default is 10 variable_park_retraction :1 # edit your retraction amount for parking, default is 1 gcode:
################################################################## ########################## EXTRUDER ############################## ################################################################## [extruder4] step_pin: so3_4:PB7 dir_pin: so3_4:PB6 enable_pin: !so3_4:PB4 microsteps: 32 full_steps_per_rotation: 200 #rotation_distance = <full_steps_per_rotation> * <microsteps> / <steps_per_mm> rotation_distance: 4.69 nozzle_diameter: 0.400 filament_diameter: 1.750 max_extrude_only_distance: 2500 max_extrude_only_velocity: 120 max_extrude_only_accel: 3000 heater_pin: so3_4:PA0 sensor_type: ATC Semitec 104NT-4-R025H42G sensor_pin: so3_4:PA2 pullup_resistor: 4700 min_temp: 0 max_temp: 300 pressure_advance: 0.03 pressure_advance_smooth_time: 0.02 min_extrude_temp: 165 smooth_time: 0.5 max_extrude_cross_section:10 max_power: 0.995 pwm_cycle_time: 0.01 control = pid #Default SO3 PID Values: pid_Kp=24.859 pid_Ki=2.072 pid_Kd=74.578 #REVO SO3 PID Values: #pid_Kp=23.445 #pid_Ki=1.839 #pid_Kd=74.733 [tmc2209 extruder4] uart_pin: so3_4:PB5 #uart_address: 3 # comment out this line in case UART address definition is not required interpolate: true run_current: 0.80 sense_resistor: 0.11 ste
#uart_address: 3 # comment out this line in case UART address definition is not required interpolate: true run_current: 0.80 sense_resistor: 0.11 stealthchop_threshold: 0
[heater_fan Hotend4_Fan] pin: !so3_4:PA8 tachometer_pin: so3_4:PA3 tachometer_ppr: 2 tachometer_poll_interval: 0.0005 heater: extruder4 cycle_time: 0.0001 #10KHz PWM frecvency #cycle_time: 0.01 #100Hz PWM frecvency heater_temp: 75 fan_speed: 0.65 hardware_pwm: false shutdown_speed: 0.0 max_power: 1.0
################################################################################################################################# ####################################### DO NOT CHANGE ANYTHING BELOW THIS LINE!!! ############################################### ################################################################################################################################# ####################################### UNLESS YOU KNOW WHAT YOU ARE DOING OR YOU ARE BORED AND NEED TO DO SOMETHING OR YOU ARE ALONE AND NEED SOMETHING TO MESS UP YOUR LIFE OR YOU CAN DO IT BETTER THAN ME OR YOU ARE A REAL SMARTASS OR JUST BECAUSE YOU CAN OR YOU RUN OUT OF BEAR OR HAS NOTHING BETTER TO DO OR ANY UNREASONABLE REASON #################################################################################################################################
[respond] default_type: echo
[force_move] enable_force_move: True
################################################################################################################################# ########################################### so3_4 Features Configuration ########################################################## #################################################################################################################################
[neopixel SO3RGB_4] pin: so3_4:PB8 chain_count: 1 color_order: GRB initial_RED: 0.5 initial_GREEN: 0.5 initial_BLUE: 0.5
[thermistor so3_4Thermistor] temperature1: 25 resistance1: 100000 beta: 3950
[temperature_sensor so3_4] sensor_type: so3_4Thermistor sensor_pin: so3_4:PB1 pullup_resistor: 4700 min_temp: 0 max_temp: 85
[output_pin HOT_LED_4] pin: so3_4:PA9 value: 1 shutdown_value: 0
[gcode_button t4_dock_sensor] pin: ^so3_4:PB0 press_gcode: release_gcode:
################################################################################################################################# ############################################## Runout and autoload macros ####################################################### #################################################################################################################################
################################################################################################################################# ####################################### DO NOT CHANGE ANYTHING BELOW THIS LINE!!! ############################################### ################################################################################################################################# ####################################### UNLESS YOU KNOW WHAT YOU ARE DOING OR YOU ARE BORED AND NEED TO DO SOMETHING OR YOU ARE ALONE AND NEED SOMETHING TO MESS UP YOUR LIFE OR YOU CAN DO IT BETTER THAN ME OR YOU ARE A REAL SMARTASS OR JUST BECAUSE YOU CAN OR YOU RUN OUT OF BEAR OR HAS NOTHING BETTER TO DO OR ANY UNREASONABLE REASON #################################################################################################################################
#*****************************initial RGB LED set at printer start**************************************************************** [delayed_gcode startup_RGB_SET_4] initial_duration: 2 gcode: {% if printer["filament_switch_sensor SO3Sensor_4"].filament_detected==true %} SET_LED LED=SO3RGB_4 RED=0.0 GREEN=1.0 BLUE=0.0 {% else %} SET_LED LED=SO3RGB_4 RED=1.0 GREEN=0.0 BLUE=0.0 {% endif %}
#***********************************filament swicth definition***********************************************************
[filament_switch_sensor SO3Sensor_4] switch_pin: so3_4:PA13 pause_on_runout: False # do not change to true, printer is paused in the next sections # runout_gcode is left out on purpose. Klipper appends M400 to every runout_gcode call, which stops the toolhead on each dropout. Runout detection is done by _runnout_poll_4 below insert_gcode: _filament_load_init_4 event_delay: 1.0 # now only spaces insert events pause_delay: 0.1
#////////////////////////////////////////Filament Unload macros/////////////////////////////////////////////////
[gcode_button _filament_unload_4] pin: !so3_4:PA10 release_gcode: # filament unload procedure SET_LED LED=SO3RGB_4 RED=0.0 GREEN=0.5 BLUE=0.5 _filament_unload_init_4 press_gcode: # do not add any macro call here
[gcode_macro _runnout_poll_4] description: Polls the filament switch and pauses only after sustained loss of contact on the active tool, measured on the MCU clock. Replaces runout_gcode so Klipper never injects an M400 on a dropout variable_extruder_name: "extruder4" # this tool's extruder. The tool is only watched while it is the active extruder AND off its dock, because a docked tool cannot run out and DOCK does not change the active extruder variable_sample_time: 0.5 # seconds between samples variable_runout_time: 4.0 # seconds of continuous no contact before pausing, after a loaded to empty transition was seen variable_pickup_grace: 10.0 # seconds of continuous no contact before pausing when no transition was seen, covers pickup, prime and a lever that settles late variable_min_samples: 6 # empty samples also required, so sparse sampling during long commands cannot pause on a few readings. 8 is the most 0.5s sampling can give in 4s variable_clear_time: 3.0 # seconds of continuous contact before an episode is closed variable_log_intermittent: True # set False to silence the intermittent contact messages variable_last_detected: True # switch st
episode is closed variable_log_intermittent: True # set False to silence the intermittent contact messages variable_last_detected: True # switch state at the previous sample, recorded as False while the tool is not active so the first look after pickup never counts as a transition variable_episode: False # a loaded to empty transition was seen while active and busy and has not been closed yet variable_confirmed: False # the open episode has already called _runnout_init_4 variable_empty_since: 0 variable_empty_count: 0 variable_loaded_since: 0 variable_absent_since: 0 # MCU clock time the switch was first seen empty, tracked in every state so autoload can tell a real insert from a wiggle variable_last_absence: 0 # seconds the switch read empty before it was last seen loaded again gcode: {% set now = printer.toolhead.estimated_print_time %} {% set detected = printer["filament_switch_sensor SO3Sensor_4"].filament_detected %} {% set busy = printer.idle_timeout.state == "Printing" %} {% set docked = printer["gcode_button t4_dock_sensor"].state == "PRESSED" %} {% set active = printer.toolhead.extruder == extruder_name and not docked %} {% set tr
rinter["gcode_button t4_dock_sensor"].state == "PRESSED" %} {% set active = printer.toolhead.extruder == extruder_name and not docked %} {% set tracking = episode or confirmed or empty_since > 0 or empty_count > 0 or loaded_since > 0 %} UPDATE_DELAYED_GCODE ID=runnout_poll_4 DURATION={sample_time} SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=last_detected VALUE={detected if active else False} {% if detected and absent_since > 0 %} SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=last_absence VALUE={(now - absent_since)|round(2)} SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=absent_since VALUE=0 {% elif not detected and absent_since == 0 %} SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=absent_since VALUE={now} {% endif %} {% if not active %} {% if tracking %} SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=episode VALUE=False SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=confirmed VALUE=False SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=empty_since VALUE=0 SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=empty_count VALUE=0 SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=loaded_since VALUE=0
SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=empty_count VALUE=0 SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=loaded_since VALUE=0 {% endif %} {% elif detected %} SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=empty_since VALUE=0 SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=empty_count VALUE=0 {% if episode %} {% set since = loaded_since if loaded_since > 0 else now %} {% if now - since >= clear_time %} SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=episode VALUE=False SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=confirmed VALUE=False SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=loaded_since VALUE=0 {% if log_intermittent and not confirmed %} M118 Runnout sensor_4 intermitent contact detected, filament confirmed present {% endif %} {% else %} SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=loaded_since VALUE={since} {% endif %} {% endif %} {% elif busy %} {% set transition = episode or last_detected %} {% set since = empty_since if empty_since > 0 else now %} {% set count = empty_count + 1 %} {% set needed = runout_time if
ast_detected %} {% set since = empty_since if empty_since > 0 else now %} {% set count = empty_count + 1 %} {% set needed = runout_time if transition else pickup_grace %} SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=episode VALUE={transition} SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=loaded_since VALUE=0 SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=empty_since VALUE={since} SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=empty_count VALUE={count} {% if not confirmed and now - since >= needed and count >= min_samples %} SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=episode VALUE=True SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=confirmed VALUE=True M118 Runnout sensor_4 no contact for {(now - since)|round(2)}s over {count} samples, runout confirmed _runnout_init_4 {% endif %} {% elif tracking %} SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=episode VALUE=False SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=confirmed VALUE=False SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=empty_since VALUE=0 SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=empty_count VALUE=0 S
SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=empty_since VALUE=0 SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=empty_count VALUE=0 SET_GCODE_VARIABLE MACRO=_runnout_poll_4 VARIABLE=loaded_since VALUE=0 {% endif %}
[delayed_gcode runnout_poll_4] initial_duration: 1 # starts polling one second after Klipper is ready and never stops gcode: _runnout_poll_4
[gcode_macro _runnout_init_4] gcode: {% set sensor = printer['gcode_macro _SENSOR_VARIABLES_4'] %} SET_LED LED=SO3RGB_4 RED=1.0 GREEN=0.0 BLUE=0.0 {% if (printer.print_stats.state == "printing") %} {% if(sensor.disable_runnout|lower == 'false') %} _filament_change_state14 {% else %} M118 Filament runnout for Sensor4 is disabled in the sensor config file! {% endif %} {% endif %} UPDATE_DELAYED_GCODE ID=clear_loadbusy_4 DURATION=2 UPDATE_DELAYED_GCODE ID=clear_unloadbusy_4 DURATION=2 SET_GCODE_VARIABLE MACRO=_filament_unload_4 VARIABLE=filamentpresent VALUE=0
[gcode_macro _filament_change_state14] variable_changebusy_4: 0 variable_temp_target: 0 gcode: {% set sensor = printer['gcode_macro _SENSOR_VARIABLES_4'] %} {% if changebusy_4 == 0 %} PAUSE # call printer pause macro SET_GCODE_VARIABLE MACRO=_filament_change_state14 VARIABLE=changebusy_4 VALUE=1 M118 Filament runnout on Extruder 4! M118 Load new filament! {% else %} {% endif %}
#############################################END filament auto load macro section END***********************************************************
[gcode_macro _filament_load_init_4] gcode: {% set sensor = printer['gcode_macro _SENSOR_VARIABLES_4'] %} {% set poll = printer['gcode_macro _runnout_poll_4'] %} {% set now = printer.toolhead.estimated_print_time %} {% set absence = (now - poll.absent_since) if poll.absent_since > 0 else poll.last_absence %} SET_LED LED=SO3RGB_4 RED=0.0 GREEN=1.0 BLUE=0.0 {% if (printer.print_stats.state != "printing") %} {% if absence < sensor.autoload_min_absence %} M118 Extruder_4 sensor blip of {absence|round(2)}s ignored, filament was not removed {% elif(sensor.disable_autoload|lower == 'false') %} _filament_load_4 {% else %} M118 Filament auto-load for Extruder_4 is disabled in the sensor config file! {% endif %} {% else %} M118 Printing! Can not load filament right now! {% endif %} SET_GCODE_VARIABLE MACRO=_filament_unload_4 VARIABLE=filamentpresent VALUE=1 UPDATE_DELAYED_GCODE ID=clear_changebusy_4 DURATION=2
[gcode_macro _filament_load_4] variable_loadbusy_4: 0 variable_user_temp: 0 gcode: T4 SET_LED LED=SO3RGB_4 RED=0.7 GREEN=0.5 BLUE=0.0 {% set sensor = printer['gcode_macro _SENSOR_VARIABLES_4'] %} {% if loadbusy_4 == 0 %} # requires [virtual_sdcard] SET_GCODE_VARIABLE MACRO=_filament_load_4 VARIABLE=loadbusy_4 VALUE=1 SET_GCODE_VARIABLE MACRO=_filament_unload_4 VARIABLE=unloadbusy_4 VALUE=1 {% set USER_TEMP = printer.extruder4.target %} # save user set temperature before loading {% set LOAD_TEMP = 0 %} {% if (printer.extruder4.can_extrude|lower != 'true') or (printer.extruder4.target < sensor.filament_load_min_temp) %} # checking for minimum extrusion temperature # check if temperature is over the minimum extrusion temp. min_extrude_temp must be defined in the extruder config (to about 185) SET_HEATER_TEMPERATURE HEATER=extruder4 TARGET={sensor.filament_load_temp} # set user defined load temperature {% set LOAD_TEMP = sensor.filament_load_temp %} # save user set temperature before loading M118 Hotend 4 heating! {% endif %} {% if (sensor.enable_beep|lower == 'true') %}
ve user set temperature before loading M118 Hotend 4 heating! {% endif %} {% if (sensor.enable_beep|lower == 'true') %} M300 # beep sound {% endif %} M118 Filament loading! M82 #set extruder to absolute mode G92 E0 G4 P1500 # wait for 1.5 seconds FORCE_MOVE STEPPER=extruder4 DISTANCE=15 VELOCITY=10 ACCEL=1000 # load filament inside the gears force move needs to be enabled TEMPERATURE_WAIT SENSOR=extruder4 MINIMUM={sensor.filament_load_temp} # wait for reaching set temperature G1 E{sensor.nozzle_purge_length} F{sensor.nozzle_purge_speed} # extrude preconfigured purge length M400 # wait to complete nozzle purge SET_HEATER_TEMPERATURE HEATER=extruder4 TARGET={USER_TEMP} # restore user temp if it was set before loading M118 Filament load complete! SET_LED LED=SO3RGB_4 RED=0.0 GREEN=1.0 BLUE=0.0 UPDATE_DELAYED_GCODE ID=clear_unloadbusy_4 DURATION=2 UPDATE_DELAYED_GCODE ID=clear_loadbusy_4 DURATION=2 M118 Clear filament spaghetti and press resume to continue printing. {% else %} M118 Filament already loaded! {% end
ATION=2 M118 Clear filament spaghetti and press resume to continue printing. {% else %} M118 Filament already loaded! {% endif %} #############################################END filament suto load macro section END***********************************************************
#############################################filament auto unload macro section***************************************************************** [gcode_macro _filament_unload_procedure_4] gcode: G92 E0 G0 E-9 F3600 # retract fast 9mm G4 P2000 # Wait 2 sec for filament cooling G0 E9 F3000 # extruder 9mm to crush filament end G0 E-5 F3000 #extract filament to cold end fast G0 E-10 F300 #extract filament to cold end slow G4 P5000 # Wait 5 sec for filament cooling G0 E-25 F3000 #extract filament completely
[gcode_macro _filament_unload_init_4] gcode: {% set sensor = printer['gcode_macro _SENSOR_VARIABLES_4'] %} {% if (printer.print_stats.state != "printing") %} {% if(sensor.disable_autounload|lower == 'false') %} _filament_unload_4 {% else %} M118 Filament auto-unload for Extruder_4 is disabled in the sensor config file! {% endif %} {% else %} M118 Printing! Can not unload filament right now! {% endif %}
[gcode_macro _filament_unload_4] variable_unloadbusy_4: 0 variable_filamentpresent: 0 gcode: T4 {% set sensor = printer['gcode_macro _SENSOR_VARIABLES_4'] %} {% if unloadbusy_4 == 0 %} # requires [virtual_sdcard] SET_GCODE_VARIABLE MACRO=_filament_unload_4 VARIABLE=unloadbusy_4 VALUE=1 SET_GCODE_VARIABLE MACRO=_filament_load_4 VARIABLE=loadbusy_4 VALUE=1 {% if (sensor.enable_beep|lower == 'true') %} M300 # beep sound {% endif %} M118 Filament_4 unloading! M83 G92 E0 {% if ((printer.extruder4.can_extrude|lower != 'true') or (printer.extruder4.target == 0))%} # checing for minimum extrusion temperature # check if temperature is over the minimum extrusion temp. min_extrude_temp must be defined in the extruder config (to about 185) M118 Hotend 4 heating! SET_HEATER_TEMPERATURE HEATER=extruder4 TARGET={sensor.filament_unload_temp} # restore user temp if it was set before loading TEMPERATURE_WAIT SENSOR=extruder4 MINIMUM={sensor.filament_unload_temp} # wait for reaching configured unload temperature {% endif %} _filament_unload_procedure_4 # call macro of
={sensor.filament_unload_temp} # wait for reaching configured unload temperature {% endif %} _filament_unload_procedure_4 # call macro of filamnet unload procedure M400 # wait to complete unload M118 Filament unload complete! UPDATE_DELAYED_GCODE ID=clear_unloadbusy_4 DURATION=2 UPDATE_DELAYED_GCODE ID=clear_loadbusy_4 DURATION=2 UPDATE_DELAYED_GCODE ID=unload_RGB_update_4 DURATION=2 SET_LED LED=SO3RGB_4 RED=0.0 GREEN=0.0 BLUE=1.0 SET_HEATER_TEMPERATURE HEATER=extruder4 TARGET=0 # Turn off heater {% else %} M118 Nothing to unload! {% endif %}
#############################################END filament auto unload macro section END***********************************************************
[delayed_gcode unload_RGB_update_4] gcode: # change RGB led to RED after filament is phisicaly removed after unload {% if printer["filament_switch_sensor SO3Sensor_4"].filament_detected==true %} UPDATE_DELAYED_GCODE ID=unload_RGB_update_4 DURATION=2 {% else %} SET_LED LED=SO3RGB_4 RED=1.0 GREEN=0.0 BLUE=0.0 {% endif %}
[delayed_gcode clear_unloadbusy_4] gcode: SET_GCODE_VARIABLE MACRO=_filament_unload_4 VARIABLE=unloadbusy_4 VALUE=0 #M118 Clear Unload busy!
[delayed_gcode clear_changebusy_4] gcode: SET_GCODE_VARIABLE MACRO=_filament_change_state14 VARIABLE=changebusy_4 VALUE=0 #M118 Clear Load busy!
[delayed_gcode set_loadbusy_4] gcode: SET_GCODE_VARIABLE MACRO=_filament_load_4 VARIABLE=loadbusy_4 VALUE=1 #M118 Set Load busy!
[delayed_gcode clear_loadbusy_4] gcode: SET_GCODE_VARIABLE MACRO=_filament_load_4 VARIABLE=loadbusy_4 VALUE=0 #M118 Clear Load busy!