From 0fc4f0946ed6801de1a3403311f5fc2836559dd9 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Wed, 4 Apr 2018 14:13:59 -0400 Subject: [PATCH] heater: Move adc logic into Thermistor class The Thermistor (and Linear) class should handle all the details of reading the ADC values and converting them to temperatures. So, move that logic out of the Heater() class. Signed-off-by: Kevin O'Connor --- klippy/extras/pid_calibrate.py | 2 +- klippy/heater.py | 84 ++++++++++++++++++++-------------- 2 files changed, 51 insertions(+), 35 deletions(-) diff --git a/klippy/extras/pid_calibrate.py b/klippy/extras/pid_calibrate.py index 1caffeb7..bd2b29d6 100644 --- a/klippy/extras/pid_calibrate.py +++ b/klippy/extras/pid_calibrate.py @@ -62,7 +62,7 @@ class ControlAutoTune: self.pwm_samples.append((read_time + heater.PWM_DELAY, value)) self.last_pwm = value self.heater.set_pwm(read_time, value) - def adc_callback(self, read_time, temp): + def temperature_callback(self, read_time, temp): self.temp_samples.append((read_time, temp)) if self.heating and temp >= self.heater.target_temp: self.heating = False diff --git a/klippy/heater.py b/klippy/heater.py index f8031538..b8f2a276 100644 --- a/klippy/heater.py +++ b/klippy/heater.py @@ -11,11 +11,18 @@ import math, logging, threading ###################################################################### KELVIN_TO_CELCIUS = -273.15 +SAMPLE_TIME = 0.001 +SAMPLE_COUNT = 8 +REPORT_TIME = 0.300 # Analog voltage to temperature converter for thermistors class Thermistor: def __init__(self, config, params): self.pullup = config.getfloat('pullup_resistor', 4700., above=0.) + ppins = config.get_printer().lookup_object('pins') + self.mcu_adc = ppins.setup_pin('adc', config.get('sensor_pin')) + self.mcu_adc.setup_adc_callback(REPORT_TIME, self.adc_callback) + self.temperature_callback = None self.c1 = self.c2 = self.c3 = 0. if 'beta' in params: self.calc_coefficients_beta(params) @@ -47,12 +54,20 @@ class Thermistor: self.c3 = 0. self.c2 = 1. / params['beta'] self.c1 = inv_t1 - self.c2 * ln_r1 - def calc_temp(self, adc): - adc = max(.00001, min(.99999, adc)) + def setup_minmax(self, min_temp, max_temp): + adc_range = [self.calc_adc(min_temp), self.calc_adc(max_temp)] + self.mcu_adc.setup_minmax(SAMPLE_TIME, SAMPLE_COUNT, + minval=min(adc_range), maxval=max(adc_range)) + def setup_callback(self, temperature_callback): + self.temperature_callback = temperature_callback + def adc_callback(self, read_time, read_value): + # Calculate temperature from adc + adc = max(.00001, min(.99999, read_value)) r = self.pullup * adc / (1.0 - adc) ln_r = math.log(r) inv_t = self.c1 + self.c2 * ln_r + self.c3 * ln_r**3 - return 1.0/inv_t + KELVIN_TO_CELCIUS + temp = 1.0/inv_t + KELVIN_TO_CELCIUS + self.temperature_callback(read_time, temp) def calc_adc(self, temp): inv_t = 1. / (temp - KELVIN_TO_CELCIUS) if self.c3: @@ -69,11 +84,22 @@ class Thermistor: class Linear: def __init__(self, config, params): adc_voltage = config.getfloat('adc_voltage', 5., above=0.) + ppins = config.get_printer().lookup_object('pins') + self.mcu_adc = ppins.setup_pin('adc', config.get('sensor_pin')) + self.mcu_adc.setup_adc_callback(REPORT_TIME, self.adc_callback) + self.temperature_callback = None slope = (params['t2'] - params['t1']) / (params['v2'] - params['v1']) self.gain = adc_voltage * slope self.offset = params['t1'] - params['v1'] * slope - def calc_temp(self, adc): - return adc * self.gain + self.offset + def setup_minmax(self, min_temp, max_temp): + adc_range = [self.calc_adc(min_temp), self.calc_adc(max_temp)] + self.mcu_adc.setup_minmax(SAMPLE_TIME, SAMPLE_COUNT, + minval=min(adc_range), maxval=max(adc_range)) + def setup_callback(self, temperature_callback): + self.temperature_callback = temperature_callback + def adc_callback(self, read_time, read_value): + temp = read_value * self.gain + self.offset + self.temperature_callback(read_time, temp) def calc_adc(self, temp): return (temp - self.offset) / self.gain @@ -95,9 +121,6 @@ Sensors = { # Heater ###################################################################### -SAMPLE_TIME = 0.001 -SAMPLE_COUNT = 8 -REPORT_TIME = 0.300 MAX_HEAT_TIME = 5.0 AMBIENT_TEMP = 25. PID_PARAM_BASE = 255. @@ -108,14 +131,14 @@ class error(Exception): class Heater: error = error - def __init__(self, config): - printer = config.get_printer() + def __init__(self, config, sensor): + self.sensor = sensor self.name = config.get_name() - sensor_type = config.get('sensor_type') - pheater = printer.lookup_object('heater') - self.sensor = pheater.setup_sensor(sensor_type, config) + printer = config.get_printer() self.min_temp = config.getfloat('min_temp', minval=KELVIN_TO_CELCIUS) self.max_temp = config.getfloat('max_temp', above=self.min_temp) + self.sensor.setup_minmax(self.min_temp, self.max_temp) + self.sensor.setup_callback(self.temperature_callback) self.min_extrude_temp = config.getfloat( 'min_extrude_temp', 170., minval=self.min_temp, maxval=self.max_temp) self.max_power = config.getfloat('max_power', 1., above=0., maxval=1.) @@ -135,13 +158,7 @@ class Heater: 'pwm_cycle_time', 0.100, above=0., maxval=REPORT_TIME) self.mcu_pwm.setup_cycle_time(pwm_cycle_time) self.mcu_pwm.setup_max_duration(MAX_HEAT_TIME) - self.mcu_adc = ppins.setup_pin('adc', config.get('sensor_pin')) - adc_range = [self.sensor.calc_adc(self.min_temp), - self.sensor.calc_adc(self.max_temp)] - self.mcu_adc.setup_minmax(SAMPLE_TIME, SAMPLE_COUNT, - minval=min(adc_range), maxval=max(adc_range)) - self.mcu_adc.setup_adc_callback(REPORT_TIME, self.adc_callback) - is_fileoutput = self.mcu_adc.get_mcu().is_fileoutput() + is_fileoutput = self.mcu_pwm.get_mcu().is_fileoutput() self.can_extrude = self.min_extrude_temp <= 0. or is_fileoutput self.control = algo(self, config) # pwm caching @@ -164,14 +181,13 @@ class Heater: self.name, value, pwm_time, self.last_temp, self.last_temp_time, self.target_temp) self.mcu_pwm.set_pwm(pwm_time, value) - def adc_callback(self, read_time, read_value): - temp = self.sensor.calc_temp(read_value) + def temperature_callback(self, read_time, temp): with self.lock: self.last_temp = temp self.last_temp_time = read_time self.can_extrude = (temp >= self.min_extrude_temp) - self.control.adc_callback(read_time, temp) - #logging.debug("temp: %.3f %f = %f", read_time, read_value, temp) + self.control.temperature_callback(read_time, temp) + #logging.debug("temp: %.3f %f = %f", read_time, temp) # External commands def set_temp(self, print_time, degrees): if degrees and (degrees < self.min_temp or degrees > self.max_temp): @@ -180,7 +196,7 @@ class Heater: with self.lock: self.target_temp = degrees def get_temp(self, eventtime): - print_time = self.mcu_adc.get_mcu().estimated_print_time(eventtime) - 5. + print_time = self.mcu_pwm.get_mcu().estimated_print_time(eventtime) - 5. with self.lock: if self.last_temp_time < print_time: return 0., self.target_temp @@ -218,7 +234,7 @@ class ControlBangBang: self.heater = heater self.max_delta = config.getfloat('max_delta', 2.0, above=0.) self.heating = False - def adc_callback(self, read_time, temp): + def temperature_callback(self, read_time, temp): if self.heating and temp >= self.heater.target_temp+self.max_delta: self.heating = False elif not self.heating and temp <= self.heater.target_temp-self.max_delta: @@ -251,7 +267,7 @@ class ControlPID: self.prev_temp_time = 0. self.prev_temp_deriv = 0. self.prev_temp_integ = 0. - def adc_callback(self, read_time, temp): + def temperature_callback(self, read_time, temp): time_diff = read_time - self.prev_temp_time # Calculate change of temperature temp_diff = temp - self.prev_temp @@ -293,19 +309,19 @@ class PrinterHeaters: self.heaters = {} def add_sensor(self, sensor_type, params): self.sensors[sensor_type] = params - def setup_sensor(self, sensor_type, config): - if sensor_type not in self.sensors: - raise self.printer.config_error("Unknown temperature sensor '%s'" % ( - sensor_type,)) - params = self.sensors[sensor_type] - return params['class'](config, params) def setup_heater(self, config): heater_name = config.get_name() if heater_name == 'extruder': heater_name = 'extruder0' if heater_name in self.heaters: raise config.error("Heater %s already registered" % (heater_name,)) - self.heaters[heater_name] = heater = Heater(config) + sensor_type = config.get('sensor_type') + if sensor_type not in self.sensors: + raise self.printer.config_error("Unknown temperature sensor '%s'" % ( + sensor_type,)) + params = self.sensors[sensor_type] + sensor = params['class'](config, params) + self.heaters[heater_name] = heater = Heater(config, sensor) return heater def lookup_heater(self, heater_name): if heater_name == 'extruder':