From 4fba8e4829d5cbf1bd2c9b41b42150001ea0bc25 Mon Sep 17 00:00:00 2001 From: Ben Lye Date: Thu, 5 Mar 2026 09:13:20 +0000 Subject: [PATCH] hall_filament_width_sensor: Toggle for flow compensation Adds a toggle to enable or disable flow compensation based on the filament width sensor readings without disabling the sensor entirely. Useful on printers where the hall effect sensor is too inaccurate to helpfully adjust the extrusion multiplier, but is good enough to act as a filament runout sensor. The new setting defaults to true to preserve the existing sensor behavior. Existing width sensor G-Code commands are updated to optionally enable and disable flow compensation and to show the state of the sensor and command outputs are normalized. Also includes updates to the relevant doc pages. Signed-off-by: Ben Lye ben@lye.co.nz --- docs/Config_Reference.md | 4 + docs/G-Codes.md | 13 ++- docs/Hall_Filament_Width_Sensor.md | 21 ++++ docs/Status_Reference.md | 1 + klippy/extras/hall_filament_width_sensor.py | 102 ++++++++++++-------- 5 files changed, 95 insertions(+), 46 deletions(-) diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index d247a9ecc..c1a70bc66 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -5114,6 +5114,10 @@ adc2: #enable: False # Sensor enabled or disabled after power on. The default is to # disable. +#enable_flow_compensation: True +# Flow compensation enabled or disabled. If set to False, the sensor +# will not modify the extrusion multiplier and will only trigger +# runout events. The default is True. #measurement_interval: 10 # The approximate distance (in mm) between sensor readings. The # default is 10mm. diff --git a/docs/G-Codes.md b/docs/G-Codes.md index 1afaa2bb8..19185ca9e 100644 --- a/docs/G-Codes.md +++ b/docs/G-Codes.md @@ -780,19 +780,22 @@ is enabled (also see [TSLl401CL Filament Width Sensor](TSL1401CL_Filament_Width_ and [Hall Filament Width Sensor](Hall_Filament_Width_Sensor.md)): #### QUERY_FILAMENT_WIDTH -`QUERY_FILAMENT_WIDTH`: Return the current measured filament width. +`QUERY_FILAMENT_WIDTH`: Return the current measured filament width, the +state of the width sensor, the state of the filament sensor and the state +of flow compensation. #### RESET_FILAMENT_WIDTH_SENSOR `RESET_FILAMENT_WIDTH_SENSOR`: Clear all sensor readings. Helpful -after filament change. +after filament change. Resets flow rate to 100%. #### DISABLE_FILAMENT_WIDTH_SENSOR `DISABLE_FILAMENT_WIDTH_SENSOR`: Turn off the filament width sensor -and stop using it for flow control. +and stop using it for flow compensation. Resets flow rate to 100%. #### ENABLE_FILAMENT_WIDTH_SENSOR -`ENABLE_FILAMENT_WIDTH_SENSOR`: Turn on the filament width sensor and -start using it for flow control. +`ENABLE_FILAMENT_WIDTH_SENSOR [FLOW_COMPENSATION=[0|1]`: Turn on the filament +width sensor and enable or disable flow compensation. If `FLOW_COMPENSATION` +is not specified, the current flow compensation state is preserved. #### QUERY_RAW_FILAMENT_WIDTH `QUERY_RAW_FILAMENT_WIDTH`: Return the current ADC channel readings diff --git a/docs/Hall_Filament_Width_Sensor.md b/docs/Hall_Filament_Width_Sensor.md index 9992ef730..f51a6aa94 100644 --- a/docs/Hall_Filament_Width_Sensor.md +++ b/docs/Hall_Filament_Width_Sensor.md @@ -56,6 +56,27 @@ By default, the sensor is disabled at power-on. To enable the sensor, issue **ENABLE_FILAMENT_WIDTH_SENSOR** command or set the `enable` parameter to `true`. +## Use as a runout switch only + +By default, the sensor measures filament diameter and adjusts the extrusion multiplier +to compensate for variations. + +If you want to use the sensor as a runout switch only, set the `enable_flow_compensation` +config parameter to `false`. In this mode, the sensor will only trigger runout +events when filament is not detected, it will not modify the extrusion multiplier. + +This is useful for printers where the filament sensor is not accurate enough for +flow compensation but can reliably detect filament runout, or when printing with +flexible filaments which have unstable diameter characteristics. + +Issue **ENABLE_FILAMENT_WIDTH_SENSOR FLOW_COMPENSATION=1** to enable flow compensation +or **ENABLE_FILAMENT_WIDTH_SENSOR FLOW_COMPENSATION=0** to disable it. + +Note that disabling filament width compensation automatically resets the extrusion +multiplier to 100%. + +**QUERY_FILAMENT_WIDTH** includes the current state of flow compensation in its output. + ## Logging By default, diameter logging is disabled at power-on. diff --git a/docs/Status_Reference.md b/docs/Status_Reference.md index 1f6704acc..913bf10a8 100644 --- a/docs/Status_Reference.md +++ b/docs/Status_Reference.md @@ -248,6 +248,7 @@ object: - all items from [filament_switch_sensor](Status_Reference.md#filament_switch_sensor) - `is_active`: Returns True if the sensor is currently active. +- `flow_compensation_enabled`: Returns True if flow compensation is enabled. - `Diameter`: The last reading from the sensor in mm. - `Raw`: The last raw ADC reading from the sensor. diff --git a/klippy/extras/hall_filament_width_sensor.py b/klippy/extras/hall_filament_width_sensor.py index 6c981303b..f391b2f77 100644 --- a/klippy/extras/hall_filament_width_sensor.py +++ b/klippy/extras/hall_filament_width_sensor.py @@ -33,6 +33,8 @@ class HallFilamentWidthSensor: self.runout_dia_min=config.getfloat('min_diameter', 1.0) self.runout_dia_max=config.getfloat('max_diameter', self.max_diameter) self.is_log =config.getboolean('logging', False) + self.enable_flow_compensation = config.getboolean( + 'enable_flow_compensation', True) # Use the current diameter instead of nominal while the first # measurement isn't in place self.use_current_dia_while_delay = config.getboolean( @@ -147,15 +149,17 @@ class HallFilamentWidthSensor: self.filament_width = self.diameter elif self.firstExtruderUpdatePosition == pending_position: self.filament_width = self.nominal_filament_dia - if ((self.filament_width <= self.max_diameter) - and (self.filament_width >= self.min_diameter)): - percentage = round(self.nominal_filament_dia**2 - / self.filament_width**2 * 100) - self.gcode.run_script("M221 S" + str(percentage)) - else: - self.gcode.run_script("M221 S100") + if self.enable_flow_compensation: + if ((self.filament_width <= self.max_diameter) + and (self.filament_width >= self.min_diameter)): + percentage = round(self.nominal_filament_dia**2 + / self.filament_width**2 * 100) + self.gcode.run_script("M221 S" + str(percentage)) + else: + self.gcode.run_script("M221 S100") else: - self.gcode.run_script("M221 S100") + if self.enable_flow_compensation: + self.gcode.run_script("M221 S100") self.filament_array = [] if self.is_active: @@ -164,12 +168,23 @@ class HallFilamentWidthSensor: return self.reactor.NEVER def cmd_M407(self, gcmd): - response = "" - if self.diameter > 0: - response += ("Filament dia (measured mm): " - + str(self.diameter)) + response = "Filament diameter: " + if self.is_active: + response += "%.3f mm" % (self.diameter) else: - response += "Filament NOT present" + response += "sensor disabled" + response += "\nFilament state:" + if self.is_active: + if self.runout_helper.filament_present: + response += " present" + else: + response += " not present" + else: + response += " sensor disabled" + response += ("\nWidth sensor:" + + (" ON" if self.is_active else " OFF")) + response += ("\nFlow compensation:" + + (" ON" if self.enable_flow_compensation else " OFF")) gcmd.respond_info(response) def cmd_ClearFilamentArray(self, gcmd): @@ -179,53 +194,58 @@ class HallFilamentWidthSensor: self.gcode.run_script_from_command("M221 S100") def cmd_M405(self, gcmd): - response = "Filament width sensor Turned On" - if self.is_active: - response = "Filament width sensor is already On" - else: - self.is_active = True - # Start extrude factor update timer + flow_comp = gcmd.get_int("FLOW_COMPENSATION", None, minval=0, maxval=1) + if flow_comp is not None: + self.enable_flow_compensation = flow_comp + if not flow_comp: + self.gcode.run_script_from_command("M221 S100") + + was_active = self.is_active + self.is_active = True + response = ("Filament width sensor: ON" + + "\nFlow compensation: " + + ("ON" if self.enable_flow_compensation else "OFF")) + + # Only start/restart timer if sensor was previously inactive + if not was_active: self.reactor.update_timer(self.extrude_factor_update_timer, - self.reactor.NOW) + self.reactor.NOW) gcmd.respond_info(response) def cmd_M406(self, gcmd): - response = "Filament width sensor Turned Off" - if not self.is_active: - response = "Filament width sensor is already Off" - else: - self.is_active = False - # Stop extrude factor update timer - self.reactor.update_timer(self.extrude_factor_update_timer, - self.reactor.NEVER) - # Clear filament array - self.filament_array = [] - # Set extrude multiplier to 100% - self.gcode.run_script_from_command("M221 S100") + response = "Filament width sensor: OFF" + self.is_active = False + # Stop extrude factor update timer + self.reactor.update_timer(self.extrude_factor_update_timer, + self.reactor.NEVER) + # Clear filament array + self.filament_array = [] + # Set extrude multiplier to 100% + self.gcode.run_script_from_command("M221 S100") gcmd.respond_info(response) def cmd_Get_Raw_Values(self, gcmd): - response = "ADC1=" - response += (" "+str(self.lastFilamentWidthReading)) - response += (" ADC2="+str(self.lastFilamentWidthReading2)) - response += (" RAW="+ - str(self.lastFilamentWidthReading - +self.lastFilamentWidthReading2)) + response = ("ADC1="+str(self.lastFilamentWidthReading)) + response += (" ADC2="+str(self.lastFilamentWidthReading2)) + response += (" RAW="+ + str(self.lastFilamentWidthReading + +self.lastFilamentWidthReading2)) gcmd.respond_info(response) def get_status(self, eventtime): status = self.runout_helper.get_status(eventtime) status.update({'Diameter': self.diameter, 'Raw':(self.lastFilamentWidthReading+ self.lastFilamentWidthReading2), - 'is_active':self.is_active}) + 'is_active':self.is_active, + 'flow_compensation_enabled':self.enable_flow_compensation}) return status def cmd_log_enable(self, gcmd): self.is_log = True - gcmd.respond_info("Filament width logging Turned On") + gcmd.respond_info("Filament width logging: ON") def cmd_log_disable(self, gcmd): self.is_log = False - gcmd.respond_info("Filament width logging Turned Off") + gcmd.respond_info("Filament width logging: OFF") def load_config(config): return HallFilamentWidthSensor(config)