changeset 430:d9d59a245461

merge
author Matt Johnston <matt@ucc.asn.au>
date Sun, 14 Oct 2012 21:51:55 +0800
parents 516bb4ef0587 (diff) 649648020123 (current diff)
children b90e9c695cfd
files
diffstat 4 files changed, 168 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/main.c	Thu Oct 11 20:41:52 2012 +0800
+++ b/main.c	Sun Oct 14 21:51:55 2012 +0800
@@ -22,6 +22,9 @@
 // - bluetooth params
 // - number of sensors (and range?)
 
+#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
+#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
+
 // TICK should be 8 or less (8 untested). all timers need
 // to be a multiple.
 
@@ -32,6 +35,14 @@
 #define VALUE_NOSENSOR 0x07D0 // 125 degrees
 #define VALUE_BROKEN 0x07D1 // 125.0625
 
+// ranges are in decicelcius
+#define OVERSHOOT_DELAY 1800 // 30 mins
+#define OVERSHOOT_FACTOR 1
+#define WORT_INVALID_TIME 900 // 15 mins
+// fridge min/max are only used if the wort sensor is invalid
+#define FRIDGE_AIR_MIN_RANGE 40 // 4º
+#define FRIDGE_AIR_MAX_RANGE 40 // 4º
+
 #define BAUD 19200
 #define UBRR ((F_CPU)/8/(BAUD)-1)
 
@@ -49,7 +60,7 @@
 
 // total amount of 16bit values available for measurements.
 // adjust emperically, be sure to allow enough stack space too
-#define TOTAL_MEASUREMENTS 840
+#define TOTAL_MEASUREMENTS 800
 
 // each sensor slot uses 8 bytes
 #define MAX_SENSORS 6
@@ -75,10 +86,11 @@
 static uint8_t wake_secs = 30;
 // decidegrees
 static int16_t fridge_setpoint = 180; // 18.0ºC
-static int16_t fridge_difference = 5; // 0.5ºC
+static int16_t fridge_difference = 3; // 0.3ºC
 static uint16_t fridge_delay = 600; // seconds
 
 // ---- Atomic guards required accessing these variables
+// clock_epoch in seconds
 static uint32_t clock_epoch;
 static uint16_t comms_count;
 static uint16_t measure_count;
@@ -117,6 +129,8 @@
 static int16_t last_fridge = DS18X20_INVALID_DECICELSIUS;
 static int16_t last_wort = DS18X20_INVALID_DECICELSIUS;
 static struct epoch_ticks fridge_off_clock = {0};
+static struct epoch_ticks fridge_on_clock = {0};
+static struct epoch_ticks wort_valid_clock = {0};
 
 int uart_putchar(char c, FILE *stream);
 static void long_delay(int ms);
@@ -204,8 +218,8 @@
     DDR_LED |= _BV(PIN_LED);
     DDR_SHDN |= _BV(PIN_SHDN);
 
+    PORT_FRIDGE &= ~_BV(PIN_FRIDGE);
     DDR_FRIDGE |= _BV(PIN_FRIDGE);
-    PORT_FRIDGE &= ~_BV(PIN_FRIDGE);
 
     // set pullup
     PORTD |= _BV(PD2);
@@ -364,6 +378,10 @@
     fprintf_P(crc_stdout, PSTR("fridge_diff=%.1f\n"), fridge_difference/10.0);
     fprintf_P(crc_stdout, PSTR("fridge_delay=%hu\n"), fridge_delay);
     fprintf_P(crc_stdout, PSTR("fridge_status=%hhu\n"), is_fridge_on());
+    fprintf_P(crc_stdout, PSTR("fridge_last_on=%lu\n"), fridge_on_clock.ticks);
+    fprintf_P(crc_stdout, PSTR("fridge_last_off=%lu\n"), fridge_off_clock.ticks);
+    fprintf_P(crc_stdout, PSTR("last_fridge=%hu\n"), last_fridge);
+    fprintf_P(crc_stdout, PSTR("last_wort=%hu\n"), last_wort);
     fprintf_P(crc_stdout, PSTR("tick_secs=%d\n"), TICK);
     fprintf_P(crc_stdout, PSTR("tick_wake=%d\n"), SLEEP_COMPARE);
     fprintf_P(crc_stdout, PSTR("maxsens=%hhu\n"), MAX_SENSORS);
@@ -574,16 +592,22 @@
         return;
     }
 
+    int16_t old_setpoint = fridge_setpoint;
+
     fridge_setpoint = new_f * 10;
     bool written = set_initial_eeprom();
     if (!written)
     {
-        ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
+        if (old_setpoint != fridge_setpoint)
         {
-            eeprom_write(fridge_setpoint, fridge_setpoint);
+            ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
+            {
+                eeprom_write(fridge_setpoint, fridge_setpoint);
+            }
         }
     }
-    printf_P(PSTR("new fridge %.1fº\n"), fridge_setpoint / 10.0f);
+    printf_P(PSTR("old fridge %.1fº new fridge %.1fº\n"), 
+            old_setpoint / 10.0f, fridge_setpoint / 10.0f);
 }
 
 static void
@@ -814,29 +838,86 @@
 {
     struct epoch_ticks now;
     get_epoch_ticks(&now);
-    uint16_t delay_delta = now.ticks - fridge_off_clock.ticks;
-    if (delay_delta < fridge_delay)
+    uint32_t off_time = now.ticks - fridge_off_clock.ticks;
+    bool wort_valid = last_wort != DS18X20_INVALID_DECICELSIUS;
+    bool fridge_valid = last_fridge != DS18X20_INVALID_DECICELSIUS;
+
+    int16_t wort_max = fridge_setpoint + fridge_difference;
+    int16_t wort_min = fridge_setpoint;
+
+    // the fridge min/max only apply if the wort sensor is broken
+    int16_t fridge_min = fridge_setpoint - FRIDGE_AIR_MIN_RANGE;
+    int16_t fridge_max = fridge_setpoint + FRIDGE_AIR_MAX_RANGE;
+
+    uint8_t fridge_on = PORT_FRIDGE & _BV(PIN_FRIDGE);
+    printf_P(PSTR("last_wort %hd (%hd, %hd), last_fridge %hd (%hd, %hd), setpoint %hd, diff %hd, fridge_on %hhu\n"), 
+            last_wort, wort_min, wort_max, 
+            last_fridge, fridge_min, fridge_max, 
+            fridge_setpoint, fridge_difference, fridge_on);
+
+    if (off_time < fridge_delay)
     {
         printf_P(PSTR("waiting for fridge delay current %hu, wait %hu\n"),
-                delay_delta, fridge_delay);
+                off_time, fridge_delay);
         return;
     }
 
-    if (last_wort == DS18X20_INVALID_DECICELSIUS)
+    // handle failure of the wort sensor. if it is a short (intermittent?)
+    // failure we wait until it has been broken for a period of time
+    // (WORT_INVALID_TIME) before doing anything.
+    if (wort_valid)
+    {
+        wort_valid_clock = now;
+    }
+    else
     {
-        // can't really do much sensible.... alert perhaps?
-        printf_P(PSTR("Bad last wort!\n"));
-        need_comms = 1;
-        return;
+        printf_P(PSTR("wort sensor is invalid\n"));
+        uint32_t invalid_time = now.ticks - wort_valid_clock.ticks;
+        if (invalid_time < WORT_INVALID_TIME)
+        {
+            printf("only been invalid for %ld, waiting\n", invalid_time);
+            return;
+        }
+    }
+
+    if (!fridge_valid)
+    {
+        printf_P(PSTR("fridge sensor is invalid\n"));
     }
 
-    int16_t wort_delta = last_wort - fridge_setpoint;
-    uint8_t fridge_on = PORT_FRIDGE & _BV(PIN_FRIDGE);
-    printf_P(PSTR("last_wort %hd, setpoint %hd, delta %hd, fridge_on %d\n"), 
-            last_wort, fridge_setpoint, wort_delta, fridge_on);
     if (fridge_on)
     {
-        if (last_wort <= fridge_setpoint)
+        bool turn_off = false;
+        uint16_t on_time = now.ticks - fridge_on_clock.ticks;
+
+        uint16_t overshoot = 0;
+        if (on_time > 1800)
+        {
+            // *10.0f for decicelcius
+            overshoot = OVERSHOOT_FACTOR * 10.0f * MIN(3600, on_time) / 3600.0;
+        }
+
+        printf_P(PSTR("on_time %hu, overshoot %hu\n"), on_time, overshoot);
+
+        // wort has cooled enough. will probably cool a bit more by itself
+        if (wort_valid)
+        {
+            if ((last_wort - overshoot) < fridge_setpoint)
+            {
+                printf_P(PSTR("wort has cooled enough, overshoot %hu on_time %hu\n"), overshoot, on_time);
+                turn_off = true;
+            }
+        }
+        else
+        {
+            if (fridge_valid && last_fridge < fridge_min)
+            {
+                printf_P(PSTR("fridge off fallback\n"));
+                turn_off = true;
+            }
+        }
+
+        if (turn_off)
         {
             // too cold, turn off
             printf_P(PSTR("Turning fridge off\n"));
@@ -846,11 +927,31 @@
     }
     else
     {
-        if (wort_delta > fridge_difference)
+        bool turn_on = false;
+
+        if (wort_valid)
+        {
+            if (last_wort >= wort_max)
+            {
+                printf_P(PSTR("wort is too hot\n"));
+                turn_on = true;
+            }
+        }
+        else
+        {
+            if (fridge_valid && last_fridge >= fridge_max)
+            {
+                printf_P(PSTR("fridge on fallback\n"));
+                turn_on = true;
+            }
+        }
+
+        if (turn_on)
         {
             // too hot, turn on
             printf_P(PSTR("Turning fridge on\n"));
             PORT_FRIDGE |= _BV(PIN_FRIDGE);
+            fridge_on_clock = now;
         }
     }
 }
@@ -889,11 +990,11 @@
 
         if (memcmp(sensor_id[s], fridge_id, sizeof(fridge_id)) == 0)
         {
-            last_fridge = ds18b20_raw16_to_decicelsius(reading);;
+            last_fridge = ds18b20_raw16_to_decicelsius(reading);
         }
         if (memcmp(sensor_id[s], wort_id, sizeof(wort_id)) == 0)
         {
-            last_wort = ds18b20_raw16_to_decicelsius(reading);;
+            last_wort = ds18b20_raw16_to_decicelsius(reading);
         }
     }
 
--- a/simple_ds18b20.c	Thu Oct 11 20:41:52 2012 +0800
+++ b/simple_ds18b20.c	Sun Oct 14 21:51:55 2012 +0800
@@ -80,7 +80,7 @@
 		decicelsius = -decicelsius;
 	}
 
-	if ( /* decicelsius == 850 || */ decicelsius < -550 || decicelsius > 1250 ) {
+	if ( decicelsius == 850 || decicelsius < -550 || decicelsius > 1250 ) {
 		return DS18X20_INVALID_DECICELSIUS;
 	} else {
 		return decicelsius;
--- a/web/config.py	Thu Oct 11 20:41:52 2012 +0800
+++ b/web/config.py	Sun Oct 14 21:51:55 2012 +0800
@@ -14,16 +14,20 @@
 
 LINE_WIDTH = 2
 
-SENSOR_NAMES = {'sensor_28 CE B2 1A 03 00 00 99': "Wort",
+SENSOR_NAMES = {'sensor_28 CE B2 1A 03 00 00 99': "Fridge",
     'sensor_28 CC C1 1A 03 00 00 D4': "Ambient",
-    'sensor_28 49 BC 1A 03 00 00 54': "Other",
+    'sensor_28 49 BC 1A 03 00 00 54': "Wort",
     'sensor_voltage': 'Voltage',
+    'sensor_fridge_setpoint': 'Setpoint',
+    'sensor_fridge_on': 'On',
     }
 
 SENSOR_COLOURS = {'Wort': 'e49222', 
                 'Ambient': '028b3d',
                 'Voltage': '7db5d3aa',
-                'Other': '78000c',
+                'Fridge': '4c40c8',
+                'Setpoint': '39c662',
+                'On': 'd7cedd',
                 }
 
 
--- a/web/log.py	Thu Oct 11 20:41:52 2012 +0800
+++ b/web/log.py	Sun Oct 14 21:51:55 2012 +0800
@@ -34,6 +34,11 @@
                 '--step', '3600',
                 'DS:temp:GAUGE:7200:1:10',
                 'RRA:AVERAGE:0.5:1:87600']
+    elif 'fridge_on' in sensor_id:
+        args = [
+                '--step', '300',
+                'DS:temp:GAUGE:600:-100:500',
+                'RRA:LAST:0.5:1:1051200']
     else:
         args = [
                 '--step', '300',
@@ -62,6 +67,7 @@
     graph_args = []
     have_volts = False
     for n, (rrdfile, sensor) in enumerate(rrds):
+        unit = None
         if 'avrtemp' in sensor:
             continue
         if 'voltage' in sensor:
@@ -69,6 +75,10 @@
             vname = 'scalevolts'
             graph_args.append('DEF:%(vname)s=%(rrdfile)s:temp:AVERAGE:step=3600' % locals())
             unit = 'V'
+        elif 'fridge_on' in sensor:
+            vname = 'fridge_on'
+            graph_args.append('DEF:raw%(vname)s=%(rrdfile)s:temp:LAST' % locals())
+            graph_args.append('CDEF:%(vname)s=raw%(vname)s,3,+' % locals())
         else:
             vname = 'temp%d' % n
             graph_args.append('DEF:raw%(vname)s=%(rrdfile)s:temp:AVERAGE' % locals())
@@ -76,12 +86,20 @@
             graph_args.append('CDEF:%(vname)s=raw%(vname)s,35,GT,UNKN,raw%(vname)s,0.1,*,2,+,IF' % locals())
             unit = '<span face="Liberation Serif">º</span>C'
 
-        last_value = float(rrdtool.info(rrdfile)['ds[temp].last_ds'])
+        format_last_value = None
+        if unit:
+            try:
+                last_value = float(rrdtool.info(rrdfile)['ds[temp].last_ds'])
+                format_last_value = ('%f' % last_value).rstrip('0.') + unit
+            except ValueError:
+                pass
         width = config.LINE_WIDTH
         legend = config.SENSOR_NAMES.get(sensor, sensor)
         colour = config.SENSOR_COLOURS.get(legend, colour_from_string(sensor))
-        format_last_value = ('%f' % last_value).rstrip('0') + unit
-        print_legend = '%s (%s)' % (legend, format_last_value)
+        if format_last_value:
+            print_legend = '%s (%s)' % (legend, format_last_value)
+        else:
+            print_legend = legend
         graph_args.append('LINE%(width)f:%(vname)s#%(colour)s:%(print_legend)s' % locals())
 
     end = int(start+length)
@@ -128,6 +146,13 @@
     #return tempf
     return tempf.read()
 
+def validate_values(measurements):
+    for m in measurements:
+        if m == 85:
+            yield 'U'
+        else:
+            yield '%f' % m
+
 def sensor_update(sensor_id, measurements, first_real_time, time_step):
     try:
         open(sensor_rrd_path(sensor_id))
@@ -135,9 +160,9 @@
         create_rrd(sensor_id)
 
     if measurements:
-        values = ['%d:%f' % p for p in 
+        values = ['%d:%s' % p for p in 
             zip((first_real_time + time_step*t for t in xrange(len(measurements))),
-                measurements)]
+                validate_values(measurements))]
 
         rrdfile = sensor_rrd_path(sensor_id)
         # XXX what to do here when it fails...
@@ -224,6 +249,13 @@
         voltage = 0.001 * int(entries['voltage'])
         sensor_update('voltage', [voltage], time.time(), 1)
 
+    if 'fridge_status' in entries:
+        fridge_on = int(entries['fridge_status'])
+        sensor_update('fridge_on', [fridge_on], time.time(), 1)
+
+    if 'fridge' in entries:
+        fridge_setpoint = float(entries['fridge'])
+        sensor_update('fridge_setpoint', [fridge_setpoint], time.time(), 1)
     #sqlite 
     # - time
     # - voltage