view sprink.c @ 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 be930b34fcd3
children 1188042ddc2f
line wrap: on
line source

/*
 * Sprinkler relay control
 *
 * Copyright (c) 2009
 *      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 <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <util/delay.h>
#include <avr/wdt.h>

#include "bitstring.h"
#include "cons.h"
#include "ds1307.h"
#include "1wire.h"
#include "water.h"

/*
** Fuse bits should be set as follows
**
** EFUSE - BOD 4.3V                       -> xxxxx100 -> 0xfc
** HFUSE - SPIEN, EESAVE
**         BOOTSZ1/0                      -> 11010001 -> 0xd1
** LFUSE - SUT1, CKSEL1 (low XTAL)        -> 11011101 -> 0xdd
**
** http://www.engbedded.com/fusecalc/
**
** From Ateml the fuses were..
** EFUSE - 0xff - 11111111 - BOD disabled
** HFUSE - 0x99 - 10011001 - OCD disabled, JTAG enabled, SPI enabled, WDT disabled, 
**                         - EESAVE off, BOOTSZ0/1, BOOTRST off
** LFUSE - 0x62 - 01100010 - CLKDIV8, no CKOUT, long SUT, CKSEL3/2/0 (internal 8Mhz)
*/

/*
 * Mirror of the MCUCSR register, taken early during startup.
 */
uint8_t mcucsr __attribute__((section(".noinit")));

void		process_cmd(void);

/*
 * Read out and reset MCUCSR early during startup.
 */
void handle_mcucsr(void)
    __attribute__((section(".init3")))
    __attribute__((naked));
void handle_mcucsr(void) {
    wdt_disable();
#ifdef MCUSR
    mcucsr = MCUSR;
    MCUSR = 0;
#else
    mcucsr = MCUCSR;
    MCUCSR = 0;
#endif
}

consbuf_t cmd;

int
main(void) {
    /* Disable interrupts while we frob stuff */
    cli();

    /* Init UART */
    cons_init();
    
    /* Init TWI etc */
    ds1307_init();

    /* Set up the one wire stuff */
    OWInit();
    
    /* Init water control state machine */
    water_init();
    
    /* Analogue input is PA0:7 */
    DDRA = 0x00;
    PORTA = 0x00;
    DIDR0 = 0xff; /* Disable digital input buffers */
#ifdef PRR
    PRR &= ~_BV(PRADC);		/* Power ADC on - note that the
				 * datasheet says this is already 0 at
				 * power on.. */
#endif
    
    /* PB0   Used for 1-wire bus
     * PB4:7 Used for relay board */
    DDRB = 0xf0;
    PORTB = 0x00;

    /* TWI (0:1) */
    DDRC = 0x03;
    PORTC = 0x03;

    /* USART (0:1) */
    DDRD = 0x03;
    PORTD = 0x03;

    printf_P(PSTR("\r\n\r\n===============\r\n"
		  "Inited!\r\n\r\n"));

    if ((mcucsr & _BV(PORF)) == _BV(PORF))
	printf_P(PSTR("Power on\r\n"));

    if ((mcucsr & _BV(EXTRF)) == _BV(EXTRF))
	printf_P(PSTR("External\r\n"));

    if ((mcucsr & _BV(BORF)) == _BV(BORF))
	printf_P(PSTR("Brown-out\r\n"));

    if ((mcucsr & _BV(WDRF)) == _BV(WDRF))
	printf_P(PSTR("Watchdog\r\n"));

#ifdef JTRF
    if ((mcucsr & _BV(JTRF)) == _BV(JTRF))
	printf_P(PSTR("JTAG\r\n"));
#endif

    /* Ready to go! */
    sei();

    /*
     * Enable the watchdog with the largest prescaler.  Will cause a
     * watchdog reset after approximately 2 s @ Vcc = 5 V
     *
     * Gets reset in the loop below and in the tempctrl.c timer IRQ
     */
    wdt_enable(WDTO_2S);

    printf_P(PSTR("> "));
    cmd.state = 0;
    
    /* Wait for user input or an "interrupt" */
    while (1) {
	wdt_reset();

	water_update();
	
	if (cmd.state == 255) {
	    process_cmd();
	    printf_P(PSTR("> "));
	    /* Allow new characters to be processed */
	    cmd.state = 0;
	}
    }
}

void
process_cmd(void) {
    /* User just pressed enter */
    if (cmd.len == 0)
	return;
	     
    if (!strcasecmp_P((char *)cmd.buf, PSTR("?")) ||
	!strcasecmp_P((char *)cmd.buf, PSTR("help"))) {
        puts_P(PSTR("help               This help\r\n"
		    "wa dly time        Water for time minutes after dly minutes\r\n"
		    "gc                 Get time of day\r\n"
		    "sc time            Set time of day (time is YYYY/MM/DD HH:MM:SS)\r\n"
		    "in port            Read from a port\r\n"
		    "ou port val        Write to a port (val in hex)\r\n"
		    "dd port val        Set DDR on port\r\n"
		    "an pin             Sample from pin\r\n"
		    "sr                 Search for 1-wire devices\r\n"
		    "te ID              Sample temperature from ID\r\n"
		    "re                 Reset 1-wire bus\r\n"
		    "rb                 Read bit from 1-wire bus\r\n"
		    "rc                 Read byte from 1-wire bus\r\n"
		    "wb bit             Write bit to 1-wire bus\r\n"
		    "wc byte            Write byte to 1-wire bus\r\n"
		    "zz                 Reset micro\r\n"));
	return;
    } else if (!strncasecmp_P((char *)cmd.buf, PSTR("wa"), 2)) {
	water_cmd((char *)cmd.buf);
    } else if (!strncasecmp_P((char *)cmd.buf, PSTR("gc"), 2)) {
	ds1307_printnow(PSTR(""), PSTR("\r\n"));
    } else if (!strncasecmp_P((char *)cmd.buf, PSTR("sc"), 2)) {
	if (cmd.len < 17) {
	    printf_P(PSTR("Invalid TOD\r\n"));
	} else {
	    if (ds1307_settod((char *)cmd.buf + 3) != 1)
		printf_P(PSTR("Unable to set TOD\r\n"));
	}
    } else if (!strncasecmp_P((char *)cmd.buf, PSTR("in"), 2)) {
	uint8_t inp;
	    
	switch (tolower(cmd.buf[3])) {
	    case 'a':
		inp = PINA;
		break;
		
	    case 'b':
		inp = PINB;
		break;
		
	    case 'c':
		inp = PINC;
		break;
		
	    case 'd':
		inp = PIND;
		break;
		
	    default:
		printf_P(PSTR("Unknown port\r\n"));
		return;
	}
	printf_P(PSTR("PORT %c <= 0x%02x\r\n"), toupper(cmd.buf[3]), inp);
    } else if (!strncasecmp_P((char *)cmd.buf, PSTR("an"), 2)) {
	uint16_t	res;
	int		pin;
	
	if (sscanf_P((char *)cmd.buf, PSTR("an %d"), &pin) != 1) {
	    printf_P(PSTR("Unable to parse\r\n"));
	    return;
	}
	if (pin < 0 || pin > 7) {
		printf_P(PSTR("Unknown pin\r\n"));
		return;
	}

	/* Select desired pin, use AVREF */
	ADMUX = _BV(pin);
	
	/* Enable ADC, start conversion, set divisor to 64
	 * (8e6 / 64 => 125kHz (max is 200kHz)
	 */
	ADCSRA = _BV(ADEN) | _BV(ADSC) | _BV(ADPS2) | _BV(ADPS1);

	/* Spin waiting for result */
	while ((ADCSRA & _BV(ADIF)) == 0)
	    ;
	res = ADCL | (ADCH << 8);
	printf_P(PSTR("Pin %d <= %hhd\r\n"), pin, res);

	/* Disable ADC */
	ADCSRA = 0;
    } else if (!strncasecmp_P((char *)cmd.buf, PSTR("ou"), 2)) {
	char port;
	int val;
	
	if (sscanf_P((char *)cmd.buf, PSTR("ou %c %x"), &port, &val) != 2) {
	    printf_P(PSTR("Unable to parse\r\n"));
	    return;
	}
	
	switch (port) {
	    case 'a':
		PORTA = val & 0xff;
		break;
		
	    case 'b':
		PORTB = val & 0xff;
		break;
		
	    case 'c':
		PORTC = val & 0xff;
		break;
		
	    case 'd':
		PORTD = val & 0xff;
		break;
		
	    default:
		printf_P(PSTR("Unknown port\r\n"));
		return;
	}
	printf_P(PSTR("PORT%c <= 0x%02x\r\n"), toupper(port), val);
    } else if (!strncasecmp_P((char *)cmd.buf, PSTR("dd"), 2)) {
	char port;
	uint8_t val;
	int num;
	
	num = sscanf_P((char *)cmd.buf, PSTR("dd %c %x"), &port, &val);
	
	if (num != 1 && num != 2) {
	    printf_P(PSTR("Unable to parse dd arguments\r\n"));
	    return;
	}
	
	if (num == 1) {
	    switch (port) {
		case 'a':
		    DDRA = val;
		    break;

		case 'b':
		    DDRB = val;
		    break;

		case 'c':
		    DDRC = val;
		    break;
		    
		case 'd':
		    DDRD = val;
		    break;
		
		default:
		    printf_P(PSTR("Unknown port\r\n"));
		    return;
	    }
	    printf_P(PSTR("DDR%c => 0x%02x\r\n"), toupper(port), val);
	} else {
	    switch (port) {
		case 'a':
		    DDRA = val & 0xff;
		    break;
		
		case 'b':
		    DDRB = val & 0xff;
		    break;
		
		case 'c':
		    DDRC = val & 0xff;
		    break;
		
		case 'd':
		    DDRD = val & 0xff;
		    break;
		
		default:
		    printf_P(PSTR("Unknown port\r\n"));
		    return;
	    }
	    printf_P(PSTR("DDR%c <= 0x%02x\r\n"), toupper(port), val);
	}
    } else if (!strncasecmp_P((char *)cmd.buf, PSTR("zz"), 2)) {
	cli();
	wdt_enable(WDTO_15MS);
	for (;;)
	    ;
    } else if (!strncasecmp_P((char *)cmd.buf, PSTR("sr"), 2)) {
	uint8_t	ROM[8];
	int8_t	i;
	
	memset(ROM, 0, 8);

	i = OWFirst(ROM, 1, 0);
	do {
	    switch (i) {
		case OW_NOMODULES:
		case OW_FOUND:
		    break;
		    
		case OW_BADWIRE:
		case OW_NOPRESENCE:
		case OW_BADCRC:
		default:
		    printf_P(PSTR("Err %d\r\n"), i);
		    break;
	    }
		
	    if (i != OW_FOUND)
		break;

	    for (i = 0; i < 8; i++)
		printf_P(PSTR("%02x%S"), ROM[i], i == 7 ? PSTR("\r\n") : PSTR(":"));

	    i = OWNext(ROM, 1, 0);
	} while (1);
    } else if (!strncasecmp_P((char *)cmd.buf, PSTR("te"), 2)) {
	uint8_t	ROM[8];
	int16_t	t;
	
	if (sscanf_P((char *)cmd.buf, PSTR("te %hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx"), 
		     &ROM[0], &ROM[1], &ROM[2], &ROM[3],
		     &ROM[4], &ROM[5], &ROM[6], &ROM[7]) != 8) {
	    printf_P(PSTR("Unable to parse ROM ID\r\n"));
	    return;
	}

	t = OWGetTemp(ROM);
	switch (t) {
	    case OW_TEMP_WRONG_FAM:
		printf_P(PSTR("ROM specified isn't a temperature sensor\r\n"));
		break;

	    case OW_TEMP_CRC_ERR:
		printf_P(PSTR("CRC mismatch\r\n"));
		break;

	    case OW_TEMP_NO_ROM:
		printf_P(PSTR("No ROM found\r\n"));
		break;

	    default:
		printf_P(PSTR("%d.%02d\r\n"), GETWHOLE(t), GETFRAC(t));
		break;
	}
    } else if (!strncasecmp_P((char *)cmd.buf, PSTR("re"), 2)) {
	printf_P(PSTR("Reset = %d\r\n"), OWTouchReset());
    } else if (!strncasecmp_P((char *)cmd.buf, PSTR("rb"), 2)) {
	printf_P(PSTR("Read %d\r\n"), OWReadBit());
    } else if (!strncasecmp_P((char *)cmd.buf, PSTR("rc"), 2)) {
	printf_P(PSTR("Read 0x%02x\r\n"), OWReadByte());
    } else if (!strncasecmp_P((char *)cmd.buf, PSTR("wb"), 2)) {
	uint8_t	d;
	
	if (sscanf_P((char *)cmd.buf, PSTR("wb %hhu"), &d) != 1) {
	    printf_P(PSTR("Can't parse bit\r\n"));
	    return;
	}
	OWWriteBit(d);
	printf_P(PSTR("Wrote %d\r\n"), d);
    } else if (!strncasecmp_P((char *)cmd.buf, PSTR("wc"), 2)) {
	uint8_t	d;
	
	if (sscanf_P((char *)cmd.buf, PSTR("wc %hhx"), &d) != 1) {
	    printf_P(PSTR("Can't parse byte\r\n"));
	    return;
	}

	OWWriteByte(d);
	printf_P(PSTR("Wrote 0x%02x\r\n"), d);
    } else {
	printf_P(PSTR("Unknown command, help for a list\r\n"));
    }
}