Add model for humidity, fix bug in Status logic
This commit is contained in:
parent
008cde9d5e
commit
9692132e75
|
@ -170,7 +170,7 @@ const sleep = (ms) => new Promise(r => setTimeout(r, ms))
|
||||||
async function chartupdater() {
|
async function chartupdater() {
|
||||||
if (chart_updater != null) return
|
if (chart_updater != null) return
|
||||||
|
|
||||||
await sleep(5000)
|
await sleep(2000)
|
||||||
// wait at least two seconds to avoid wasting a lot of bandwidth
|
// wait at least two seconds to avoid wasting a lot of bandwidth
|
||||||
const resp = await fetch("/api/update")
|
const resp = await fetch("/api/update")
|
||||||
chart_updater = null
|
chart_updater = null
|
||||||
|
@ -181,7 +181,10 @@ var status_updater = null
|
||||||
async function updateStatus() {
|
async function updateStatus() {
|
||||||
const status_resp = await fetch("/api/status")
|
const status_resp = await fetch("/api/status")
|
||||||
const status = await status_resp.json()
|
const status = await status_resp.json()
|
||||||
document.getElementById("device-status").textContent = "connected: " + status.connected + ", humdifier: " + (status.humdifier ? "on" : "off")
|
//console.log(status)
|
||||||
|
const humidifier_state = (status.humidifier ? "on" : "off")
|
||||||
|
//console.log(humidifier_state)
|
||||||
|
document.getElementById("device-status").textContent = "connected: " + status.connected + ", humidifier: " + humidifier_state
|
||||||
|
|
||||||
if (autoupdate) {
|
if (autoupdate) {
|
||||||
status_updater = waitThenUpdateStatus()
|
status_updater = waitThenUpdateStatus()
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
import serial
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
|
||||||
|
s = serial.Serial("/dev/ttyUSB0", 115200, timeout=10)
|
||||||
|
q = queue.Queue()
|
||||||
|
|
||||||
|
process = subprocess.Popen(["ssh", "shrooms@threefortiethofonehamster.com", "python", "/home/shrooms/go/src/shroom-server/shroom-pipe.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||||
|
|
||||||
|
# TODO run thread to process data from process's stdout
|
||||||
|
|
||||||
|
def reset_serial():
|
||||||
|
s.close()
|
||||||
|
s = serial.Serial("/dev/ttyUSB0", 115200, timeout = 10)
|
||||||
|
time.sleep(10)
|
||||||
|
|
||||||
|
class Humidifier:
|
||||||
|
def __init__(self):
|
||||||
|
self.on = False
|
||||||
|
self.history = np.array(30)
|
||||||
|
self.switch_timeout = 0
|
||||||
|
|
||||||
|
def update(self, volts):
|
||||||
|
self.history[1:] = self.history[:-1]
|
||||||
|
self.history[0] = volts
|
||||||
|
avg = np.sum(self.history)/self.history.shape[0]
|
||||||
|
if self.state:
|
||||||
|
if avg < 0.2:
|
||||||
|
self.on = False
|
||||||
|
else:
|
||||||
|
if avg > 2.6:
|
||||||
|
self.on = True
|
||||||
|
|
||||||
|
def toggle(self, s):
|
||||||
|
if time.time() > self.switch_timeout:
|
||||||
|
s.write(b"h")
|
||||||
|
self.switch_timeout = time.time() + 7
|
||||||
|
|
||||||
|
humidifier = Humidifier()
|
||||||
|
target_lower = 0.85
|
||||||
|
target_higher = 0.90
|
||||||
|
try:
|
||||||
|
last_sample = 0
|
||||||
|
while True:
|
||||||
|
now = time.time()
|
||||||
|
if now - last_sample < 0.5:
|
||||||
|
s.write(b"s")
|
||||||
|
resp = s.read(120)
|
||||||
|
if len(resp) == 0:
|
||||||
|
reset_serial()
|
||||||
|
time.sleep(5)
|
||||||
|
continue
|
||||||
|
parts = resp.split(b",")
|
||||||
|
humidity = float(parts[0])
|
||||||
|
temp = float(parts[1])
|
||||||
|
volts = float(parts[2])
|
||||||
|
print(humidity, temp, volts)
|
||||||
|
|
||||||
|
humidifier.update(volts)
|
||||||
|
if humidity < target_lower and humidifier.off:
|
||||||
|
humidifier.toggle(s)
|
||||||
|
elif humidity > target_upper and humidifier.on:
|
||||||
|
humidifier.toggle(s)
|
||||||
|
# TODO check on the process
|
||||||
|
else:
|
||||||
|
time.sleep(0.5-(now - last_sample))
|
||||||
|
|
||||||
|
finally:
|
||||||
|
# TODO kill ssh connection
|
||||||
|
process.
|
||||||
|
pass
|
|
@ -8,20 +8,47 @@ import time
|
||||||
#process = subprocess.Popen(["ssh", "shrooms@localhost", "/usr/bin/env", "python", "/home/shrooms/go/src/shroom-server/shroom-pipe.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
#process = subprocess.Popen(["ssh", "shrooms@localhost", "/usr/bin/env", "python", "/home/shrooms/go/src/shroom-server/shroom-pipe.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||||
process = subprocess.Popen(["/usr/bin/env", "python", "/home/kelvin/src/shroom-server/shroom_pipe.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
process = subprocess.Popen(["/usr/bin/env", "python", "/home/kelvin/src/shroom-server/shroom_pipe.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||||
|
|
||||||
|
def send_update(msg):
|
||||||
|
global process
|
||||||
|
process.stdin.write(bytes(json.dumps(msg) + "\n", "utf8"))
|
||||||
|
process.stdin.flush()
|
||||||
|
|
||||||
# TODO run thread to process data from process's stdout
|
# TODO run thread to process data from process's stdout
|
||||||
|
|
||||||
class MockSerial:
|
class MockSerial:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
self.humidity = np.zeros(100)
|
||||||
|
self.humidifier_on = False
|
||||||
|
self.humidity[:] = 0.80
|
||||||
|
self.humidity[-1] = 0.20
|
||||||
|
self.humidity[0] = 0.20
|
||||||
|
|
||||||
def write(self, _):
|
def write(self, msg):
|
||||||
pass
|
if msg == b'h':
|
||||||
|
print("mock hum toggle")
|
||||||
|
self.humidifier_on = not self.humidifier_on
|
||||||
|
|
||||||
def read(self, _):
|
def read(self, _):
|
||||||
t = time.time()
|
t = time.time()
|
||||||
temp = 25 + np.sin(0.01*2*np.pi*t) + 0.5*np.sin(0.0001*2*np.pi*t + 7)
|
temp = 25 + np.sin(0.01*2*np.pi*t) + 0.5*np.sin(0.0001*2*np.pi*t + 7)
|
||||||
humidity = 0.90 + 0.01*np.sin(0.05*2*np.pi*t) + 0.03*np.sin(0.1*2*np.pi*t + 7)
|
|
||||||
hv = 3.3*int(1 + np.sin(0.2*np.pi*t))
|
# very janky model of humidity diffusion
|
||||||
|
# fix end conditions
|
||||||
|
for _ in range(20):
|
||||||
|
self.humidity[-1] = 0.2*0.20 + 0.8*self.humidity[-2]
|
||||||
|
self.humidity[0] = 0.20
|
||||||
|
if self.humidifier_on:
|
||||||
|
self.humidity[20] = 2
|
||||||
|
# use the gradient to determine the change in humidity
|
||||||
|
avg = 0.5*(self.humidity[:-2] + self.humidity[2:])
|
||||||
|
self.humidity[1:-1] += 0.2*(avg - self.humidity[1:-1])
|
||||||
|
#print(self.humidity)
|
||||||
|
|
||||||
|
humidity = self.humidity[60]
|
||||||
|
if self.humidifier_on:
|
||||||
|
hv = 3.3
|
||||||
|
else:
|
||||||
|
hv = 0.0
|
||||||
return bytes("{},{},{}\n".format(humidity, temp, hv), "utf8")
|
return bytes("{},{},{}\n".format(humidity, temp, hv), "utf8")
|
||||||
|
|
||||||
s = MockSerial()
|
s = MockSerial()
|
||||||
|
@ -33,7 +60,7 @@ def reset_serial():
|
||||||
class Humidifier:
|
class Humidifier:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.on = False
|
self.on = False
|
||||||
self.history = np.zeros(30)
|
self.history = np.zeros(10)
|
||||||
self.switch_timeout = 0
|
self.switch_timeout = 0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -43,24 +70,26 @@ class Humidifier:
|
||||||
def update(self, volts):
|
def update(self, volts):
|
||||||
self.history[1:] = self.history[:-1]
|
self.history[1:] = self.history[:-1]
|
||||||
self.history[0] = volts
|
self.history[0] = volts
|
||||||
|
#print(self.history)
|
||||||
avg = np.sum(self.history)/self.history.shape[0]
|
avg = np.sum(self.history)/self.history.shape[0]
|
||||||
if self.on:
|
if self.on:
|
||||||
if avg < 0.2:
|
if avg < 0.2:
|
||||||
self.on = False
|
self.on = False
|
||||||
|
print("send status off")
|
||||||
|
send_update({"status": 0})
|
||||||
|
self.switch_timeout = time.time() + 1
|
||||||
else:
|
else:
|
||||||
if avg > 2.6:
|
if avg > 2.6:
|
||||||
self.on = True
|
self.on = True
|
||||||
|
print("send status on")
|
||||||
|
send_update({"status": 1})
|
||||||
|
self.switch_timeout = time.time() + 1
|
||||||
|
|
||||||
def toggle(self, s):
|
def toggle(self, s):
|
||||||
if time.time() > self.switch_timeout:
|
if time.time() > self.switch_timeout:
|
||||||
s.write(b"h")
|
s.write(b"h")
|
||||||
self.switch_timeout = time.time() + 7
|
self.switch_timeout = time.time() + 7
|
||||||
|
|
||||||
def send_update(msg):
|
|
||||||
global process
|
|
||||||
process.stdin.write(bytes(json.dumps(update) + "\n", "utf8"))
|
|
||||||
process.stdin.flush()
|
|
||||||
|
|
||||||
humidifier = Humidifier()
|
humidifier = Humidifier()
|
||||||
target_lower = 0.85
|
target_lower = 0.85
|
||||||
target_upper = 0.90
|
target_upper = 0.90
|
||||||
|
@ -87,6 +116,7 @@ try:
|
||||||
|
|
||||||
humidifier.update(volts)
|
humidifier.update(volts)
|
||||||
if humidity < target_lower and humidifier.off:
|
if humidity < target_lower and humidifier.off:
|
||||||
|
print("try toggle")
|
||||||
humidifier.toggle(s)
|
humidifier.toggle(s)
|
||||||
elif humidity > target_upper and humidifier.on:
|
elif humidity > target_upper and humidifier.on:
|
||||||
humidifier.toggle(s)
|
humidifier.toggle(s)
|
||||||
|
|
|
@ -22,7 +22,7 @@ type ShroomData struct {
|
||||||
Temperature float32 `json:"temp"`
|
Temperature float32 `json:"temp"`
|
||||||
Humidity float32 `json:"hum"`
|
Humidity float32 `json:"hum"`
|
||||||
HumidifierVolts float32 `json:"hv"`
|
HumidifierVolts float32 `json:"hv"`
|
||||||
Status uint32 `json:"status"`
|
Status int32 `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ShroomStatus struct {
|
type ShroomStatus struct {
|
||||||
|
@ -51,9 +51,9 @@ func parseMsg(line []byte, db *sql.DB, status *ShroomStatus) {
|
||||||
data := ShroomData{
|
data := ShroomData{
|
||||||
Time: 0,
|
Time: 0,
|
||||||
Temperature: -274,
|
Temperature: -274,
|
||||||
Humidity: -100,
|
Humidity: -1,
|
||||||
HumidifierVolts: -100,
|
HumidifierVolts: -1,
|
||||||
Status: 0,
|
Status: -1,
|
||||||
}
|
}
|
||||||
err := json.Unmarshal(line, &data)
|
err := json.Unmarshal(line, &data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -62,19 +62,21 @@ func parseMsg(line []byte, db *sql.DB, status *ShroomStatus) {
|
||||||
log.Println(line)
|
log.Println(line)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if data.Time > 0 && data.Temperature > -274 && data.Humidity > -100 && data.HumidifierVolts > -100 {
|
//log.Println("received data ", data)
|
||||||
|
if data.Time > 0 && data.Temperature > -275 && data.Humidity > -1 && data.HumidifierVolts > -1 {
|
||||||
err = InsertRow(db, &data)
|
err = InsertRow(db, &data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("unable to write to database: ", err)
|
log.Println("unable to write to database: ", err)
|
||||||
}
|
}
|
||||||
// we got a data packet
|
// we got a data packet
|
||||||
status.Update()
|
status.Update()
|
||||||
} else if data.Status > 0 {
|
} else if data.Status != -1 {
|
||||||
|
log.Println("received status ", data.Status)
|
||||||
status.Lock()
|
status.Lock()
|
||||||
// TODO change to have more detailed data
|
// TODO change to have more detailed data
|
||||||
status.HumidifierOn = (data.Status & 1) == 1
|
status.HumidifierOn = (data.Status & 1) == 1
|
||||||
status.Unlock()
|
status.Unlock()
|
||||||
status.Update()
|
status.StatusUpdate()
|
||||||
} else {
|
} else {
|
||||||
log.Println("unknown packet: ", line, string(line))
|
log.Println("unknown packet: ", line, string(line))
|
||||||
}
|
}
|
||||||
|
@ -120,6 +122,7 @@ func InitTcpServer(db *sql.DB, status *ShroomStatus) {
|
||||||
|
|
||||||
for {
|
for {
|
||||||
num_read, err := conn.Read(left)
|
num_read, err := conn.Read(left)
|
||||||
|
//log.Println("received: ", string(left[:num_read]))
|
||||||
left = left[num_read:]
|
left = left[num_read:]
|
||||||
//log.Println("buf ", buf)
|
//log.Println("buf ", buf)
|
||||||
//log.Println("left ", left)
|
//log.Println("left ", left)
|
||||||
|
|
Loading…
Reference in New Issue