view sprink.c @ 7:78983502a4e9

Use base 16 for IO port values
author Daniel O'Connor <darius@dons.net.au>
date Sun, 15 Feb 2015 17:47:25 +1030
parents 951277329ee6
children
line wrap: on
line source

/*
 * Sprinkler relay control
 *
 * 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 <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 "1wire.h"
#include "bitstring.h"
#include "cons.h"
#include "ds1307.h"
#include "splitargv.h"
#include "water.h"

#define MAXARGS 10

/*
** 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();

    /* Disable watchdog in case it was previously on */
    wdt_disable();

    /* Init UART */
    cons_init();

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

    /* Init TWI etc */
    ds1307_init();

    /* Set up the one wire stuff */
    OWInit();

    /* 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;

    fputs_P(PSTR("Reset cause: "), stdout);
    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();

    /* Init water control state machine */
    water_init();

    /*
     * Enable the watchdog with the largest prescaler.  Will cause a
     * watchdog reset after approximately 2 s @ Vcc = 5 V
     */
    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) {
    int argc;
    char *argv[MAXARGS];

    /* User just pressed enter */
    if (cmd.len == 0)
	return;

    /* Split buffer into argv/argc (and de-volatilise) */
    splitargv((char *)cmd.buf, 0, argv, MAXARGS, &argc);

    if (argc == 0)
	return;

    if (!strcasecmp_P(argv[0], PSTR("?")) ||
	!strcasecmp_P(argv[0], PSTR("help"))) {
        puts_P(PSTR("help               This help\r\n"
		    "wa all dly time    Water all regions 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 (!strcasecmp_P(argv[0], PSTR("wa"))) {
	water_cmd(argc - 1, argv + 1);
    } else if (!strcasecmp_P(argv[0], PSTR("an"))) {
	uint16_t	res;
	int		pin;
	char		*eptr;

	if (argc != 2) {
	    printf_P(PSTR("Bad usage\r\n"));
	    return;
	}

	pin = strtol(argv[1], &eptr, 0);
	if (eptr == argv[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 (!strcasecmp_P(argv[0], PSTR("ou"))) {
	int val;
	char *eptr;

	if (argc != 3) {
	    printf_P(PSTR("Bad usage\r\n"));
	    return;
	}

	val = strtol(argv[2], &eptr, 16);
	if (eptr == argv[2]) {
	    printf_P(PSTR("Unable to parse val\r\n"));
	    return;
	}

	switch (argv[1][0]) {
	    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(argv[1][0]), val);
    } else if (!strcasecmp_P(argv[0], PSTR("dd"))) {
	uint8_t val;
	char *eptr;

	if (argc != 2 && argc != 3) {
	    printf_P(PSTR("Bad usage\r\n"));
	    return;
	}

	if (argc == 1) {
	    switch (argv[1][0]) {
		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(argv[1][0]), val);
	} else {
	    val = strtol(argv[2], &eptr, 16);
	    if (eptr == argv[2]) {
		printf_P(PSTR("Unable to parse val\r\n"));
		return;
	    }

	    switch (argv[1][0]) {
		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(argv[1][0]), val);
	}
    } else if (!strcasecmp_P(argv[0], PSTR("zz"))) {
	cli();
	wdt_enable(WDTO_15MS);
	for (;;)
	    ;
    } else if (!strcasecmp_P(argv[0], PSTR("sr"))) {
	uint8_t	ROM[8];
	int8_t	i;

	i = OWFirst(ROM, 1, 0);
	do {
	    switch (i) {
		case OW_NOMODULES:
		    return;

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

		case OW_BADWIRE:
		case OW_NOPRESENCE:
		case OW_BADCRC:
		default:
		    printf_P(PSTR("Err %d\r\n"), i);
		    return;
	    }
	    i = OWNext(ROM, 1, 0);
	} while (1);
    } else if (!strcasecmp_P(argv[0], PSTR("te"))) {
	uint8_t	ROM[8];
	int16_t	t;

	if (sscanf_P(argv[1], PSTR("%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 (!strcasecmp_P(argv[0], PSTR("re"))) {
	printf_P(PSTR("Reset = %d\r\n"), OWTouchReset());
    } else if (!strcasecmp_P(argv[0], PSTR("rb"))) {
	printf_P(PSTR("Read %d\r\n"), OWReadBit());
    } else if (!strcasecmp_P(argv[0], PSTR("rc"))) {
	printf_P(PSTR("Read 0x%02x\r\n"), OWReadByte());
    } else if (!strcasecmp_P(argv[0], PSTR("wb"))) {
	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 (!strcasecmp_P(argv[0], PSTR("wc"))) {
	uint8_t	d;
	char *eptr;

	d = strtol(argv[1], &eptr, 16);
	if (eptr == argv[1]) {
	    printf_P(PSTR("Unable to 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"));
    }
}