changeset 3:e75ecd162da3

Add watering logic ressurected from 2013. Handles basic "water all regions for X minutes in X minutes time" Tolerates power failures.
author Daniel O'Connor <darius@dons.net.au>
date Tue, 27 Jan 2015 23:40:01 +1030
parents ae8fb85a4949
children 1188042ddc2f
files water.c water.h
diffstat 2 files changed, 389 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/water.c	Tue Jan 27 23:40:01 2015 +1030
@@ -0,0 +1,359 @@
+/*
+ * Watering logic
+ *
+ * Copyright (c) 2015
+ *      Daniel O'Connor <darius@dons.net.au>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/pgmspace.h>
+#include <avr/eeprom.h>
+#include <util/crc16.h>
+
+#include "ds1307.h"
+#include "water.h"
+
+struct tm {
+    int32_t	sec;
+    int32_t	usec;
+};
+
+volatile struct tm nowtm;
+
+#define RELAY_MASK 0xf0
+
+typedef struct {
+    char	*name;
+    uint8_t	relay;
+} region_t;
+
+static region_t regions[] = {
+    { "front", 	4 },
+    { "beds", 	5 },
+//    { "side", 	6 },
+};
+
+#define NJOBS	6
+
+typedef struct {
+    uint8_t	valid;
+    uint32_t	start;
+    uint16_t	length;
+    uint8_t	region;
+} job_t;
+
+typedef struct {
+    job_t	jobs[NJOBS];
+} settings_t;
+
+/* Current settings in RAM */
+static settings_t	settings;
+/* EEPROM copy of settings */
+struct {
+    settings_t  settings;
+    uint16_t    crc;
+} ee_area __attribute__((section(".eeprom")));
+
+/* Local function prototypes */
+static void		water_default_settings(void);
+static void		water_write_settings(void);
+static void		water_load_or_init_settings(void);
+static void		water_addjob(int region, uint16_t delay, uint16_t time);
+static void		water_printjobs(void);
+
+void
+water_init(void) {
+    puts_P(PSTR("Initing water stuff\r\n"));
+
+    water_load_or_init_settings();
+
+    /* Setup timer */
+    /* 8Mhz / 256 = 31250 Hz / 250 = 125 Hz = IRQ every 8 ms */
+
+    /* CTC mode, no output on pin, Divide clock by 256 */
+    TCCR2A = _BV(WGM21);
+    TCCR2B = _BV(CS22) | _BV(CS21);
+
+    /* Compare with ... */
+    OCR2A = 250;
+
+    /* Enable interrupt for match on A */
+    TIMSK2 = _BV(OCIE2A);
+
+    nowtm.sec = 0;
+    nowtm.usec = 0;
+}
+
+/*
+ * Timer 2 Compare IRQ
+ *
+ * Update time counter
+ */
+
+ISR(TIMER2_COMPA_vect) {
+    nowtm.usec += 8000;
+    while (nowtm.usec > 1000000) {
+	nowtm.usec -= 1000000;
+	nowtm.sec++;
+    }
+}
+
+#define MAXARGS 10
+
+/* Parse water related command */
+void
+water_cmd(char *buf) {
+    char	*e, **btmp;
+    uint16_t	delay;
+    uint16_t	time;
+    long	tmp;
+    int		i, argc, skip;
+    char	*argv[MAXARGS];
+
+    /* Split command string on space/tab boundaries into argv/argc */
+    argc = 0;
+    btmp = &buf;
+    skip = 1;
+    while (1) {
+	    argv[argc] = strsep(btmp, " \t");
+	    if (argv[argc] == '\0')
+		    break;
+	    /* Skip first arg (ie 'wa') */
+	    if (skip) {
+		    skip = 0;
+		    continue;
+	    }
+	    argc++;
+	    if (argc == MAXARGS)
+		    break;
+    }
+
+    if (!strcmp(argv[0], "all")) {
+	    if (argc != 3) {
+		    puts_P(PSTR("Wrong number of arguments, usage: wa all delay time\r\n"));
+		    return;
+	    }
+
+	    tmp = strtol(argv[1], &e, 10);
+	    if (e == argv[1]) {
+		    puts_P(PSTR("Unable to parse delay\r\n"));
+		    return;
+	    }
+	    if (tmp < 0 || tmp > 65535) {
+		    puts_P(PSTR("delay out of range, must be 0 - 65535\r\n"));
+		    return;
+	    }
+	    delay = tmp;
+
+	    tmp = strtol(argv[2], &e, 10);
+	    if (e == argv[2]) {
+		    puts_P(PSTR("Unable to parse time\r\n"));
+		    return;
+	    }
+	    if (tmp < 1 || tmp > 65535) {
+		    puts_P(PSTR("time out of range, must be 1 - 65535\r\n"));
+		    return;
+	    }
+	    time = tmp;
+
+	    water_printjobs();
+
+	    /* add for each region one after the other */
+	    for (i = 0; i < sizeof(regions) / sizeof(regions[0]); i++) {
+		    water_addjob(i, delay + time * i, time);
+	    }
+
+	    water_printjobs();
+    } else {
+	    puts_P(PSTR("Unknown 'wa' sub-command\r\n"));
+	    return;
+    }
+}
+
+static void
+water_addjob(int region, uint16_t delay, uint16_t time) {
+    int		i;
+    time_t	now;
+
+    printf_P(PSTR("adding region %d, delay %d, time %d\r\n"), region, delay, time);
+
+    now = ds1307_time();
+    if (now == -1) {
+	puts_P(PSTR("Unable to read time\r\n"));
+	return;
+    }
+
+    /* Find an open job slot */
+    for (i = 0; i < NJOBS; i++) {
+	if (settings.jobs[i].valid == 0)
+	    break;
+    }
+
+    if (i == NJOBS) {
+	puts_P(PSTR("Couldn't find free slot\r\n"));
+	return;
+    }
+
+    settings.jobs[i].start = now + delay * 60;
+    settings.jobs[i].length = time * 60;
+    settings.jobs[i].region = region;
+    settings.jobs[i].valid = 1;
+
+    water_write_settings();
+}
+
+static void
+water_printjobs(void) {
+    int		i;
+
+    for (i = 0; i < NJOBS; i++) {
+	printf_P(PSTR("%d: "), i);
+	if (settings.jobs[i].valid) {
+	    printf_P(PSTR("region %d (%s) start %ld length %d\r\n"), settings.jobs[i].region, regions[settings.jobs[i].region].name,
+		     settings.jobs[i].start, settings.jobs[i].length);
+	} else {
+	    printf_P(PSTR("invalid\r\n"));
+	}
+    }
+}
+
+/* Update water related state machine */
+void
+water_update(void) {
+    static time_t	lastcheck = 0;
+    int			i;
+    uint8_t		relays;
+    static int16_t	lastrelays = -1;
+    time_t		now;
+    static uint8_t	logged[NJOBS] = {0};
+
+    if (lastcheck + 5 > nowtm.sec)
+	return;
+
+    lastcheck = nowtm.sec;
+
+    now = ds1307_time();
+    if (now == -1) {
+	puts_P(PSTR("Unable to read time\r\n"));
+	return;
+    }
+
+    relays = 0;
+
+    for (i = 0; i < NJOBS; i++) {
+	if (settings.jobs[i].valid) {
+	    if (now > settings.jobs[i].start + settings.jobs[i].length) {
+		printf_P(PSTR("Marking job %d (%s) done\r\n"), i, regions[settings.jobs[i].region].name);
+		settings.jobs[i].valid = 0;
+		logged[i] = 0;
+		continue;
+	    }
+
+	    if (now > settings.jobs[i].start) {
+		if (logged[i] == 0) {
+		    printf_P(PSTR("Job %d (%s) running\r\n"), i, regions[settings.jobs[i].region].name);
+		    logged[i] = 1;
+		}
+		relays |= 1 << regions[settings.jobs[i].region].relay;
+		continue;
+	    }
+	}
+    }
+
+    /* Update EEPROM with jobs */
+    water_write_settings();
+
+    if (lastrelays == -1 || lastrelays != relays) {
+	    printf_P(PSTR("[%ld] Setting relays to 0x%02x\r\n"), now, relays);
+	    lastrelays = relays;
+    }
+
+    PORTB = (PORTB & ~RELAY_MASK) | relays;
+}
+
+/* Read the settings from EEPROM
+ * If the CRC fails then reload from flash
+ */
+static void
+water_load_or_init_settings(void) {
+    uint8_t	*dptr;
+    uint16_t	crc, strcrc;
+    int		i;
+
+    puts_P(PSTR("Loading settings\r\n"));
+
+    crc = 0;
+    eeprom_busy_wait();
+    eeprom_read_block(&settings, &ee_area.settings, sizeof(settings_t));
+    strcrc = eeprom_read_word(&ee_area.crc);
+
+    dptr = (uint8_t *)&settings;
+
+    for (i = 0; i < sizeof(settings_t); i++)
+	crc = _crc16_update(crc, dptr[i]);
+
+    /* All OK? */
+    if (crc == strcrc)
+	return;
+
+    printf_P(PSTR("CRC mismatch got 0x%04x vs 0x%04x, setting defaults\r\n"), crc, strcrc);
+    water_default_settings();
+    water_write_settings();
+}
+
+/* Set defaults */
+static void
+water_default_settings(void) {
+    int		i;
+
+    puts_P(PSTR("Defaulting settings\r\n"));
+
+    for (i = 0; i < NJOBS; i++)
+	settings.jobs[i].valid = 0;
+}
+
+/* Write the current settings out to EEPROM */
+static void
+water_write_settings(void) {
+    uint16_t	crc;
+    uint8_t	*dptr;
+    int		i;
+
+    //puts_P(PSTR("Saving settings\r\n"));
+
+    eeprom_busy_wait();
+    /* Use update variant to save EEPROM cycles */
+    eeprom_update_block(&settings, &ee_area.settings, sizeof(settings_t));
+
+    dptr = (uint8_t *)&settings;
+    crc = 0;
+    for (i = 0; i < sizeof(settings_t); i++)
+	crc = _crc16_update(crc, dptr[i]);
+
+    eeprom_update_word(&ee_area.crc, crc);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/water.h	Tue Jan 27 23:40:01 2015 +1030
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2012
+ *      Daniel O'Connor <darius@dons.net.au>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+void		water_init(void);
+void		water_cmd(char *buf);
+void		water_update(void);
+