diff --git a/shroom_controller.py b/shroom_controller.py index 0ddd1ca..2a4fda4 100644 --- a/shroom_controller.py +++ b/shroom_controller.py @@ -73,6 +73,10 @@ def reset_serial(): class Humidifier: def __init__(self): + self.off_threshold = 0.2 + self.on_threshold = 2.6 + self.toggle_cooldown = 7 + self.on = False self.history = np.zeros(10) self.switch_timeout = 0 @@ -87,13 +91,13 @@ class Humidifier: #print(self.history) avg = np.sum(self.history)/self.history.shape[0] if self.on: - if avg < 0.2: + if avg < self.off_threshold: self.on = False print("send status off") send_update({"status": {"humidifier": False}}) self.switch_timeout = time.time() + 1 else: - if avg > 2.6: + if avg > self.on_threshold: self.on = True print("send status on") send_update({"status": {"humidifier": True}}) @@ -102,17 +106,56 @@ class Humidifier: def toggle(self, s): if time.time() > self.switch_timeout: s.write(b"h") - self.switch_timeout = time.time() + 7 + self.switch_timeout = time.time() + self.toggle_cooldown + +class Controller: + def __init__(self): + self.target_lower = 0.85 + self.target_upper = 0.90 + self.feedforward_coeff = 50 + + self.manual_mode = False + self.manual_on = False + self.manual_timeout = 0 + self.manual_duration = 120 + + self.humidifier_history = np.zeros(30) + self.first_sample = False + + def update(self, humidifier, humidity): + if self.first_sample: + self.humidifier_history[:] = humidity + self.first_sample = False + else: + self.humidifier_history[:-1] = self.humidifier_history[1:] + self.humidifier_history[-1] = humidity + + # compensate for the slow response time by adding a little feed forward + # using the slope of the humidifier data + slope = (self.humidifier_history[-1] - self.humidifier_history[0])/self.humidifier_history.shape[0] + comp_humidity = humidity + self.feedforward_coeff*slope + + if self.manual_mode and time.time() > self.manual_timeout: + self.manual_mode = False + + if self.manual_mode: + if humidifier.off and self.manual_on: + humidifier.toggle(s) + elif humdifier.on and not self.manual_on: + humidifier.toggle(s) + else: + if comp_humidity < self.target_lower and humidifier.off: + humidifier.toggle(s) + elif comp_humidity > self.target_upper and humidifier.on: + humidifier.toggle(s) humidifier = Humidifier() -target_lower = 0.85 -target_upper = 0.90 -feedforward_coeff = 50 +controller = Controller() exiting = False # run thread to process data from process's stdout def stdout_loop(): - global process, target_lower, target_upper, feedforward_coeff + global process, controller, humidifier while not exiting: msg = process.stdout.readline() if len(msg) == 0: @@ -123,33 +166,56 @@ def stdout_loop(): if "query_params" in msg_js: if msg_js["query_params"]: send_update({"params": { - "target_lower": target_lower, - "target_upper": target_upper, - "feedforward_coeff": feedforward_coeff + "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: - if type(set_params["value"]) is float: - if set_params["name"] == "target_lower": - target_lower = set_params["value"] - elif set_params["name"] == "target_upper": - target_upper = set_params["value"] - elif set_params["name"] == "feedforward_coeff": - feedforward_coeff = set_params["value"] + 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 "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() -humidifier_history = np.zeros(30) -first_sample = False frame_num = 0 +last_sample = 0 try: - last_sample = 0 while True: now = time.time() if now - last_sample < SAMPLE_PERIOD: @@ -167,24 +233,10 @@ try: humidity = float(parts[0]) temp = float(parts[1]) volts = float(parts[2]) - 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 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) + controller.update(humidifier, humidity) if frame_num == 0: print(humidity, temp, volts)