diff --git a/config/example-extras.cfg b/config/example-extras.cfg index c5b02957..14ea6391 100644 --- a/config/example-extras.cfg +++ b/config/example-extras.cfg @@ -7,6 +7,16 @@ # section defining the pins must be listed in the config file before # any sections using those pins. +# TSLl401CL Based Filament Width Sensor +#[tsl1401cl_filament_width_sensor] +#pin: analog5 +#default_nominal_filament_diameter: 1.75 # (mm) +# Maximum allowed filament diameter difference as mm +#max_difference: 0.2 +# The distance from sensor to the melting chamber as mm +#measurement_delay 100 + + # Z height probe. One may define this section to enable Z height # probing hardware. When this section is enabled, PROBE and diff --git a/docs/TSL1401CL_Filament_Width_Sensor.md b/docs/TSL1401CL_Filament_Width_Sensor.md new file mode 100644 index 00000000..98f7ff24 --- /dev/null +++ b/docs/TSL1401CL_Filament_Width_Sensor.md @@ -0,0 +1,31 @@ +This document describes Filament Width Sensor host module. Hardware used for developing this host module is based on TSL1401CL linear sensor array but it can work with any sensor array that has analog output. You can find designs at [thingiverse.com](https://www.thingiverse.com/search?q=filament%20width%20sensor) + +## How does it work? +Sensor generates analog output based on calculated filament width. Output voltage always equals to detected filament width (Ex. 1.65v, 1.70v, 3.0v). Host module monitors voltage changes and adjusts extrusion multiplier. + +## Configuration + [tsl1401cl_filament_width_sensor] + pin: analog5 + # Analog input pin for sensor output on Ramps board + + default_nominal_filament_diameter: 1.75 + # This parameter is in millimeters (mm) + + max_difference: 0.2 + # Maximum allowed filament diameter difference in millimeters (mm) + # If difference between nominal filament diameter and sensor output is more + # than +- max_difference, extrusion multiplier set back to %100 + + measurement_delay 100 + # The distance from sensor to the melting chamber/hot-end in millimeters (mm). + # The filament between the sensor and the hot-end will be treated as the default_nominal_filament_diameter. + # Host module works with FIFO logic. It keeps each sensor value and position in + # an array and POP them back in correct position. + +Sensor readings done with 10 mm intervals by default. If necessary you are free to change this setting by editing ***MEASUREMENT_INTERVAL_MM*** parameter in **filament_width_sensor.py** file. + +## Commands +**QUERY_FILAMENT_WIDTH** - Return the current measured filament width as result +**RESET_FILAMENT_WIDTH_SENSOR** – Clear all sensor readings. Can be used after filament change. +**DISABLE_FILAMENT_WIDTH_SENSOR** – Turn off the filament width sensor and stop using it to do flow control +**ENABLE_FILAMENT_WIDTH_SENSOR** - Turn on the filament width sensor and start using it to do flow control diff --git a/klippy/extras/tsl1401cl_filament_width_sensor.py b/klippy/extras/tsl1401cl_filament_width_sensor.py new file mode 100644 index 00000000..06530bf8 --- /dev/null +++ b/klippy/extras/tsl1401cl_filament_width_sensor.py @@ -0,0 +1,130 @@ +# Support for filament width sensor +# +# Copyright (C) 2019 Mustafa YILDIZ +# +# This file may be distributed under the terms of the GNU GPLv3 license. + +ADC_REPORT_TIME = 0.500 +ADC_SAMPLE_TIME = 0.001 +ADC_SAMPLE_COUNT = 8 +MEASUREMENT_INTERVAL_MM = 10 + +class FilamentWidthSensor: + def __init__(self, config): + self.printer = config.get_printer() + self.reactor = self.printer.get_reactor() + self.pin = config.get('pin') + self.nominal_filament_dia = config.getfloat('default_nominal_filament_diameter', above=1.0) + self.measurement_delay = config.getfloat('measurement_delay', above=0.) + self.measurement_max_difference = config.getfloat('max_difference', above=0.) + self.max_diameter = self.nominal_filament_dia + self.measurement_max_difference + self.min_diameter = self.nominal_filament_dia - self.measurement_max_difference + self.is_active = True + # filament array [position, filamentWidth] + self.filament_array = [] + self.lastFilamentWidthReading = 0 + # printer objects + self.gcode = self.toolhead = self.ppins = self.mcu_adc = None + self.printer.register_event_handler("klippy:ready", self.handle_ready) + # Start adc + self.ppins = self.printer.lookup_object('pins') + self.mcu_adc = self.ppins.setup_pin('adc', self.pin) + self.mcu_adc.setup_minmax(ADC_SAMPLE_TIME, ADC_SAMPLE_COUNT) + self.mcu_adc.setup_adc_callback(ADC_REPORT_TIME, self.adc_callback) + # extrude factor updating + self.extrude_factor_update_timer = self.reactor.register_timer( + self.extrude_factor_update_event) + + # Initialization + def handle_ready(self): + # Load printer objects + self.gcode = self.printer.lookup_object('gcode') + self.toolhead = self.printer.lookup_object('toolhead') + self.gcode.register_command('QUERY_FILAMENT_WIDTH', self.cmd_M407) + self.gcode.register_command('RESET_FILAMENT_WIDTH_SENSOR', self.cmd_ClearFilamentArray) + self.gcode.register_command('DISABLE_FILAMENT_WIDTH_SENSOR', self.cmd_M406) + self.gcode.register_command('ENABLE_FILAMENT_WIDTH_SENSOR', self.cmd_M405) + + # Start extrude factor update timer + self.reactor.update_timer(self.extrude_factor_update_timer, self.reactor.NOW) + + def adc_callback(self, read_time, read_value): + # read sensor value + self.lastFilamentWidthReading = round(read_value * 5, 2) + + def update_filament_array(self, last_epos): + # Fill array + if len(self.filament_array) > 0: + # Get last reading position in array & calculate next reading position + next_reading_position = self.filament_array[-1][0] + MEASUREMENT_INTERVAL_MM + if next_reading_position <= (last_epos + self.measurement_delay): + self.filament_array.append([last_epos + self.measurement_delay, self.lastFilamentWidthReading]) + else: + # add first item to array + self.filament_array.append([self.measurement_delay + last_epos, self.lastFilamentWidthReading]) + + def extrude_factor_update_event(self, eventtime): + # Update extrude factor + pos = self.toolhead.get_position() + last_epos = pos[3] + # Update filament array for lastFilamentWidthReading + self.update_filament_array(last_epos) + # Does filament exists + if self.lastFilamentWidthReading > 0.5: + if len(self.filament_array) > 0: + # Get first position in filament array + pending_position = self.filament_array[0][0] + if pending_position <= last_epos: + # Get first item in filament_array queue + item = self.filament_array.pop(0) + filament_width = item[1] + if (filament_width <= self.max_diameter) and (filament_width >= self.min_diameter): + percentage = round(self.nominal_filament_dia / filament_width * 100) + self.gcode.run_script("M221 S" + str(percentage)) + else: + self.gcode.run_script("M221 S100") + else: + self.gcode.run_script("M221 S100") + self.filament_array = [] + return eventtime + 1 + + def cmd_M407(self, params): + response = "" + if self.lastFilamentWidthReading > 0: + response += "Filament dia (measured mm): " + str(self.lastFilamentWidthReading) + else: + response += "Filament NOT present" + self.gcode.respond(response) + + def cmd_ClearFilamentArray(self, params): + self.filament_array = [] + self.gcode.respond("Filament width measurements cleared!") + # Set extrude multiplier to 100% + self.gcode.run_script_from_command("M221 S100") + + def cmd_M405(self, params): + 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 + self.reactor.update_timer(self.extrude_factor_update_timer, self.reactor.NOW) + self.gcode.respond(response) + + def cmd_M406(self, params): + 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") + self.gcode.respond(response) + +def load_config(config): + return FilamentWidthSensor(config)