kopia lustrzana https://github.com/jbruce12000/kiln-controller
exception handling completed for 31855 and 31856
rodzic
d6163a4c6e
commit
2fa18589a5
48
config.py
48
config.py
|
@ -66,17 +66,20 @@ gpio_heat = board.D23 #output that controls relay
|
|||
### Thermocouple Adapter selection:
|
||||
# max31855 - bitbang SPI interface
|
||||
# max31856 - bitbang SPI interface. must specify thermocouple_type.
|
||||
#max31855 = 1
|
||||
#max31856 = 0
|
||||
# see lib/max31856.py for other thermocouple_type, only applies to max31856
|
||||
max31855 = 1
|
||||
max31856 = 0
|
||||
# uncomment this if using MAX-31856
|
||||
#thermocouple_type = MAX31856.MAX31856_S_TYPE
|
||||
#thermocouple_type = ThermocoupleType.S
|
||||
|
||||
### Thermocouple Connection (using bitbang interfaces)
|
||||
#gpio_sensor_cs = 27
|
||||
#gpio_sensor_clock = 22
|
||||
#gpio_sensor_data = 17
|
||||
#gpio_sensor_di = 10 # only used with max31856
|
||||
# here are the possible max-31856 thermocouple types
|
||||
# ThermocoupleType.B
|
||||
# ThermocoupleType.E
|
||||
# ThermocoupleType.J
|
||||
# ThermocoupleType.K
|
||||
# ThermocoupleType.N
|
||||
# ThermocoupleType.R
|
||||
# ThermocoupleType.S
|
||||
# ThermocoupleType.T
|
||||
|
||||
########################################################################
|
||||
#
|
||||
|
@ -112,7 +115,7 @@ stop_integral_windup = True
|
|||
########################################################################
|
||||
#
|
||||
# Simulation parameters
|
||||
simulate = True
|
||||
simulate = False
|
||||
sim_t_env = 60.0 # deg C
|
||||
sim_c_heat = 500.0 # J/K heat capacity of heat element
|
||||
sim_c_oven = 5000.0 # J/K heat capacity of oven
|
||||
|
@ -179,16 +182,25 @@ ac_freq_50hz = False
|
|||
# - unknown error with thermocouple
|
||||
# - too many errors in a short period from thermocouple
|
||||
# but in some cases, you might want to ignore a specific error, log it,
|
||||
# and continue running your profile.
|
||||
# and continue running your profile instead of having the process die.
|
||||
#
|
||||
# You should only set these to True if you experience a problem
|
||||
# and WANT to ignore it to complete a firing.
|
||||
ignore_temp_too_high = False
|
||||
ignore_lost_connection_tc = False
|
||||
ignore_unknown_tc_error = False
|
||||
ignore_too_many_tc_errors = False
|
||||
# some kilns/thermocouples start erroneously reporting "short"
|
||||
# errors at higher temperatures due to plasma forming in the kiln.
|
||||
# Set this to True to ignore these errors and assume the temperature
|
||||
# reading was correct anyway
|
||||
ignore_tc_lost_connection = False
|
||||
ignore_tc_cold_junction_range_error = False
|
||||
ignore_tc_range_error = False
|
||||
ignore_tc_cold_junction_temp_high = False
|
||||
ignore_tc_cold_junction_temp_low = False
|
||||
ignore_tc_temp_high = False
|
||||
ignore_tc_temp_low = False
|
||||
ignore_tc_voltage_error = False
|
||||
ignore_tc_short_errors = False
|
||||
ignore_tc_unknown_error = False
|
||||
|
||||
# This overrides all possible thermocouple errors and prevents the
|
||||
# process from exiting.
|
||||
ignore_tc_too_many_errors = False
|
||||
|
||||
########################################################################
|
||||
# automatic restarts - if you have a power brown-out and the raspberry pi
|
||||
|
|
265
lib/max31855.py
265
lib/max31855.py
|
@ -1,265 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
import RPi.GPIO as GPIO
|
||||
import math
|
||||
|
||||
class MAX31855(object):
|
||||
'''Python driver for [MAX38155 Cold-Junction Compensated Thermocouple-to-Digital Converter](http://www.maximintegrated.com/datasheet/index.mvp/id/7273)
|
||||
Requires:
|
||||
- The [GPIO Library](https://code.google.com/p/raspberry-gpio-python/) (Already on most Raspberry Pi OS builds)
|
||||
- A [Raspberry Pi](http://www.raspberrypi.org/)
|
||||
|
||||
'''
|
||||
def __init__(self, cs_pin, clock_pin, data_pin, units = "c", board = GPIO.BCM):
|
||||
'''Initialize Soft (Bitbang) SPI bus
|
||||
|
||||
Parameters:
|
||||
- cs_pin: Chip Select (CS) / Slave Select (SS) pin (Any GPIO)
|
||||
- clock_pin: Clock (SCLK / SCK) pin (Any GPIO)
|
||||
- data_pin: Data input (SO / MOSI) pin (Any GPIO)
|
||||
- units: (optional) unit of measurement to return. ("c" (default) | "k" | "f")
|
||||
- board: (optional) pin numbering method as per RPi.GPIO library (GPIO.BCM (default) | GPIO.BOARD)
|
||||
|
||||
'''
|
||||
self.cs_pin = cs_pin
|
||||
self.clock_pin = clock_pin
|
||||
self.data_pin = data_pin
|
||||
self.units = units
|
||||
self.data = None
|
||||
self.board = board
|
||||
self.noConnection = self.shortToGround = self.shortToVCC = self.unknownError = False
|
||||
|
||||
# Initialize needed GPIO
|
||||
GPIO.setmode(self.board)
|
||||
GPIO.setup(self.cs_pin, GPIO.OUT)
|
||||
GPIO.setup(self.clock_pin, GPIO.OUT)
|
||||
GPIO.setup(self.data_pin, GPIO.IN)
|
||||
|
||||
# Pull chip select high to make chip inactive
|
||||
GPIO.output(self.cs_pin, GPIO.HIGH)
|
||||
|
||||
def get(self):
|
||||
'''Reads SPI bus and returns current value of thermocouple.'''
|
||||
self.read()
|
||||
self.checkErrors()
|
||||
#return getattr(self, "to_" + self.units)(self.data_to_tc_temperature())
|
||||
return getattr(self, "to_" + self.units)(self.data_to_LinearizedTempC())
|
||||
|
||||
def get_rj(self):
|
||||
'''Reads SPI bus and returns current value of reference junction.'''
|
||||
self.read()
|
||||
return getattr(self, "to_" + self.units)(self.data_to_rj_temperature())
|
||||
|
||||
def read(self):
|
||||
'''Reads 32 bits of the SPI bus & stores as an integer in self.data.'''
|
||||
bytesin = 0
|
||||
# Select the chip
|
||||
GPIO.output(self.cs_pin, GPIO.LOW)
|
||||
# Read in 32 bits
|
||||
for i in range(32):
|
||||
GPIO.output(self.clock_pin, GPIO.LOW)
|
||||
bytesin = bytesin << 1
|
||||
if (GPIO.input(self.data_pin)):
|
||||
bytesin = bytesin | 1
|
||||
GPIO.output(self.clock_pin, GPIO.HIGH)
|
||||
# Unselect the chip
|
||||
GPIO.output(self.cs_pin, GPIO.HIGH)
|
||||
# Save data
|
||||
self.data = bytesin
|
||||
|
||||
def checkErrors(self, data_32 = None):
|
||||
'''Checks error bits to see if there are any SCV, SCG, or OC faults'''
|
||||
if data_32 is None:
|
||||
data_32 = self.data
|
||||
anyErrors = (data_32 & 0x10000) != 0 # Fault bit, D16
|
||||
if anyErrors:
|
||||
self.noConnection = (data_32 & 0x00000001) != 0 # OC bit, D0
|
||||
self.shortToGround = (data_32 & 0x00000002) != 0 # SCG bit, D1
|
||||
self.shortToVCC = (data_32 & 0x00000004) != 0 # SCV bit, D2
|
||||
self.unknownError = not (self.noConnection | self.shortToGround | self.shortToVCC) # Errk!
|
||||
else:
|
||||
self.noConnection = self.shortToGround = self.shortToVCC = self.unknownError = False
|
||||
|
||||
def data_to_tc_temperature(self, data_32 = None):
|
||||
'''Takes an integer and returns a thermocouple temperature in celsius.'''
|
||||
if data_32 is None:
|
||||
data_32 = self.data
|
||||
tc_data = ((data_32 >> 18) & 0x3FFF)
|
||||
return self.convert_tc_data(tc_data)
|
||||
|
||||
def data_to_rj_temperature(self, data_32 = None):
|
||||
'''Takes an integer and returns a reference junction temperature in celsius.'''
|
||||
if data_32 is None:
|
||||
data_32 = self.data
|
||||
rj_data = ((data_32 >> 4) & 0xFFF)
|
||||
return self.convert_rj_data(rj_data)
|
||||
|
||||
def convert_tc_data(self, tc_data):
|
||||
'''Convert thermocouple data to a useful number (celsius).'''
|
||||
if tc_data & 0x2000:
|
||||
# two's compliment
|
||||
without_resolution = ~tc_data & 0x1FFF
|
||||
without_resolution += 1
|
||||
without_resolution *= -1
|
||||
else:
|
||||
without_resolution = tc_data & 0x1FFF
|
||||
return without_resolution * 0.25
|
||||
|
||||
def convert_rj_data(self, rj_data):
|
||||
'''Convert reference junction data to a useful number (celsius).'''
|
||||
if rj_data & 0x800:
|
||||
without_resolution = ~rj_data & 0x7FF
|
||||
without_resolution += 1
|
||||
without_resolution *= -1
|
||||
else:
|
||||
without_resolution = rj_data & 0x7FF
|
||||
return without_resolution * 0.0625
|
||||
|
||||
def to_c(self, celsius):
|
||||
'''Celsius passthrough for generic to_* method.'''
|
||||
return celsius
|
||||
|
||||
def to_k(self, celsius):
|
||||
'''Convert celsius to kelvin.'''
|
||||
return celsius + 273.15
|
||||
|
||||
def to_f(self, celsius):
|
||||
'''Convert celsius to fahrenheit.'''
|
||||
return celsius * 9.0/5.0 + 32
|
||||
|
||||
def cleanup(self):
|
||||
'''Selective GPIO cleanup'''
|
||||
GPIO.setup(self.cs_pin, GPIO.IN)
|
||||
GPIO.setup(self.clock_pin, GPIO.IN)
|
||||
|
||||
def data_to_LinearizedTempC(self, data_32 = None):
|
||||
'''Return the NIST-linearized thermocouple temperature value in degrees
|
||||
celsius. See https://learn.adafruit.com/calibrating-sensors/maxim-31855-linearization for more infoo.
|
||||
This code came from https://github.com/nightmechanic/FuzzypicoReflow/blob/master/lib/max31855.py
|
||||
'''
|
||||
if data_32 is None:
|
||||
data_32 = self.data
|
||||
# extract TC temp
|
||||
# Check if signed bit is set.
|
||||
if data_32 & 0x80000000:
|
||||
# Negative value, take 2's compliment. Compute this with subtraction
|
||||
# because python is a little odd about handling signed/unsigned.
|
||||
data_32 >>= 18
|
||||
data_32 -= 16384
|
||||
else:
|
||||
# Positive value, just shift the bits to get the value.
|
||||
data_32 >>= 18
|
||||
# Scale by 0.25 degrees C per bit and return value.
|
||||
TC_temp = data_32 * 0.25
|
||||
# Extract Internal Temp
|
||||
data_32 = self.data
|
||||
# Ignore bottom 4 bits of thermocouple data.
|
||||
data_32 >>= 4
|
||||
# Grab bottom 11 bits as internal temperature data.
|
||||
Internal_Temp= data_32 & 0x7FF
|
||||
if data_32 & 0x800:
|
||||
# Negative value, take 2's compliment. Compute this with subtraction
|
||||
# because python is a little odd about handling signed/unsigned.
|
||||
Internal_Temp -= 4096
|
||||
# Scale by 0.0625 degrees C per bit and return value.
|
||||
Internal_Temp = Internal_Temp * 0.0625
|
||||
|
||||
# MAX31855 thermocouple voltage reading in mV
|
||||
thermocoupleVoltage = (TC_temp - Internal_Temp) * 0.041276
|
||||
# MAX31855 cold junction voltage reading in mV
|
||||
coldJunctionTemperature = Internal_Temp
|
||||
coldJunctionVoltage = (-0.176004136860E-01 +
|
||||
0.389212049750E-01 * coldJunctionTemperature +
|
||||
0.185587700320E-04 * math.pow(coldJunctionTemperature, 2.0) +
|
||||
-0.994575928740E-07 * math.pow(coldJunctionTemperature, 3.0) +
|
||||
0.318409457190E-09 * math.pow(coldJunctionTemperature, 4.0) +
|
||||
-0.560728448890E-12 * math.pow(coldJunctionTemperature, 5.0) +
|
||||
0.560750590590E-15 * math.pow(coldJunctionTemperature, 6.0) +
|
||||
-0.320207200030E-18 * math.pow(coldJunctionTemperature, 7.0) +
|
||||
0.971511471520E-22 * math.pow(coldJunctionTemperature, 8.0) +
|
||||
-0.121047212750E-25 * math.pow(coldJunctionTemperature, 9.0) +
|
||||
0.118597600000E+00 * math.exp(-0.118343200000E-03 * math.pow((coldJunctionTemperature-0.126968600000E+03), 2.0)))
|
||||
# cold junction voltage + thermocouple voltage
|
||||
voltageSum = thermocoupleVoltage + coldJunctionVoltage
|
||||
# calculate corrected temperature reading based on coefficients for 3 different ranges
|
||||
# float b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10;
|
||||
if voltageSum < 0:
|
||||
b0 = 0.0000000E+00
|
||||
b1 = 2.5173462E+01
|
||||
b2 = -1.1662878E+00
|
||||
b3 = -1.0833638E+00
|
||||
b4 = -8.9773540E-01
|
||||
b5 = -3.7342377E-01
|
||||
b6 = -8.6632643E-02
|
||||
b7 = -1.0450598E-02
|
||||
b8 = -5.1920577E-04
|
||||
b9 = 0.0000000E+00
|
||||
elif voltageSum < 20.644:
|
||||
b0 = 0.000000E+00
|
||||
b1 = 2.508355E+01
|
||||
b2 = 7.860106E-02
|
||||
b3 = -2.503131E-01
|
||||
b4 = 8.315270E-02
|
||||
b5 = -1.228034E-02
|
||||
b6 = 9.804036E-04
|
||||
b7 = -4.413030E-05
|
||||
b8 = 1.057734E-06
|
||||
b9 = -1.052755E-08
|
||||
elif voltageSum < 54.886:
|
||||
b0 = -1.318058E+02
|
||||
b1 = 4.830222E+01
|
||||
b2 = -1.646031E+00
|
||||
b3 = 5.464731E-02
|
||||
b4 = -9.650715E-04
|
||||
b5 = 8.802193E-06
|
||||
b6 = -3.110810E-08
|
||||
b7 = 0.000000E+00
|
||||
b8 = 0.000000E+00
|
||||
b9 = 0.000000E+00
|
||||
else:
|
||||
# TODO: handle error - out of range
|
||||
return 0
|
||||
return (b0 +
|
||||
b1 * voltageSum +
|
||||
b2 * pow(voltageSum, 2.0) +
|
||||
b3 * pow(voltageSum, 3.0) +
|
||||
b4 * pow(voltageSum, 4.0) +
|
||||
b5 * pow(voltageSum, 5.0) +
|
||||
b6 * pow(voltageSum, 6.0) +
|
||||
b7 * pow(voltageSum, 7.0) +
|
||||
b8 * pow(voltageSum, 8.0) +
|
||||
b9 * pow(voltageSum, 9.0))
|
||||
|
||||
|
||||
class MAX31855Error(Exception):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
def __str__(self):
|
||||
return repr(self.value)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
# Multi-chip example
|
||||
import time
|
||||
cs_pins = [4, 17, 18, 24]
|
||||
clock_pin = 23
|
||||
data_pin = 22
|
||||
units = "f"
|
||||
thermocouples = []
|
||||
for cs_pin in cs_pins:
|
||||
thermocouples.append(MAX31855(cs_pin, clock_pin, data_pin, units))
|
||||
running = True
|
||||
while(running):
|
||||
try:
|
||||
for thermocouple in thermocouples:
|
||||
rj = thermocouple.get_rj()
|
||||
try:
|
||||
tc = thermocouple.get()
|
||||
except MAX31855Error as e:
|
||||
tc = "Error: "+ e.value
|
||||
running = False
|
||||
print("tc: {} and rj: {}".format(tc, rj))
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
running = False
|
||||
for thermocouple in thermocouples:
|
||||
thermocouple.cleanup()
|
|
@ -1,36 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
import logging
|
||||
|
||||
from Adafruit_MAX31855 import MAX31855
|
||||
|
||||
class MAX31855SPI(object):
|
||||
'''Python driver for [MAX38155 Cold-Junction Compensated Thermocouple-to-Digital Converter](http://www.maximintegrated.com/datasheet/index.mvp/id/7273)
|
||||
Requires:
|
||||
- adafruit's MAX31855 SPI-only device library
|
||||
|
||||
'''
|
||||
def __init__(self, spi_dev):
|
||||
self.max31855 = MAX31855.MAX31855(spi=spi_dev)
|
||||
self.log = logging.getLogger(__name__)
|
||||
|
||||
def get(self):
|
||||
'''Reads SPI bus and returns current value of thermocouple.'''
|
||||
state = self.max31855.readState()
|
||||
self.log.debug("status %s" % state)
|
||||
if state['openCircuit']:
|
||||
raise MAX31855Error('Not Connected')
|
||||
elif state['shortGND']:
|
||||
raise MAX31855Error('Short to Ground')
|
||||
elif state['shortVCC']:
|
||||
raise MAX31855Error('Short to VCC')
|
||||
elif state['fault']:
|
||||
raise MAX31855Error('Unknown Error')
|
||||
return self.max31855.readLinearizedTempC()
|
||||
|
||||
|
||||
class MAX31855SPIError(Exception):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
return repr(self.value)
|
341
lib/max31856.py
341
lib/max31856.py
|
@ -1,341 +0,0 @@
|
|||
"""
|
||||
max31856.py
|
||||
|
||||
Class which defines interaction with the MAX31856 sensor.
|
||||
|
||||
Copyright (c) 2019 John Robinson
|
||||
Author: John Robinson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
"""
|
||||
import logging
|
||||
import warnings
|
||||
|
||||
import Adafruit_GPIO as Adafruit_GPIO
|
||||
import Adafruit_GPIO.SPI as SPI
|
||||
|
||||
|
||||
class MAX31856(object):
|
||||
"""Class to represent an Adafruit MAX31856 thermocouple temperature
|
||||
measurement board.
|
||||
"""
|
||||
|
||||
# Board Specific Constants
|
||||
MAX31856_CONST_THERM_LSB = 2**-7
|
||||
MAX31856_CONST_THERM_BITS = 19
|
||||
MAX31856_CONST_CJ_LSB = 2**-6
|
||||
MAX31856_CONST_CJ_BITS = 14
|
||||
|
||||
### Register constants, see data sheet Table 6 (in Rev. 0) for info.
|
||||
# Read Addresses
|
||||
MAX31856_REG_READ_CR0 = 0x00
|
||||
MAX31856_REG_READ_CR1 = 0x01
|
||||
MAX31856_REG_READ_MASK = 0x02
|
||||
MAX31856_REG_READ_CJHF = 0x03
|
||||
MAX31856_REG_READ_CJLF = 0x04
|
||||
MAX31856_REG_READ_LTHFTH = 0x05
|
||||
MAX31856_REG_READ_LTHFTL = 0x06
|
||||
MAX31856_REG_READ_LTLFTH = 0x07
|
||||
MAX31856_REG_READ_LTLFTL = 0x08
|
||||
MAX31856_REG_READ_CJTO = 0x09
|
||||
MAX31856_REG_READ_CJTH = 0x0A # Cold-Junction Temperature Register, MSB
|
||||
MAX31856_REG_READ_CJTL = 0x0B # Cold-Junction Temperature Register, LSB
|
||||
MAX31856_REG_READ_LTCBH = 0x0C # Linearized TC Temperature, Byte 2
|
||||
MAX31856_REG_READ_LTCBM = 0x0D # Linearized TC Temperature, Byte 1
|
||||
MAX31856_REG_READ_LTCBL = 0x0E # Linearized TC Temperature, Byte 0
|
||||
MAX31856_REG_READ_FAULT = 0x0F # Fault status register
|
||||
|
||||
# Write Addresses
|
||||
MAX31856_REG_WRITE_CR0 = 0x80
|
||||
MAX31856_REG_WRITE_CR1 = 0x81
|
||||
MAX31856_REG_WRITE_MASK = 0x82
|
||||
MAX31856_REG_WRITE_CJHF = 0x83
|
||||
MAX31856_REG_WRITE_CJLF = 0x84
|
||||
MAX31856_REG_WRITE_LTHFTH = 0x85
|
||||
MAX31856_REG_WRITE_LTHFTL = 0x86
|
||||
MAX31856_REG_WRITE_LTLFTH = 0x87
|
||||
MAX31856_REG_WRITE_LTLFTL = 0x88
|
||||
MAX31856_REG_WRITE_CJTO = 0x89
|
||||
MAX31856_REG_WRITE_CJTH = 0x8A # Cold-Junction Temperature Register, MSB
|
||||
MAX31856_REG_WRITE_CJTL = 0x8B # Cold-Junction Temperature Register, LSB
|
||||
|
||||
# Pre-config Register Options
|
||||
MAX31856_CR0_READ_ONE = 0x40 # One shot reading, delay approx. 200ms then read temp registers
|
||||
MAX31856_CR0_READ_CONT = 0x80 # Continuous reading, delay approx. 100ms between readings
|
||||
|
||||
# Thermocouple Types
|
||||
MAX31856_B_TYPE = 0x0 # Read B Type Thermocouple
|
||||
MAX31856_E_TYPE = 0x1 # Read E Type Thermocouple
|
||||
MAX31856_J_TYPE = 0x2 # Read J Type Thermocouple
|
||||
MAX31856_K_TYPE = 0x3 # Read K Type Thermocouple
|
||||
MAX31856_N_TYPE = 0x4 # Read N Type Thermocouple
|
||||
MAX31856_R_TYPE = 0x5 # Read R Type Thermocouple
|
||||
MAX31856_S_TYPE = 0x6 # Read S Type Thermocouple
|
||||
MAX31856_T_TYPE = 0x7 # Read T Type Thermocouple
|
||||
|
||||
def __init__(self, tc_type=MAX31856_S_TYPE, units="c", avgsel=0x0, ac_freq_50hz=False, ocdetect=0x1, software_spi=None, hardware_spi=None, gpio=None):
|
||||
"""
|
||||
Initialize MAX31856 device with software SPI on the specified CLK,
|
||||
CS, and DO pins. Alternatively can specify hardware SPI by sending an
|
||||
SPI.SpiDev device in the spi parameter.
|
||||
|
||||
Args:
|
||||
tc_type (1-byte Hex): Type of Thermocouple. Choose from class variables of the form
|
||||
MAX31856.MAX31856_X_TYPE.
|
||||
avgsel (1-byte Hex): Type of Averaging. Choose from values in CR0 table of datasheet.
|
||||
Default is single sample.
|
||||
ac_freq_50hz: Set to True if your AC frequency is 50Hz, Set to False for 60Hz,
|
||||
ocdetect: Detect open circuit errors (ie broken thermocouple). Choose from values in CR1 table of datasheet
|
||||
software_spi (dict): Contains the pin assignments for software SPI, as defined below:
|
||||
clk (integer): Pin number for software SPI clk
|
||||
cs (integer): Pin number for software SPI cs
|
||||
do (integer): Pin number for software SPI MISO
|
||||
di (integer): Pin number for software SPI MOSI
|
||||
hardware_spi (SPI.SpiDev): If using hardware SPI, define the connection
|
||||
"""
|
||||
self._logger = logging.getLogger('Adafruit_MAX31856.MAX31856')
|
||||
self._spi = None
|
||||
self.tc_type = tc_type
|
||||
self.avgsel = avgsel
|
||||
self.units = units
|
||||
self.noConnection = self.shortToGround = self.shortToVCC = self.unknownError = False
|
||||
|
||||
# Handle hardware SPI
|
||||
if hardware_spi is not None:
|
||||
self._logger.debug('Using hardware SPI')
|
||||
self._spi = hardware_spi
|
||||
elif software_spi is not None:
|
||||
self._logger.debug('Using software SPI')
|
||||
# Default to platform GPIO if not provided.
|
||||
if gpio is None:
|
||||
gpio = Adafruit_GPIO.get_platform_gpio()
|
||||
self._spi = SPI.BitBang(gpio, software_spi['clk'], software_spi['di'],
|
||||
software_spi['do'], software_spi['cs'])
|
||||
else:
|
||||
raise ValueError(
|
||||
'Must specify either spi for for hardware SPI or clk, cs, and do for softwrare SPI!')
|
||||
self._spi.set_clock_hz(5000000)
|
||||
# According to Wikipedia (on SPI) and MAX31856 Datasheet:
|
||||
# SPI mode 1 corresponds with correct timing, CPOL = 0, CPHA = 1
|
||||
self._spi.set_mode(1)
|
||||
self._spi.set_bit_order(SPI.MSBFIRST)
|
||||
|
||||
self.cr0 = self.MAX31856_CR0_READ_CONT | ((ocdetect & 3) << 4) | (1 if ac_freq_50hz else 0)
|
||||
self.cr1 = (((self.avgsel & 7) << 4) + (self.tc_type & 0x0f))
|
||||
|
||||
# Setup for reading continuously with T-Type thermocouple
|
||||
self._write_register(self.MAX31856_REG_WRITE_CR0, 0)
|
||||
self._write_register(self.MAX31856_REG_WRITE_CR1, self.cr1)
|
||||
self._write_register(self.MAX31856_REG_WRITE_CR0, self.cr0)
|
||||
|
||||
@staticmethod
|
||||
def _cj_temp_from_bytes(msb, lsb):
|
||||
"""
|
||||
Takes in the msb and lsb from a Cold Junction (CJ) temperature reading and converts it
|
||||
into a decimal value.
|
||||
|
||||
This function was removed from readInternalTempC() and moved to its own method to allow for
|
||||
easier testing with standard values.
|
||||
|
||||
Args:
|
||||
msb (hex): Most significant byte of CJ temperature
|
||||
lsb (hex): Least significant byte of a CJ temperature
|
||||
|
||||
"""
|
||||
# (((msb w/o +/-) shifted by number of 1 byte above lsb)
|
||||
# + val_low_byte)
|
||||
# >> shifted back by # of dead bits
|
||||
temp_bytes = (((msb & 0x7F) << 8) + lsb) >> 2
|
||||
|
||||
if msb & 0x80:
|
||||
# Negative Value. Scale back by number of bits
|
||||
temp_bytes -= 2**(MAX31856.MAX31856_CONST_CJ_BITS -1)
|
||||
|
||||
# temp_bytes*value of lsb
|
||||
temp_c = temp_bytes*MAX31856.MAX31856_CONST_CJ_LSB
|
||||
|
||||
return temp_c
|
||||
|
||||
@staticmethod
|
||||
def _thermocouple_temp_from_bytes(byte0, byte1, byte2):
|
||||
"""
|
||||
Converts the thermocouple byte values to a decimal value.
|
||||
|
||||
This function was removed from readInternalTempC() and moved to its own method to allow for
|
||||
easier testing with standard values.
|
||||
|
||||
Args:
|
||||
byte2 (hex): Most significant byte of thermocouple temperature
|
||||
byte1 (hex): Middle byte of thermocouple temperature
|
||||
byte0 (hex): Least significant byte of a thermocouple temperature
|
||||
|
||||
Returns:
|
||||
temp_c (float): Temperature in degrees celsius
|
||||
"""
|
||||
# (((val_high_byte w/o +/-) shifted by 2 bytes above LSB)
|
||||
# + (val_mid_byte shifted by number 1 byte above LSB)
|
||||
# + val_low_byte )
|
||||
# >> back shift by number of dead bits
|
||||
temp_bytes = (((byte2 & 0x7F) << 16) + (byte1 << 8) + byte0)
|
||||
temp_bytes = temp_bytes >> 5
|
||||
|
||||
if byte2 & 0x80:
|
||||
temp_bytes -= 2**(MAX31856.MAX31856_CONST_THERM_BITS -1)
|
||||
|
||||
# temp_bytes*value of LSB
|
||||
temp_c = temp_bytes*MAX31856.MAX31856_CONST_THERM_LSB
|
||||
|
||||
return temp_c
|
||||
|
||||
def read_internal_temp_c(self):
|
||||
"""
|
||||
Return internal temperature value in degrees celsius.
|
||||
"""
|
||||
val_low_byte = self._read_register(self.MAX31856_REG_READ_CJTL)
|
||||
val_high_byte = self._read_register(self.MAX31856_REG_READ_CJTH)
|
||||
|
||||
temp_c = MAX31856._cj_temp_from_bytes(val_high_byte, val_low_byte)
|
||||
self._logger.debug("Cold Junction Temperature {0} deg. C".format(temp_c))
|
||||
|
||||
return temp_c
|
||||
|
||||
def read_temp_c(self):
|
||||
"""
|
||||
Return the thermocouple temperature value in degrees celsius.
|
||||
"""
|
||||
val_low_byte = self._read_register(self.MAX31856_REG_READ_LTCBL)
|
||||
val_mid_byte = self._read_register(self.MAX31856_REG_READ_LTCBM)
|
||||
val_high_byte = self._read_register(self.MAX31856_REG_READ_LTCBH)
|
||||
|
||||
temp_c = MAX31856._thermocouple_temp_from_bytes(val_low_byte, val_mid_byte, val_high_byte)
|
||||
|
||||
self._logger.debug("Thermocouple Temperature {0} deg. C".format(temp_c))
|
||||
|
||||
return temp_c
|
||||
|
||||
def read_fault_register(self):
|
||||
"""Return bytes containing fault codes and hardware problems.
|
||||
|
||||
TODO: Could update in the future to return human readable values
|
||||
"""
|
||||
reg = self._read_register(self.MAX31856_REG_READ_FAULT)
|
||||
return reg
|
||||
|
||||
def _read_register(self, address):
|
||||
"""
|
||||
Reads a register at address from the MAX31856
|
||||
|
||||
Args:
|
||||
address (8-bit Hex): Address for read register. Format 0Xh. Constants listed in class
|
||||
as MAX31856_REG_READ_*
|
||||
|
||||
Note:
|
||||
SPI transfer method is used. The address is written in as the first byte, and then a
|
||||
dummy value as the second byte. The data from the sensor is contained in the second
|
||||
byte, the dummy byte is only used to keep the SPI clock ticking as we read in the
|
||||
value. The first returned byte is discarded because no data is transmitted while
|
||||
specifying the register address.
|
||||
"""
|
||||
raw = self._spi.transfer([address, 0x00])
|
||||
if raw is None or len(raw) != 2:
|
||||
raise RuntimeError('Did not read expected number of bytes from device!')
|
||||
|
||||
value = raw[1]
|
||||
self._logger.debug('Read Register: 0x{0:02X}, Raw Value: 0x{1:02X}'.format(
|
||||
(address & 0xFFFF), (value & 0xFFFF)))
|
||||
return value
|
||||
|
||||
def _write_register(self, address, write_value):
|
||||
"""
|
||||
Writes to a register at address from the MAX31856
|
||||
|
||||
Args:
|
||||
address (8-bit Hex): Address for read register. Format 0Xh. Constants listed in class
|
||||
as MAX31856_REG_WRITE_*
|
||||
write_value (8-bit Hex): Value to write to the register
|
||||
"""
|
||||
self._spi.transfer([address, write_value])
|
||||
self._logger.debug('Wrote Register: 0x{0:02X}, Value 0x{1:02X}'.format((address & 0xFF),
|
||||
(write_value & 0xFF)))
|
||||
|
||||
# If we've gotten this far without an exception, the transmission must've gone through
|
||||
return True
|
||||
|
||||
# Deprecated Methods
|
||||
def readTempC(self): #pylint: disable-msg=invalid-name
|
||||
"""Depreciated due to Python naming convention, use read_temp_c instead
|
||||
"""
|
||||
warnings.warn("Depreciated due to Python naming convention, use read_temp_c() instead", DeprecationWarning)
|
||||
return read_temp_c(self)
|
||||
|
||||
def readInternalTempC(self): #pylint: disable-msg=invalid-name
|
||||
"""Depreciated due to Python naming convention, use read_internal_temp_c instead
|
||||
"""
|
||||
warnings.warn("Depreciated due to Python naming convention, use read_internal_temp_c() instead", DeprecationWarning)
|
||||
return read_internal_temp_c(self)
|
||||
|
||||
# added by jbruce to mimic MAX31855 lib
|
||||
def to_c(self, celsius):
|
||||
'''Celsius passthrough for generic to_* method.'''
|
||||
return celsius
|
||||
|
||||
def to_k(self, celsius):
|
||||
'''Convert celsius to kelvin.'''
|
||||
return celsius + 273.15
|
||||
|
||||
def to_f(self, celsius):
|
||||
'''Convert celsius to fahrenheit.'''
|
||||
return celsius * 9.0/5.0 + 32
|
||||
|
||||
def checkErrors(self):
|
||||
data = self.read_fault_register()
|
||||
self.noConnection = (data & 0x00000001) != 0
|
||||
self.unknownError = (data & 0xfe) != 0
|
||||
|
||||
def get(self):
|
||||
self.checkErrors()
|
||||
celcius = self.read_temp_c()
|
||||
return getattr(self, "to_" + self.units)(celcius)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
# Multi-chip example
|
||||
import time
|
||||
cs_pins = [6]
|
||||
clock_pin = 13
|
||||
data_pin = 5
|
||||
di_pin = 26
|
||||
units = "c"
|
||||
thermocouples = []
|
||||
for cs_pin in cs_pins:
|
||||
thermocouples.append(MAX31856(avgsel=0, ac_freq_50hz=True, tc_type=MAX31856.MAX31856_K_TYPE, software_spi={'clk': clock_pin, 'cs': cs_pin, 'do': data_pin, 'di': di_pin}, units=units))
|
||||
|
||||
running = True
|
||||
while(running):
|
||||
try:
|
||||
for thermocouple in thermocouples:
|
||||
rj = thermocouple.read_internal_temp_c()
|
||||
tc = thermocouple.get()
|
||||
print("tc: {} and rj: {}, NC:{} ??:{}".format(tc, rj, thermocouple.noConnection, thermocouple.unknownError))
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
running = False
|
||||
for thermocouple in thermocouples:
|
||||
thermocouple.cleanup()
|
223
lib/oven.py
223
lib/oven.py
|
@ -1,6 +1,5 @@
|
|||
import threading
|
||||
import time
|
||||
import random
|
||||
import datetime
|
||||
import logging
|
||||
import json
|
||||
|
@ -30,8 +29,12 @@ class Duplogger():
|
|||
|
||||
duplog = Duplogger().logref()
|
||||
|
||||
|
||||
class Output(object):
|
||||
'''This represents a GPIO output that controls a solid
|
||||
state relay to turn the kiln elements on and off.
|
||||
inputs
|
||||
config.gpio_heat
|
||||
'''
|
||||
def __init__(self):
|
||||
self.active = False
|
||||
self.heater = digitalio.DigitalInOut(config.gpio_heat)
|
||||
|
@ -48,11 +51,18 @@ class Output(object):
|
|||
|
||||
# wrapper for blinka board
|
||||
class Board(object):
|
||||
'''This represents a blinka board where this code
|
||||
runs.
|
||||
'''
|
||||
def __init__(self):
|
||||
log.info("board: %s" % (self.name))
|
||||
self.temp_sensor.start()
|
||||
|
||||
class RealBoard(Board):
|
||||
'''Each board has a thermocouple board attached to it.
|
||||
Any blinka board that supports SPI can be used. The
|
||||
board is automatically detected by blinka.
|
||||
'''
|
||||
def __init__(self):
|
||||
self.name = None
|
||||
self.load_libs()
|
||||
|
@ -70,12 +80,18 @@ class RealBoard(Board):
|
|||
return Max31856()
|
||||
|
||||
class SimulatedBoard(Board):
|
||||
'''Simulated board used during simulations.
|
||||
See config.simulate
|
||||
'''
|
||||
def __init__(self):
|
||||
self.name = "simulated"
|
||||
self.temp_sensor = TempSensorSimulated()
|
||||
Board.__init__(self)
|
||||
|
||||
class TempSensor(threading.Thread):
|
||||
'''Used by the Board class. Each Board must have
|
||||
a TempSensor.
|
||||
'''
|
||||
def __init__(self):
|
||||
threading.Thread.__init__(self)
|
||||
self.daemon = True
|
||||
|
@ -83,7 +99,7 @@ class TempSensor(threading.Thread):
|
|||
self.status = ThermocoupleTracker()
|
||||
|
||||
class TempSensorSimulated(TempSensor):
|
||||
'''not much here, just need to be able to set the temperature'''
|
||||
'''Simulates a temperature sensor '''
|
||||
def __init__(self):
|
||||
TempSensor.__init__(self)
|
||||
self.simulated_temperature = 0
|
||||
|
@ -91,8 +107,11 @@ class TempSensorSimulated(TempSensor):
|
|||
return self.simulated_temperature
|
||||
|
||||
class TempSensorReal(TempSensor):
|
||||
'''real temperature sensor thread that takes N measurements
|
||||
during the time_step'''
|
||||
'''real temperature sensor that takes many measurements
|
||||
during the time_step
|
||||
inputs
|
||||
config.temperature_average_samples
|
||||
'''
|
||||
def __init__(self):
|
||||
TempSensor.__init__(self)
|
||||
self.sleeptime = self.time_step / float(config.temperature_average_samples)
|
||||
|
@ -102,35 +121,20 @@ class TempSensorReal(TempSensor):
|
|||
|
||||
def get_temperature(self):
|
||||
'''read temp from tc and convert if needed'''
|
||||
temp = self.raw_temp() # raw_temp provided by subclasses
|
||||
if config.temp_scale.lower() == "f":
|
||||
temp = (temp*9/5)+32
|
||||
return temp
|
||||
|
||||
#def get_temperature(self):
|
||||
# try:
|
||||
# temp = self.raw_temp
|
||||
# if config.temp_scale.lower() == "f":
|
||||
# temp = (temp*9/5)+32
|
||||
# log.info("temp = %0.2f" % temp)
|
||||
# return temp
|
||||
# except RuntimeError as rte:
|
||||
# if rte.args and rte.args[0] == "thermocouple not connected":
|
||||
# self.bad_count = self.bad_count + 1
|
||||
# if rte.args and rte.args[0] == "short circuit to ground":
|
||||
# if not config.ignore_tc_short_errors:
|
||||
# self.bad_count = self.bad_count + 1
|
||||
# if rte.args and rte.args[0] == "short circuit to power":
|
||||
# if not config.ignore_tc_short_errors:
|
||||
# self.bad_count = self.bad_count + 1
|
||||
# if rte.args and rte.args[0] == "faulty reading":
|
||||
# self.bad_count = self.bad_count + 1
|
||||
#
|
||||
# log.error("Problem reading temp %s" % (rte.args[0]))
|
||||
# # fix still need to include max-31856 errors by calling fault
|
||||
# # and checking a dict of possible faults. what a shitty way to handle
|
||||
# # errors.
|
||||
# return None
|
||||
try:
|
||||
temp = self.raw_temp() # raw_temp provided by subclasses
|
||||
if config.temp_scale.lower() == "f":
|
||||
temp = (temp*9/5)+32
|
||||
self.status.good()
|
||||
return temp
|
||||
except ThermocoupleError as tce:
|
||||
if tce.ignore:
|
||||
log.error("Problem reading temp (ignored) %s" % (tce.message))
|
||||
self.status.good()
|
||||
else:
|
||||
log.error("Problem reading temp %s" % (tce.message))
|
||||
self.status.bad()
|
||||
return None
|
||||
|
||||
def temperature(self):
|
||||
'''average temp over a duty cycle'''
|
||||
|
@ -138,20 +142,14 @@ class TempSensorReal(TempSensor):
|
|||
|
||||
def run(self):
|
||||
while True:
|
||||
# not at all sure this try/except should be here
|
||||
# might be better getting the temp
|
||||
try:
|
||||
temp = self.get_temperature()
|
||||
self.status.good()
|
||||
temp = self.get_temperature()
|
||||
if temp:
|
||||
self.temptracker.add(temp)
|
||||
except RuntimeError as rte:
|
||||
self.status.bad()
|
||||
log.error("Problem reading temp %s" % (rte.args[0]))
|
||||
|
||||
time.sleep(self.sleeptime)
|
||||
|
||||
class TempTracker(object):
|
||||
'''creates a sliding window of temperatures
|
||||
'''creates a sliding window of N temperatures per
|
||||
config.sensor_time_wait
|
||||
'''
|
||||
def __init__(self):
|
||||
self.size = config.temperature_average_samples
|
||||
|
@ -204,8 +202,6 @@ class ThermocoupleTracker(object):
|
|||
|
||||
class Max31855(TempSensorReal):
|
||||
'''each subclass expected to handle errors and get temperature'''
|
||||
# FIX I need unified errors from these classes since the underlying
|
||||
# implementations are different
|
||||
def __init__(self):
|
||||
TempSensorReal.__init__(self)
|
||||
log.info("thermocouple MAX31855")
|
||||
|
@ -213,7 +209,83 @@ class Max31855(TempSensorReal):
|
|||
self.thermocouple = adafruit_max31855.MAX31855(self.spi, self.cs)
|
||||
|
||||
def raw_temp(self):
|
||||
return self.thermocouple.temperature_NIST
|
||||
try:
|
||||
return self.thermocouple.temperature_NIST
|
||||
except RuntimeError as rte:
|
||||
if rte.args and rte.args[0]:
|
||||
raise Max31855_Error(rte.args[0])
|
||||
raise Max31855_Error('unknown')
|
||||
|
||||
class ThermocoupleError(Exception):
|
||||
'''
|
||||
thermocouple exception parent class to handle mapping of error messages
|
||||
and make them consistent across adafruit libraries. Also set whether
|
||||
each exception should be ignored based on settings in config.py.
|
||||
'''
|
||||
def __init__(self, message):
|
||||
self.ignore = False
|
||||
self.message = message
|
||||
self.map_message()
|
||||
self.set_ignore()
|
||||
super().__init__(self.message)
|
||||
|
||||
def set_ignore(self):
|
||||
if self.message == "not connected" and config.ignore_tc_lost_connection == True:
|
||||
self.ignore = True
|
||||
if self.message == "short circuit" and config.ignore_tc_short_errors == True:
|
||||
self.ignore = True
|
||||
if self.message == "unknown" and config.ignore_tc_unknown_error == True:
|
||||
self.ignore = True
|
||||
if self.message == "cold junction range fault" and config.ignore_tc_cold_junction_range_error == True:
|
||||
self.ignore = True
|
||||
if self.message == "thermocouple range fault" and config.ignore_tc_range_error == True:
|
||||
self.ignore = True
|
||||
if self.message == "cold junction temp too high" and config.ignore_tc_cold_junction_temp_high == True:
|
||||
self.ignore = True
|
||||
if self.message == "cold junction temp too low" and config.ignore_tc_cold_junction_temp_low == True:
|
||||
self.ignore = True
|
||||
if self.message == "thermocouple temp too high" and config.ignore_tc_temp_high == True:
|
||||
self.ignore = True
|
||||
if self.message == "thermocouple temp too low" and config.ignore_tc_temp_low == True:
|
||||
self.ignore = True
|
||||
if self.message == "voltage too high or low" and config.ignore_tc_voltage_error == True:
|
||||
self.ignore = True
|
||||
|
||||
def map_message(self):
|
||||
try:
|
||||
self.message = self.map[self.orig_message]
|
||||
except KeyError:
|
||||
self.message = "unknown"
|
||||
|
||||
class Max31855_Error(ThermocoupleError):
|
||||
'''
|
||||
All children must set self.orig_message and self.map
|
||||
'''
|
||||
def __init__(self, message):
|
||||
self.orig_message = message
|
||||
# this purposefully makes "fault reading" and
|
||||
# "Total thermoelectric voltage out of range..." unknown errors
|
||||
self.map = {
|
||||
"thermocouple not connected" : "not connected",
|
||||
"short circuit to ground" : "short circuit",
|
||||
"short circuit to power" : "short circuit",
|
||||
}
|
||||
super().__init__(self.message)
|
||||
|
||||
class Max31856_Error(ThermocoupleError):
|
||||
def __init__(self, message):
|
||||
self.orig_message = message
|
||||
self.map = {
|
||||
"cj_range" : "cold junction range fault",
|
||||
"tc_range" : "thermocouple range fault",
|
||||
"cj_high" : "cold junction temp too high",
|
||||
"cj_low" : "cold junction temp too low",
|
||||
"tc_high" : "thermocouple temp too high",
|
||||
"tc_low" : "thermocouple temp too low",
|
||||
"voltage" : "voltage too high or low",
|
||||
"open_tc" : "not connected"
|
||||
}
|
||||
super().__init__(self.message)
|
||||
|
||||
class Max31856(TempSensorReal):
|
||||
'''each subclass expected to handle errors and get temperature'''
|
||||
|
@ -229,7 +301,16 @@ class Max31856(TempSensorReal):
|
|||
self.thermocouple.noise_rejection(60)
|
||||
|
||||
def raw_temp(self):
|
||||
return self.thermocouple.temperature
|
||||
# The underlying adafruit library does not throw exceptions
|
||||
# for thermocouple errors. Instead, they are stored in
|
||||
# dict named self.thermocouple.fault. Here we check that
|
||||
# dict for errors and raise an exception.
|
||||
# and raise Max31856_Error(message)
|
||||
temp = self.thermocouple.temperature
|
||||
for k,v in self.thermocouple.fault:
|
||||
if v:
|
||||
raise Max31856_Error(k)
|
||||
return temp
|
||||
|
||||
class Oven(threading.Thread):
|
||||
'''parent oven class. this has all the common code
|
||||
|
@ -254,21 +335,6 @@ class Oven(threading.Thread):
|
|||
|
||||
def run_profile(self, profile, startat=0):
|
||||
self.reset()
|
||||
|
||||
# FIX, these need to be moved
|
||||
#if self.board.temp_sensor.noConnection:
|
||||
# log.info("Refusing to start profile - thermocouple not connected")
|
||||
# return
|
||||
#if self.board.temp_sensor.shortToGround:
|
||||
# log.info("Refusing to start profile - thermocouple short to ground")
|
||||
# return
|
||||
#if self.board.temp_sensor.shortToVCC:
|
||||
# log.info("Refusing to start profile - thermocouple short to VCC")
|
||||
# return
|
||||
#if self.board.temp_sensor.unknownError:
|
||||
# log.info("Refusing to start profile - thermocouple unknown error")
|
||||
# return
|
||||
|
||||
self.startat = startat * 60
|
||||
self.runtime = self.startat
|
||||
self.start_time = datetime.datetime.now() - datetime.timedelta(seconds=self.startat)
|
||||
|
@ -310,27 +376,16 @@ class Oven(threading.Thread):
|
|||
|
||||
def reset_if_emergency(self):
|
||||
'''reset if the temperature is way TOO HOT, or other critical errors detected'''
|
||||
# FIX - need to fix this whole thing...
|
||||
#if (self.board.temp_sensor.temperature() + config.thermocouple_offset >=
|
||||
# config.emergency_shutoff_temp):
|
||||
# log.info("emergency!!! temperature too high")
|
||||
# if config.ignore_temp_too_high == False:
|
||||
# self.abort_run()
|
||||
|
||||
#if self.board.temp_sensor.noConnection:
|
||||
# log.info("emergency!!! lost connection to thermocouple")
|
||||
# if config.ignore_lost_connection_tc == False:
|
||||
# self.abort_run()
|
||||
|
||||
#if self.board.temp_sensor.unknownError:
|
||||
# log.info("emergency!!! unknown thermocouple error")
|
||||
# if config.ignore_unknown_tc_error == False:
|
||||
# self.abort_run()
|
||||
|
||||
#if self.board.temp_sensor.status.over_error_limit():
|
||||
# log.info("emergency!!! too many errors in a short period")
|
||||
# if config.ignore_too_many_tc_errors == False:
|
||||
# self.abort_run()
|
||||
if (self.board.temp_sensor.temperature() + config.thermocouple_offset >=
|
||||
config.emergency_shutoff_temp):
|
||||
log.info("emergency!!! temperature too high")
|
||||
if config.ignore_temp_too_high == False:
|
||||
self.abort_run()
|
||||
|
||||
if self.board.temp_sensor.status.over_error_limit():
|
||||
log.info("emergency!!! too many errors in a short period")
|
||||
if config.ignore_too_many_tc_errors == False:
|
||||
self.abort_run()
|
||||
|
||||
def reset_if_schedule_ended(self):
|
||||
if self.runtime > self.totaltime:
|
||||
|
|
|
@ -6,9 +6,12 @@ gevent-websocket
|
|||
websocket-client
|
||||
|
||||
|
||||
#RPi.GPIO
|
||||
#Adafruit-MAX31855
|
||||
#Adafruit-GPIO
|
||||
|
||||
adafruit-circuitpython-max31856
|
||||
# List of all supported adafruit modules for thermocouples
|
||||
adafruit-circuitpython-max31855
|
||||
adafruit-circuitpython-max31856
|
||||
|
||||
# untested - for PT100 platinum thermocouples
|
||||
#adafruit-circuitpython-max31865
|
||||
|
||||
# untested - for mcp9600 and mcp9601
|
||||
#adafruit-circuitpython-mcp9600
|
||||
|
|
Ładowanie…
Reference in New Issue