Skip to content

Commit

Permalink
tsl1401cl_filament_width_sensor: Filament Width Sensor Support (#1132)
Browse files Browse the repository at this point in the history
Support added for filament diameter sensor. Sample configuration parameters added to example-extras.cfg.

Signed-off-by: Mustafa YILDIZ <mydiz@hotmail.com>
  • Loading branch information
nopbit authored and KevinOConnor committed Feb 14, 2019
1 parent c6d1d1b commit 0f674ef
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 0 deletions.
10 changes: 10 additions & 0 deletions config/example-extras.cfg
Expand Up @@ -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
Expand Down
31 changes: 31 additions & 0 deletions 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
130 changes: 130 additions & 0 deletions klippy/extras/tsl1401cl_filament_width_sensor.py
@@ -0,0 +1,130 @@
# Support for filament width sensor
#
# Copyright (C) 2019 Mustafa YILDIZ <mydiz@hotmail.com>
#
# 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)

0 comments on commit 0f674ef

Please sign in to comment.