diff py/fridge.py @ 143:0895f5ad7731

copied fridge logic from main.c
author Matt Johnston <matt@ucc.asn.au>
date Tue, 20 Nov 2012 22:03:10 +0800
parents 4755e6f9a5b8
children 482d7852b511
line wrap: on
line diff
--- a/py/fridge.py	Mon Nov 19 22:46:45 2012 +0800
+++ b/py/fridge.py	Tue Nov 20 22:03:10 2012 +0800
@@ -1,38 +1,68 @@
+# -*- coding: utf-8 -*-
 from utils import L,W,E
 import config
 
 class Fridge(object):
+
+    OVERSHOOT_MAX_DIV = 1800.0 # 30 mins
+    FRIDGE_AIR_MIN_RANGE = 4 # ÂșC
+    FRIDGE_AIR_MAX_RANGE = 4
+
     def __init__(self):
         self.setup_gpio()
         self.wort_valid_clock = 0
+        self.fridge_on_clock = 0
+        self.fridge_off_clock = 0
 
     def setup_gpio(self):
-        fn = '%s/direction' % config.FRIDGE_GPIO
-        f = open(fn, 'w')
-        f.write('low')
-        f.close()
-        # .off() shouldn't do anything, but tests that "value" is writable
-        self.off()
+        dir_fn = '%s/direction' % config.FRIDGE_GPIO
+        with f = open(dir_fn, 'w'):
+            f.write('low')
+        val_fn = '%s/value' % config.FRIDGE_GPIO
+        self.value_file = f.open(val_fn, 'r+')
 
     def turn(self, value):
-        fn = '%s/value' % config.FRIDGE_GPIO
-        f = open(fn, 'w')
+        self.value_file.seek(0)
         if value:
-            f.write('1')
+            self.value_file.write('1')
         else:
-            f.write('0')
-        f.close()
+            self.value_file.write('0')
+        self.value_file.flush()
 
     def on(self):
-        self.turn(1)
+        self.turn(True)
 
     def off(self):
-        self.turn(0)
+        self.turn(False)
 
-    def do(self):
+    def is_on(self):
+        self.value_file.seek(0)
+        buf = self.value_file.read().strip()
+        if buf == '0':
+            return False
+        if buf != '1':
+            E("Bad value read from gpio '%s': '%s'" 
+                % (self.value_file.name, buf))
+        return True
+
+    def run(self, server):
+
+        while True:
+            self.do(server)
+            gevent.sleep(config.FRIDGE_SLEEP)
+
+    def do(self, server):
+        """ this is the main fridge control logic """
         wort, fridge, ambient = server.current_temps()
 
-        if server.uptime() < config.FRIDGE_DELAY:
+        fridge_min = params.fridge_setpoint - self.FRIDGE_AIR_MIN_RANGE
+        fridge_max = params.fridge_setpoint + self.FRIDGE_AIR_MAX_RANGE
+
+        wort_max = params.fridge_setpoint + params.fridge_difference
+
+        off_time = server.now() - self.fridge_off_clock
+
+        if off_time < config.FRIDGE_DELAY:
             L("fridge skipping, too early")
             return
 
@@ -49,15 +79,46 @@
         if fridge is None:
             W("Invalid fridge sensor")
 
-        
+        if self.is_on():
+            turn_off = False
+            on_time = server.now() - self.fridge_on_clock
 
-        
+            overshoot = 0
+            if on_time > params.overshoot_delay:
+                overshoot = params.overshoot_factor \
+                    * min(self.OVERSHOOT_MAX_DIV, on_time) \
+                    / self.OVERSHOOT_MAX_DIV
+            L("on_time %(on_time)f, overshoot %(overshoot)f" % locals())
 
-        
+            if wort is not None:
+                if (wort - overshoot) < params.fridge_setpoint:
+                    L("wort has cooled enough")
+                    turn_off = True
+            else:
+                # wort sensor is broken
+                if fridge is not None and last_fridge < fridge_min:
+                    W("fridge off fallback")
+                    turn_off = True
 
-    def run(self, server):
-        self.server = server
+            if turn_off:
+                L("Turning fridge off")
+                self.off()
+                self.fridge_off_clock = server.now()
 
-        while True:
-            self.do()
-            gevent.sleep(config.FRIDGE_SLEEP)
+        else:
+            # fridge is off
+            turn_on = False
+            if wort is not None:
+                if wort >= wort_max:
+                    L("Wort is too hot")
+                    turn_on = True
+            else:
+                # wort sensor is broken
+                if fridge is not None and fridge >= fridge_max:
+                    W("frdge on fallback")
+                    turn_on = True
+
+            if turn_on:
+                L("Turning fridge on")
+                self.on()
+                fridge_on_clock = server.now()