Mercurial > ~darius > hgwebdir.cgi > sprink
view water.c @ 11:13903d8343a6 default tip
Catch up properly with settings change.
author | Daniel O'Connor <darius@dons.net.au> |
---|---|
date | Sun, 15 Feb 2015 22:33:40 +1030 |
parents | c32b5792683a |
children |
line wrap: on
line source
/* * 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 "splitargv.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 relays; } 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(uint8_t relays, uint16_t delay, uint16_t time); static void water_deljob(int job); 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++; } } /* Parse water related command */ void water_cmd(int argc, char **argv) { char *e; uint16_t delay, time; uint8_t relays; long tmp; int region; if (!strcasecmp_P(argv[0], PSTR("add"))) { if (argc != 4) { puts_P(PSTR("Wrong number of arguments, usage: wa add region delay time\r\n")); return; } relays = 0; if (!strcasecmp_P(argv[1], PSTR("all"))) { /* all => Or everything together */ for (region = 0; region < sizeof(regions) / sizeof(regions[0]); region++) relays |= 1 << regions[region].relay; } else { /* find if it's a valid region */ for (region = 0; region < sizeof(regions) / sizeof(regions[0]); region++) if (!strcasecmp(argv[1], regions[region].name)) relays |= 1 << regions[region].relay; if (relays == 0) { puts_P(PSTR("Unknown region\r\n")); return; } } tmp = strtol(argv[2], &e, 10); if (e == argv[2]) { 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[3], &e, 10); if (e == argv[3]) { 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_addjob(relays, delay, time); water_printjobs(); } else if (!strcasecmp_P(argv[0], PSTR("del"))) { if (argc != 2) { puts_P(PSTR("Wrong number of arguments, usage: wa del job\r\n")); return; } tmp = strtol(argv[1], &e, 10); if (e == argv[1]) { puts_P(PSTR("Unable to parse job\r\n")); return; } water_deljob(tmp); } else if (!strcasecmp_P(argv[0], PSTR("pr"))) { if (argc != 1) { puts_P(PSTR("Wrong number of arguments, usage: wa pr\r\n")); return; } water_printjobs(); } else if (!strcasecmp_P(argv[0], PSTR("regions"))) { int i; if (argc != 1) { puts_P(PSTR("Wrong number of arguments, usage: wa regions\r\n")); return; } printf_P(PSTR("%-20S %S\r\n"), PSTR("Region"), PSTR("Relay")); for (i = 0; i < sizeof(regions) / sizeof(regions[0]); i++) printf_P(PSTR("%-20s %d\r\n"), regions[i].name, regions[i].relay); } else if (!strcasecmp_P(argv[0], PSTR("help"))) { puts_P(PSTR( "wa help This help\r\n" "wa add region dly time Water region (or all) for time minutes after dly minutes\r\n" "wa del job Delete job\r\n" "wa pr Print list of jobs\r\n" "wa regions Print list of regions\r\n" )); } else { puts_P(PSTR("Unknown 'wa' sub-command. 'wa help' for help\r\n")); return; } } static void water_addjob(uint8_t relays, uint16_t delay, uint16_t time) { int i; time_t now; printf_P(PSTR("adding delays 0x%02x, delay %d, time %d\r\n"), relays, 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].relays = relays; settings.jobs[i].valid = 1; water_write_settings(); } static void water_deljob(int job) { if (job < 0 || job >= NJOBS) { puts_P(PSTR("Invalid job number\r\n")); return; } if (settings.jobs[job].valid == 0) { puts_P(PSTR("Job already deleted\r\n")); return; } settings.jobs[job].valid = 0; printf_P(PSTR("Removed job %d\r\n"), job); 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("relays 0x%02x start %ld length %d\r\n"), settings.jobs[i].relays, 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 (relays 0x%02x) done\r\n"), i, settings.jobs[i].relays); settings.jobs[i].valid = 0; logged[i] = 0; continue; } if (now > settings.jobs[i].start) { if (logged[i] == 0) { printf_P(PSTR("Job %d (relays 0x%02x) running\r\n"), i, settings.jobs[i].relays); logged[i] = 1; } relays |= settings.jobs[i].relays; 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); }