diff --git a/dev/dev.htm b/dev/dev.htm index 783ff22..4f1b702 100644 --- a/dev/dev.htm +++ b/dev/dev.htm @@ -170,7 +170,7 @@ const sleep = (ms) => new Promise(r => setTimeout(r, ms)) async function chartupdater() { if (chart_updater != null) return - await sleep(5000) + await sleep(2000) // wait at least two seconds to avoid wasting a lot of bandwidth const resp = await fetch("/api/update") chart_updater = null @@ -181,7 +181,10 @@ var status_updater = null async function updateStatus() { const status_resp = await fetch("/api/status") 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) { status_updater = waitThenUpdateStatus() diff --git a/shroom_controller.py b/shroom_controller.py new file mode 100644 index 0000000..f5577a1 --- /dev/null +++ b/shroom_controller.py @@ -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 diff --git a/shroom_controller_mock.py b/shroom_controller_mock.py index f904409..a73b4c7 100644 --- a/shroom_controller_mock.py +++ b/shroom_controller_mock.py @@ -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(["/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 class MockSerial: 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, _): - pass + def write(self, msg): + if msg == b'h': + print("mock hum toggle") + self.humidifier_on = not self.humidifier_on def read(self, _): t = time.time() 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") s = MockSerial() @@ -33,7 +60,7 @@ def reset_serial(): class Humidifier: def __init__(self): self.on = False - self.history = np.zeros(30) + self.history = np.zeros(10) self.switch_timeout = 0 @property @@ -43,24 +70,26 @@ class Humidifier: def update(self, volts): self.history[1:] = self.history[:-1] self.history[0] = volts + #print(self.history) avg = np.sum(self.history)/self.history.shape[0] if self.on: if avg < 0.2: self.on = False + print("send status off") + send_update({"status": 0}) + self.switch_timeout = time.time() + 1 else: if avg > 2.6: self.on = True + print("send status on") + send_update({"status": 1}) + self.switch_timeout = time.time() + 1 def toggle(self, s): if time.time() > self.switch_timeout: s.write(b"h") 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() target_lower = 0.85 target_upper = 0.90 @@ -87,6 +116,7 @@ try: humidifier.update(volts) if humidity < target_lower and humidifier.off: + print("try toggle") humidifier.toggle(s) elif humidity > target_upper and humidifier.on: humidifier.toggle(s) diff --git a/shroom_internals/tcp_server.go b/shroom_internals/tcp_server.go index 95d8e93..bf32325 100644 --- a/shroom_internals/tcp_server.go +++ b/shroom_internals/tcp_server.go @@ -22,7 +22,7 @@ type ShroomData struct { Temperature float32 `json:"temp"` Humidity float32 `json:"hum"` HumidifierVolts float32 `json:"hv"` - Status uint32 `json:"status"` + Status int32 `json:"status"` } type ShroomStatus struct { @@ -51,9 +51,9 @@ func parseMsg(line []byte, db *sql.DB, status *ShroomStatus) { data := ShroomData{ Time: 0, Temperature: -274, - Humidity: -100, - HumidifierVolts: -100, - Status: 0, + Humidity: -1, + HumidifierVolts: -1, + Status: -1, } err := json.Unmarshal(line, &data) if err != nil { @@ -62,19 +62,21 @@ func parseMsg(line []byte, db *sql.DB, status *ShroomStatus) { log.Println(line) 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) if err != nil { log.Println("unable to write to database: ", err) } // we got a data packet status.Update() - } else if data.Status > 0 { + } else if data.Status != -1 { + log.Println("received status ", data.Status) status.Lock() // TODO change to have more detailed data status.HumidifierOn = (data.Status & 1) == 1 status.Unlock() - status.Update() + status.StatusUpdate() } else { log.Println("unknown packet: ", line, string(line)) } @@ -120,6 +122,7 @@ func InitTcpServer(db *sql.DB, status *ShroomStatus) { for { num_read, err := conn.Read(left) + //log.Println("received: ", string(left[:num_read])) left = left[num_read:] //log.Println("buf ", buf) //log.Println("left ", left)