1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
# Temperature measurements with thermistors
#
# Copyright (C) 2016-2019 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import math, logging
from . import adc_temperature
KELVIN_TO_CELSIUS = -273.15
# Analog voltage to temperature converter for thermistors
class Thermistor:
def __init__(self, pullup, inline_resistor):
self.pullup = pullup
self.inline_resistor = inline_resistor
self.c1 = self.c2 = self.c3 = 0.
def setup_coefficients(self, t1, r1, t2, r2, t3, r3, name=""):
# Calculate Steinhart-Hart coefficents from temp measurements.
# Arrange samples as 3 linear equations and solve for c1, c2, and c3.
inv_t1 = 1. / (t1 - KELVIN_TO_CELSIUS)
inv_t2 = 1. / (t2 - KELVIN_TO_CELSIUS)
inv_t3 = 1. / (t3 - KELVIN_TO_CELSIUS)
ln_r1 = math.log(r1)
ln_r2 = math.log(r2)
ln_r3 = math.log(r3)
ln3_r1, ln3_r2, ln3_r3 = ln_r1**3, ln_r2**3, ln_r3**3
inv_t12, inv_t13 = inv_t1 - inv_t2, inv_t1 - inv_t3
ln_r12, ln_r13 = ln_r1 - ln_r2, ln_r1 - ln_r3
ln3_r12, ln3_r13 = ln3_r1 - ln3_r2, ln3_r1 - ln3_r3
self.c3 = ((inv_t12 - inv_t13 * ln_r12 / ln_r13)
/ (ln3_r12 - ln3_r13 * ln_r12 / ln_r13))
if self.c3 <= 0.:
beta = ln_r13 / inv_t13
logging.warn("Using thermistor beta %.3f in heater %s", beta, name)
self.setup_coefficients_beta(t1, r1, beta)
return
self.c2 = (inv_t12 - self.c3 * ln3_r12) / ln_r12
self.c1 = inv_t1 - self.c2 * ln_r1 - self.c3 * ln3_r1
def setup_coefficients_beta(self, t1, r1, beta):
# Calculate equivalent Steinhart-Hart coefficents from beta
inv_t1 = 1. / (t1 - KELVIN_TO_CELSIUS)
ln_r1 = math.log(r1)
self.c3 = 0.
self.c2 = 1. / beta
self.c1 = inv_t1 - self.c2 * ln_r1
def calc_temp(self, adc):
# Calculate temperature from adc
adc = max(.00001, min(.99999, adc))
r = self.pullup * adc / (1.0 - adc)
ln_r = math.log(r - self.inline_resistor)
inv_t = self.c1 + self.c2 * ln_r + self.c3 * ln_r**3
return 1.0/inv_t + KELVIN_TO_CELSIUS
def calc_adc(self, temp):
# Calculate adc reading from a temperature
if temp <= KELVIN_TO_CELSIUS:
return 1.
inv_t = 1. / (temp - KELVIN_TO_CELSIUS)
if self.c3:
# Solve for ln_r using Cardano's formula
y = (self.c1 - inv_t) / (2. * self.c3)
x = math.sqrt((self.c2 / (3. * self.c3))**3 + y**2)
ln_r = math.pow(x - y, 1./3.) - math.pow(x + y, 1./3.)
else:
ln_r = (inv_t - self.c1) / self.c2
r = math.exp(ln_r) + self.inline_resistor
return r / (self.pullup + r)
# Create an ADC converter with a thermistor
def PrinterThermistor(config, params):
pullup = config.getfloat('pullup_resistor', 4700., above=0.)
inline_resistor = config.getfloat('inline_resistor', 0., minval=0.)
thermistor = Thermistor(pullup, inline_resistor)
if 'beta' in params:
thermistor.setup_coefficients_beta(
params['t1'], params['r1'], params['beta'])
else:
thermistor.setup_coefficients(
params['t1'], params['r1'], params['t2'], params['r2'],
params['t3'], params['r3'], name=config.get_name())
return adc_temperature.PrinterADCtoTemperature(config, thermistor)
# Custom defined thermistors from the config file
class CustomThermistor:
def __init__(self, config):
self.name = " ".join(config.get_name().split()[1:])
t1 = config.getfloat("temperature1", minval=KELVIN_TO_CELSIUS)
r1 = config.getfloat("resistance1", minval=0.)
beta = config.getfloat("beta", None, above=0.)
if beta is not None:
self.params = {'t1': t1, 'r1': r1, 'beta': beta}
return
t2 = config.getfloat("temperature2", minval=KELVIN_TO_CELSIUS)
r2 = config.getfloat("resistance2", minval=0.)
t3 = config.getfloat("temperature3", minval=KELVIN_TO_CELSIUS)
r3 = config.getfloat("resistance3", minval=0.)
(t1, r1), (t2, r2), (t3, r3) = sorted([(t1, r1), (t2, r2), (t3, r3)])
self.params = {'t1': t1, 'r1': r1, 't2': t2, 'r2': r2,
't3': t3, 'r3': r3}
def create(self, config):
return PrinterThermistor(config, self.params)
# Default sensors
Sensors = {
"EPCOS 100K B57560G104F": {
't1': 25., 'r1': 100000., 't2': 150., 'r2': 1641.9,
't3': 250., 'r3': 226.15 },
"ATC Semitec 104GT-2": {
't1': 20., 'r1': 126800., 't2': 150., 'r2': 1360.,
't3': 300., 'r3': 80.65 },
"SliceEngineering 450": {
't1': 25., 'r1': 500000., 't2': 200., 'r2': 3734.,
't3': 400., 'r3': 240. },
"TDK NTCG104LH104JT1": {
't1': 25., 'r1': 100000., 't2': 50., 'r2': 31230.,
't3': 125., 'r3': 2066. },
"NTC 100K beta 3950": { 't1': 25., 'r1': 100000., 'beta': 3950. },
"Honeywell 100K 135-104LAG-J01": { 't1': 25., 'r1': 100000., 'beta': 3974.},
"NTC 100K MGB18-104F39050L32": { 't1': 25., 'r1': 100000., 'beta': 4100. },
}
def load_config(config):
# Register default thermistor types
pheaters = config.get_printer().load_object(config, "heaters")
for sensor_type, params in Sensors.items():
func = (lambda config, params=params: PrinterThermistor(config, params))
pheaters.add_sensor_factory(sensor_type, func)
def load_config_prefix(config):
thermistor = CustomThermistor(config)
pheaters = config.get_printer().load_object(config, "heaters")
pheaters.add_sensor_factory(thermistor.name, thermistor.create)
|