Published using Google Docs
( EGATE ACCESS CONTROL )
Updated automatically every 5 minutes

TACHYON

[~

FORGET EGATE.fth

pub EGATE.fth                PRINT" RFID Gate Access interface V1.1 150310.1500 - Peter Jakacki" ;

{ MODULE DESCRIPTION

eGATE provides RFID and timer functions to existing gate controllers using a Propeller controller and a Parallax serial RFID card reader.

eGATE P1419 PCB

In this implementation the EGATE uses a hacked P1419 interfaced to a TC-128 gate controller that is operated by wireless remotes but lacks simple features such as timed operations and RFID card access control.

The interface to the TC-128 uses simple switched inputs which switch between 0 and 34VDC and open-collector outputs interfaced opto-inputs which are pulled up to 34V.There are 4 limit switch inputs, a serial input from the card reader, 2 open-collector outputs to operate the open and close, and another LED+Buzzer line to the card reader.

A Bluetooth serial communications module is fitted in place of the USB serial module once it has been installed.

Link to current formatted log file document

NEW: OCT 13, 2014

Added scheduling in place of the simple open/close timer, it is now possible to paste in readable schedules for any day or date or action etc.

TO DO:

Add touch sensors to gate ends - reverses action, delays, tries again after 1 minute - for 3 attempts

<Google webpage version>

}

( I/O DEFINITIONS )

--- The gate controller has two limit switches per motor but these signals switch between 0 to +34V

--- 100K resistors are used to limit the current into the port inputs.

--- optional pulldown/dividers may be added to the Prop side, with values from 10K to 100K suggested.

#P0        == closed2

#P1        == opened2

#P2        == closed1

#P3        == opened1

--- We have a CE0814 PIO with 4 darlington OC outputs plus shared 10K resistor inputs

--- the resistors have been removed except for the serial input from the card reader

#P9        == LED --- setting this high will turn on the card reader white LED (+Piezo buzzer)

#P10        == open --- setting this high will activate the open input on the gate controller

#P11        == close --- activate the close input on the gatby e controller

#P12        == serinp --- #2400 baud serial input from card reader PCB

--- EGATE interfaces to the TC-128 Gate Controller ---

( Limit switch sensing )

{

GATE LIMIT SWITCH TRUTH TABLE

3 2 1 0 = INPUTS

------------------

0 1 0 1 = OPEN

1 1 1 1 = OPENING/CLOSING

1 0 1 0 = CLOSED

0 = closed2

1 = opened2

2 = closed1

3 = opened1

}

DECIMAL

pub LIMITS@ ( --- inputs ) --- pulldown inputs before reading and masking

        $0F OUTCLR #10 us $0F INPUTS P@ %1111 AND

         ;

pub OPEN? ( -- flg )

        LIMITS@ %0101 =

        ;

pub CLOSED? ( -- flg )

        LIMITS@ %1010 =

        ;

( hardcoded master card number )

--- NOTE, could change this so that if the card file is blank then the first card read becomes the master.

#6201860 == mastercard

( *** RFID INTERFACE *** )

--- RFID SERIAL CARD READER 125kHz

--- FORMAT: 2400 BAUD : LF+360073E043+CR

--- 5100-18C3FC 0001623036

pub FLASH ( ms -- )        LED PINSET ms LED PINCLR ;

pub FLASHES ( n -- )         FOR #30 FLASH #150 ms NEXT ;         

DOUBLE cardbuf --- assemble card id here and transfer to "card" when complete

DOUBLE card --- updated with latest valid number, app can clear this afterwards rather than using a flag.

BYTE cardcnt

#10 LONGS stk2                                                                 --- private data stack area for serial task

pub CardChar ( ch -- )

         DUP >DIGIT                                                          --- try to convert it to a hex digit

         IF ( ch val )                                                         --- this character is a digit

            NIP cardcnt C++

           cardbuf @ 16 UM* ( val newval.l newval.h )

           cardbuf 4 + @ 4 SHL + ( val newval.l newval2.h )         --- add in overflow into top 32-bits of double

           cardbuf 4 + ! + cardbuf !                                         --- store double

         ELSE ( ch )                                                         --- not a digit

           $0D = cardcnt C@ #10 = AND

           IF

            cardbuf card 8 CMOVE                                         --- move completed number to "card"

               cardcnt C@ card 7 + C!

             #100 FLASH

           THEN

           cardbuf ~                                                         --- clear cardbuf

           cardcnt C~

        THEN

          ;

--- Receive and process card data at 2400 baud, move from "cardbuf" to "card" when complete

pub SERIN.TASK

        !SP stk2 SP!                                                         --- setup stack for this task

         cardbuf ~ cardcnt C~

        #2400 SERBAUD                                                         --- RFID card reader baud rate

        BEGIN

           !SP

           LED PINCLR serinp SERIN LED PINSET                         --- get a character from the card reader & bleep

           ?DUP IF CardChar THEN

        AGAIN

        ;

( TIME STAMPING )

pub MINUTES@ ( -- mins )                                                 --- read hour and minutes of the day as minutes

         TIME@ #100 U/ #100 U/MOD #60 * +

        ;

\ 1 month time stamp - 5 bits of day of month and 11 bits of minutes

pub STAMP@ ( -- dmmm )                                                         --- read day date and hours and minutes and encode as 16-bits

         DATE@ #100 MOD #11 SHL MINUTES@ +

         ;

{ FILE STRUCTURES

Card records are held in EEPROM. All card numbers are 5 bytes long but are converted to a byte index (1-255, 0=invalid)

CARD FILE -  EEPROM $9000-$97FF+4 8 bytes/record - 2kB = 255 records + dummy

CARD(5)

ATR(1)       Attributes

SPARE(2)

TIMERS FILE - EEPROM $9810

weekdays(8b)

minutes(11b),code(8b)

CARD LOG - EEPROM $8000-$8FFF  4 bytes/record - 4kB = 1,023 records + eof marker

STAMP(2)

CARD INDEX(1)

CODE(1)

CARD NAMES (OPTIONAL) 32 byte records indexed by card index = 8kB

NAME(32)

}

( EEPROM FILE AREAS )

$8000 == LOGS                                                                 --- logs are maintained in 2nd 32k section of EEPROM

$1000 == logsz                                                                 --- size of log file where records are 4 bytes long

LOGS logsz + == CARDS

$800 == cardsz

( CARD FILE )

--- CARD RECORDS = CARD(5),ATR(1),SPARE(2)

--- this will add the current card to the valid card file

pub +CARD ( -- )

        CARDS 8 + BEGIN DUP E@ 1+ WHILE 8 + REPEAT                 --- scan for a blank entry ( ~ 500us/entry)

         ( addr )

          card @ OVER E! card 4 + @ SWAP 4 + E!

         ;

BYTE index --- a saved copy of the index of the last card processed (for logging)

--- lookup the current card in the card file and return with it's index else zero = false

pub CARD? ( -- index )

         card @ DUP 0EXIT

        CARDS cardsz + E!

        CARDS 8 + BEGIN DUP E@ card @ <> WHILE 8 + REPEAT

         CARDS - 8 U/ $FF AND

         DUP index C!                                                         --- maintain a copy of the current index for logging purposes

        ;

pub .CARD ( index --- )

         ?DUP

         IF

          8 * CARDS + E@ $080A .NUM

         ELSE

           \  "*" 8 EMITS

         THEN

        ;

pub LC --- list card file

pub lc --- list card file

pub .CARDS

        ."  List cards "

         CARDS 8 +

         BEGIN

           DUP E@ 1+

           IF

             CR ." #" DUP CARDS - 3 SHR $30A .NUM SPACE

             DUP E@ $80A .NUM

           THEN

           8 + --- jump to next record 8 bytes away

           DUP CARDS - cardsz =

         UNTIL

         DROP --- discard the pointer - chuck out the garbage as we mess it up

        ;

   

pub ERASE-CARDS        CARDS cardsz ADO -1 I E! 4 +LOOP ;

pub FIX-CARDS        

         CARDS cardsz ADO  

           I E@ #1,000,000 U< IF -1 I E! -1 I 4 + E! THEN

         8 +LOOP

         ;

pub AKEY ( -- ch ) --- always wait for a key from any device, that is, ignore nulls

         BEGIN KEY ?DUP UNTIL DUP EMIT

         ;

pri GETDEC ( -- n )

         0 BEGIN

           AKEY

           DUP "0" "9" WITHIN

         WHILE

            "0" - SWAP #10 * +

         REPEAT

         DROP

          ;

pub ADD-CARDS

         BEGIN

           AKEY

           DUP "#" = IF GETDEC 3 SHL CARDS + GETDEC SWAP E! THEN

           $1B =

         UNTIL

         ;         

( *** LOG FILE *** )

--- RECORD: STAMP(2) INDEX(1) CODE(1)

LONG logptr --- write address of entry into log file

LONG logptr2

--- add the latest card to the log file, record the card index, the time stamp, and the action taken

--- if this code is the same as the last then don't create a new log entry, just overwrite the last

pub +LOG? ( code -- )

         logptr2 @ 3 + LOGS + EC@ OVER = IF logptr2 @ logptr ! THEN

pub +LOG ( code -- )

         #24 SHL STAMP@

         index C@ 16 SHL + +                                 --- create a 32-bit number from stamp(16),card(8),code(8)

         logptr @ LOGS + E!                                 --- store in EEPROM

         logptr @ logptr2 !                                 --- update previous pointer with current

         logptr @ 4 + logsz 1- AND DUP logptr !         --- update write pointer as wraparound

        -1 SWAP LOGS + E!                                 --- write a null to indicate end of log file

         index C~

        ;

BYTE reset

--- init logging - find the location where we were writing to last, it should be a -1

pub SETLOG

         LOGS BEGIN DUP E@ -1 <> WHILE 4 + REPEAT LOGS - logptr !

         reset C++

         reset C@ DUP index C! reset EC! --- log index as current reset number plus back it up.

        "R" +LOG --- log this reboot

         ;

{ REPORTING LOGS

LL

17-03:06 OPEN   #003 01563569

17-02:29 OPEN   #007 01567269

17-03:53 RESET  #002 01608827

17-03:56 RESET  #002 01608827

17-03:59 RESET  #002 01608827

17-08:44 RESET  #002 01608827

17-08:40 RESET  #002 01608827

17-09:12 RESET  #002 01608827

17-09:13 OPEN   #009 94967295

17-09:14 OPEN   #009 94967295

17-09:14 OPEN   #009 94967295

17-09:26 OPEN   #008 06201860

17-09:27 OPEN   #006 01598675

17-09:50 CLOSE  #006 01598675 ok

}

--- 0101        == inside closed  - outside closed        

--- 1101        == inside opening - outside closed

--- 1111        == inside opening - outside opening

--- 1011        == inside opened  - outside opening

--- 1010        == inside opened  - outside opening

\ 12345678123456781234567812345678

pub .CINDEX ( code -- index )

        16 SHR >B DUP DUP IF ."  #" $30A .NUM SPACE ELSE DROP ."    * AUTO * " THEN --- display the index value

        ;

pub ATYPE         ADO I C@ DUP BL <> IF EMIT ELSE DROP THEN LOOP ;

" fault   opened  closed  swinging" 0 STRING gate$

pub .GATECODE ( code -- )

         16 SHR >B

pub .GATES ( state -- )

         ."  ["

         DUP $0C AND 2* gate$ + 8 ATYPE

         SPACE

         3 AND 2* 2* 2* gate$ + 8 ATYPE

         ." ]"

         ;

--- Convert the single character code to a verbose label

pri .CODE ( code ch -- )

         SWITCH

         "O" CASE PRINT" OPENING GATE " .CINDEX .CARD BREAK

         "C" CASE PRINT" CLOSING GATE " .CINDEX .CARD BREAK

         "R" CASE PRINT" REBOOTING    " .CINDEX DROP BREAK

         "M" CASE PRINT" MASTER MODE  " .CINDEX .CARD BREAK

         "+" CASE PRINT" ADDING CARD  " .CINDEX .CARD BREAK

         "G" CASE PRINT" GATES MOVING " .GATECODE BREAK

         "H" CASE PRINT" GATES CHECK  " .GATECODE BREAK

        2 SPACES SWITCH@ EMIT 3 SPACES --- or just the code plus spaces

         ;

pub .LOG ( addr -- ) --- print the log entry at this address

         E@ DUP 1+ 0= IF DROP EXIT THEN

         CR DUP #11 SHR $1F AND $20A .NUM ." -"

         DUP $7FF AND #60 U/MOD $20A .NUM ." :" $20A .NUM  

         DUP #24 SHR SPACE .CODE --- display the log code

        ;

pub LL --- list log file (shortcut for bluetooth use)

pub ll --- list log file (shortcut for bluetooth use)

pub .LOGS

        ."  List logs @" .DT

        logptr @ 4 + logsz ADO I logsz 1- AND LOGS + .LOG 4 +LOOP

         ;

--- console commands to clear the files

pub ERASE-LOGS        LOGS logsz ADO -1 I E! 4 +LOOP ;

pub INVALID                        card 8 ERASE ;

TIMER master        --- just a timer/flag to indicate master mode is set

pub HEADER                         CR .DT SPACE ;

pub MASTER --- enter master mode with timeout

         "M" +LOG

         HEADER PRINT" YES MASTER "

         #600,000 master TIMEOUT --- set master timer to 10 minutes (in ms)

         #10 FLASHES card ~

         ;

\ This card didn't register but check to see if it's the master card

pub CHECK-MASTER

         card @ mastercard = --- is this card the mastercard?
        IF MASTER ELSE INVALID THEN

        ;

pub ADDING.CARD

      HEADER PRINT" Adding card "

         +CARD 5 FLASHES --- add a card into the file and acknowledge with 5 flashes

        CARD? DROP --- generate an index for this new card

        "+" +LOG

         card ~

         #600,000 master TIMEOUT --- retrigger master timeout while we are still adding cards

        ;

--- card is INVALID but check to see if master mode is enabled so we can make this card valid

pub CHECK-INVALID                

         master @

         IF

           card 4 + C@ --- and there is a proper card (5th byte is set)

           IF ADDING.CARD ELSE INVALID THEN

         ELSE

           CHECK-MASTER

         THEN

         ;

( OPEN & CLOSE )

TIMER operation                 --- timing open/close operation, allows immediate abort if active

TIMER delay                 --- delay until open/close operation

TIMER autotmr                 --- autoclose timer - triggered on opening

WORD action

BYTE op,autoclose?

LONG autotime

pub MINUTES                60 *

pub SECONDS                1,000 * ;

{HELP AUTOCLOSE ( ms -- )

Change the time that the gate will automatically closed after non-timed opening during closing hours

Usage: 15 AUTOCLOSE

}

pub AUTOCLOSE ( minutes -- )

        60,000 *

         DUP autotime !

         autotime E!

         ;

--- check for any delayed actions

pub CHECK-DELAYED

        delay TIMEOUT? IF action W@ ?DUP IF CALL action W~ THEN THEN

        ;

#10 == exitdelay --- default exit delay in seconds

pub EXIT-DELAY                

        operation TIMEOUT?

         IF

           exitdelay #1000 * delay TIMEOUT

           LED APIN 1 HZ --- automatic 1 flash/second (until muted)

         THEN

;

pub C

pub c

pub CLOSE-NOW        

         MUTE        

         LED PINSET

         #10,000 operation TIMEOUT

         "C" op C!

          close PINSET #500 ms close PINCLR

         "C" +LOG

         PRINT" -- gate now closing"

         #500 FLASH

         ;

pub CLOSE                         

         autotime @ IF 1 autoclose? C! THEN                                --- remember to autoclose from now on until reset

pub CLOSE2

         PRINT"  Closing gate "

         EXIT-DELAY

         ' CLOSE-NOW action W!

         ;

pub O

pub o

pub OPEN-NOW                        

         MUTE                                                                         --- stop the automatic flashing

         LED PINSET                                                                 --- light up the card LED indicator ( 1 second total)

         #10,000 operation TIMEOUT

         "O" op C!

         open PINSET #500 ms open PINCLR                                 --- pulse the open signal to the TC-128 "open pin"

         "O" +LOG                                                                 --- log that we are now opening the gate  

         PRINT" -- gate now opening"

         #500 FLASH

         autoclose? C@

           IF autotime @ autotmr TIMEOUT 2 autoclose? C! THEN        --- close again after delay if autoclose set

;

pub OPEN                         

         autoclose? C~                                                        --- clear autoclose if this is a scheduled opening

pub OPEN2

         PRINT"  Opening gate "

         EXIT-DELAY

         ' OPEN-NOW action W!

         ;

{ Terminal output for valid card

2014/04/21 16:45:40 01560448 #0003 Closing gate                                                

}

--- since the card was valid we either want to open or close the gate

pub VALID

        delay @ --- if this is a valid card presented during a delay then treat it as an abort

         IF

           PRINT" -- aborting "

           action W~ MUTE

         ELSE

           operation TIMEOUT? --- if it's in the middle of an operation then limit switches are invalid.

           IF

              OPEN? IF CLOSE2 ELSE OPEN2 THEN

           ELSE --- can't rely on limit switches to tell us what to do so just reverse last op immediately

             op C@ "C" = IF OPEN-NOW ELSE CLOSE-NOW THEN

           THEN

           1 second --- debounce, don't read same card too quickly

         THEN

         card ~ --- clear the card number (accepted)

        ;

--- using the "card" number scan the card file for a valid entry

pub CHECK-CARDS

         card @ --- is there a new card? (otherwise zero)

         IF --- we have a card number but is it valid?

           HEADER card @ $080A .NUM SPACE

           PRINT" #" CARD? DUP IF DUP .DEC ELSE PRINT" INVALID" THEN --- print the card index (0=not valid!!!)

           mastercard card @ = master @ 0= AND NOT AND --- oh, and a master is a master first, access operation second

           IF

            VALID --- card has been validated so now check to see if we need to open or close

        ELSE

          CHECK-INVALID

           THEN

         THEN

         ;

BYTE limits --- change of state latch for gate limit switches

pub CHECK-GATES

         LIMITS@ limits C@ <>

         IF

           LIMITS@

           DUP CR ." GATE CHANGE = " $402 .NUM

           DUP limits C! index C! "G" +LOG

           #100 ms --- debounce

         THEN

         ;

( SCHEDULING )

// define days of the week and their constants

// The month is 0 so that the date is interpreted as a day of week mask

pub MON ( -- day month )        0 MASK 0 ;

pub TUE                                1 MASK 0 ;

pub WED                                2 MASK 0 ;

pub THU                                3 MASK 0 ;

pub FRI                                4 MASK 0 ;

pub SAT                                5 MASK 0 ;

pub SUN                                6 MASK 0 ;

pub WEEKDAYS                        %0011111 0 ;

pub WEEKENDS                        %1100000 0 ;

pub DAILY                                %1111111 0 ;

{

0 |<                 == MON

1 |<                 == TUE

2 |<                 == WED

3 |<                 == THU

4 |<                 == FRI

5 |<                 == SAT

6 |<                 == SUN

%0011111        == WEEKDAY

%1100000        == WEEKEND

7 MASKS        == DAILY

}

1                == JAN

2                == FEB

3                == MAR

4                == APR

5                == MAY

6                == JUN

7                == JUL

8                == AUG

9                == SEP

#10                == OCT

#11                == NOV

#12                == DEC

pub AM                ;

pub PM                 DUP #1200 < IF #1200 + THEN ;

{ TIMERS

Each long in the schedules array packs an action, a date or day mask, and the minute of the day

A ACTION[10]        --- vector index for code to execute

Y DAY[1]                 --- if set then interpret MMDD as week day mask (0-6 = MON-SUN)

M MM[4] 1-12

D DD[5] 1-31

T minutes[12]         --- time encoded as minutes of the day

If MM is 0 then DD = day of week mask (8 bits)

3322222222 2 2111 11111 110000000000

1098765432 1 0987 65432 109876543210

AAAAAAAAAA Y MMMM DDDDD TTTTTTTTTTTT

}

#64        == entries

#64 AVAR schedules

entries 3 * ALLOT

pub @SCH ( index -- addr )                2* 2* schedules + ;

pub SAVESCH                schedules DUP entries 2* 2* ESAVE ;

 

--- fetch the date for this schedule and a flag to indicate if it's a specific date or a day mask

pub SCH.DATE@ ( index -- MMDD or days.mask+$800 )

         @SCH @ #12 SHR DUP 9 MASK AND

           IF >B $800 +

           ELSE DUP $1F AND SWAP 5 SHR >N #100 * +

           THEN

         ;

--- fetch the time for this schedule - in minutes

pub SCH.TIME@ ( index -- time )                

         @SCH @ $0FFF AND

;

--- fetch the action for this schedule

pub SCH.JOB@ ( index -- cfa )

        @SCH @ #22 SHR 2* XCALLS + W@

;

pub .SCHDAY

         $7F AND SWITCH

         $7F CASE PRINT" DAILY " BREAK

         $1F CASE PRINT" WEEKDAY" BREAK

         $60 CASE PRINT" WEEKEND" BREAK

         7 0 DO I MASK SWITCH@ AND IF I 3 * " MONTUEWEDTHUFRISATSUN" + 3 CTYPE SPACE THEN LOOP

         ;

pub .SCHDATE

         DUP $800 AND IF .SCHDAY

         ELSE #100 U/MOD SWAP PRINT SPACE 1- 3 * " JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC" + 3 CTYPE SPACE

         THEN

         ;

pub .SCH

         I @SCH @ IF

           CR I $20A .NUM PRINT" : "

           " AM"

           I SCH.TIME@ #60 U/MOD DUP #12 > IF #12 - ROT DROP " PM" ROT ROT THEN

           SWAP <# # # DROP "." HOLD # # #> PRINT$ SPACE PRINT$ SPACE

           I SCH.DATE@ .SCHDATE PRINT" WILL "

           I SCH.JOB@ PFA>NFA PRINT$

         THEN

          ;

pub LIST

         DECIMAL

         entries 0 DO

           .SCH

        LOOP

         CR 4 SPACES autotime @ ?DUP IF 60,000 / PRINT ELSE PRINT" NO" THEN PRINT"  AUTOCLOSE "

        ;

pub MINUTES@ ( -- minutes )         TIME@ #100 / #100 U/MOD #60 * + ;

WORD now

pub MINUTE? ( -- flg )                MINUTES@ now W@ OVER now W! <> ;

pub NOW? ( index -- flg )         SCH.TIME@ now W@ = ;

pub TODAY? ( index -- flg )        SCH.DATE@ DUP $800 AND IF DAY@ 1- MASK AND ELSE DATE@ #100 / = THEN ;

pub CHECK-SCHEDULE

         MINUTE?                                                        --- every minute

         IF

          entries 1 DO                                                 --- scan through all the timers

             I NOW?                                                        --- is there a match here?

             I TODAY? AND

             IF CR .DT SPACE .SCH

               I SCH.JOB@ ?DUP IF CALL THEN                        --- perform the job request

             THEN

           LOOP

         THEN

         ;

{HELP SCHEDULE

Start a new schedule by wiping the working copy in RAM

and clearing errors etc.

}

pub SCHEDULE

        !SP errors W~

        schedules entries 4 * ERASE                                --- clear all the timers - but backup is still in EEPROM for now

        DECIMAL

        ;

{HELP DONE

Check for errors and finalize schedule by backing up to eeprom if there were no errors

}

pub DONE

        DEPTH errors W@ OR IF CR ." Schedule has not been backed up due to errors "

         ELSE CR ." Backing up settings" SAVESCH

         THEN

        ;

pub +SCH ( long --- )

        0 #64 1 DO I @SCH @ 0= IF I OR LEAVE THEN LOOP

         ?DUP IF @SCH ! THEN

        ;

{

Y DAY[1] --- if set then interpret MMDD as week day mask

M MM[4] 1-12

D DD[5] 1-31

3322222222 2 2111 11111 110000000000

1098765432 1 0987 65432 109876543210

AAAAAAAAAA Y MMMM DDDDD TTTTTTTTTTTT

}

pub WILL        ( time day month <action> -- ) IMMEDIATE

         [NFA'] NFA>CFA >VEC XCALLS - 2/ #22 SHL                                 --- form the action code in the high bits of the long

         [COMPILE] GRAB ( vec time day month )                                 --- grab the time day month

         ?DUP IF ( time date month )                                                --- process a specific date (if month is non-zero)

           #17 SHL SWAP #12 SHL +                                                 --- form MMMM DDDDD

         ELSE ( vec time day-mask )                                                 --- handle field as a day of week mask (month = 0)

           $200 + #12 SHL                                                                --- Set day of week bit

         THEN

         ( vec time day ) SWAP #100 U/MOD #60 * + ( vec day minutes )

         + +

         +SCH

        ;

TIMER checktimer

pub CHECK-TIMERS

         --- if gate has had a non-scheduled opening then check to see if we are ready to close it again

         autoclose? C@ 2 = IF autotmr TIMEOUT? CLOSED? NOT AND IF CLOSE THEN THEN

         --- log regular checkup

        checktimer TIMEOUT? IF #300,000 checktimer TIMEOUT LIMITS@ index C! "H" +LOG? THEN

        ;

pub CHECK-EEPROM

         0 E@ CLKFREQ <> IF REBOOT THEN

         ;

\ generate a heartbeat by blinking the led and beeping very briefly every second or so

\ do this by reading the system counter

pub HEARTBEAT

        delay @ NOT IF CNT@ $3FC.0000 AND $3FC.0000 = LED PIN! THEN                --- heartbeat click and flash

        ;

--- main loop to check the cards and timers etc

pub EGATE.POLL

        HEARTBEAT                 --- short blinks to indicate we're still alive

        CHECK-CARDS         --- check for any cards

         CHECK-TIMERS         --- check timers 

         CHECK-SCHEDULE

         CHECK-GATES

         CHECK-DELAYED

         CHECK-EEPROM

         #30,000 WATCHDOG         --- kick it back into life if it somehow gets lost

        ;

--- main entry for EGATE, init tasks and timers etc

pub EGATE 

        CR EGATE.fth                 --- announce the application is up and running

         CR .DT CR                         --- report the date and time

         --- set a default date and time if it's not right

         DATE@ #10,000 U/ #14 < IF PRINT"  Warning!, time and date not set ! " #000101 DATE! #120000 TIME! THEN

        card ~                         --- init card variable

        SETLOG                         --- find where we were writing to last and set the pointer

         ' SERIN.TASK 6 RUN         --- run the serial input processing from the card in it's own task (actually 1 of 8 CPU cores)
        ' EGATE.POLL keypoll W!
--- allow the console to continue running while we check cards etc in the background

        0 checktimer TIMEOUT

         0 delay TIMEOUT

         0 operation TIMEOUT

         0 autotmr TIMEOUT

        #30,000 master TIMEOUT --- force master mode for 32 seconds after power-up

        ;

{ TERMINAL OUTPUT 

NAMES:  $59AA...7422 for 6776 (0054 bytes added)                                                                          

CODE:   $0000...3ABA for 8258 (0028 bytes added)                                                                          

CALLS:  0396 vectors free                                                                                                  

RAM:    7920 bytes free                                                                                                    

                                                                                                                           

MODULES LOADED:                                                                                                            

348E: EGATE.fth           RFID Gate Access interface V1.0 140414.1200 - Peter Jakacki & Jack Stubbs                        

337A: MCP79410-RTC.fth    MCP79410 I2C RTC DRIVER - 130529.1330                                                            

1880: EXTEND.fth          Primary extensions to TACHYON kernel - 140402-14OO                                              

BOOT: EXTEND.boot                                                                                                          

BOOT: MAIN                                                                                                                

                                                                                                                           

RFID Gate Access interface V1.0 140414.1200 - Peter Jakacki & Jack Stubbs                                                  

2014/04/23 16:25:06                                                                                                        

}

{ 10 second BCA analysis - sorted

64 : W@                     1202513

2B : 1 -                    961997

CE : (IF)                   721843

EB : (REG)                  721567

16 : ?DUP                   721507

17 : DUP                    481349

21 : SWAP                   481025

CD : (UNTIL)                240809

89 : 0                      240799

0F : EXIT                   240576

C2 : XCALL                  240573

1A : OVER                   240541

57 : =                      240533

13 : 2DROP                  240493

29 : +                      608

62 : C@                     339

59 : 0=                     330

5F : C@++                   314

04 : XOP                    307

87 : 3                      301

7E : (BYTE)                 62

6A : C!                     35

3A : AND                    32

5C : >                      24

2C : 1+                     22

28 : -                      13

81 : BL                     10

14 : DROP                   6

6E : W!                     6

7D : (WORD)                 6

85 : 4                      6

C9 : (AGAIN)                6

0D : 0EXIT                  5

3E : OR                     5

55 : >B                     5

33 : U/MOD                  4

84 : 1                      4

E6 : >R                     4

E9 : R>                     4

36 : NIP                    3

40 : XOR                    3

6C : W+!                    3

86 : 2                      3

1C : 3RD                    1

77 : CLR                    1

88 : ON                     1

BC : CALL                   1

BF : VCALL                  1

}

]~

END

--- Example of a schedule file that can be pasted in via the console - comments allowed

SCHEDULE - local trading hours 8.30 AM to 9.00 PM

07.30 AM DAILY WILL OPEN        --- default opening time

09.30 PM DAILY WILL CLOSE

07.30 AM SUN WILL CLOSE                --- don't open early on Sunday

09.30 AM SUN WILL OPEN                --- open later on a Sunday

06.30 PM SUN WILL CLOSE                --- early close on Sundays

05.30 PM SAT WILL CLOSE                --- early close on Saturdays

05.30 PM TUE WILL OPEN

05.30 PM WED WILL OPEN

05.30 PM THU WILL OPEN

07.30 AM 8 JUN WILL CLOSE         --- don't bother opening (overrides any previous schedule opening)

07.30 AM 25 DEC WILL CLOSE

07.30 AM 1 JAN WILL CLOSE        --- always closed (need to improve and say "1 JAN CLOSED")

5 AUTOCLOSE                      --- will close automatically 5 mins after remotely opened

DONE

--- If CLOSE is selected then 15 min AUTOCLOSE stays in operation until a timed OPEN operation

AUTORUN EGATE

DATE@ build.date E! TIME@ build.time E!

?BACKUP

REBOOT