changeset 0:93d4ddff7dd0

Jumbo commit since I appear to have forgotten to do this before..
author Daniel O'Connor <darius@dons.net.au>
date Wed, 04 Jan 2012 23:19:12 +1030
parents
children be930b34fcd3
files .hgignore 1wire-config.h Makefile README.txt sprink.c
diffstat 5 files changed, 624 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore	Wed Jan 04 23:19:12 2012 +1030
@@ -0,0 +1,6 @@
+.*~
+.*\.lst
+.*\.o
+.*.\dmp
+.*\.hex
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/1wire-config.h	Wed Jan 04 23:19:12 2012 +1030
@@ -0,0 +1,97 @@
+/*
+ * 1 wire header
+ *
+ * This is the user servicable stuff - how to do delays and how to
+ * frob the IO pins.
+ *
+ * Copyright (c) 2004-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.
+ */
+
+/*
+ * Alter these for your configuration
+ */
+
+#define OWBUSINIT()
+
+/* Set the IO to input */
+#define OWSETREAD() \
+    do {					\
+	__asm__ volatile ("" ::: "memory");	\
+	DDRB &= ~_BV(0);			\
+	PORTB &= ~_BV(0);			\
+	__asm__ volatile ("" ::: "memory");	\
+    } while (0)
+
+/* Read the 1-wire bus, non-inverting logic */
+#define OWREADBUS()		(PINB & _BV(0) ? 1 : 0)
+
+/* Set the 1-wire bus to 0
+ * Drive output low
+ */
+#define OWSETBUSLOW() \
+    do {					\
+	__asm__ volatile ("" ::: "memory");	\
+	DDRB |= _BV(0);				\
+	PORTB &= ~_BV(0);			\
+    __asm__ volatile ("" ::: "memory");		\
+    } while (0)
+
+/* Set the 1-wire bus to 1
+ * Allow to float, use pullup
+ */
+#define OWSETBUSHIGH() \
+    do {					\
+    __asm__ volatile ("" ::: "memory");		\
+	DDRB &= ~_BV(0);			\
+    __asm__ volatile ("" ::: "memory");		\
+    } while (0)
+
+/* _delay_us can only do a delay of 768/clock_freq */
+#if F_CPU > 16000000
+#error F_CPU > 16MHz, delays need adjusting
+#endif
+
+#define OWDELAY_A _delay_us(6)						/* 6 usec */
+#define OWDELAY_B do { _delay_us(48); _delay_us(16); } while (0)	/* 64 usec */
+#define OWDELAY_C do { _delay_us(48); _delay_us(12); } while (0)	/* 60 usec */
+#define OWDELAY_D _delay_us(10)						/* 10 usec */
+#define OWDELAY_E _delay_us(9)						/* 9 usec */
+#define OWDELAY_F do { _delay_us(55); } while (0)			/* 55 usec */
+#define OWDELAY_G							/* 0 usec */
+#define OWDELAY_H do { _delay_us(48); _delay_us(48);  _delay_us(48); 	\
+	_delay_us(48);	_delay_us(48);  _delay_us(48);  _delay_us(48);  \
+	_delay_us(48);_delay_us(48);  _delay_us(48); } while (0)	/* 480 usec */
+#define OWDELAY_I do { _delay_us(48); _delay_us(22); } while (0)	/* 70 usec */
+
+#ifdef OW_DEBUG
+#define OWPUTS(x)		puts_P(x)
+#define OWPUTSP(x)		puts_P(x)
+#define OWPRINTFP(fmt, ...)	printf_P(fmt, ## __VA_ARGS__)
+#else
+#define OWPUTS(x)
+#define OWPUTSP(x)
+#define OWPRINTFP(fmt, ...)
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile	Wed Jan 04 23:19:12 2012 +1030
@@ -0,0 +1,40 @@
+PROG=	sprink
+SRCS=	sprink.c cons.c ds1307.c 1wire.c
+
+TPREFIX=	/usr/local/CrossPack-AVR/bin
+
+# Should be 324A but that doesn't work (broken avr-gcc?)
+PART=324p
+PROGPART=m324p
+MCU=atmega${PART}
+
+# Clock frequency
+CFLAGS+=-DF_CPU=8000000
+
+CFLAGS+=--param inline-call-cost=2
+
+# Enable optimisation & debugging
+CFLAGS+=-Os -Wall -g
+#CFLAGS+=-Wunreachable-code
+#CFLAGS+= -mcall-prologues -frename-registers -fstrict-aliasing -fnew-ra
+
+#CFLAGS+=-DOW_DEBUG
+
+# Use minimal printf
+#LDFLAGS+=-Wl,-u,vfprintf -lprintf_min 
+
+# Allow linker relaxation - optimise after linking
+#LDFLAGS+=-Wl,-relax
+
+#PROGTYPE=dragon_isp
+#PROGPORT=usb
+
+PROGTYPE=buspirate
+PROGPORT=/dev/cu.usbserial-BP1
+
+# Holds cons, ds1307 & 1wire
+LIBDIR=	../avr-lib
+CFLAGS+=-I${LIBDIR} -I.
+.PATH:	${LIBDIR}
+
+.include "${LIBDIR}/Makefile.avr"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.txt	Wed Jan 04 23:19:12 2012 +1030
@@ -0,0 +1,7 @@
+4 output relay board is connected to PB4:7.
+1 wire bus is connected to PB0.
+MaxStream ZigBee module is connected to USART0 (in transparent mode) PD0:1.
+A DS1307 is connected via I2C to PC0:1
+
+Relay board drives solenoids through a PTC and the voltage is monitored by a 910k/100k resistor divider with Zener diode protection to PA0.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sprink.c	Wed Jan 04 23:19:12 2012 +1030
@@ -0,0 +1,474 @@
+/*
+ * 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"
+
+/*
+** 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)
+*/
+
+#define NUMSPRINKS 6
+
+/* Holds all the settings needed */
+typedef struct {
+    bitstr_t	bit_decl(mon[NUMSPRINKS], 24);
+    bitstr_t	bit_decl(tue[NUMSPRINKS], 24);
+    bitstr_t	bit_decl(wed[NUMSPRINKS], 24);
+    bitstr_t	bit_decl(thu[NUMSPRINKS], 24);
+    bitstr_t	bit_decl(fri[NUMSPRINKS], 24);
+    bitstr_t	bit_decl(sat[NUMSPRINKS], 24);
+    bitstr_t	bit_decl(sun[NUMSPRINKS], 24);
+    
+} __attribute__((packed)) settings_t;
+
+/* Current settings in RAM */
+static settings_t	settings;
+
+/* Our map of EEPROM */
+struct {
+    settings_t	settings;
+    uint16_t	crc;
+} ee_area __attribute__((section(".eeprom")));
+
+/* Defaults that are shoved into EEPROM if it fails checksum */
+const PROGMEM settings_t	default_settings = {
+    /* XXX: no handy macro for this */
+    .mon[0] = {0, 0, 0},
+    .mon[1] = {0, 0, 0},
+    .mon[2] = {0, 0, 0},
+    .mon[3] = {0, 0, 0},
+    .mon[4] = {0, 0, 0},
+    .mon[5] = {0, 0, 0}
+};
+
+/*
+ * 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();
+    
+    /* Analogue input is PA0:7 */
+    DDRA = 0x00;
+    PORTA = 0x00;
+    DIDR0 = 0xff; /* Disable digital input buffers */
+#ifdef PRR
+    PRR &= ~_BV(PRADC);		/* Power ADV 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();
+
+	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"))) {
+        printf_P(PSTR("help             This help\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("gc"), 2)) {
+	ds1307_printtime(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 VCC reference */
+	ADMUX = _BV(REFS0) | 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"));
+    }
+}
+