basics working
This commit is contained in:
parent
bb7f9f9353
commit
5172d8c294
172
lib/oven.py
172
lib/oven.py
@ -7,6 +7,7 @@ import json
|
||||
import config
|
||||
import os
|
||||
import digitalio
|
||||
import busio
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -48,23 +49,29 @@ class Output(object):
|
||||
# wrapper for blinka board
|
||||
class Board(object):
|
||||
def __init__(self):
|
||||
log.info("board: %s" % (self.name))
|
||||
self.temp_sensor.start()
|
||||
|
||||
class BoardReal(Board):
|
||||
class RealBoard(Board):
|
||||
def __init__(self):
|
||||
self.name = None
|
||||
self.load_libs()
|
||||
self.temp_sensor = TempSensorReal()
|
||||
self.temp_sensor = self.choose_tempsensor()
|
||||
Board.__init__(self)
|
||||
|
||||
def load_libs(self):
|
||||
import board
|
||||
log.info("blinka board recognized: %s" % (board.board_id))
|
||||
self.name = board.board_id
|
||||
|
||||
class BoardSimulated(Board):
|
||||
def choose_tempsensor(self):
|
||||
if config.max31855:
|
||||
return Max31855()
|
||||
if config.max31856:
|
||||
return Max31856()
|
||||
|
||||
class SimulatedBoard(Board):
|
||||
def __init__(self):
|
||||
self.name = "simulated board"
|
||||
self.name = "simulated"
|
||||
self.temp_sensor = TempSensorSimulated()
|
||||
Board.__init__(self)
|
||||
|
||||
@ -72,7 +79,6 @@ class TempSensor(threading.Thread):
|
||||
def __init__(self):
|
||||
threading.Thread.__init__(self)
|
||||
self.daemon = True
|
||||
self.temperature = 0
|
||||
self.bad_percent = 0
|
||||
self.time_step = config.sensor_time_wait
|
||||
self.noConnection = self.shortToGround = self.shortToVCC = self.unknownError = False
|
||||
@ -81,59 +87,73 @@ class TempSensorSimulated(TempSensor):
|
||||
'''not much here, just need to be able to set the temperature'''
|
||||
def __init__(self):
|
||||
TempSensor.__init__(self)
|
||||
self.simulated_temperature = 0
|
||||
def temperature(self):
|
||||
return self.simulated_temperature
|
||||
|
||||
class TempSensorReal(TempSensor):
|
||||
'''real temperature sensor thread that takes N measurements
|
||||
during the time_step'''
|
||||
def __init__(self):
|
||||
TempSensor.__init__(self)
|
||||
self.temps = []
|
||||
self.sleeptime = self.time_step / float(config.temperature_average_samples)
|
||||
self.bad_count = 0
|
||||
self.ok_count = 0
|
||||
self.bad_stamp = 0
|
||||
|
||||
import busio
|
||||
self.spi = busio.SPI(config.spi_sclk, config.spi_mosi, config.spi_miso)
|
||||
self.cs = DigitalInOut(config.spi_cs)
|
||||
self.cs = digitalio.DigitalInOut(config.spi_cs)
|
||||
|
||||
if config.max31855:
|
||||
log.info("init MAX31855")
|
||||
import adafruit_max31855
|
||||
self.thermocouple = adafruit_max31855.MAX31855(spi, cs)
|
||||
def get_temperature(self):
|
||||
'''must be provided by subclass'''
|
||||
temp = self.raw_temp()
|
||||
if config.temp_scale.lower() == "f":
|
||||
temp = (temp*9/5)+32
|
||||
return temp
|
||||
|
||||
if config.max31856:
|
||||
log.info("init MAX31856")
|
||||
import adafruit_max31856
|
||||
self.thermocouple = adafruit_max31856.MAX31856(spi, cs)
|
||||
# FIX not sure what to do with config.ac_freq_50hz
|
||||
#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
|
||||
# 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
|
||||
|
||||
def temperature(self):
|
||||
try:
|
||||
if config.max31855:
|
||||
temp = self.thermocouple.temperature_NIST
|
||||
else:
|
||||
temp = self.thermocouple.temperature
|
||||
return temp
|
||||
except RuntimeError as rte:
|
||||
if rte.args && rte.args[0] == "thermocouple not connected":
|
||||
self.bad_count = self.bad_count + 1
|
||||
if rte.args && rte.args[0] == "short circuit to ground":
|
||||
if not config.ignore_tc_short_errors:
|
||||
self.bad_count = self.bad_count + 1
|
||||
if rte.args && rte.args[0] == "short circuit to power":
|
||||
if not config.ignore_tc_short_errors:
|
||||
self.bad_count = self.bad_count + 1
|
||||
if rte.args && rte.args[0] == "faulty reading":
|
||||
self.bad_count = self.bad_count + 1
|
||||
if rte.args && rte.args[0] == "faulty reading":
|
||||
self.bad_count = self.bad_count + 1
|
||||
|
||||
log.error("Problem reading temp %s" % (rte.args[0]))
|
||||
return None
|
||||
if (self.temps):
|
||||
return self.get_avg_temp(self.temps)
|
||||
return 0
|
||||
|
||||
def add_temp(self,temp):
|
||||
if temp:
|
||||
self.temps.append(temp)
|
||||
# fix this should happen someplace else
|
||||
self.ok_count += 1
|
||||
while(len(self.temps) > config.temperature_average_samples):
|
||||
del self.temps[0]
|
||||
|
||||
def run(self):
|
||||
'''use a moving average of config.temperature_average_samples across the time_step'''
|
||||
temps = []
|
||||
while True:
|
||||
# reset error counter if time is up
|
||||
if (time.time() - self.bad_stamp) > (self.time_step * 2):
|
||||
@ -145,31 +165,9 @@ class TempSensorReal(TempSensor):
|
||||
self.ok_count = 0
|
||||
self.bad_stamp = time.time()
|
||||
|
||||
try:
|
||||
temp = self.thermocouple.temperature
|
||||
except:
|
||||
|
||||
self.noConnection = self.thermocouple.noConnection
|
||||
self.shortToGround = self.thermocouple.shortToGround
|
||||
self.shortToVCC = self.thermocouple.shortToVCC
|
||||
self.unknownError = self.thermocouple.unknownError
|
||||
|
||||
is_bad_value = self.noConnection | self.unknownError
|
||||
if not config.ignore_tc_short_errors:
|
||||
is_bad_value |= self.shortToGround | self.shortToVCC
|
||||
|
||||
if not is_bad_value:
|
||||
temps.append(temp)
|
||||
if len(temps) > config.temperature_average_samples:
|
||||
del temps[0]
|
||||
self.ok_count += 1
|
||||
|
||||
else:
|
||||
log.error("Problem reading temp N/C:%s GND:%s VCC:%s ???:%s" % (self.noConnection,self.shortToGround,self.shortToVCC,self.unknownError))
|
||||
self.bad_count += 1
|
||||
|
||||
if len(temps):
|
||||
self.temperature = self.get_avg_temp(temps)
|
||||
temp = self.get_temperature()
|
||||
#log.info("temp = %0.2f" % temp)
|
||||
self.add_temp(temp)
|
||||
time.sleep(self.sleeptime)
|
||||
|
||||
def get_avg_temp(self, temps, chop=25):
|
||||
@ -184,6 +182,34 @@ class TempSensorReal(TempSensor):
|
||||
temps = temps[items:total-items]
|
||||
return sum(temps) / len(temps)
|
||||
|
||||
|
||||
class Max31855(TempSensorReal):
|
||||
'''each subclass expected to handle errors and get temperature'''
|
||||
def __init__(self):
|
||||
TempSensorReal.__init__(self)
|
||||
log.info("init MAX31855")
|
||||
import adafruit_max31855
|
||||
self.thermocouple = adafruit_max31855.MAX31855(self.spi, self.cs)
|
||||
|
||||
def raw_temp(self):
|
||||
return self.thermocouple.temperature_NIST
|
||||
|
||||
class Max31856(TempSensorReal):
|
||||
'''each subclass expected to handle errors and get temperature'''
|
||||
def __init__(self):
|
||||
TempSensorReal.__init__(self)
|
||||
log.info("init MAX31856")
|
||||
import adafruit_max31856
|
||||
adafruit_max31856.ThermocoupleType(config.thermocouple_type)
|
||||
self.thermocouple = adafruit_max31856.MAX31856(spi, cs)
|
||||
if (config.ac_freq_50hz == True):
|
||||
self.thermocouple.noise_rejection(50)
|
||||
else:
|
||||
self.thermocouple.noise_rejection(60)
|
||||
|
||||
def raw_temp(self):
|
||||
return self.thermocouple.temperature
|
||||
|
||||
class Oven(threading.Thread):
|
||||
'''parent oven class. this has all the common code
|
||||
for either a real or simulated oven'''
|
||||
@ -238,7 +264,7 @@ class Oven(threading.Thread):
|
||||
'''shift the whole schedule forward in time by one time_step
|
||||
to wait for the kiln to catch up'''
|
||||
if config.kiln_must_catch_up == True:
|
||||
temp = self.board.temp_sensor.temperature + \
|
||||
temp = self.board.temp_sensor.temperature() + \
|
||||
config.thermocouple_offset
|
||||
# kiln too cold, wait for it to heat up
|
||||
if self.target - temp > config.pid_control_window:
|
||||
@ -262,7 +288,7 @@ class Oven(threading.Thread):
|
||||
|
||||
def reset_if_emergency(self):
|
||||
'''reset if the temperature is way TOO HOT, or other critical errors detected'''
|
||||
if (self.board.temp_sensor.temperature + config.thermocouple_offset >=
|
||||
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:
|
||||
@ -299,7 +325,7 @@ class Oven(threading.Thread):
|
||||
def get_state(self):
|
||||
temp = 0
|
||||
try:
|
||||
temp = self.board.temp_sensor.temperature + config.thermocouple_offset
|
||||
temp = self.board.temp_sensor.temperature() + config.thermocouple_offset
|
||||
except AttributeError as error:
|
||||
# this happens at start-up with a simulated oven
|
||||
temp = 0
|
||||
@ -397,7 +423,7 @@ class Oven(threading.Thread):
|
||||
class SimulatedOven(Oven):
|
||||
|
||||
def __init__(self):
|
||||
self.board = BoardSimulated()
|
||||
self.board = SimulatedBoard()
|
||||
self.t_env = config.sim_t_env
|
||||
self.c_heat = config.sim_c_heat
|
||||
self.c_oven = config.sim_c_oven
|
||||
@ -436,11 +462,11 @@ class SimulatedOven(Oven):
|
||||
self.p_env = (self.t - self.t_env) / self.R_o_nocool
|
||||
self.t -= self.p_env * self.time_step / self.c_oven
|
||||
self.temperature = self.t
|
||||
self.board.temp_sensor.temperature = self.t
|
||||
self.board.temp_sensor.simulated_temperature = self.t
|
||||
|
||||
def heat_then_cool(self):
|
||||
pid = self.pid.compute(self.target,
|
||||
self.board.temp_sensor.temperature +
|
||||
self.board.temp_sensor.temperature() +
|
||||
config.thermocouple_offset)
|
||||
heat_on = float(self.time_step * pid)
|
||||
heat_off = float(self.time_step * (1 - pid))
|
||||
@ -486,7 +512,7 @@ class SimulatedOven(Oven):
|
||||
class RealOven(Oven):
|
||||
|
||||
def __init__(self):
|
||||
self.board = Board()
|
||||
self.board = RealBoard()
|
||||
self.output = Output()
|
||||
self.reset()
|
||||
|
||||
@ -502,7 +528,7 @@ class RealOven(Oven):
|
||||
|
||||
def heat_then_cool(self):
|
||||
pid = self.pid.compute(self.target,
|
||||
self.board.temp_sensor.temperature +
|
||||
self.board.temp_sensor.temperature() +
|
||||
config.thermocouple_offset)
|
||||
heat_on = float(self.time_step * pid)
|
||||
heat_off = float(self.time_step * (1 - pid))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user