• Picģ Basic




  • PASSWORD CONTROLLED ENTRY MODULE

    By: Tim Box


    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_K EY,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 for a key press, if a new number has been entered it adds it to the password array. Lastly it checks how many entries have been made and sets a flag if itís added enough.

    PASSWORD_ACCUMULATOR:

    GOSUB CHK_KEY_0_9 ' CHECK FOR KEYS 0 - 9
    IF KEY_STATUS = TRUE THEN ' DID WE SEE ONE?
    GOSUB LOAD_INPUT_TIMER ' YES SO RELOAD INPUT TIMER
    PASSWORD_STR[PASSWORD_POINTER] = KEY ' SAVE THE ENTERED NUMBER IN THE ARRAY
    INC PASSWORD_POINTER ' UP THE POINTER
    IF PASSWORD_POINTER > MAX_NO_NOS-1 THEN ' HAVE WE EXCEEDED THE MAXIMUM NUMBER OF CHARS
    PASSWORD_ENTERED = TRUE ' YES SO THATS A WHOLE PASSWORD
    ENDIF
    ENDIF ' NO SO BACK TO STANDBY MODE
    RETURN ' ALL DONE SO RETURN

    PASSWORD COMPARISON

    At some stage we have to compare the entered password with that stored in Eeprom. This is a relatively simple procedure of comparing each character in turn in the password array with that in the Eeprom. This is done in a couple of REPEAT LOOPS comparing each password set in turn against that stored. If it finds a match it does not clear the passed flag and returns

    PASSWORD_COMPARE:
    EEPASSWORD_ADDRESS = 0
    REPEAT ' CHECK ALL THE PASSWORDS STORED
    PASSWORD_OK = TRUE ' WE NEED TO PRE LOAD THIS FLAG FOR THE ROUTINE TO WORK
    CLEAR OFFSET
    REPEAT ' LOOP THROUGH ALL THE CHARS OF THE NUMBER
    EEPASSWORD_DATA = EREAD EEPASSWORD_ADDRESS + OFFSET ' FIRST READ IN THE EEPROM STORE PASSWORD
    IF PASSWORD_STR[OFFSET] <> EEPASSWORD_DATA THEN ' THEN COMPARE IT AGAINST THE ONE ON OFFER
    PASSWORD_OK = FALSE ' THIS CHAR DOES NOT MATCH SO CHECK AGAINST NEXT ONE
    ENDIF
    INC OFFSET
    UNTIL OFFSET = MAX_NO_NOS
    IF PASSWORD_OK = TRUE THEN ' A MATCH HAS BEEN FOUND!
    PASSWORD = EEPASSWORD_ADDRESS / MAX_NO_NOS ' WORK OUT A NUMBER VALUE FOR WHICH PASSWORD PASSED I.E. MASTER OR USER
    RETURN
    ENDIF
    EEPASSWORD_ADDRESS = EEPASSWORD_ADDRESS + MAX_NO_NOS
    UNTIL EEPASSWORD_ADDRESS = MAX_NO_NOS * NO_PASSWORDS
    RETURN

    MODE CONTROL

    As I mentioned above there are three modes, these modes are indicated by the flash pattern of the led. Standby has no flashing, password_entry flashes about every half a second and new_password_entry every 10 th of a second. As these modes are based on the reload value of the led timers, to change mode you simply change these settings. There have been though a couple of tricks required to make it work properly. Firstly to ensure you can safely reload the timers you have to prevent an interrupt occurring. Also note the forcing of the timer to 1 to ensure the new values are loaded in straight away, and the forcing of the toggle state to that required to light the LED almost instantly.

    MODE_CONTROL:
    GIE = ' TURN OFF GLOBAL INTERRUPTS
    WHILE GIE = 1 : GIE = 0 : WEND ' AND MAKE SURE THEY ARE OFF
    IF MODE = PASSWORD_ENTRY THEN
    LED_FORCED_OFF = FALSE ' ENABLE THE LED TO LIGHT
    LED_ON = P_ENTRY_ON_TIME ' LOAD THE RELOAD VARS WITH THE
    LED_OFF = P_ENTRY_OFF_TIME ' PASSWORD ENTRY LED FLASH TIMES
    LED_TMR = 1 ' FORCE A RELOAD NEXT INTERRUPT
    LED_TOGGLE_STATE = 1
    ENDIF
    ÖÖ
    Ö

    MAIN CONTROL LOOPS

    Two control loops are used, the first while waiting for a key press and when entering a standard or master password, and the second just when entering a new password.
    Main_loop runs in two modes, waiting for a keypress (standby) and password being entered (password_entry). Something to note is the fact that on receiving a key press the new_key flag is reset as it would other wise require two key presses before the password accumulator actually started filling up. Once in password entry mode the input timer running flag and the cancel key is checked. If either is not as it should be, standby mode is re-entered. At some stage the password accumulator will inform you that there have been enough keys pressed to make a password. We now get the numbers checked and make a decision on the result of this check. Passed as STANDARD and were off to that routine, MASTER and we go to enter a new password. The last scenario is you get the number wrong, so we send it back to standby mode.
    New_password_entry runs in one mode only and expects only one thing, thatís a new password. Obviously it quits back to Standby if you take to long pressing the buttons. Entering the correct number of digits again is flagged, causing the numbers to be checked against that in Eeprom, only this time you have to enter a password not being currently used. Being successful at this results in this new number being written to Eeprom. Either way, correct password or not you are returned to Standby at the end of it all.

    SUMMARY

    What I hope you have gained from this project is the importance of key press latching, the ease with which an interrupt routine can be written, and used to generate software timers. With out either of these this program would have been harder to code and at lot larger. Also note the use of the number/character accumulator, a very simple routine to implement. I'm sure you will find a number of uses for it in your own code. Lastly I hope you will saw there are no numbers used with in the body of the code, every value has been aliased, so increasing/decreasing the number of chars in the password only takes one change. No need to hunt through the code looking for all the references to it.

    MODIFICATIONS

    You might want to make noises whenever a key is pressed, simply replace the DELAYMS in the key scan routine with a SOUNDER or FREQOUT statement. Success failure indications can be added to the MODE_CONTROL routines.
    To alter the flash rates, look through the dims, itís all there.
    As this device lends itís self to being battery powered, you could easily send the device to sleep when entering standby, leaving it setup to wake on a change to the inputs on portb.
    I hope you found this project of use and will come back with one of your own.
    Tim
    November 2003

    About the Proton Compiler

    Crownhill's Proton Plus Compiler is a part of the Proton Development Suite - A suite of British-developed applications enabling fast development of PICģ micro's using the PICģ BASIC Language.
    For more information on the Proton Development Suite, please visit www.picbasic.org