Filament Motion Sensor on 2-in-1-out hotend?

Basic Information:

Printer Model: Heavily Modded Ender 3
MCU / Printerboard: BTT SKR 1.4 Turbo
Host / SBC Raspberry Pi 4B
klippy.log

Describe your issue:

Hi,

does anyone here have a working configuration for a 2-in-1-out hotend with (BTT Smart) Filament Motion Sensors on both extruder motors? Since I had to switch from two different [extruder] sections to [extruder] and [extruder_stepper], I haven’t gotten it to play nice with my toolchange macro.


My first approach was to configure the e1_sensor for extruder1 or extruder_stepper extruder1. However, this didn’t work:

Internal error during ready callback: Unknown config object ‘extruder1’

Internal error during ready callback: ‘PrinterExtruderStepper’ object has no attribute ‘find_past_position’

I suspect that this is a bug in klipper, respectively something that got overlooked when implementing the PrinterExtruderStepper class? But since I can find no configuration reference, I can’t be sure…


My second approach was to configure both sensors for extruder and switch manually during the Toolchange:

[filament_motion_sensor e0_sensor]
detection_length: 9
extruder: extruder

[filament_motion_sensor e1_sensor]
detection_length: 9
extruder: extruder
[gcode_macro CHANGE_TOOLHEAD]
G92 E0
SAVE_GCODE_STATE NAME=change_extruder
  
# Disable Filament sensor
M400
SET_FILAMENT_SENSOR SENSOR=e0_sensor ENABLE=0
SET_FILAMENT_SENSOR SENSOR=e1_sensor ENABLE=0
M83
#
[...] # unloading
# 
# Switch active tool
M400
SYNC_EXTRUDER_MOTION EXTRUDER="extruder" MOTION_QUEUE=""
SYNC_EXTRUDER_MOTION EXTRUDER="extruder1" MOTION_QUEUE=""
SYNC_EXTRUDER_MOTION EXTRUDER={ params.TARGET } MOTION_QUEUE="extruder"
SAVE_VARIABLE VARIABLE=currentextruder VALUE='"{ params.TARGET }"'
G92 E0
# Re-Enable Filament sensor
M400
{% if params.TARGET == "extruder" %}
    SET_FILAMENT_SENSOR SENSOR=e0_sensor ENABLE=1
{% elif params.TARGET == "extruder1" %}
    SET_FILAMENT_SENSOR SENSOR=e1_sensor ENABLE=1
{% else %}
    M118 No matching filament sensor found.
{% endif %}
#
[...] # loading
# 
RESTORE_GCODE_STATE NAME=change_extruder MOVE=1 MOVE_SPEED=12000
G92 E0

However, no matter what I do (e.g. switching the order of extruder movements in the macro or sending M400 and G92 E0), this makes the e0_sensor trigger immediately after switching (back) to extruder and resuming to print.


I suspect that there’s at least one bug somewhere, but I’m not quite sure where. I would be grateful if someone could tell me how it’s supposed to work, before I make this a bug report and upload my klippy.log. I will then clean up my printer.cfg and try to produce a clean klippy.log. (My current one is an absolute mess at the moment.)

Thank you!

This is likely a timing / template extension problem.
See Help with Macro: SET_GCODE_VARIABLE doesnt set my variable - #12 by theophile and maybe Help with Macro: SET_GCODE_VARIABLE doesnt set my variable - #15 by Sineos

The flow should be:

  1. Call own “disable sensor” macro
  2. Call Extruder change macro (maybe even with a short delay to 1.)
  3. Call own “enable sensor” macro (maybe even with a short delay to 2.)

Thank you for these hints. So I guess my second approach (assigning both sensors to extruder and switching manually) is the correct one?

By introducing G4 delays and splitting the commands up into individual macros, I managed to seemingly switch the active sensor without e0_sensor triggering every time I switch back to extruder. Sometimes it still does, however… Right now I’m looking at my printer telling me that the switch failed, even while the new filament is happily oozing out of the nozzle.

Additionally, I have now noticed that e1_sensor seems not to detect jams while it is active.

My strong suspicion is, that the [filament_motion_sensor] always uses the position of the original stepper specified under its extruder property, disregarding any other steppers that have been assigned via SYNC_EXTRUDER_MOTION:

filament_moption_sensor.py

def _handle_ready(self):
        self.extruder = self.printer.lookup_object(self.extruder_name)
[...]
def _get_extruder_pos(self, eventtime=None):
    if eventtime is None:
        eventtime = self.reactor.monotonic()
    print_time = self.estimated_print_time(eventtime)
    return self.extruder.find_past_position(print_time)

find_past_position() seems to be a method of ExtruderStepper, not of PrinterExtruder, so I thinnk it refers directly to the stepper with the name extruder:

def find_past_position(self, print_time):
        mcu_pos = self.stepper.get_past_mcu_position(print_time)
        return self.stepper.mcu_to_commanded_position(mcu_pos)

and at this point, I’m pretty sure that we’ve bypassed the MOTION_QUEUE altogether and both sensors are patiently waiting for position updates from the same stepper extruder, probably causing…

  • the e1_sensor to not work when extruder1 is active
  • the e0_sensor to freak out when it gets reactivated

The obvious solution would be to assign the sensor to extruder_stepper extruder1, but as I already stated:

Internal error during ready callback: ‘PrinterExtruderStepper’ object has no attribute ‘find_past_position’

For now, that’s all speculation on my part, but it looks pretty conclusive to me.

If I’m wrong and there’s another solution to reliably switch sensors, I am grateful for any further input. Otherwise, if I happen to be right, please let me know what would be needed to pursue this further. Do you still require a klippy.log file, and if so, is there anything in particular you want me to try?

These Filament Motion sensors follow a pretty simple logic:
If the extruder is advancing, then (within the given timeout) the logic is expecting pulses from the sensor. If the pulse is received then the timeout counting is reset, if not, it executes its runout routines.

If this is potentially referenced to the “wrong” extruder, I cannot tell.

Solved it, more or less.

[filament_motion_sensor] indeed expects a stepper, not an [extruder].

After adding

def find_past_position(self, print_time):
    return self.extruder_stepper.find_past_position(print_time)

to the PrinterExtruder class, I was able to set the following in my printer.cfg:

[filament_motion_sensor e0_sensor]
detection_length: 9
extruder: extruder
switch_pin: P1.26
pause_on_runout: False
runout_gcode: 
    RUNOUT_MACRO SOURCE=extruder

[filament_motion_sensor e1_sensor]
detection_length: 9
extruder: extruder_stepper extruder1
switch_pin: P1.25
pause_on_runout: False
runout_gcode:
    RUNOUT_MACRO SOURCE=extruder1

Now each filament_motion_sensor refers to its assigned stepper and ignores the movements of the other.

However, now both sensors triggered during toolchanges. I worked around this by setting pause_on_runout to False, using a custom runout macro and a delayed_gcode to ignore this during and immediately after toolchanges:

[gcode_macro RUNOUT_MACRO]
variable_toolchange: 0
gcode:
    {% set svv = printer.save_variables.variables %}
    {% if svv.currentextruder != params.SOURCE %}
        M118 IGNORING filament sensor { params.SOURCE } triggered while { svv.currentextruder } is active.
    {% elif toolchange == 1 %}
        M118 IGNORING filament sensor { params.SOURCE } triggered during toolchange.
    {% else %}
        M118 HALTING: Filament sensor { params.SOURCE } triggered.
        PAUSE_MACRO
    {% endif %}

[gcode_macro CHANGE_TOOLHEAD]
gcode:
    DISABLE_FILAMENT_SENSORS
    SWITCH_TOOL TARGET={ params.TARGET }
    UPDATE_DELAYED_GCODE ID=ENABLE_FILAMENT_SENSOR DURATION=10

[gcode_macro DISABLE_FILAMENT_SENSORS]
gcode:
    SET_GCODE_VARIABLE MACRO=RUNOUT_MACRO VARIABLE=toolchange VALUE=1

[delayed_gcode ENABLE_FILAMENT_SENSOR]
gcode:
  SET_GCODE_VARIABLE MACRO=RUNOUT_MACRO VARIABLE=toolchange VALUE=0

[gcode_macro SWITCH_TOOL]
gcode:
  M400
  SYNC_EXTRUDER_MOTION EXTRUDER="extruder" MOTION_QUEUE=""
  SYNC_EXTRUDER_MOTION EXTRUDER="extruder1" MOTION_QUEUE=""
  SYNC_EXTRUDER_MOTION EXTRUDER={ params.TARGET } MOTION_QUEUE="extruder"
  SAVE_VARIABLE VARIABLE=currentextruder VALUE='"{ params.TARGET }"'
  G92 E0

It’s not pretty, but it works.
Plus: This solution is independent from the ENABLE state of each sensor, meaning that I can toggle them from Mainsail at any time, without having my manual setting reset by the next toolchange.

If there aren’t any objections, I might submit a PR for my fix to the PrinterExtruder class.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.