Have only one version of the controller, TODO add flags to switch from mock and actual version

This commit is contained in:
Kelvin Ly 2023-05-16 09:42:25 -04:00
parent 3a7bb60360
commit 09504e9ec5
2 changed files with 129 additions and 200 deletions

View File

@ -1,37 +1,97 @@
import numpy as np
import json
import serial
import subprocess
import threading
import time
s = serial.Serial("/dev/ttyUSB0", 115200, timeout=10)
q = queue.Queue()
#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(["ssh", "shrooms@threefortiethofonehamster.com", "python", "/home/shrooms/go/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
exiting = False
# run thread to process data from process's stdout
def stdout_loop():
while not exiting:
msg = process.stdout.readline()
print("got message ", msg)
stdout_thread = threading.Thread(target=stdout_loop)
stdout_thread.start()
class MockSerial:
def __init__(self):
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, 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)
# 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] = 1.5
# 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] + np.random.random()*0.003
if self.humidifier_on:
hv = 3.3
else:
hv = 0.0
return bytes("{},{},{}\n".format(humidity, temp, hv), "utf8")
s = MockSerial()
def reset_serial():
s.close()
s = serial.Serial("/dev/ttyUSB0", 115200, timeout = 10)
pass
time.sleep(10)
class Humidifier:
def __init__(self):
self.on = False
self.history = np.array(30)
self.history = np.zeros(10)
self.switch_timeout = 0
@property
def off(self):
return not self.on
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.state:
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:
@ -40,34 +100,73 @@ class Humidifier:
humidifier = Humidifier()
target_lower = 0.85
target_higher = 0.90
target_upper = 0.90
feedforward_coeff = 50
humidifier_history = np.zeros(30)
first_sample = False
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)
time.sleep(0.5 - (now - last_sample))
continue
last_sample = now
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
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)
if first_sample:
humidifier_history[:] = humidity
first_sample = False
else:
time.sleep(0.5-(now - last_sample))
humidifier_history[:-1] = humidifier_history[1:]
humidifier_history[-1] = humidity
# compensate for the slow response time by adding a little feed forward
# using the slope of the humidifier data
slope = (humidifier_history[-1] - humidifier_history[0])/humidifier_history.shape[0]
comp_humidity = humidity + feedforward_coeff*slope
try:
humidifier.update(volts)
if comp_humidity < target_lower and humidifier.off:
humidifier.toggle(s)
elif comp_humidity > target_upper and humidifier.on:
humidifier.toggle(s)
update = {
"time": int(now*1000),
"temp": temp,
"hum": humidity,
"hv": volts
}
send_update(update)
except Exception as e:
print("pipe errored out, restarting: ", e)
# restart the process I guess
exiting = True
process.kill()
time.sleep(0.1)
stdout_thread.join()
exiting = False
process = subprocess.Popen(["/usr/bin/env", "python", "/home/kelvin/src/shroom-server/shroom_pipe.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
stdout_thread = threading.Thread(target=stdout_loop)
stdout_thread.start()
finally:
# TODO kill ssh connection
process.
pass
# kill ssh connection
exiting = True
process.kill()
stdout_thread.join()

View File

@ -1,170 +0,0 @@
import numpy as np
import json
import serial
import subprocess
import threading
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()
exiting = False
# run thread to process data from process's stdout
def stdout_loop():
while not exiting:
msg = process.stdout.readline()
print("got message ", msg)
stdout_thread = threading.Thread(target=stdout_loop)
stdout_thread.start()
class MockSerial:
def __init__(self):
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, 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)
# 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] + np.random.random()*0.003
if self.humidifier_on:
hv = 3.3
else:
hv = 0.0
return bytes("{},{},{}\n".format(humidity, temp, hv), "utf8")
s = MockSerial()
def reset_serial():
pass
time.sleep(10)
class Humidifier:
def __init__(self):
self.on = False
self.history = np.zeros(10)
self.switch_timeout = 0
@property
def off(self):
return not self.on
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
humidifier = Humidifier()
target_lower = 0.85
target_upper = 0.90
humidifier_history = np.zeros(30)
first_sample = False
try:
last_sample = 0
while True:
now = time.time()
if now - last_sample < 0.5:
time.sleep(0.5 - (now - last_sample))
continue
last_sample = now
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)
if first_sample:
humidifier_history[:] = humidity
first_sample = False
else:
humidifier_history[:-1] = humidifier_history[1:]
humidifier_history[-1] = humidity
# compensate for the slow response time
slope = (humidifier_history[-1] - humidifier_history[0])/humidifier_history.shape[0]
comp_humidity = humidity + 50*slope
try:
humidifier.update(volts)
if comp_humidity < target_lower and humidifier.off:
humidifier.toggle(s)
elif comp_humidity > target_upper and humidifier.on:
humidifier.toggle(s)
update = {
"time": int(now*1000),
"temp": temp,
"hum": humidity,
"hv": volts
}
send_update(update)
except Exception as e:
print("pipe errored out, restarting: ", e)
# restart the process I guess
exiting = True
process.kill()
time.sleep(0.1)
stdout_thread.join()
exiting = False
process = subprocess.Popen(["/usr/bin/env", "python", "/home/kelvin/src/shroom-server/shroom_pipe.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
stdout_thread = threading.Thread(target=stdout_loop)
stdout_thread.start()
finally:
# kill ssh connection
exiting = True
process.kill()
stdout_thread.join()