To use this file copy and paste this:    // #URL-lib "http://pin1.org/forthlib/flb/Miscl/HA Interface v1.0.fth"   into BV Terminal 3 or here to download.

// Home Automation Project // S. Clarkson // 03-Feb-2009 //

// REQUIRES: // #URL-lib "http://pin1.org/forthlib/flb/General/soft1.flb" sid=99 // #URL-lib "http://pin1.org/forthlib/flb/General/pinsel.flb" sid=100


Full Contents of File

// Home Automation Project
// S. Clarkson
// 03-Feb-2009
//

// REQUIRES:
// #URL-lib "http://pin1.org/forthlib/flb/General/soft1.flb" sid=99
// #URL-lib "http://pin1.org/forthlib/flb/General/pinsel.flb" sid=100

// VARIABLES:
// INPUTS
variable  I_kWh-p
variable  I_alm-set
variable  I_intrude

// OUTPUTS
variable  O_led

// Initialise the ports for use by the HA system

// Port configuration will be as follows:
//
// INPUTS
// P1.16 - KWh Pulse - 200 pulses = 1 KWh
// P1.17 - Alarm armed
// P1.18 - Alarm activated (intruder)
// P1.27 - Escape run loop (for debug)
//
// OUTPUTS
// P0.31 - Flash with kWh pulse trailing edge
//

// Word to initialise the I/O ports
: init-io
        16  io1-in
        17  io1-in
        18  io1-in
        27  io1-in
        28  io1-in

        31  io0-out
;

// Words to implement the io scans
: scan-in
        // Scan inputs
        16  p1@  I_kwh-p  !
        17  p1@  I_alm-set  !
        18  p1@  I_intrude  !
;

: wr-out
        // Write outputs

        O_led  @  31  p0!
;

// ---------------------------------------------------------

// Below are the user logic sections

variable  kW
variable  kwh
variable  kwh-1000th
variable  osf
variable  a1 
variable  pr
variable  bpos
variable  prev-c
variable  inpkt    // True when an STX has been received but no ETX yet
variable  Address  // our bus address - configured in sys-init

// Comms constants and variables
2  constant  STX
6  constant  ACK
21  constant  NAK
3  constant  ETX
16  constant  DLE
1  constant  SOH
27  constant  ESC

// Packet constants
20  constant  SYNC-CLK
21  constant  RQ-KWH
22  constant  SD-KWH
23  constant  RQ-W
24  constant  SD-W

20  constant  BLEN 

BLEN  vspace$  d-buff  // data goes here
BLEN  vspace$  s-buff  // send data goes here

: inpkt?
        inpkt  @  0  <>
;

: c-buf
        // Empty internal buffer
        0  bpos  ! 
        BLEN  for  0  i  d-buff  +  c!  next
        0  inpkt  !
;

: dump-buf
INT k
        // Clear the h/w input buffer, if we get an ETX with no preceding ESC, stop
        0  =>  k
        begin
                key1?  k  ETX  <>  and
        while
                key1  =>  k
                prev-c  @  ESC  =  if  0  =>  k  then  // If previous key was esc, throw away this key
                k  prev-c  !
        repeat

        // Empty local buffer too
        c-buf
        0  prev-c  !
;

// Wait for an STX character that was not preceded with ESC
: stx?
        -1    // if we are already inside packet processing, send true
        inpkt?  0  =  if
                drop
                0
                key1?  if
                        // Key on the serial input buffer
                        drop
                        prev-c  @  ESC  <>  // Get previous key and store true if not escape
                        key1  dup  prev-c  !  // Store as previous key
                        STX  =  and    // True if received STX and previous was not escape char
                        dup  if  -1  inpkt  !  then    // Set flag to indicate that we are processing a packet
                then 
        then
;

: get-buf
        // Get characters from serial buffer, return -1 after an ETX has been received
        // Characters are added to the buffer until either it is full or we get an ETX
        -2  // Will return -2 if the buffer is full
        bpos  @  BLEN  <  if
                drop 
                -3  // Will return -3 if no keys on serial port
                key1?  if
                        drop
                        prev-c  @  ESC  <>    // Do check for preceding esc
                        key1  dup  prev-c  !  // Get character and store as previous char
                        dup  ESC  <>  if  // Don't store escape chars
                                dup  d-buff  bpos  @  +  c!  // Store character
                                bpos  @  1  +  bpos  !  // Increment buffer pointer
                        then
                        ETX  =  and  // Return true if ETX without ESC preceding
                then
        then
;

: getall
        // Get as many characters as are available on the port
        begin
                get-buf  dup  0  =
        while
                drop
        repeat
;

: send-data
        kwh  @  iu.
        46  emit1
        kwh-1000th  @  iu.
        32  emit1
        kw  @  iu.
        87  emit1
        13  emit1
        10  emit1
        10  emit1
;

: send-nak
        NAK  emit1
;

: send-ack
        ACK  emit1
;

: chk-len?
        // Check packet length is at least minimum
int len
        -1
        bpos  @  6  <  if
                drop
                send-nak
                0
        then
        
        // Now check that packet length matches declared length
        d-buff  3  +  c@  =>  len    // declared data length (bytes)
        bpos  @  6  len  +  <>  if
                // Packet different than declared
                drop
                send-nak
                0
        then

        // length is OK
;

: !sync-clk { src }
        send-ack
;

: !send-kwh { src }  
        send-ack
        STX  emit1
        Address  @  emit1
        src  emit1
        SD-KWH  emit1
        9  emit1
        kwh  @  <#  #  #  #  #  #  #>  stype1
        46  emit1
        kwh-1000th  @  <#  #  #  #  #>  stype1
        0  emit1  // CRC omitted for now
        ETX  emit1
;

: !send-w { src }
        send-ack
        STX  emit1
        Address  @  emit1
        src  emit1
        SD-W  emit1
        5  emit1
        kw  @  <#  #  #  #  #  #  #>  stype1
        0  emit1  // CRC omitted for now
        ETX  emit1
;

: do-pkt
        // Decode the received packet and respond accordingly
int src  cmd  len  crc

        // Minimum packet length is 6 characters. If this is less than that, send a NAK
        chk-len?  if 
                // Packet basic length check OK
                // Break up packet
                d-buff  c@  =>  src
                d-buff  2  +  c@  =>  cmd
                d-buff  3  +  c@  =>  len
                d-buff  4  len  +  +  c@  =>  crc
                
                // Add CRC check later!

                // Process command
                cmd  SYNC-CLK  =  if
                        // Synchronise RTC to source
                        src  !sync-clk
                then

                cmd  RQ-KWH  =  if
                        // Send KWH
                        src  !send-kwh
                        0  kwh  !  0  kwh-1000th  !
                then

                cmd  RQ-W  =  if
                        // Send W
                        src  !send-w
                then
        then
;

: parse-pkt { p-status --- Status from get-buff}
        // Determine if this packet is for us and if so is it valid, if it is process it
int# ourmsg  0    // true if message addressed to us
        
        p-status  -3  <>  if
                // Packet is complete or buffer is full, we need to check

                // Check if addressed to us
                bpos  1  >  if
                        // See if we are addressed (buffer + 1 since we don't store the STX)
                        d-buff  1  +  c@  Address  @  =  =>  ourmsg
                then

                p-status  -2  =  if
                        // Buffer is full, we need to clear our receive
                        // buffer and send a NAK if addressed to us
                        dump-buf
                        ourmsg  if  send-nak  then
                then

                p-status  -1  =  if
                        // We have a complete packet -
                        // or at least one that ended with ETX
                        // Is this for us?
                        ourmsg  if
                                do-pkt
                        then
                        // dump our buffer
                        c-buf
                then
        then
;

: do-comms
// Handle bus comms

        // Don't do anything before we get a non escaped STX
        stx?  if
                // While we have an stx, get all the data
                // from the packet and parse it when complete
                getall  parse-pkt
        then
        
;

: 1st-scan
        // This word will be executed on the first program
        // scan only

        
        1000  set-t1
        1500  set-t2
        700  set-t3
        180000000  set-t4    // for calculating Kw load
        1  O_led  !

        0  kW  !
        0  kwh  !
        0  kwh-1000th  !        // reset kwh pulse counter

        0  osf  !
;

// Logic loop
: ob1
        // This word is called on every program scan

        // Reset t4 when done (this would be <= 1W usage)
        t4-dn  0  =  if  en-t4  then

        // Debounce falling edge
        osf  @  1  =  I_kwh-p  @  0  <>  and  if
                en-t2
        then

        osf  @  1  =  t2-dn  and  if
                // Ensure one shot
                0  osf  !

                // i.e. pulse off (logic true) and timer done
                5  kwh-1000th  @  +  kwh-1000th  !

                // Check for roll into kwh
                kwh-1000th  @  999  >  if
                        0  kwh-1000th  !
                        1  kwh  @  +  kwh  !
                then

                // If kw, kwh-1000th and kwh zero ignore
                kwh  @  0  <>  kwh-1000th  @  0  <>  or  if
                        // Calculate instant Kw usage (actually Watts)
                        t4pre  @  dup  t4acc  @  -  / kw !
                        // Reset kW timer
                        t4pre  @  t4acc  !
                then

                1  pr  !
        then

        I_kwh-p  @  0  =  if 
                en-t1 
        then    // Run debounce timer while ip on

        // Store flag - debounce on done
        t1-dn  if
                1  osf  !
        then


        // Flash led for n ms (t3)

        // while timer done, run the off debounce
        t2-dn  t3-tt  or  if 
                en-t3
        then

        1  O_led  ! 
        t3-tt  if 
                0  O_led  ! 
        then

;

// Test mode logic
: ob1-test

        // Reflect sensor input to on board led for tuning
        I_kwh-p  @  O_led  !
;

// ----------------------------------------------------------
// END OF USER LOGIC
// ----------------------------------------------------------

: sys-init

        init-io 

        65  Address  !
        c-buf  // Initialise the comms by clearing the internal buffer

        1st-scan 

        t-init
;

// ---------------------------------------------------------

// Main program loop

: sgl-step
// Main program loop here
                scan-in
                ob1
                wr-out
                do-comms

                // Timer status handling
                TSTATS
;

: test-m
// Test program loop here
                scan-in
                ob1-test
                wr-out
                do-comms

                // Timer status handling
                TSTATS
;

// Initialise system, then loop through the
: run

// Initialise io & timers
        sys-init

        // 1000 ms

        begin
                27  p1@  -1  =
        while
                // Normal running
                28  p1@  -1  =  if
                        sgl-step
                then

                // Setup mode
                28  p1@  0  =  if
                        test-m
                then

        repeat

        drop  drop
        
        // Clear all outputs
        
        1  O_led  !

        // Final output cycle
        wr-out
;