wispcarb - mods for EV battery tetsing
authordrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Thu, 25 Jun 2009 09:07:11 +0000 (09:07 +0000)
committerdrowe67 <drowe67@01035d8c-6547-0410-b346-abe4f91aad63>
Thu, 25 Jun 2009 09:07:11 +0000 (09:07 +0000)
git-svn-id: https://svn.code.sf.net/p/freetel/code@26 01035d8c-6547-0410-b346-abe4f91aad63

wispcar/README [new file with mode: 0644]
wispcar/batt_test.m [new file with mode: 0644]
wispcar/batt_test.sh [new file with mode: 0755]
wispcar/caliba.m [new file with mode: 0644]
wispcar/calibb.m [new file with mode: 0644]
wispcar/documentation.txt
wispcar/log2octave.sh [new file with mode: 0755]
wispcar/stty [new file with mode: 0755]
wispcar/wispcar.asm
wispcar/wispcarb.asm [new file with mode: 0755]

diff --git a/wispcar/README b/wispcar/README
new file mode 100644 (file)
index 0000000..2d8aede
--- /dev/null
@@ -0,0 +1,11 @@
+wispcar README
+--------------
+
+David Rowe 22 June 2009
+
+stty is a WRT54GL OpenWRT  binary utility some one had compiled on the web, used
+for setting serial port speeds
+
+wispcar.asm is the original code as per the blog post: http://www.rowetel.com/blog/?p=68
+
+wispcarb.asm has a mod to the watchdog timer to stay off, used for battery testing
diff --git a/wispcar/batt_test.m b/wispcar/batt_test.m
new file mode 100644 (file)
index 0000000..7d8d761
--- /dev/null
@@ -0,0 +1,14 @@
+% batt_test.m
+% David Rowe 23 June 2009
+% Plot battery test results
+
+function batt_test(f)
+  d = load(f);
+  mins = d(:,1)*60 + d(:,2);
+  mins = mins - mins(1);
+  plot(mins, 0.072*d(:,4) + 0.82,'*');
+  xlabel('time (min)');  ylabel('V');
+  axis([0 60 11 13]) 
+  grid
+endfunction
+
diff --git a/wispcar/batt_test.sh b/wispcar/batt_test.sh
new file mode 100755 (executable)
index 0000000..e7d472f
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/sh
+# batt_test.sh
+# David Rowe 22 June 2009
+
+if [ $# -ne 2 ]
+then
+  echo "usage ./batt_test.sh cutoff_Vcode logfile"
+  exit 0
+fi
+
+/home/stty -F /dev/tts/1 speed 4800 > /dev/null
+
+rm -f $2
+
+# sample initial voltage
+
+dd if=/dev/tts/1 bs=1 count=36 1>w 2>/dev/null&
+echo -n 'w' > /dev/tts/1
+sleep 2
+v=`cut -f 2 -d ' ' w`
+echo $v
+
+# stop when voltage reaches a threshold
+
+while [ $v -gt $1 ]
+do
+
+  # sample voltage
+  
+  dd if=/dev/tts/1 bs=1 count=36 1>w 2>/dev/null&
+  echo -n 'w' > /dev/tts/1
+  sleep 2
+  v=`cut -f 2 -d ' ' w`
+  i=`cut -f 3 -d ' ' w`
+  echo $v $i
+  
+  echo -n `date` ' ' >> $2
+  echo $v $i >> $2    
+
+  sleep 5
+  
+done
+  
diff --git a/wispcar/caliba.m b/wispcar/caliba.m
new file mode 100644 (file)
index 0000000..3c648b9
--- /dev/null
@@ -0,0 +1,29 @@
+% 0.1 ohm current sense resistor 20 June 2009
+
+% Vbatt V0.1 I VbattADC IADC
+d = [ 
+[  8.0 0.121 1.15 36 99]
+[  8.9 0.134 1.27 41 109]
+[  9.8 0.148 1.40 44 121]
+[ 11.1 0.167 1.58 50 136]
+[ 12.7 0.191 1.80 58 153]
+[ 13.1 0.198 1.87 59 159]  
+]
+
+figure(1)
+plot(d(:,1), d(:,4))
+ylabel("ADC V"); xlabel("Vbatt (V)");
+
+figure(2)
+plot(d(:,3), d(:,5))
+ylabel('ADC I'); xlabel('I (A)');
+
+figure(3)
+plot(d(:,2), d(:,3))
+xlabel('Vsense I'); ylabel('I (A)');
+
+figure(4)
+plot(d(:,2), d(:,5))
+xlabel('Vsense I'); ylabel('ADC I');
+grid
+
diff --git a/wispcar/calibb.m b/wispcar/calibb.m
new file mode 100644 (file)
index 0000000..3600688
--- /dev/null
@@ -0,0 +1,29 @@
+% 0.1 ohm current sense resistor 20 June 2009
+
+% Vbatt VbattADC IADC
+d = [ 
+[  9.2 118 ]
+[ 10.0 129 ]
+[ 11.1 142 ]
+[ 12.2 156 ]
+[ 13.2 169 ]
+[ 14.0 179 ]
+]
+
+d1 = [ 
+[ 12.12 157 ]
+[ 12.00 155 ]
+[ 11.86 153 ]
+[ 11.68 151 ]
+[ 11.47 148 ]
+]
+
+figure(1)
+plot(d(:,1), d(:,2))
+hold on
+plot(d1(:,1), d1(:,2),'g')
+hold off
+ylabel("ADC V"); xlabel("Vbatt (V)");
+grid
+axis([11 13 140 170])
+
index 7aff11130dedfd3ce007c176de8730625ee1d388..26e7de396e10cc7d8c11b7ab90026dae89345f54 100644 (file)
@@ -60,7 +60,7 @@ Project Plan
     [ ] Brown out test
         + vary Vbat from 30V down to 2V 10 times
         + make sure PIC does not hang
-        
+         
 Specs:
 -----
 
@@ -168,6 +168,11 @@ TODO List
     [X] pinout of BC548
     [ ] connect + & - together on unsued op-amp sections
     [X] TAPR open harwdare license
+[ ] PCB ideas
+    [ ] extra pads for zener to drop input voltage
+    [ ] extra pads for general prototyping
+    [ ] break out extra op amp pins
+        + dont hard-tie them
 
 [X] svn repository
     + with datasheet pdfs ?
diff --git a/wispcar/log2octave.sh b/wispcar/log2octave.sh
new file mode 100755 (executable)
index 0000000..7067eb8
--- /dev/null
@@ -0,0 +1,8 @@
+#!/bin/sh
+# log2octave.sh
+# David Rowe 23 June 2009
+
+# strip out time stamp and voltage into four columns so octave batt_test.m 
+# can read
+
+cut -f 4,8 -d ' ' $1 | sed -e 's/:/ /g' > $1.oct
diff --git a/wispcar/stty b/wispcar/stty
new file mode 100755 (executable)
index 0000000..d350fcd
Binary files /dev/null and b/wispcar/stty differ
index 8550fa45b8be95344b6d114ebcc7d88a5331841f..a6a3bb10756f2038cf4a3fdfe5941cbde2e8e3f7 100644 (file)
 ;\r
 ; 4800 BAUD RS232 OUTPUT\r
 ; ======================\r
+; \r
+; The following line is printed every time you send 'w' or '.', if you\r
+; send nothing to wispcar it will remain silent.  The specific chars\r
+; minimise the chance of wispcar interfering with your boot loader.\r
+;\r
 ; Name of script on host that parses this line\r
 ; |       Vsense\r
 ; |       |   Isense\r
 ; 1/ Send a 'w' every WD_TIMEOUT seconds to prevent Watchdog timer\r
 ; firing and cutting power.\r
 ;\r
-; 2/ Send the string 'sleepxyz' to cut the power for xyz seconds,\r
+; 2/ Send a '.' to report status without resetting watchdog.\r
+;\r
+; 3/ Send the string 'sleepxyz' to cut the power for xyz seconds,\r
 ; where x,y and z are digits in the range 0-9. If any part of the\r
 ; string is invalid it will be ignored.  Wait 5 seconds and start\r
 ; again.\r
 ;\r
-; 3/ Wispcar cannot rx and tx RS232 at the same time.  It is best to\r
+; 4/ Wispcar cannot rx and tx RS232 at the same time.  Wait until after\r
+; the current output has finished to send the next command. It is best to\r
 ; wait until just after the output line is received by the host, then\r
 ; send your command.  If the command (such as sleep) gets messed up,\r
 ; just wait 5 seconds and wispcar will reset it's internal state\r
 ; 4/ The final field is reset to a space when a 'w' is sent to\r
 ; Wispcar. This field lets the host know just _why_ it was rebooted.\r
 \r
+\r
 #include <p12f510.inc>\r
 \r
 ; VARIABLES ---------------------------------------------------------------------\r
@@ -683,10 +692,23 @@ sleep_cont
 \r
 ; START MAIN LOOP -----------------------------------------------\r
 \r
-main\r
+main   \r
+       call    inch_n_delay    ; RS232 RX and 1 second delay\r
 \r
-       ; sample AN[0] (voltage)\r
+       ; sample V, I, and print if we received a 'w' or a ' '\r
+\r
+       movlw   'w'\r
+       subwf   SERBUFI,0\r
+       btfsc   STATUS,Z        ; skip if SERBUFI != 'w'\r
+       goto    sample  \r
+\r
+       movlw   '.'\r
+       subwf   SERBUFI,0\r
+       btfss   STATUS,Z        ; skip if SERBUFI == ' '\r
+       goto    no_print        \r
 \r
+       ; sample AN[0] (voltage)\r
+sample\r
        movlw   b'10110001'\r
        movwf   ADCON0  \r
        bsf     ADCON0,1        ; start A/D conversion\r
@@ -848,10 +870,7 @@ wait_adc_i
        movlw   '\n'\r
        call    outch_n\r
 \r
-       ; RS232 RX and 1 second delay\r
-\r
-       call    inch_n_delay    \r
-\r
+no_print\r
        ; has watchdog fired (wd_timeout == 0) ?\r
 \r
        movf    wd_timeout,1\r
diff --git a/wispcar/wispcarb.asm b/wispcar/wispcarb.asm
new file mode 100755 (executable)
index 0000000..aec22c4
--- /dev/null
@@ -0,0 +1,1020 @@
+; wispcarb.asm\r
+; David Rowe June 2009\r
+;\r
+; WISPCAR modified for EV battery testing.  When watchdog fires PowerSwitch\r
+; (pin 6) stays off until the host sends a "w".  This switches off current\r
+; to the battery under test if the host side dies.  This mod is just a hack\r
+; for now.\r
+;\r
+;\r
+; PIC12F510 Assembler program, MPLAB V8.10 IDE used to build.\r
+;\r
+; ********************************************************\r
+; * Wifi Station Power Controller And Reporter - WiSPCaR *\r
+; ********************************************************\r
+;\r
+;---------------------------------------------------------------------\r
+; Copyright (C) 2008 David Rowe\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License\r
+; along with this program; if not, write to the Free Software\r
+; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 \r
+; USA\r
+;---------------------------------------------------------------------\r
+;\r
+; FUNCTIONS\r
+; =========\r
+;\r
+; 1/ Monitor Voltage\r
+; 2/ Monitor Current\r
+; 3/ Watchdog timer\r
+; 4/ Sleep timer\r
+;\r
+;\r
+; PINOUT\r
+; ======\r
+;                   ____  ____\r
+;                  |    \/    |   \r
+;            Vcc --| 1      8 |-- GND\r
+;                  |          |\r
+;      Spare I/O --| 2      7 |-- Vsense (in)\r
+;                  |          |\r
+; RS232 Tx (out) --| 3      6 |-- PowerSwitch (out)\r
+;                  |          |\r
+;  RS232 Rx (in) --| 4      5 |-- Isense (in)\r
+;                  |__________|\r
+;\r
+;\r
+;             Pin | Function    | PIC Name\r
+;            -----|-------------|---------\r
+;              2  | Spare I/O   |  GP5 \r
+;              3  | RS232 Tx    |  GP4\r
+;              4  | RS232 Rx    |  GP3\r
+;              5  | Isense      |  AN2\r
+;              6  | PowerSwitch |  GP1\r
+;              7  | Vsense      |  AN0\r
+;\r
+; \r
+; PIC PROGRAMMING OPTIONS\r
+; =======================\r
+;\r
+; 8MHz clock, MCLRE=I/O, INTOSC\r
+;\r
+;\r
+; 4800 BAUD RS232 OUTPUT\r
+; ======================\r
+; \r
+; The following line is printed every time you send 'w' or '.', if you\r
+; send nothing to wispcar it will remain silent.  The specific chars\r
+; minimise the chance of wispcar interfering with your boot loader.\r
+;\r
+; Name of script on host that parses this line\r
+; |       Vsense\r
+; |       |   Isense\r
+; |       |   |   char received (ASCII)\r
+; |       |   |   | Last char received (decimal)\r
+; |       |   |   | |   Watchdog timeout counter\r
+; |       |   |   | |   |   Watchdog fire counter\r
+; |       |   |   | |   |   |   Sleep state machine state\r
+; |       |   |   | |   |   |   | Sleep timeout counter\r
+; |       |   |   | |   |   |   | |    \r
+; wispcar 040 031 - 032 012 000 0 000 w---reason for last restart\r
+;                                     b - Wispcar (re)booted  \r
+;                                     w - watchdog timer fired \r
+;                                     s - we went to sleep\r
+;                                     - - restart flag reset\r
+;\r
+; COMMANDS\r
+; ========\r
+;\r
+; 1/ Send a 'w' every WD_TIMEOUT seconds to prevent Watchdog timer\r
+; firing and cutting power.\r
+;\r
+; 2/ Send a '.' to report status without resetting watchdog.\r
+;\r
+; 3/ Send the string 'sleepxyz' to cut the power for xyz seconds,\r
+; where x,y and z are digits in the range 0-9. If any part of the\r
+; string is invalid it will be ignored.  Wait 5 seconds and start\r
+; again.\r
+;\r
+; 4/ Wispcar cannot rx and tx RS232 at the same time.  Wait until after\r
+; the current output has finished to send the next command. It is best to\r
+; wait until just after the output line is received by the host, then\r
+; send your command.  If the command (such as sleep) gets messed up,\r
+; just wait 5 seconds and wispcar will reset it's internal state\r
+; machine.  You can then try again.\r
+;\r
+; 4/ The final field is reset to a space when a 'w' is sent to\r
+; Wispcar. This field lets the host know just _why_ it was rebooted.\r
+\r
+\r
+#include <p12f510.inc>\r
+\r
+; VARIABLES ---------------------------------------------------------------------\r
+\r
+TEMP           equ     0x10\r
+SERBUF         equ     0x11    ; RS232 char to transmit\r
+count          equ     0x12    ; temp reg used by "BIN2BCD" and "baud" routines\r
+BIN            equ     0x13    \r
+huns           equ     0x14\r
+tens           equ     0x15\r
+ones           equ     0x16\r
+thirty         equ     0x17    ; constant set to (guess what) 30\r
+SERBUFI                equ     0x18    ; last RS232 character we received\r
+wd_timeout     equ     0x19    ; current watchdog timer value (in secs)\r
+                               ; when this hits zero we turn the power off\r
+wd_fire                equ     0x1a    ; count down timer when watchdog fires (in secs)\r
+restart_flag   equ     0x1b    ; tells host why we just restarted (WD or sleep)\r
+\r
+sleep_state    equ     0x1c\r
+sleep_timeout  equ     0x1d    ; current value of sleep time out (in secs)\r
+                               ; measures time between rx-ed chars in\r
+                               ; s-l-e-e-p-x-y-z sequence.\r
+                               ; If this hits zero (like s-l- (BIG GAP))\r
+                               ; we assume sleep command\r
+                               ; was invalid and we reset the input state machine\r
+\r
+                               ; three digit sleep time (up to 999 secs)\r
+                               ; these count down to zero while we are sleeping\r
+                               ; when count down hits 0 power is switched back on\r
+\r
+sleeping       equ     0x1e    ; asserted if we are sleeping (power off)\r
+\r
+; following variables are in bank1, which makes life difficult....\r
+\r
+d1             equ     0x10\r
+d2             equ     0x11\r
+d3             equ     0x12\r
+\r
+x              equ     0x13    ; sleep time (hundreds of secs)\r
+y              equ     0x14    ; sleep time (tens of secs)\r
+z              equ     0x15    ; sleep time (secs)\r
+\r
+; CONSTANTS ---------------------------------------------------------------------\r
+\r
+WD_TIMEOUT     equ     d'18'   ; watchdog timeout in seconds\r
+WD_FIRE                equ     d'5'    ; watchdog firing time in seconds\r
+                               ; how long power is cut off to host\r
+\r
+SLEEP_TIMEOUT   equ     d'5'   ; timeout between rx-ed chars in sleep input\r
+                               ; sequence.\r
+\r
+; Values for restart_flag - tell host why we just cut power after we woke up\r
+\r
+BOOT_FLAG      equ     'b'     ; Wispcar just rebooted\r
+WD_FLAG                equ     'w'     ; Watchdog fired\r
+SLEEP_FLAG     equ     's'     ; We slept\r
+\r
+; START PROGRAM ----------------------------------------------------------------\r
+\r
+; set up osc cal (temp code just for experimenting)\r
+; TODO - FIX ME for general case\r
+\r
+        movlw   16\r
+       movwf   OSCCAL\r
+\r
+; configure AN[1:0] as analog inputs\r
+\r
+       movlw   b'10110001'\r
+       movwf   ADCON0  \r
+\r
+; configure GP[3] as digital input   (RS232 RX)\r
+; configure GP[4] as digital output  (RS232 TX)\r
+; configure GP[1] as digital output  (PowerSwitch)\r
+\r
+; disble the comparitor so we can use GP[1]\r
+\r
+       bcf     CM1CON0,3\r
+\r
+       ; set up GPIO directions\r
+\r
+       movlw   b'00001101'\r
+       tris    GPIO\r
+\r
+; turn on PowerSwitch \r
+\r
+       bsf     GPIO,1  \r
+\r
+; set up a convenient constant\r
+\r
+        movlw  h'30'\r
+       movwf   thirty\r
+\r
+; init watchdog \r
+\r
+       movlw   WD_TIMEOUT\r
+       movwf   wd_timeout\r
+       clrf    wd_fire\r
+\r
+; init sleep\r
+\r
+       clrf    sleep_state\r
+       clrf    sleeping  \r
+       clrf    sleep_timeout  \r
+       bsf     FSR,5          ; x,y,z are in bank 1 (grumble)\r
+       clrf    x\r
+       clrf    y\r
+       clrf    z\r
+       bcf     FSR,5          ; x,y,z are in bank 1 (grumble)\r
+\r
+; init restart_flag\r
+\r
+       movlw   BOOT_FLAG\r
+       movwf   restart_flag\r
+\r
+       goto    main\r
+\r
+; START FUNCTIONS ------------------------------------------------------------\r
+\r
+; BIN2BCD\r
+;\r
+;   Convert 8 bit value in BIN to a 3 digit decimal value.\r
+;\r
+;   in.......: BIN\r
+;   out......: huns, tens, ones\r
+;   Reference: http://www.piclist.com/techref/microchip/math/radix/b2a-8b3d-ab.htm\r
+;\r
+;   Uses temp reg 'count'\r
+\r
+; uses ADD-3 algorithm\r
+\r
+BIN2BCD\r
+       movlw 8\r
+       movwf count\r
+       clrf huns\r
+       clrf tens\r
+       clrf ones\r
+BCDADD3\r
+       movlw 5\r
+       subwf huns, 0\r
+       btfsc STATUS, C\r
+       CALL ADD3HUNS\r
+\r
+       movlw 5\r
+       subwf tens, 0\r
+       btfsc STATUS, C\r
+       CALL ADD3TENS\r
+\r
+       movlw 5\r
+       subwf ones, 0\r
+       btfsc STATUS, C\r
+       CALL ADD3ONES\r
+\r
+       decf count, 1\r
+       bcf STATUS, C\r
+       rlf BIN, 1\r
+       rlf ones, 1\r
+       btfsc ones,4 ; \r
+       CALL CARRYONES\r
+       rlf tens, 1\r
+\r
+       btfsc tens,4 ; \r
+       CALL CARRYTENS\r
+       rlf huns,1\r
+       bcf STATUS, C\r
+\r
+       movf count, 0\r
+       btfss STATUS, Z\r
+       GOTO BCDADD3\r
+\r
+       movf huns, 0    ; add ASCII Offset\r
+       addwf thirty, 0\r
+       movwf huns\r
+\r
+       movf tens, 0    ; add ASCII Offset\r
+       addwf thirty, 0\r
+       movwf tens\r
+\r
+       movf ones, 0    ; add ASCII Offset\r
+       addwf thirty, 0\r
+       movwf ones\r
+\r
+       retlw 0\r
+\r
+ADD3HUNS\r
+       movlw 3\r
+       addwf huns,1\r
+       retlw 0\r
+\r
+ADD3TENS\r
+       movlw 3\r
+       addwf tens,1\r
+       retlw 0\r
+\r
+ADD3ONES\r
+       movlw 3\r
+       addwf ones,1\r
+       retlw 0\r
+\r
+CARRYONES\r
+       bcf ones, 4\r
+       bsf STATUS, C\r
+       retlw 0\r
+\r
+CARRYTENS\r
+       bcf tens, 4\r
+       bsf STATUS, C\r
+       retlw 0 \r
+\r
+; INCH_N is from "PIC Software UART Routines"\r
+; John Massa, Datadog Systems (C) 2005\r
+\r
+; ********************************************************************\r
+;                            INCH_N\r
+; THIS ROUTINE INPUTS RS232 DATA USING AN INVERTER, LIKE THE MAX232.\r
+; THIS ROUTINE USES A 8-DATA BIT PER CHARACTER PROTOCOL\r
+; GPIO,0 = RX (MARK = 1, SPACE = 0).\r
+; TO RECIEVE A CHARACTER, CALL inch_n, THE RECEIVED CHARACTER IS PLACED\r
+; IN THE REG 'W' AND IN THE REG 'SERBUF'.\r
+; THE RECEIVED CHARACTER WILL ECHO IF 'RETLW   0' IS REM-ED OUT.\r
+; VARIABLES USED: REG 'TEMP' AND REG 'SERBUF' BOTH VARIABLES ARE\r
+;                 SHARED WITH THE 'outch_n' ROUTINE.\r
+; ROUTINES CALLED: 'half_baud'AND 'baud' TO SET THE BAUD-RATE\r
+; ********************************************************************\r
+\r
+; Modified by David Rowe May 2008 to combine a 1 second delay with\r
+; character reception.\r
+\r
+inch_n_delay\r
+\r
+       ; clear previous rx char\r
+\r
+       movlw   '-'\r
+       movwf   SERBUFI\r
+\r
+       ; init delay loop for 1999996 cycles\r
+\r
+       bsf     FSR,5         ; d1, d2, d3 in Bank 1 (groan)\r
+       movlw   0x11\r
+       movwf   d1\r
+       movlw   0x5D\r
+       movwf   d2\r
+       movlw   0x05\r
+       movwf   d3\r
+\r
+wait_for_start\r
+       bcf     FSR,5         ; just in case goto below fires\r
+        btfss   GPIO,3          ; SKIP UNLESS WE GET A START BIT = 1 (A "MARK")\r
+        goto    start_serial    ; start bit - process character\r
+\r
+       ; process delay loop while we look for start bit\r
+\r
+       bsf     FSR,5\r
+       decfsz  d1, f\r
+       goto    $+2\r
+       decfsz  d2, f\r
+       goto    $+2\r
+       decfsz  d3, f\r
+       goto    wait_for_start\r
+\r
+       ; delay finished\r
+\r
+       bcf     FSR,5   \r
+       retlw   0\r
\r
+start_serial\r
+       bsf     GPIO,5\r
+       bcf     GPIO,5\r
+\r
+        movlw   8               ; START SERIAL INPUT SEQUENCE\r
+        movwf   TEMP            ; COLLECT 8 DATA BITS\r
+        clrf    SERBUFI         ; CLEAR SERIAL CHARACTER BUFFER\r
+        call    half_baud       ; DELAY FOR ONE HALF BAUD TIME\r
+        btfsc   GPIO,3          ; FALL THRU IF START BIT STILL = 1 (A "MARK")\r
+        goto    wait_for_start  ; ELSE IT WAS JUST A NOISE SPIKE, KEEP LOOKING\r
+inch_n1\r
+        call    baud            ; DELAY ONE BAUD-BIT TIME ( = 1/BAUD-RATE)\r
+        bcf     STATUS,0        ; CLEAR THE CARRY BIT\r
+        rrf     SERBUFI,F       ; ROTATE CRY -> MSB, ROTATE MSB RIGHT\r
+        btfsc   GPIO,3          ; IS IT A "MARK" ?\r
+        bsf     SERBUFI,7       ; ...SKIP IF YES, ELSE SET BIT TO LOGIC '1'\r
+        decfsz TEMP,F          ; EIGHT COUNTS YET?\r
+        goto    inch_n1         ; ...NO, GET ANOTHER BIT\r
+        call    baud            ; DELAY FOR THE STOP BIT\r
+        movf    SERBUFI,W       ; PUT THE RECEIVED CHARACTER IN REG 'W'\r
+\r
+       ; process received character, this can be considered an "event\r
+       ; handler" for rx-chars.  We expect either a 'w' to reset the\r
+       ; watchdog or a tightly defined sequence of sleep characters.\r
+\r
+       movlw   'w'             \r
+       subwf   SERBUFI,0       ; is it a 'w' for watchdog reset?\r
+       btfss   STATUS,Z        ; skip if received char matches\r
+       goto    handle_sleepin  ; only process sleep input if not a 'w'\r
+\r
+       ; handle 'w' input \r
+       \r
+       ; Note if we are sleeping or WD is firing we will keep power\r
+       ; off and receiving 'w' will have no effect.  Unlikely unless\r
+       ; device sending rx chars is powered indepednantly (ie during\r
+       ; testing).\r
+\r
+       movlw   WD_TIMEOUT      ; reset watchdog timer\r
+       movwf   wd_timeout\r
+\r
+       ; DR June 2009 - mods\r
+       bsf             GPIO,1\r
+\r
+       movlw   '-'             ; clear reboot flag\r
+        movwf  restart_flag\r
+\r
+       clrf    sleep_state     ; reset sleep state machine for good measure\r
+       clrf    sleep_timeout   ; just in case we get s-l-w-e-e-p or something\r
+\r
+       goto    wait_for_start\r
+\r
+handle_sleepin\r
+       call    sleep_input\r
+       goto    wait_for_start\r
+\r
+; OUTCH_N and BAUD are from "PIC Software UART Routines"\r
+; John Massa, Datadog Systems (C) 2005\r
+\r
+;*********************************************************************\r
+;                               OUTCH_N\r
+; THIS ROUTINE OUTPUTS RS232 DATA THROUGH AN INVERTER.\r
+; THIS ROUTINE USES AN 8-DATA BIT PER CHARACTER PROTOCOL.\r
+; TO PRINT A CHARACTER, LOAD BYTE INTO REG 'W' AND CALL outch_n.\r
+; GPIO,1 = TX (MARK = 1, SPACE = 0)   ; USE INVERTER ON THE OUTPUT\r
+; VARIABLES USED: REG 'TEMP' AND SHARE REG 'SERBUF' WITH THE ROUTINE\r
+; 'inch_n'\r
+; CALLS THE ROUTINE 'baud' TO SET THE BAUD-RATE TIMING.\r
+;*********************************************************************\r
+\r
+outch_n                         ; THIS ROUTINE USES 8 DATA BITS\r
+        movwf   SERBUF          ; SERBUF CONTAINS CHARACTER TO XMT\r
+        movlw   8               ; THE CHARACTER HAS 8 BITS\r
+        movwf   TEMP\r
+        bcf     GPIO,4          ; SET START-BIT TO A "SPACE"\r
+        call    baud            ; WAIT ONE BAUD TIME\r
+outch_n1\r
+        rrf     SERBUF,F        ; ROTATE THE FIRST BIT INTO CARRY\r
+        btfss   STATUS,0        ; TEST THE CARRY BIT\r
+        bcf     GPIO,4          ; IF BIT IS 0 SET OUTPUT PIN TO A "0" (SPACE)\r
+        btfsc   STATUS,0        ; TEST THE CARRY BIT AGAIN\r
+        bsf     GPIO,4          ; IF BIT IS 1 SET OUTPUT PIN TO A "1" (MARK)\r
+        call    baud            ; ONE BAUD-BIT DELAY\r
+        decfsz TEMP,F           ; IF COUNT IS ZERO THEN XMIT A STOP BIT\r
+        goto    outch_n1        ; ...ELSE XMIT NEXT BIT\r
+        rrf     SERBUF,F        ; ROTATE CARRY, GET THE MSB BACK INTO BIT 7\r
+        bsf     GPIO,4          ; SET PIN TO A 1 (A "MARK") FOR THE STOP BIT\r
+        call    baud            ; FIRST BAUD-BIT DELAY\r
+        call    baud            ; SECOND BAUD-BIT DELAY\r
+        retlw   0               ; RETURN WITH THE CHARACTER IN SERBUF\r
+       \r
+; ********************************************************************\r
+;                       BAUD ROUTINE @ 4 MHz\r
+; BAUD RATE:      CONSTANT:\r
+;   1200 Baud     D'137'\r
+;   2400 Baud     D'68'\r
+;   4800 Baud     D'34'\r
+;   9600 Baud     D'16'\r
+; 19200 Baud      D'8'\r
+; 38400 Baud and up - use 'NOP' delays\r
+; VARIABLES USED: REG 'count'\r
+; ROUTINES CALLED: NONE\r
+; ********************************************************************\r
+\r
+baud                            ; AT 2400 BAUD THE PERIOD IS 416.6 US\r
+                                ; CLK = 4MHz\r
+        movlw   D'68'           ; 1 US   (BAUD RATE CONSTANT)\r
+        movwf   count           ; 1 US\r
+baud1\r
+        decfsz  count,F         ; 1 US (+ 1 US MORE IF SKIP)\r
+        goto    baud1           ; 2 US\r
+                                ; FALL THRU...AFTER 1+1+3x68+1 = 207 US\r
+half_baud\r
+        movlw   D'68'           ; 1 US\r
+        movwf   count           ; 1 US\r
+hbaud1\r
+        decfsz  count,F         ; 1 US (+ 1 US MORE IF SKIP)\r
+        goto    hbaud1          ; 2 US\r
+        retlw   0               ; ...AFTER 1+1+3x68+1 = 207 US (X2=414 US)\r
+\r
+; http://www.piclist.com/techref/piclist/codegen/delay.htm\r
+; Delay = 1 seconds\r
+; Clock frequency = 8 MHz\r
+\r
+; Actual delay = 1 seconds = 2000000 cycles\r
+; Error = 0 %\r
+\r
+delay\r
+                       ;1999996 cycles\r
+       bsf     FSR,5\r
+       movlw   0x11\r
+       movwf   d1\r
+       movlw   0x5D\r
+       movwf   d2\r
+       movlw   0x05\r
+       movwf   d3\r
+Delay_0\r
+       decfsz  d1, f\r
+       goto    $+2\r
+       decfsz  d2, f\r
+       goto    $+2\r
+       decfsz  d3, f\r
+       goto    Delay_0\r
+\r
+                       ;4 cycles\r
+       goto    $+1\r
+       goto    $+1\r
+\r
+       bcf     FSR,5\r
+       retlw   0\r
+\r
+; sleep input state machine\r
+; \r
+; we expect the chars s-l-e-e-p-x-y-z where xyz are digits\r
+; if wrong char received reset\r
+; if 5 sec timout between chars reset\r
+; if xyz are not digits reset\r
+\r
+sleep_input\r
+\r
+       ; big case statement to work out what state we are in\r
+\r
+       movlw   0\r
+       subwf   sleep_state,0\r
+       btfsc   STATUS,Z        ; Z set if in state 's'\r
+       goto    sleep_state_s\r
+       movlw   1\r
+       subwf   sleep_state,0\r
+       btfsc   STATUS,Z        ; Z set if in state 'l'\r
+       goto    sleep_state_l\r
+       movlw   2\r
+       subwf   sleep_state,0\r
+       btfsc   STATUS,Z        ; Z set if in state 'e1'\r
+       goto    sleep_state_e1\r
+       movlw   3\r
+       subwf   sleep_state,0\r
+       btfsc   STATUS,Z        ; Z set if in state 'e2'\r
+       goto    sleep_state_e2\r
+       movlw   4\r
+       subwf   sleep_state,0\r
+       btfsc   STATUS,Z        ; Z set if in state 'p'\r
+       goto    sleep_state_p\r
+       movlw   5\r
+       subwf   sleep_state,0\r
+       btfsc   STATUS,Z        ; Z set if in state 'x'\r
+       goto    sleep_state_x\r
+       movlw   6\r
+       subwf   sleep_state,0\r
+       btfsc   STATUS,Z        ; Z set if in state 'y'\r
+       goto    sleep_state_y\r
+       movlw   7\r
+       subwf   sleep_state,0\r
+       btfsc   STATUS,Z        ; Z set if in state 'z'\r
+       goto    sleep_state_z\r
+\r
+       ; should never get here but just in case\r
+\r
+       goto    sleep_reset\r
+\r
+       ; if we have rx-ed a 's' continue to next state\r
+sleep_state_s\r
+       movlw   's'\r
+       subwf   SERBUFI,0\r
+       btfss   STATUS,Z        ; Z set if we received a 's'\r
+       goto    sleep_reset\r
+       goto    sleep_cont\r
+\r
+       ; if we have rx-ed a 'l' continue to next state\r
+sleep_state_l\r
+       movlw   'l'\r
+       subwf   SERBUFI,0\r
+       btfss   STATUS,Z        ; Z set if we received a 'l'\r
+       goto    sleep_reset\r
+       goto    sleep_cont\r
+\r
+       ; if we have rx-ed a 'e' continue to next state\r
+sleep_state_e1\r
+       movlw   'e'\r
+       subwf   SERBUFI,0\r
+       btfss   STATUS,Z        ; Z set if we received a 'e'\r
+       goto    sleep_reset\r
+       goto    sleep_cont\r
+\r
+       ; if we have rx-ed a 'e' continue to next state\r
+sleep_state_e2\r
+       movlw   'e'\r
+       subwf   SERBUFI,0\r
+       btfss   STATUS,Z        ; Z set if we received a 'e'\r
+       goto    sleep_reset\r
+       goto    sleep_cont\r
+\r
+       ; if we have rx-ed a 'p' continue to next state\r
+sleep_state_p\r
+       movlw   'p'\r
+       subwf   SERBUFI,0\r
+       btfss   STATUS,Z        ; Z set if we received a 'p'\r
+       goto    sleep_reset\r
+       goto    sleep_cont\r
+\r
+       ; if we have rx-ed a digit 0-9 continue to next state\r
+sleep_state_x\r
+\r
+       ; check if >= '0'\r
+       movlw   '0'\r
+       subwf   SERBUFI,0\r
+       btfss   STATUS,C        ; C set if valid\r
+       goto    sleep_reset\r
+\r
+       ; check if x <= 9\r
+       bsf     FSR,5           ; x is in bank 1\r
+       movwf   x\r
+       movlw   d'10'\r
+       subwf   x,0\r
+       bcf     FSR,5\r
+       btfsc   STATUS,C        ; C reset if valid\r
+       goto    sleep_reset\r
+       goto    sleep_cont\r
+\r
+       ; if we have rx-ed a digit 0-9 continue to next state\r
+sleep_state_y\r
+\r
+       ; check if >= '0'\r
+       movlw   '0'\r
+       subwf   SERBUFI,0\r
+       btfss   STATUS,C        ; C set if valid\r
+       goto    sleep_reset\r
+\r
+       ; check if y <= 9\r
+       bsf     FSR,5           ; y is in bank 1\r
+       movwf   y\r
+       movlw   d'10'\r
+       subwf   y,0\r
+       bcf     FSR,5\r
+       btfsc   STATUS,C        ; C reset if valid\r
+       goto    sleep_reset\r
+       goto    sleep_cont\r
+\r
+       ; if we have rx-ed a digit 0-9 continue to next state\r
+sleep_state_z\r
+\r
+       ; check if >= '0'\r
+       movlw   '0'\r
+       subwf   SERBUFI,0\r
+       btfss   STATUS,C        ; C set if valid\r
+       goto    sleep_reset\r
+\r
+       ; check if z <= 9\r
+       bsf     FSR,5           ; z is in bank 1\r
+       movwf   z\r
+       movlw   d'10'\r
+       subwf   z,0\r
+       bcf     FSR,5\r
+       btfsc   STATUS,C        ; C reset if valid\r
+       goto    sleep_reset\r
+\r
+       ; OK we made it! Lets go to SLEEP, ZZZZZzzzzzzzzzz\r
+\r
+       clrf    sleep_state\r
+       clrf    sleep_timeout\r
+       movlw   1\r
+       movwf   sleeping\r
+       bcf     GPIO,1          ; power switch off\r
+\r
+       retlw   0\r
+\r
+       ; we bombed out in rx-ing sleep string\r
+sleep_reset\r
+       clrf    sleep_state\r
+       clrf    sleep_timeout\r
+       retlw   0\r
+       \r
+       ; increment to next state, and reset sleep timeout\r
+sleep_cont\r
+       incf    sleep_state,1\r
+       movlw   SLEEP_TIMEOUT\r
+       movwf   sleep_timeout\r
+       retlw   0\r
+\r
+; START MAIN LOOP -----------------------------------------------\r
+\r
+main   \r
+       call    inch_n_delay    ; RS232 RX and 1 second delay\r
+\r
+       ; sample V, I, and print if we received a 'w' or a ' '\r
+\r
+       movlw   'w'\r
+       subwf   SERBUFI,0\r
+       btfsc   STATUS,Z        ; skip if SERBUFI != 'w'\r
+       goto    sample  \r
+\r
+       movlw   '.'\r
+       subwf   SERBUFI,0\r
+       btfss   STATUS,Z        ; skip if SERBUFI == ' '\r
+       goto    no_print        \r
+\r
+       ; sample AN[0] (voltage)\r
+sample\r
+       movlw   b'10110001'\r
+       movwf   ADCON0  \r
+       bsf     ADCON0,1        ; start A/D conversion\r
+wait_adc_v\r
+       btfsc   ADCON0,1        ; skip if A/D conversion finished\r
+       goto    wait_adc_v\r
+\r
+       ; print 'wispcar' to get host to call script called wispcar.\r
+       ; This script can then parse wispcar output\r
+\r
+       movlw   'w'\r
+       call    outch_n\r
+       movlw   'i'\r
+       call    outch_n\r
+       movlw   's'\r
+       call    outch_n\r
+       movlw   'p'\r
+       call    outch_n\r
+       movlw   'c'\r
+       call    outch_n\r
+       movlw   'a'\r
+       call    outch_n\r
+       movlw   'r'\r
+       call    outch_n\r
+       movlw   ' '\r
+       call    outch_n\r
+\r
+       ; print voltage to RS232\r
+\r
+       movf    ADRES, 0\r
+       movwf   BIN\r
+       call    BIN2BCD         ; convert voltage to decimal ASCII \r
+\r
+       movf    huns, 0         \r
+       call    outch_n\r
+       movf    tens, 0\r
+       call    outch_n\r
+       movf    ones, 0\r
+       call    outch_n\r
+\r
+       ; print space\r
+\r
+       movlw   ' '\r
+       call    outch_n\r
+\r
+       ; sample AN[1] (current)\r
+\r
+       movlw   b'10111001'\r
+       movwf   ADCON0  \r
+       bsf     ADCON0,1        ; start A/D conversion\r
+wait_adc_i\r
+       btfsc   ADCON0,1        ; skip if A/D conversion finished\r
+       goto    wait_adc_i\r
+\r
+       ; print current to RS232\r
+\r
+       movf    ADRES, 0\r
+       movwf   BIN\r
+       call    BIN2BCD         ; convert current to decimal & ASCII \r
+\r
+       movf    huns, 0         \r
+       call    outch_n\r
+       movf    tens, 0\r
+       call    outch_n\r
+       movf    ones, 0\r
+       call    outch_n\r
+\r
+       ; print rx char\r
+\r
+       movlw   ' '\r
+       call    outch_n\r
+       movf    SERBUFI, 0\r
+       call    outch_n\r
+\r
+       ; print rx char in dec\r
+\r
+       movlw   ' '\r
+       call    outch_n\r
+       movf    SERBUFI, 0\r
+       movwf   BIN\r
+       call    BIN2BCD         ; convert rx char to decimal & ASCII \r
+\r
+       movf    huns, 0         \r
+       call    outch_n\r
+       movf    tens, 0\r
+       call    outch_n\r
+       movf    ones, 0\r
+       call    outch_n\r
+\r
+       ; print wd_timeout in dec\r
+\r
+       movlw   ' '\r
+       call    outch_n\r
+       movf    wd_timeout, 0\r
+       movwf   BIN\r
+       call    BIN2BCD         \r
+\r
+       movf    huns, 0         \r
+       call    outch_n\r
+       movf    tens, 0\r
+       call    outch_n\r
+       movf    ones, 0\r
+       call    outch_n\r
+\r
+       ; print wd_fire in dec\r
+\r
+       movlw   ' '\r
+       call    outch_n\r
+       movf    wd_fire, 0\r
+       movwf   BIN\r
+       call    BIN2BCD         \r
+\r
+       movf    huns, 0         \r
+       call    outch_n\r
+       movf    tens, 0\r
+       call    outch_n\r
+       movf    ones, 0\r
+       call    outch_n\r
+\r
+       ; print sleep_state \r
+\r
+       movlw   ' '\r
+       call    outch_n\r
+       movlw   '0'             \r
+       addwf   sleep_state,0\r
+       call    outch_n\r
+\r
+       ; print sleep timer in dec\r
+\r
+       movlw   ' '\r
+       call    outch_n\r
+       movlw   '0'     ; print x\r
+       bsf     FSR,5\r
+       addwf   x,0\r
+       bcf     FSR,5\r
+       call    outch_n\r
+       movlw   '0'     ; print y\r
+       bsf     FSR,5\r
+       addwf   y,0\r
+       bcf     FSR,5\r
+       call    outch_n\r
+       movlw   '0'     ; print z\r
+       bsf     FSR,5\r
+       addwf   z,0\r
+       bcf     FSR,5\r
+       call    outch_n\r
+\r
+       ; print restart_flag\r
+\r
+       movlw   ' '\r
+       call    outch_n\r
+       movf    restart_flag, 0\r
+       call    outch_n\r
+\r
+       ; print CR-LF\r
+\r
+       movlw   '\r'\r
+       call    outch_n\r
+       movlw   '\n'\r
+       call    outch_n\r
+\r
+no_print\r
+       ; has watchdog fired (wd_timeout == 0) ?\r
+\r
+       movf    wd_timeout,1\r
+       btfsc   STATUS,Z        ; skip if not fired\r
+       goto    watchdog_firing ; Yes? Then goto watchdog firing code\r
+       \r
+       ; are we sleeping (sleeping == 1)?\r
+\r
+       movf    sleeping,1\r
+       btfss   STATUS,Z        ; skip if not sleeping\r
+       goto    snooze          ; Yes? Then goto sleeping code\r
+\r
+       ; No? Then count down watchdog\r
+\r
+       movlw   1\r
+       subwf   wd_timeout,1\r
+\r
+       ; Has watchdog just fired (wd_timeout == 0)?\r
+\r
+       btfss   STATUS,Z        ; skip if wd_countdown is zero\r
+       goto    dec_sleeptimeout ; No - continue\r
+\r
+       ; Yes - start watchdog firing code\r
+\r
+       bcf     GPIO,1          ; PowerSwitch Off\r
+       movlw   WD_FIRE\r
+       movwf   wd_fire\r
+       goto    main\r
+\r
+dec_sleeptimeout\r
+       ; has sleep state machine accepted a rx char?\r
+\r
+       movf    sleep_state,1\r
+       btfsc   STATUS,Z        \r
+       goto    main            ; No? Then continue processing\r
+\r
+       decfsz  sleep_timeout,1\r
+       goto    main\r
+\r
+       ; sleep state machine timeout, reset state machine\r
+       clrf    sleep_state\r
+       goto    main\r
+\r
+       ; watchdog firing code\r
+\r
+watchdog_firing\r
+\r
+       movlw   1\r
+\r
+       ; DR June 2009 - make watchdog fire forerver, cutting power\r
+       ; from battery indefinately until host returns\r
+       subwf   wd_fire,0\r
+\r
+       ; watchdog firing complete? (wd_fire == 0)?\r
+\r
+       btfss   STATUS,Z        ; skip if wd_fire is zero\r
+       goto    main            ; No - continue\r
+\r
+       ; Yes - reset watchdog\r
+\r
+       bsf     GPIO,1          ; PowerSwitch On\r
+       movlw   WD_TIMEOUT\r
+       movwf   wd_timeout\r
+\r
+       ; Indicate WD has fired since last 'w' sent by host.  A rx of\r
+       ; 'w' resets this flag.\r
+       \r
+       movlw   WD_FLAG\r
+       movwf   restart_flag\r
+       \r
+       goto    main\r
+\r
+       ; sleeping code\r
+       ; sleep for xyz seconds, need to count down in decimal\r
+snooze\r
+       ; 3 digit decimal count down timer xyz, e.g. 999, 998,...001,000\r
+       ; it's time's like these I wonder why I volunteered for this project!! :-)\r
+       ; see countdown.c for C version of this algorithm\r
+\r
+       bsf     FSR,5\r
+       movf    z,1\r
+       btfss   STATUS,Z\r
+       goto    z_not_zero\r
+       movf    y,1\r
+       btfss   STATUS,Z\r
+       goto    y_not_zero\r
+       movf    x,1\r
+       btfss   STATUS,Z\r
+       goto    x_not_zero\r
+       \r
+       ; x=y=z=0 so we are finished\r
+\r
+       bcf     FSR,5\r
+       goto sleep_finished\r
+\r
+z_not_zero\r
+       decf    z,1\r
+       goto    continue_sleep\r
+\r
+y_not_zero\r
+       decf    y,1\r
+       movlw   9\r
+       movwf   z\r
+       goto    continue_sleep\r
+\r
+x_not_zero\r
+       decf    x,1\r
+       movlw   9\r
+       movwf   y\r
+       movwf   z\r
+       goto    continue_sleep\r
+\r
+       ; sleep finished\r
+\r
+sleep_finished\r
+       bsf     GPIO,1          ; PowerSwitch On\r
+       movlw   WD_TIMEOUT      ; reset watchdog for good measure\r
+       movwf   wd_timeout\r
+       clrf    wd_fire\r
+       clrf    sleep_timeout   ; clear sleep timeout counter\r
+       clrf    sleeping        ; clear sleep flag\r
+       \r
+       ; Indicate we have just woken up from a sleep. A rx of 'w'\r
+       ; resets this.\r
+       \r
+       movlw   SLEEP_FLAG      \r
+       movwf   restart_flag\r
+       goto    main\r
+       \r
+continue_sleep\r
+       bcf     FSR,5\r
+       goto    main\r
+       \r
+; END MAIN LOOP -----------------------------------------------\r
+\r
+        end\r
+\r
+\r