kopia lustrzana https://github.com/jamesgao/kiln_controller
Backend state machine finished, untested
rodzic
af8e6c6e00
commit
7183181d44
BIN
BOM.ods
BIN
BOM.ods
Plik binarny nie jest wyświetlany.
100
kiln/manager.py
100
kiln/manager.py
|
@ -6,21 +6,25 @@ import warnings
|
||||||
import threading
|
import threading
|
||||||
import traceback
|
import traceback
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import states
|
import states
|
||||||
|
import PID
|
||||||
|
|
||||||
logger = logging.getLogger("kiln.manager")
|
logger = logging.getLogger("kiln.manager")
|
||||||
|
|
||||||
class Manager(threading.Thread):
|
class Manager(threading.Thread):
|
||||||
def __init__(self, start=states.Idle):
|
def __init__(self, start=states.Idle, simulate=False):
|
||||||
"""
|
"""
|
||||||
Implement a state machine that cycles through States
|
Implement a state machine that cycles through States
|
||||||
"""
|
"""
|
||||||
super(Manager, self).__init__()
|
super(Manager, self).__init__()
|
||||||
self._send = None
|
self._send = None
|
||||||
self.thermocouple = thermo.Monitor(self._send_state)
|
|
||||||
self.regulator = stepper.Regulator()
|
|
||||||
|
|
||||||
self.profile = None
|
self.regulator = stepper.Regulator(simulate=simulate)
|
||||||
|
if simulate:
|
||||||
|
self.therm = thermo.Simulate(regulator=self.regulator)
|
||||||
|
else:
|
||||||
|
self.therm = thermo.MAX31850()
|
||||||
|
|
||||||
self.state = start(self)
|
self.state = start(self)
|
||||||
self.state_change = threading.Event()
|
self.state_change = threading.Event()
|
||||||
|
@ -28,28 +32,14 @@ class Manager(threading.Thread):
|
||||||
self.running = True
|
self.running = True
|
||||||
self.start()
|
self.start()
|
||||||
|
|
||||||
def register(self, webapp):
|
|
||||||
self._send = webapp.send
|
|
||||||
|
|
||||||
def notify(self, data):
|
def notify(self, data):
|
||||||
if self._send is not None:
|
if self._send is not None:
|
||||||
self._send(data)
|
self._send(data)
|
||||||
else:
|
else:
|
||||||
logging.warn("No notifier set, ignoring message: %s"%data)
|
logging.warn("No notifier set, ignoring message: %s"%data)
|
||||||
|
|
||||||
def _send_state(self, time, temp):
|
def __del__(self):
|
||||||
profile = None
|
self.manager_stop()
|
||||||
if self.profile is not None:
|
|
||||||
profile.get_state()
|
|
||||||
|
|
||||||
state = dict(
|
|
||||||
output=self.regulator.output,
|
|
||||||
profile=profile,
|
|
||||||
time=time,
|
|
||||||
temp=temp,
|
|
||||||
)
|
|
||||||
if self._send is not None:
|
|
||||||
self._send(state)
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
"""Mutates the manager to return State actions
|
"""Mutates the manager to return State actions
|
||||||
|
@ -65,23 +55,77 @@ class Manager(threading.Thread):
|
||||||
return attr
|
return attr
|
||||||
|
|
||||||
def _change_state(self, output):
|
def _change_state(self, output):
|
||||||
if isinstance(output, states.State) :
|
if isinstance(output, type) and issubclass(output, states.State) :
|
||||||
self.state = output()
|
self.state = output(self)
|
||||||
self.state_change.set()
|
self.state_change.set()
|
||||||
self.notify(dict(type="change", state=newstate.__name__))
|
self.notify(dict(type="change", state=output.__name__))
|
||||||
elif isinstance(output, tuple) and isinstance(output[0], states.State):
|
elif isinstance(output, tuple) and issubclass(output[0], states.State):
|
||||||
newstate, kwargs = output
|
newstate, kwargs = output
|
||||||
self.state = output(**kwargs)
|
self.state = newstate(self, **kwargs)
|
||||||
self.notify(dict(type="change", state=newstate.__name__))
|
self.notify(dict(type="change", state=newstate.__name__))
|
||||||
elif isinstance(output, dict) and "type" in dict:
|
elif isinstance(output, dict) and "type" in dict:
|
||||||
self.notify(output)
|
self.notify(output)
|
||||||
elif output is not None:
|
elif output is not None:
|
||||||
logger.warn("Unknown state output: %s"%output)
|
logger.warn("Unknown state output: %r"%output)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
while running:
|
while self.running:
|
||||||
self._change_state(self.state.run())
|
self._change_state(self.state.run())
|
||||||
|
|
||||||
|
def manager_stop(self):
|
||||||
|
self.running = False
|
||||||
|
self.state_change.set()
|
||||||
|
|
||||||
|
|
||||||
|
class Profile(threading.Thread):
|
||||||
|
"""Performs the PID loop required for feedback control"""
|
||||||
|
def __init__(self, schedule, therm, regulator, interval=5, start_time=None, callback=None,
|
||||||
|
Kp=.025, Ki=.01, Kd=.005):
|
||||||
|
self.schedule = schedule
|
||||||
|
self.therm = therm
|
||||||
|
self.regulator = regulator
|
||||||
|
self.interval = interval
|
||||||
|
self.start_time = start_time
|
||||||
|
if start_time is None:
|
||||||
|
self.start_time = time.time()
|
||||||
|
|
||||||
|
self.pid = PID.PID(Kp, Ki, Kd)
|
||||||
|
self.callback = callback
|
||||||
|
self.running = True
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def elapsed(self):
|
||||||
|
''' Returns the elapsed time from start in seconds'''
|
||||||
|
return time.time() - self.start_time
|
||||||
|
|
||||||
|
@property
|
||||||
|
def completed(self):
|
||||||
|
return self.elapsed > self.schedule[-1][0]
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.running = False
|
self.running = False
|
||||||
self.state_change.set()
|
|
||||||
|
def run(self):
|
||||||
|
while not self.completed and self.running:
|
||||||
|
now = time.time()
|
||||||
|
ts = self.elapsed
|
||||||
|
#find epoch
|
||||||
|
for i in range(len(self.schedule)-1):
|
||||||
|
if self.schedule[i][0] < ts < self.schedule[i+1][0]:
|
||||||
|
time0, temp0 = self.schedule[i]
|
||||||
|
time1, temp1 = self.schedule[i+1]
|
||||||
|
frac = (ts - time0) / (time1 - time0)
|
||||||
|
setpoint = frac * (temp1 - temp0) + temp0
|
||||||
|
self.pid.setPoint(setpoint)
|
||||||
|
|
||||||
|
temp = self.thermocouple.temperature
|
||||||
|
pid_out = self.pid.update(temp)
|
||||||
|
if pid_out < 0: pid_out = 0
|
||||||
|
if pid_out > 1: pid_out = 1
|
||||||
|
self.regulator.set(pid_out)
|
||||||
|
|
||||||
|
if self.callback is not None:
|
||||||
|
self.callback(temp, setpoint, pid_out)
|
||||||
|
|
||||||
|
time.sleep(self.interval - (time.time()-now))
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import time
|
import time
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
|
import traceback
|
||||||
import tornado.ioloop
|
import tornado.ioloop
|
||||||
import tornado.web
|
import tornado.web
|
||||||
from tornado import websocket
|
from tornado import websocket
|
||||||
|
@ -22,7 +23,7 @@ class DataRequest(tornado.web.RequestHandler):
|
||||||
self.manager = manager
|
self.manager = manager
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
data = self.manager.thermocouple.history
|
data = list(self.manager.history)
|
||||||
output = [dict(time=ts.time, temp=ts.temp) for ts in data]
|
output = [dict(time=ts.time, temp=ts.temp) for ts in data]
|
||||||
self.write(json.dumps(output))
|
self.write(json.dumps(output))
|
||||||
|
|
||||||
|
@ -31,7 +32,20 @@ class DoAction(tornado.web.RequestHandler):
|
||||||
self.manager = manager
|
self.manager = manager
|
||||||
|
|
||||||
def get(self, action):
|
def get(self, action):
|
||||||
pass
|
try:
|
||||||
|
func = getattr(self.manager, action)
|
||||||
|
func()
|
||||||
|
self.write(json.dumps(dict(type="success")))
|
||||||
|
except:
|
||||||
|
self.write(json.dumps(dict(type="error", msg=traceback.format_exc())))
|
||||||
|
|
||||||
|
def post(self, action):
|
||||||
|
try:
|
||||||
|
func = getattr(self.manager, action)
|
||||||
|
func()
|
||||||
|
self.write(json.dumps(dict(type="success")))
|
||||||
|
except:
|
||||||
|
self.write(json.dumps(dict(type="error", msg=traceback.format_exc())))
|
||||||
|
|
||||||
class WebApp(object):
|
class WebApp(object):
|
||||||
def __init__(self, manager, port=8888):
|
def __init__(self, manager, port=8888):
|
||||||
|
@ -57,11 +71,10 @@ class WebApp(object):
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
try:
|
try:
|
||||||
import manager
|
import manager
|
||||||
kiln = manager.Manager()
|
kiln = manager.Manager(simulate=True)
|
||||||
app = WebApp(kiln)
|
app = WebApp(kiln)
|
||||||
kiln.register(app)
|
kiln._send = app.send
|
||||||
kiln.start()
|
|
||||||
|
|
||||||
app.run()
|
app.run()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
kiln.stop()
|
kiln.manager_stop()
|
||||||
|
|
102
kiln/states.py
102
kiln/states.py
|
@ -3,7 +3,8 @@ http://python-3-patterns-idioms-test.readthedocs.org/en/latest/StateMachine.html
|
||||||
"""
|
"""
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
import PID
|
import manager
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
class State(object):
|
class State(object):
|
||||||
def __init__(self, machine):
|
def __init__(self, machine):
|
||||||
|
@ -15,15 +16,29 @@ class State(object):
|
||||||
self.parent.state_change.wait()
|
self.parent.state_change.wait()
|
||||||
|
|
||||||
class Idle(State):
|
class Idle(State):
|
||||||
|
def __init__(self, parent):
|
||||||
|
super(Idle, self).__init__(parent)
|
||||||
|
self.history = deque(maxlen=1024) #about 10 minutes worth
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
ts = self.parent.therm.get()
|
||||||
|
self.history.append(ts)
|
||||||
|
self.parent.notify(dict(type="temperature", time=ts.time, temp=ts.temp))
|
||||||
|
|
||||||
def ignite(self):
|
def ignite(self):
|
||||||
_ignite(self.parent.regulator, self.parent.notify)
|
_ignite(self.parent.regulator, self.parent.notify)
|
||||||
return Lit
|
return Lit, dict(history=self.history)
|
||||||
|
|
||||||
def start(self, **kwargs):
|
def start(self, **kwargs):
|
||||||
_ignite(self.parent.regulator, self.parent.notify)
|
_ignite(self.parent.regulator, self.parent.notify)
|
||||||
|
kwargs['history'] = deque(self.history)
|
||||||
return Running, kwargs
|
return Running, kwargs
|
||||||
|
|
||||||
class Lit(State):
|
class Lit(State):
|
||||||
|
def __init__(self, parent, history):
|
||||||
|
super(Lit, self).__init__(parent)
|
||||||
|
self.history = deque(history)
|
||||||
|
|
||||||
def set(self, value):
|
def set(self, value):
|
||||||
try:
|
try:
|
||||||
self.parent.regulator.set(value)
|
self.parent.regulator.set(value)
|
||||||
|
@ -31,71 +46,62 @@ class Lit(State):
|
||||||
except:
|
except:
|
||||||
self.parent.notify(dict(type="error", msg=traceback.format_exc()))
|
self.parent.notify(dict(type="error", msg=traceback.format_exc()))
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
ts = self.parent.therm.get()
|
||||||
|
self.history.append(ts)
|
||||||
|
|
||||||
def start(self, **kwargs):
|
def start(self, **kwargs):
|
||||||
|
kwargs['history'] = self.history
|
||||||
return Running, kwargs
|
return Running, kwargs
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
_shutoff(self.parent.regulator, self.parent.notify)
|
_shutoff(self.parent.regulator, self.parent.notify)
|
||||||
return Idle
|
return Idle
|
||||||
|
|
||||||
class Running(State):
|
class Cooling(State):
|
||||||
def __init__(self, parent, schedule, interval=5, start_time=None, Kp=.025, Ki=.01, Kd=.005):
|
def __init__(self, parent, history):
|
||||||
self.schedule = schedule
|
super(Cooling, self).__init__(parent)
|
||||||
self.thermocouple = parent.thermocouple
|
self.history = history
|
||||||
self.interval = interval
|
|
||||||
self.start_time = start_time
|
|
||||||
if start_time is None:
|
|
||||||
self.start_time = time.time()
|
|
||||||
self.regulator = parent.regulator
|
|
||||||
self.pid = PID.PID(Kp, Ki, Kd)
|
|
||||||
self.running = True
|
|
||||||
super(Running, self).__init__(parent)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def elapsed(self):
|
|
||||||
''' Returns the elapsed time from start in seconds'''
|
|
||||||
return time.time() - self.start_time
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _info(self):
|
|
||||||
return dict(type="profile",
|
|
||||||
output=pid_out,
|
|
||||||
start_time=self.start_time,
|
|
||||||
elapsed=self.elapsed,
|
|
||||||
)
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
now = time.time()
|
ts = self.parent.therm.get()
|
||||||
ts = self.elapsed
|
self.history.append(ts)
|
||||||
#find epoch
|
if ts.temp < 50:
|
||||||
for i in range(len(self.schedule)-1):
|
#TODO: save temperature log somewhere
|
||||||
if self.schedule[i][0] < ts < self.schedule[i+1][0]:
|
return Idle
|
||||||
time0, temp0 = self.schedule[i]
|
|
||||||
time1, temp1 = self.schedule[i+1]
|
|
||||||
frac = (ts - time0) / (time1 - time0)
|
|
||||||
setpoint = frac * (temp1 - temp0) + temp0
|
|
||||||
self.pid.setPoint(setpoint)
|
|
||||||
|
|
||||||
temp = self.thermocouple.temperature
|
class Running(State):
|
||||||
pid_out = self.pid.update(temp)
|
def __init__(self, parent, history, **kwargs):
|
||||||
if pid_out < 0: pid_out = 0
|
super(Running, self).__init__(parent)
|
||||||
if pid_out > 1: pid_out = 1
|
self.profile = manager.Profile(therm=self.parent.therm, regulator=self.parent.regulator,
|
||||||
self.regulator.set(pid_out, block=True)
|
callback=self._notify, **kwargs)
|
||||||
self.parent.notify(self.info)
|
self.history = history
|
||||||
|
|
||||||
if ts > self.schedule[-1][0]:
|
def _notify(self, therm, setpoint, out):
|
||||||
|
self.parent.notify(dict(
|
||||||
|
type="profile",
|
||||||
|
temp=therm,
|
||||||
|
setpoint=setpoint,
|
||||||
|
output=out,
|
||||||
|
ts=self.profile.elapsed,
|
||||||
|
))
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
if self.profile.completed:
|
||||||
self.parent.notify(dict(type="profile",status="complete"))
|
self.parent.notify(dict(type="profile",status="complete"))
|
||||||
_shutoff(self.parent.regulator, self.parent.notify)
|
_shutoff(self.parent.regulator, self.parent.notify)
|
||||||
return Idle,
|
return Cooling, dict(history=self.history)
|
||||||
|
|
||||||
time.sleep(self.interval - (time.time()-now))
|
self.history.append(self.parent.therm.get())
|
||||||
|
|
||||||
def pause(self):
|
def pause(self):
|
||||||
return Lit
|
self.profile.stop()
|
||||||
|
return Lit, dict(history=self.history)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
|
self.profile.stop()
|
||||||
_shutoff(self.parent.regulator, self.parent.notify)
|
_shutoff(self.parent.regulator, self.parent.notify)
|
||||||
return Idle
|
return Cooling, dict(history=self.history)
|
||||||
|
|
||||||
|
|
||||||
def _ignite(regulator, notify):
|
def _ignite(regulator, notify):
|
||||||
|
|
|
@ -189,7 +189,7 @@ class Regulator(threading.Thread):
|
||||||
if self.current != 0:
|
if self.current != 0:
|
||||||
raise ValueError("Must be off to ignite")
|
raise ValueError("Must be off to ignite")
|
||||||
|
|
||||||
logger.log("Ignition start")
|
logger.info("Ignition start")
|
||||||
self.stepper.step(start, self.speed, block=True)
|
self.stepper.step(start, self.speed, block=True)
|
||||||
if self.ignite_pin is not None:
|
if self.ignite_pin is not None:
|
||||||
GPIO.output(self.ignite_pin, True)
|
GPIO.output(self.ignite_pin, True)
|
||||||
|
@ -198,10 +198,10 @@ class Regulator(threading.Thread):
|
||||||
GPIO.output(self.ignite_pin, False)
|
GPIO.output(self.ignite_pin, False)
|
||||||
self.stepper.step(self.min - start, self.speed, block=True)
|
self.stepper.step(self.min - start, self.speed, block=True)
|
||||||
self.current = self.min
|
self.current = self.min
|
||||||
logger.log("Ignition complete")
|
logger.info("Ignition complete")
|
||||||
|
|
||||||
def off(self, block=True):
|
def off(self, block=True):
|
||||||
logger.log("Shutting off gas")
|
logger.info("Shutting off gas")
|
||||||
self.stepper.step(-self.current, self.speed, block=block)
|
self.stepper.step(-self.current, self.speed, block=block)
|
||||||
self.current = 0
|
self.current = 0
|
||||||
|
|
||||||
|
@ -218,7 +218,10 @@ class Regulator(threading.Thread):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def output(self):
|
def output(self):
|
||||||
return (self.current - self.min) / (self.max - self.min)
|
out = (self.current - self.min) / float(self.max - self.min)
|
||||||
|
if out < 0:
|
||||||
|
return -1
|
||||||
|
return out
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""Check the status of the flame sensor"""
|
"""Check the status of the flame sensor"""
|
||||||
|
|
|
@ -17,9 +17,11 @@ var tempgraph = (function(module) {
|
||||||
var now = new Date(data.time*1000.);
|
var now = new Date(data.time*1000.);
|
||||||
var temp = this.scalefunc(data.temp);
|
var temp = this.scalefunc(data.temp);
|
||||||
|
|
||||||
|
var hourstr = now.getHours() % 12;
|
||||||
|
hourstr = hourstr == 0 ? 12 : houstr;
|
||||||
var minstr = now.getMinutes();
|
var minstr = now.getMinutes();
|
||||||
minstr = minstr.length < 2 ? "0"+minstr : minstr;
|
minstr = minstr.length < 2 ? "0"+minstr : minstr;
|
||||||
var nowstr = now.getHours() % 12 + ":" + minstr + (now.getHours() > 12 ? " pm" : " am");
|
var nowstr = hourstr + ":" + minstr + (now.getHours() >= 12 ? " pm" : " am");
|
||||||
$("#current_time").text(nowstr);
|
$("#current_time").text(nowstr);
|
||||||
$("#current_temp").text(this.temp_prefix+temp+this.temp_suffix);
|
$("#current_temp").text(this.temp_prefix+temp+this.temp_suffix);
|
||||||
|
|
||||||
|
@ -93,13 +95,18 @@ var tempgraph = (function(module) {
|
||||||
module.Monitor.prototype._map_temp = function(d) {
|
module.Monitor.prototype._map_temp = function(d) {
|
||||||
return {x:new Date(d.time*1000), y:this.scalefunc(d.temp)};
|
return {x:new Date(d.time*1000), y:this.scalefunc(d.temp)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.Monitor.prototype.setState = function(name) {
|
||||||
|
|
||||||
|
}
|
||||||
module.Monitor.prototype._bindUI = function() {
|
module.Monitor.prototype._bindUI = function() {
|
||||||
try {
|
try {
|
||||||
var sock = new WebSocket("ws://"+window.location.hostname+":"+window.location.port+"/ws/", "protocolOne");
|
var sock = new WebSocket("ws://"+window.location.hostname+":"+window.location.port+"/ws/", "protocolOne");
|
||||||
|
|
||||||
sock.onmessage = function(event) {
|
sock.onmessage = function(event) {
|
||||||
var data = JSON.parse(event.data);
|
var data = JSON.parse(event.data);
|
||||||
this.update_temp(data);
|
if (data.type == "temperature")
|
||||||
|
this.update_temp(data);
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
|
@ -120,7 +127,7 @@ var tempgraph = (function(module) {
|
||||||
return module;
|
return module;
|
||||||
}(tempgraph || {}));
|
}(tempgraph || {}));
|
||||||
|
|
||||||
d3.json("data2.json", function(error, data) {
|
d3.json("temperature.json", function(error, data) {
|
||||||
monitor = new tempgraph.Monitor(data);
|
monitor = new tempgraph.Monitor(data);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -109,9 +109,6 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-8 col-md-8 row-space">
|
<div class="col-sm-8 col-md-8 row-space">
|
||||||
<svg id="graph" class="row-space"></svg>
|
<svg id="graph" class="row-space"></svg>
|
||||||
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
|
|
||||||
<script type="text/javascript" src="temp_graph.js"></script>
|
|
||||||
<script type="text/javascript" src="temp_monitor.js"></script>
|
|
||||||
|
|
||||||
<div class="btn-group btn-group-justified row-space">
|
<div class="btn-group btn-group-justified row-space">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
|
@ -159,5 +156,8 @@
|
||||||
|
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
|
||||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
|
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
|
||||||
|
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
|
||||||
|
<script type="text/javascript" src="temp_graph.js"></script>
|
||||||
|
<script type="text/javascript" src="temp_monitor.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -20,26 +20,11 @@ def temp_to_cone(temp):
|
||||||
|
|
||||||
tempsample = namedtuple("tempsample", ['time', 'temp'])
|
tempsample = namedtuple("tempsample", ['time', 'temp'])
|
||||||
|
|
||||||
class Monitor(threading.Thread):
|
class MAX31850(object):
|
||||||
def __init__(self, name="3b-000000182b57", callback=None):
|
def __init__(self, name="3b-000000182b57", smooth_window=4):
|
||||||
super(Monitor, self).__init__()
|
|
||||||
self.daemon = True
|
|
||||||
|
|
||||||
self.device = "/sys/bus/w1/devices/%s/w1_slave"%name
|
self.device = "/sys/bus/w1/devices/%s/w1_slave"%name
|
||||||
self.history = deque(maxlen=1048576)
|
self.temps = deque(maxlen=smooth_window)
|
||||||
self.callback = callback
|
self.last = None
|
||||||
|
|
||||||
try:
|
|
||||||
from Adafruit_alphanumeric import AlphaScroller
|
|
||||||
self.display = AlphaScroller(interval=.4)
|
|
||||||
self.display.start()
|
|
||||||
self.display.hide()
|
|
||||||
except ImportError:
|
|
||||||
logger.info("Could not start AlphaScroller")
|
|
||||||
self.display = None
|
|
||||||
|
|
||||||
self.running = True
|
|
||||||
self.start()
|
|
||||||
|
|
||||||
def _read_temp(self):
|
def _read_temp(self):
|
||||||
with open(self.device, 'r') as f:
|
with open(self.device, 'r') as f:
|
||||||
|
@ -54,35 +39,62 @@ class Monitor(threading.Thread):
|
||||||
if match is not None:
|
if match is not None:
|
||||||
return float(match.group(1)) / 1000.0
|
return float(match.group(1)) / 1000.0
|
||||||
|
|
||||||
def stop(self):
|
def get(self):
|
||||||
self.running = False
|
"""Blocking call to retrieve latest temperature sample"""
|
||||||
if self.display is not None:
|
temp = self._read_temp()
|
||||||
self.display.stop()
|
self.last = time.time()
|
||||||
|
self.history.append(temp)
|
||||||
|
return self.temperature
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def temperature(self):
|
def temperature(self):
|
||||||
return self.history[-1][1]
|
if self.last is None or time.time() - self.last > 5:
|
||||||
|
return self.get()
|
||||||
|
|
||||||
|
return tempsample(self.last, sum(self.history) / float(len(self.history)))
|
||||||
|
|
||||||
|
class Simulate(object):
|
||||||
|
def __init__(self, regulator):
|
||||||
|
self.regulator = regulator
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
time.sleep(.8)
|
||||||
|
return self.temperature
|
||||||
|
|
||||||
|
@property
|
||||||
|
def temperature(self):
|
||||||
|
return tempsample(time.time(), self.regulator.output * 1000. + 15)
|
||||||
|
|
||||||
|
class Monitor(threading.Thread):
|
||||||
|
def __init__(self, cls=MAX31850, **kwargs):
|
||||||
|
self.therm = cls(**kwargs)
|
||||||
|
self.running = True
|
||||||
|
|
||||||
|
from Adafruit_alphanumeric import AlphaScroller
|
||||||
|
self.display = AlphaScroller(interval=.4)
|
||||||
|
self.display.start()
|
||||||
|
self.display.hide()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
while self.running:
|
while self.running:
|
||||||
temp = self._read_temp()
|
_, temp = self.therm.get()
|
||||||
now = time.time()
|
|
||||||
self.history.append(tempsample(now, temp))
|
if temp > 50:
|
||||||
if self.callback is not None:
|
if not self.display.shown:
|
||||||
self.callback(now, temp)
|
self.display.show()
|
||||||
|
fahr = temp * 9. / 5. + 32.
|
||||||
|
text = list('%0.0f'%temp) + ['degree'] + list('C %0.0f'%fahr)+['degree'] + list("F")
|
||||||
|
if 600 <= temp:
|
||||||
|
text += [' ', ' ', 'cone']+list(temp_to_cone(temp))
|
||||||
|
self.display.set_text(text, reset=False)
|
||||||
|
elif self.display.shown:
|
||||||
|
self.display.hide()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.running = False
|
||||||
|
self.display.stop()
|
||||||
|
|
||||||
if self.display is not None:
|
|
||||||
if temp > 50:
|
|
||||||
if not self.display.shown:
|
|
||||||
self.display.show()
|
|
||||||
fahr = temp * 9. / 5. + 32.
|
|
||||||
text = list('%0.0f'%temp) + ['degree'] + list('C %0.0f'%fahr)+['degree'] + list("F")
|
|
||||||
if 600 <= temp:
|
|
||||||
text += [' ', ' ', 'cone']+list(temp_to_cone(temp))
|
|
||||||
self.display.set_text(text, reset=False)
|
|
||||||
elif self.display.shown:
|
|
||||||
self.display.hide()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
mon = Monitor()
|
monitor = Monitor()
|
||||||
mon.join()
|
monitor.start()
|
Ładowanie…
Reference in New Issue