PASSWORD CONTROLLED ENTRY MODULE

By: Tim Box

November 2003

INTRODUCTION

Recently I wrote a password controlled switch module for a friend and thought it might make a good project.

The other one was quite sophisticated so for this project so we are going to make a simple version.

As with all projects you have to make a specification list. We will start by making the spec very simple and as we enhance it we establish more what is needed.

PASSWORD CONTROL MODULE REQUIREMENTS 

  1. Make a switch open when you enter a correct password  
  2. Enable the changing of the password

OK thatís phase 1. But itís not really enough to go on, so we need to elaborate more.

  1. Enter a preset number of key entries to be compared against a stored number   
  2. Enable the ability to change and store a new password   
  3. Indicate whatís going on by flashing an LED   
  4. Only enable the lock switch to be on for a limited time

Thatís it were getting there.

Stage 1

Do we let people spend a year entering the password? That would not work, and what if they knew they entered a wrong number? They have to be able to start afresh. So an expansion of part 1 would be.

1.a Password entry is timed, X seconds between each key press or it resets.

1.b A cancel key is to be implemented

Stage 2

Not much to add to that apart from to decide how we enable the password change procedure, a key? A switch inside the case? Well there are lots of options but I am going to choose a Master password.

Stage 3

This is quite important, as we need some indication what is going on. We need to be made aware if we are in master mode other wise we might change the password inadvertently. So we will implement a flashing led to indicate the current status.

Stage 4

As you will not be entering any passwords at this stage a simple delay will do controlling the switch or what ever the device is used for.

SOLUTIONS

From the above, some things stand out as posing a problem, the time out on the key entry and the flashing lights. You could have timed loops but personally I hate them, so I am going to introduce an Interrupt routine. This will take care of the timing of the key entry and the led flashing.

HARDWARE

Really all we need is a 12 button, key pad. There are loads out there but for this project I picked one up from a mail order firm. The Pic I choose is a 16F268 as it had enough pins for the keypad the LEDís and it also has a 16 bit timer, TMR1. As I did not fancy pulling the pic out every time I changed my code, the development was done on my Proton board. Having a display is vital to aid debugging and as it turned out I was able to use the key pad as well, I just matched the wiring as per the Proton board.

With the spec and hardware decided, I knocked up a schematic on a friends PC using ISIS. On completion of the code I was able to test it on a 16F628a as a simulation, great fun. See Fig 1

Figure 1

THE CODING

(Find the code here )

KEY ENTRY

To start with we need to sort out the key scanning. There really is no point in making life hard and reinventing the wheel, so I opted to use the INKEY command. Having wired up the keypad I copied the example code from the manual, placed it in a loop, and ran it.

DIM VAR1 as BYTE
 LOOP:
 VAR1 = INKEY ' Scan the keypad
 DELAYMS 50 ' Debounce by waiting 50ms
 PRINT @VAR1 , " " ' Display the result on the LCD
 GOTO LOOP

When you press a key you see that the numbers do not make much sense  1 = 0, 2 = 1.. 9 = 10.

What we need to do is convert the data from the keypad to a more useful numeric arrangement. Again I looked in the manual and saw how its done. After messing with the values I came up with this.

LOOP:
 VAR1 = INKEY ' SCAN THE KEYPAD
 DELAYMS 50 ' DEBOUNCE BY WAITING 50MS
 KEY = LOOKUP VAR1, [1,2,3,255,4,5,6,255,7,8,9,255,"*",0,"#",255,255]
 PRINT AT 1,1,@KEY,32,@VAR1 , " " ' DISPLAY THE RESULT ON THE LCD
 GOTO LOOP 

As I said above, the wiring matched that of the built in keypad, minus the right column. So I stuck with the Proton keypad for the duration of the coding / debugging session.

Only one last thing to do and that is a way to prevent the same key press being read twice. To do that we need a latch type arrangement. The method I adopted was:-

When no key press is detected it clears the NEW_KEY flag. Also note that the key is de-bounced only once, the first time it is seen.

KEY_SCAN:
 KEY = INKEY ' SCAN THE KEYPAD
 KEY = LOOKUP KEY, [1,2,3,255,4,5,6,NO_KEY,7,8,9,NO_KEY,"*",0,"#",NO_KEY,NO_KEY]    
 IF KEY = NO_KEY THEN
     NEW_KEY = TRUE
     GOTO KEY_EXIT ' NEW KEY IS ONLY CLEARED BY A NO_KEY BEING SEEN
 ENDIF
 IF NEW_KEY = TRUE THEN ' IF THIS IS THE FIRST TIME WE HAVE SEEN
     IF KEY <> LAST_KEY THEN DELAYMS 50 ' THIS KEY, DE-BOUNCE
 ENDIF  
 KEY_EXIT: 
 LAST_KEY = KEY ' SAVE THE KEY VALUE
 RETURN 

Thatís the key scan routine written, lets now see how you use it. Basically you only bother acting on a key if the key has not been read before. Something like this:-

IF NEW_KEY = TRUE ' IF THIS IS A NEW KEY THEN
     IF KEY = REQUIRED_KEY THEN ' IF ITS THE KEY YOU WANT
         NEW_KEY = FALSE ' LATCH TO PREVENT A RE-READ
     '    REST OF CODE HERE
     ENDIF
 ENDIF

That is real load of code to add every time you want to read a key.  Itís better to keep it in a subroutine, and when required just make a call to that sub. We will though, need another flag to indicate if the result of the key check turned out to be true. I used KEY_STATUS

CHK_KEY_0_9:
 KEY_STATUS = FALSE ' PRECONDITION THE RESULT
 IF NEW_KEY = TRUE THEN ' IF THIS PRESS IS NEW TO US
     IF KEY >= 0 THEN ' IF WE ARE IN THE RANGE 0 - 9
         IF KEY <= 9 THEN
             KEY_STATUS = TRUE ' RESULT IS TRUE
             NEW_KEY = FALSE ' LATCH THE KEY AS BEING READ
             RETURN
         ENDIF
     ENDIF
 ENDIF
 RETURN

To use the routine we make a call and check the result.

GOSUB CHK_KEY_0_9
 IF KEY_STATUS = TRUE THEN
     ' REST OF CODE HERE
 ENDIF

In the above example, we check if the current key pressed is in the range of 0 Ė 9. If it is, we can use the value in KEY in the rest of the process.

In our password routine we only need to read the 0 Ė 9 keys and ď#Ē key so I only wrote two routines.

INTERRUPTS

As I stated in the spec, we need to keep track of time. To do this I used an interrupt routine. I like running my interrupts at 100hz on TMR1, so used my standard interrupt routine.

The interrupt is a true hardware interrupt, as apposed to the software variant, this means you have to watch what instructions you use to prevent any of the system variables getting corrupted. Luckily single comparison IF THENís and DECís do not mess things up so we can use them with impunity.

For this project we need two timers, INPUT_TMR and LED_TMR. As we will be updating the timers 100 times a second, and we need to time for more than 2.5 seconds, so we need a WORD sized Var. The LED timer though can be BYTE sized. Lastly we need a flag to indicate if the timer is needed to run and when it has timed up.  INPUT_TMR_RUNNING. The code to implement this is dead simple.

IF INPUT_TMR_RUNNING = TRUE THEN ' IS THIS TIMER RUNNING
        DEC INPUT_TMR ' DEC THE TIMER
       IF INPUT_TMR = 0 THEN INPUT_TMR_RUNNING = FALSE' IF WE ARE AT ZERO INDICATE SO
 ENDIF 

In use, you simply load the timer with the required time in 100thís of a second. Then set it running with INPUT_TMR_RUNNING = TRUE. Its then only requires you to check the status of the flag to tell when times up.

Thatís the timer taken care of now for the LED. Some thing not discussed is the fact that we will have system Modes, STANDBY, PASSWORD_ENTRY and NEW_PASSWORD_ENTRY. These modeís will be indicated by the flash rate of the LED. As you have seen above we have allocated a timer to control how long the LED is ON or OFF. When the timer reaches 0 it looks at LED_TOGGLE_STATE. If itís set it un-sets it, turns off the LED and loads the value in LED_OFF into the LED_TMR. If itís un-set it resets it, turns on the led and loads in the value in LED_ON. By loading LED_ON and LED_OFF with appropriate values,  the led will flash away all by itís self in the background.

Thatís the theory lets see the code

DEC LED_TMR ' DEC THE TIMER
 IF LED_TMR = 0 THEN ' IF ZERO
     IF LED_TOGGLE_STATE = TRUE THEN ' TOGGLE THE STATE
         IF LED_FORCED_OFF = FALSE THEN LED = ON
             LED_TMR = LED_ON ' LOAD THE TIMER
             LED_TOGGLE_STATE = FALSE ' TOGGLE STATE
         ELSE
             LED = OFF
             LED_TMR = LED_OFF ' LOAD THE TIMER
             LED_TOGGLE_STATE = TRUE ' TOGGLE STATE
         ENDIF
      ENDIF
 ENDIF   

There is one problem though and thatís what happens when you want no LED flashes, as it will always flash for 100th of a second at the very least. So we have to force it off if required with another flag.

IF LED_FORCED_OFF = FALSE THEN LED = ON  

PASSWORD ENTRY

Now to the password entry routine or PASSWORD_ ACCUMULATOR routine as I called it in the code. It works by checking