shrooms-server/shroom_controller.py

221 lines
6.1 KiB
Python

import numpy as np
import json
import os
import serial
import subprocess
import threading
import time
from humidifier import Humidifier, HumidifierV2
from humidifier_v3 import HumidifierV3
from controller import Controller
import utils
from utils import start_process, send_update
SERIAL_PATH = "/dev/ttyACM0"
SERIAL_BAUD = 115200
SAMPLE_PERIOD = 0.5
DECIMATION_RATE = 1
try:
is_mock = os.environ['MOCK']
except KeyError:
is_mock = False
print("controller start")
start_process()
if is_mock:
import mock_serial
s = mock_serial.MockSerial()
else:
s = serial.Serial(SERIAL_PATH, SERIAL_BAUD, timeout = 0.3)
print("pausing for bootloader...")
time.sleep(10)
print("pause done")
def reset_serial():
global s
if not is_mock:
s.close()
s = serial.Serial(SERIAL_PATH, SERIAL_BAUD, timeout = 0.3)
time.sleep(10)
humidifier = HumidifierV3()
controller = Controller([humidifier])
exiting = False
# run thread to process data from process's stdout
def stdout_loop():
global process, controller, humidifier, humidifier2
while not exiting:
msg = utils.process.stdout.readline()
if len(msg) <= 1:
continue
print("got message ", msg)
try:
msg_js = json.loads(msg)
if "query_params" in msg_js:
if msg_js["query_params"]:
send_update({"params": {
"target_lower": controller.target_lower,
"target_upper": controller.target_upper,
"feedforward_coeff": controller.feedforward_coeff,
"manual_timeout": controller.manual_timeout,
"manual_duration_s": controller.manual_duration,
"manual_mode": 1.0 if controller.manual_mode else 0.0,
"manual_hum_on": 1.0 if controller.manual_on else 0.0,
"off_threshold_volts": humidifier.off_threshold,
"on_threshold_volts": humidifier.on_threshold,
"toggle_cooldown": humidifier.toggle_cooldown
}
})
elif "set_params" in msg_js:
if type(msg_js["set_params"]) is dict:
set_params = msg_js["set_params"]
if "name" in set_params and "value" in set_params:
name, value = set_params["name"], set_params["value"]
if type(value) is float:
if name == "target_lower":
controller.target_lower = value
elif name == "target_upper":
controller.target_upper = value
elif name == "feedforward_coeff":
controller.feedforward_coeff = value
elif name == "manual_timeout":
controller.manual_timeout = value
elif name == "manual_duration_s":
controller.manual_duration = value
elif name == "off_threshold_volts":
humidifier.off_threshold = value
elif name == "on_threshold_volts":
humidifier.on_threshold = value
elif name == "toggle_cooldown":
humidifier.toggle_cooldown = value
elif name == "off_threshold_volts2":
humidifier2.off_threshold = value
elif name == "on_threshold_volts2":
humidifier2.on_threshold = value
elif name == "toggle_cooldown2":
humidifier2.toggle_cooldown = value
elif "manual_mode" in msg_js:
controller.manual_mode = msg_js["manual_mode"]
if controller.manual_mode:
controller.manual_timeout = time.time() + controller.manual_duration
elif "manual_mode_on" in msg_js:
controller.manual_on = msg_js["manual_mode_on"]
except json.JSONDecodeError as e:
print("received bad json ", msg)
stdout_thread = threading.Thread(target=stdout_loop)
stdout_thread.start()
def restart_pipe():
global exiting, stdout_thread
exiting = True
utils.process.kill()
time.sleep(0.1)
stdout_thread.join()
exiting = False
start_process()
stdout_thread = threading.Thread(target=stdout_loop)
stdout_thread.start()
frame_num = 0
last_sample = 0
last_pipe_reboot = 0
last_running = False
pipe_timeout = 10
fan_on = False
try:
while True:
now = time.time()
# check to see if the SSH pipe is working
if utils.running():
if not last_running and utils.running():
print("pipe is now running, resetting timeout", pipe_timeout)
pipe_timeout = 10
last_running = utils.running()
if not utils.running() and (now - last_pipe_reboot) > pipe_timeout:
try:
restart_pipe()
except Exception as e:
print("error restarting pipe: {}".format(repr(e)))
last_pipe_reboot = now
pipe_timeout *= 1.5
pipe_timeout = min(pipe_timeout, 5*60)
print("new pipe timeout ", pipe_timeout)
now = time.time()
if now - last_sample < SAMPLE_PERIOD:
time.sleep(SAMPLE_PERIOD - (now - last_sample) + 0.001)
continue
last_sample = now
# turn on the fan 1 minutes every 5 minutes
cur_min = int(now / 60)
fan_should_be_on = (cur_min % 5 == 0)
if fan_on != fan_should_be_on:
if fan_should_be_on:
s.write(b"Y")
else:
s.write(b"y")
#print("write s")
s.write(b"s")
s.flush()
resp = s.read(120)
#print("read", resp)
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])
volts2 = float(parts[3])
fan_on = int(volts2)
#print(parts)
try:
humidifier.update(volts)
controller.update(s, humidity)
if frame_num == 0:
#print(humidity, temp, volts)
update = {
"data": {
"time": int(now*1000),
"temp": temp,
"hum": humidity,
"hv": volts,
"hv2": volts2,
}
}
send_update(update)
#print("sending update {}".format(update))
frame_num = (frame_num + 1) % DECIMATION_RATE
except Exception as e:
print("pipe errored out, restarting: ", repr(e))
# restart the process I guess
restart_pipe()
finally:
# kill ssh connection
exiting = True
utils.process.kill()
stdout_thread.join()