This is a demonstration program which draws a keypad on the LCD and uses the touch screen to allow an operator to key-in an access code. Two possible codes are allowed to grant to different levels of access. Additionally the push button is used to allow an operator to send Moorse Code pulses in to the device which is checked against two characters to determine if there was a match, and if so, access is granted.

Dependencies:   LCD_DISCO_F429ZI mbed TS_DISCO_F429ZI mbed-os BSP_DISCO_F429ZI

Also draws a mushroom on the screen and animates it.

Revision:
0:04d4cc695e56
Child:
1:316582aec4fb
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SecurityUnlockDemo-Main.cpp	Sat Jun 01 05:32:16 2019 +0000
@@ -0,0 +1,641 @@
+
+// ----------------------------------------------------------------------
+// SecurityUnlockDemo-Main.cpp
+//
+// Fredric L. Rice, May 2019
+//
+// This is a demonstration of how to build a keypad on the liquid 
+// crystal display and then use the touch screen as a keypad to enter
+// an access code which grants access to other functionality. Two
+// levels of access is possible, with the more permissive access a
+// longer series of digits than the lesser-permissive access code.
+//
+// The keypad is built using position information in a table with some
+// fairly easy ways to move the keypad around on the display.
+//
+// The demonstration code also allows for the push button to be used
+// to key in Moorse Code pulses which are timed and then checked to
+// see if they match an expected access code for Moorse access.
+//
+// ----------------------------------------------------------------------
+
+#include "mbed.h"                       // The mbed operating system
+#include "LCD_DISCO_F429ZI.h"           // For controlling the LCD
+#include "TS_DISCO_F429ZI.h"            // For controlling the touch screen
+#include "SecurityUnlockDemo-Main.h"    // Always include our own header
+
+// ----------------------------------------------------------------------
+// Defined constants that we will use here
+//
+// ----------------------------------------------------------------------
+
+// For some purposes we may want to know the dimentions of the screen
+#define LCD_WIDTH               240
+#define LCD_HEIGHT              320
+
+// We define this value to position the entire keypad on the screen
+#define KEYPAD_LEFT_MARGIN      42
+#define KEYPAD_TOP_MARGIN       60
+
+// The "line number" on the display to show the keys that get entered
+#define ENTERED_KEYS_LINE       18
+
+// We allow the operator to enter a maximum number of digits for
+// the access code
+#define MAX_SECURITY_DIGITS     15
+
+// We allow a maximum number of Moorse Code characters to be entered
+// using the push button
+#define MAX_MOORSE_PULSES       10
+
+// In this demonstration we define two access levels, access level 1
+// grants full access to all functionality whereas access level 2 is
+// for accessing less functionality
+static const uint8_t * ACCESS_LEVEL_1 = "31415926";
+static const uint8_t * ACCESS_LEVEL_2 = "2040";
+
+// For the Moorse Code access, we describe the dots and dashes which
+// describe the letters C and Q. We do not care about testing for a
+// period of quiet beteen characters, we ignore silence, we only
+// test the length of when the key is held down
+static const uint8_t * ACCESS_MOORSE  = "-.-.--.-";
+
+// ----------------------------------------------------------------------
+// Define local data storage
+//
+// ----------------------------------------------------------------------
+
+    // We will be using the LCD so instantiate an object locally
+    static LCD_DISCO_F429ZI st_lcd;
+
+    // We will be using the touch screen so instantiate an object
+    static TS_DISCO_F429ZI st_touchScreen;
+    
+    // Instantiate a digitial input mapped as our push button
+    static DigitalIn st_pushButton(PA_0);
+    
+    // When we build the keypad screen, we use the data in this table
+    // to position the touch keys rather than use math to determine
+    // their locations. The reason why we do this is so that we can
+    // use this table to determine which key was pressed on the touch
+    // screen, but also we may want to place some keys offset from
+    // others. A table gives us greater control over key location.
+    // We place 10 pixels between each keypad.
+    static KeypadLocation_t st_KeypadInformation[] =
+    {
+        //                       X,                       Y,  H,  W, Character
+        { KEYPAD_LEFT_MARGIN +   0, KEYPAD_TOP_MARGIN +   0, 40, 40, '1' },
+        { KEYPAD_LEFT_MARGIN +  60, KEYPAD_TOP_MARGIN +   0, 40, 40, '2' },
+        { KEYPAD_LEFT_MARGIN + 120, KEYPAD_TOP_MARGIN +   0, 40, 40, '3' },
+        { KEYPAD_LEFT_MARGIN +   0, KEYPAD_TOP_MARGIN +  60, 40, 40, '4' },
+        { KEYPAD_LEFT_MARGIN +  60, KEYPAD_TOP_MARGIN +  60, 40, 40, '5' },
+        { KEYPAD_LEFT_MARGIN + 120, KEYPAD_TOP_MARGIN +  60, 40, 40, '6' },
+        { KEYPAD_LEFT_MARGIN +   0, KEYPAD_TOP_MARGIN + 120, 40, 40, '7' },
+        { KEYPAD_LEFT_MARGIN +  60, KEYPAD_TOP_MARGIN + 120, 40, 40, '8' },
+        { KEYPAD_LEFT_MARGIN + 120, KEYPAD_TOP_MARGIN + 120, 40, 40, '9' },
+        { KEYPAD_LEFT_MARGIN +   0, KEYPAD_TOP_MARGIN + 180, 40, 40, 'C' },
+        { KEYPAD_LEFT_MARGIN +  60, KEYPAD_TOP_MARGIN + 180, 40, 40, '0' },
+        { KEYPAD_LEFT_MARGIN + 120, KEYPAD_TOP_MARGIN + 180, 40, 40, 'E' },
+        {                        0,                       0,  0,  0, '+' }   // End of table
+    } ;
+    
+    // We allow a maximum number of keys to be entered for the access code
+    static uint8_t au8_enteredKeys[MAX_SECURITY_DIGITS + 1];
+    
+    // We keep track of the number of digits entered for the access code
+    static uint8_t u8_enteredKeyCount;
+    
+    // To drive the Moorse Code access, we maintain counters which keep
+    // trash of how long the push button has been detected to be held
+    // down. Since the button is checked 10 times a second, the count
+    // indicates about how many milliseconds the button was held down.
+    // If it wraps, we don't care since that means the operator is holding
+    // the button down for a very long time
+    static uint16_t u16_buttonDownCount;
+    
+    // To determine whether the operator is finished with entering a
+    // Moorse Code access code, we maintain a counter of "up" time, 
+    // a.k.a. quiet time.
+    static uint16_t u16_buttonUpCount;
+    
+    // When Moorse Code pulses are entered, we store the down time for
+    // else pulse in this array
+    static uint16_t au16_moorseCharacters[MAX_MOORSE_PULSES];
+    
+    // As Moorse Code pulses are entered with the push button, we keep
+    // track of how many down presses there have been
+    static uint8_t u8_moorsePulseCount;
+
+// ----------------------------------------------------------------------
+// MainInit()
+//
+// This function will:
+//      o Initialize the module's locally-held data
+//      o Clear the LCD and set the display to WHITE
+//      o Set the default character font size
+//      o Initialize the touch screen by polling the size of the LCD
+//      o Sets the push button to have no pull-up
+//
+// ----------------------------------------------------------------------
+static void MainInit(void)
+{
+    uint8_t u8_TouchScreenStatus = 0;
+    
+    // Initialize locall-held data in this module
+    u8_enteredKeyCount    = 0;
+    u16_buttonDownCount   = 0;
+    u16_buttonUpCount     = 0;
+    u8_moorsePulseCount   = 0;
+    
+    // Bring the LCD up 
+    st_lcd.Clear(LCD_COLOR_WHITE);
+    
+    // Set the default font size
+    BSP_LCD_SetFont(&Font16);
+    
+    // Initialize the touch screen
+    u8_TouchScreenStatus = st_touchScreen.Init(st_lcd.GetXSize(), st_lcd.GetYSize());
+    
+    if (TS_OK != u8_TouchScreenStatus)
+    {
+        st_lcd.DisplayStringAt(1, LINE(ENTERED_KEYS_LINE), 
+            (uint8_t *)"Touch screen failed", CENTER_MODE);    
+    }
+    
+    // Set the push button to not have an internal pull-up or down
+    st_pushButton.mode(PullNone);
+}
+
+// ----------------------------------------------------------------------
+// MainDisplayEnteredKeys()
+//
+// This function will display the entered keys, if any, on the display
+// on the line defined for the entered keys. It will center the 
+// characters.
+//
+// ----------------------------------------------------------------------
+static void MainDisplayEnteredKeys(void)
+{
+    // Display the accumulated security code digits
+    st_lcd.DisplayStringAt(1, LINE(ENTERED_KEYS_LINE), au8_enteredKeys, CENTER_MODE);
+}
+
+// ----------------------------------------------------------------------
+// MainGrantAccess()
+//
+// This function is invoked to display what access level has been 
+// granted.
+//
+// ----------------------------------------------------------------------
+static void MainGrantAccess(uint8_t u8_thisAccessLevel)
+{
+    uint8_t au8_reportString[21] = { 0 };
+    
+    // Clear the display 
+    st_lcd.Clear(LCD_COLOR_WHITE);
+    
+    // Build a report to offer    
+    (void)sprintf((char *)au8_reportString, "Level %u", u8_thisAccessLevel);
+    
+    // Display the level of access that was granted
+    st_lcd.DisplayStringAt(1, LINE(1), (uint8_t *)"Access granted", CENTER_MODE);
+    st_lcd.DisplayStringAt(1, LINE(2), au8_reportString, CENTER_MODE);
+    st_lcd.DisplayStringAt(1, LINE(3), (uint8_t *)"Press RESET to exit", CENTER_MODE);
+}
+
+// ----------------------------------------------------------------------
+// MainProcessEntryCode()
+//
+// This function will:
+//      o Check the size of Level 1 access to see if it matches the
+//        number of characters that were entered
+//      o Check to see if the Level 1 access code matches the digits
+//        entered
+//      o Grant Access Level 1 if both the size and the digits match
+//
+//      o Check the size of Level 2 access to see if it matches the
+//        number of characters that were entered
+//      o Check to see if the Level 2 access code matches the digits
+//        entered
+//      o Grant Access Level 2 if both the size and the digits match
+//
+// ----------------------------------------------------------------------
+static void MainProcessEntryCode(uint8_t u8_originalKeyCount)
+{
+    // See if the access code that was entered is level 1
+    if (strlen((char *)ACCESS_LEVEL_1) == u8_originalKeyCount)
+    {
+        // Do the digits entered match the level 1 access code?
+        if (! memcmp(ACCESS_LEVEL_1, au8_enteredKeys, u8_originalKeyCount))
+        {
+            // It does so grant access level 1
+            MainGrantAccess(1);            
+            return;
+        }
+    }
+    
+    // That did not grant access, see if the entered value is level 2
+    if (strlen((char *)ACCESS_LEVEL_2) == u8_originalKeyCount)
+    {
+        // Do the digits entered match the level 2 access code?
+        if (! memcmp(ACCESS_LEVEL_2, au8_enteredKeys, u8_originalKeyCount))
+        {
+            // It does so grant access level 1
+            MainGrantAccess(2);
+            return;
+        }
+    }
+}
+
+// ----------------------------------------------------------------------
+// MainProcessKeypadKey()
+//
+// This function will:
+//      o Store the number of characters that have been entered, if any
+//
+//      o Check to see if the character that is passed to the function
+//        is a 'C' for Clear
+//      o Clear the line of entered digits, if any
+//      o Set the acquired character count to zero
+//
+//      o Check ti see if the character entered is an 'E' for Enter
+//      o Clear the line of any entered digits, if any
+//      o Set the acquired character count to zero
+//      o Call a function which evaluates the digits that have been
+//        entered, if any
+//
+//      o Checks ti see if there is room to store the newly-entered
+//        digit in the accumulation buffer
+//      o Stores the character in to the buffer andincrements the
+//        entered digit counter
+//      o Ensures that the string of entered digits is NULL terminated
+//      o Calls a function which displays the entered digits
+//
+// ----------------------------------------------------------------------
+static void MainProcessKeypadKey(uint8_t  u8_thisKeyASCIICharacter)
+{
+    uint8_t u8_originalKeyCount = u8_enteredKeyCount;
+    
+    // Is the key a C for Clear?
+    if ('C' == u8_thisKeyASCIICharacter)
+    {
+        // It is, so clear the display line of accumulated characters
+        st_lcd.ClearStringLine(ENTERED_KEYS_LINE);
+        
+        // Discard our accumulated digit count
+        u8_enteredKeyCount = 0;
+    }
+    
+    // Is the character that was pressed en E for Enter?
+    else if ('E' == u8_thisKeyASCIICharacter)
+    {
+        // It is, so before we process the code, clear the entered digits
+        st_lcd.ClearStringLine(ENTERED_KEYS_LINE);
+        
+        // Discard our accumulated digit count
+        u8_enteredKeyCount = 0;
+        
+        // Process the entry code
+        MainProcessEntryCode(u8_originalKeyCount);
+    }
+    
+    // Anything else we assume is a numertic digit
+    else
+    {
+        // Do we have room for more digits?
+        if (u8_enteredKeyCount < MAX_SECURITY_DIGITS)
+        {
+            // Store the entered digit in to the accumulated key buffer
+            au8_enteredKeys[u8_enteredKeyCount++] = u8_thisKeyASCIICharacter;
+            
+            // Make sure that the character string is NULL terminated
+            au8_enteredKeys[u8_enteredKeyCount] = 0x00;
+    
+            // Update the display with the new key value
+            MainDisplayEnteredKeys();
+        }
+    }
+}
+
+// ----------------------------------------------------------------------
+// MainHandleKeyPress()
+//
+// When a key is pressed, the X and Y coordinates of the position where
+// the LCD was touched gets passed to this function. 
+//
+// The function steps throug each of the keys defined in the keypad
+// map, checking an area bounded by the beginning X and Y coordinates
+// of the keys, and by that position plus the height and width of 
+// trhe key.
+//
+// If the touch screen position that was touched matches the area of
+// a known key, a function is called to process the new key.
+//
+// If a position of the screen was touched that does not match any
+// known key position, the function ignores the screen touch.
+//
+// ----------------------------------------------------------------------
+static void MainHandleKeyPress(uint16_t u16_screenX, uint16_t u16_screenY)
+{
+    uint16_t u16_keyMinimumX = 0;
+    uint16_t u16_keyMaximumX = 0;
+    uint16_t u16_keyMinimumY = 0;
+    uint16_t u16_keyMaximumY = 0;
+    uint8_t  u8_keyPadItem   = 0;
+    
+    // Step through the keys to check until we reach an entry that's zero
+    while(0 != st_KeypadInformation[u8_keyPadItem].u16_screenXLocation)
+    {
+        // Calculate the boundaries of this key
+        u16_keyMinimumX = st_KeypadInformation[u8_keyPadItem].u16_screenXLocation;
+        u16_keyMaximumX = u16_keyMinimumX + st_KeypadInformation[u8_keyPadItem].u16_keyHeight;
+        u16_keyMinimumY = st_KeypadInformation[u8_keyPadItem].u16_screenYLocation;
+        u16_keyMaximumY = u16_keyMinimumY + st_KeypadInformation[u8_keyPadItem].u16_keyWidth;
+
+        // Is this the key that was pressed?
+        if (u16_screenX > u16_keyMinimumX && u16_screenX < u16_keyMaximumX)
+        {
+            if (u16_screenY > u16_keyMinimumY && u16_screenY < u16_keyMaximumY)
+            {
+                // This is the key that was pressed
+                MainProcessKeypadKey(st_KeypadInformation[u8_keyPadItem].u8_keyASCIICharacter);
+                
+                // We are finished searching
+                break;
+            }
+        }
+        
+        // Check the next possible key area
+        u8_keyPadItem++;
+    }
+}
+
+// ----------------------------------------------------------------------
+// MainProcessButtonDownThenRelease()
+//
+// After the push button has been used to send a down pulse, this
+// function is called to store the down time, if there is room to
+// store it.
+//
+// ----------------------------------------------------------------------
+static void MainProcessButtonDownThenRelease(void)
+{
+    // Make sure that we have a valid button down timer
+    if (u16_buttonDownCount > 0)
+    {
+        // Is there room to store another pulse period?
+        if (u8_moorsePulseCount < MAX_MOORSE_PULSES)
+        {
+            // There is, so store the down counter timer
+            au16_moorseCharacters[u8_moorsePulseCount++] = u16_buttonDownCount;
+        }
+    }
+}
+
+// ----------------------------------------------------------------------
+// MainProcessMoorseDigits()
+//
+// After the push button has been used to send Moore pulses, and after
+// the button has not been pressed for 3 seconds, this function gets 
+// called to process the pulses.
+//
+// We consider a value of 5 or higher to be 500 milliseconds down, or
+// a "dash." Any down pulse shorter than that is considere to be a "dot."
+//
+// ----------------------------------------------------------------------
+static void MainProcessMoorseDigits(void)
+{
+    uint8_t u8_testLoop          = 0;
+    uint8_t au8_reportString[21] = { 0 };
+    uint8_t au8_moorseString[21] = { 0 };
+    
+    // Since we have pulses, clear the display to get ready for a report
+    st_lcd.Clear(LCD_COLOR_WHITE);
+
+    for (u8_testLoop = 0; u8_testLoop < u8_moorsePulseCount; u8_testLoop++)
+    {
+        // Build a report of the times that we measured
+        (void)sprintf((char *)au8_reportString, "Time: %u", 
+            au16_moorseCharacters[u8_testLoop]);
+        
+        // Was the time down greater than 500 milliseconds?
+        if (au16_moorseCharacters[u8_testLoop] > 5)
+        {
+            // It was, so we consider that to be a dash
+            au8_moorseString[u8_testLoop] = '-';
+        }
+        else
+        {
+            // It was not so we consider that to be a dot
+            au8_moorseString[u8_testLoop] = '.';
+        }
+        
+        // Display the times that we measured
+        st_lcd.DisplayStringAt(1, LINE(u8_testLoop + 1), au8_reportString, LEFT_MODE);
+    }
+    
+    // Make sure that the string of Moorse Code pulses is NULL terminated
+    au8_moorseString[u8_testLoop] = 0x00;
+    
+    // Build a report showing what the Moorse Code looks like    
+    (void)sprintf((char *)au8_reportString, "Moorse code: %s", au8_moorseString);
+    
+    // Display what the Moorse Code looked like
+    st_lcd.DisplayStringAt(1, LINE(u8_testLoop + 1), au8_moorseString, LEFT_MODE);
+
+    // Is that enough pulses to satisfy all required Moorse pulses?
+    if (u8_moorsePulseCount == strlen((char *)ACCESS_MOORSE))
+    {
+        // It was, are the pulses what we expect for Level 1 access?
+        if (! memcmp(au8_moorseString, ACCESS_MOORSE, u8_moorsePulseCount))
+        {
+            st_lcd.DisplayStringAt(1, LINE(u8_testLoop + 2), 
+                (uint8_t *)"Access granted", LEFT_MODE);
+            
+            // Leave everything up on the screen for 5 seconds
+            wait(5.0);
+            
+            // Grant access level 1
+            MainGrantAccess(1);            
+        }
+    }
+}
+
+// ----------------------------------------------------------------------
+// MainScanPushButton()
+//
+// This function will check to see what the state of the push button
+// is, and it will drive the Moorse Code entry access functionality.
+//
+// A push button held down for 500 milliseconds or longer is considered
+// to be a "dash." And down time shorter than that is considered to be
+// a "dot." After 3 seconds of quiet time after a down time has been
+// detected, we consider the operator to have finished pulsing in all
+// Moorse Code pulses.
+//
+// ----------------------------------------------------------------------
+static void MainScanPushButton(void)
+{
+    // Acquire / poll the state of the push button
+    int i_buttonState = st_pushButton.read();
+    
+    // Is the button not being pressed?
+    if (0 == i_buttonState)
+    {
+        // The button is not down, was the button down before?
+        if (u16_buttonDownCount > 0)
+        {
+            // The push button was down and now it has been released.           
+            // Process the button down time
+            MainProcessButtonDownThenRelease();
+        }
+        
+        // Add another count to the time that the button has been up
+        u16_buttonUpCount++;
+        
+        // Has the button been up for at least 3 seconds? Since
+        // we are called 10 times a second, we check for 3 counts.
+        // We only check if there has been at least 1 down pulse.
+        if (u8_moorsePulseCount > 0 && u16_buttonUpCount >= 30)
+        {
+            // The operator appears to be finished with entering 
+            // the Moorse digits so process what may have come in
+            MainProcessMoorseDigits();
+                
+            // Discard all Moorse down pulses we may have accumulated
+            u8_moorsePulseCount = 0;
+        }
+            
+        // Discard the button down counter
+        u16_buttonDownCount = 0;
+     }
+    else
+    {
+        // The button is down so count how long it is down
+        u16_buttonDownCount++;
+        
+        // Discard any button on time
+        u16_buttonUpCount = 0;
+    }
+}
+
+// ----------------------------------------------------------------------
+// MainThread()
+//
+// Called ten times a second.
+//
+// This function will:
+//      o Get the state of the touch screen
+//      o Check to see if a touch screen touch was detected
+//      o Extract the X and Y coordinates of the screen touch
+//      o Call a function which will handle the screen touch
+//      o Call a function to monitor the push button to drive the
+//        Moorse Code access functionality
+//
+// ----------------------------------------------------------------------
+static void MainThread(void)
+{
+    uint16_t        u16_screenX          = 0;
+    uint16_t        u16_screenY          = 0;
+    TS_StateTypeDef st_touchScreenState;
+    
+    // Get the status of the Touch Screen interface
+    st_touchScreen.GetState(&st_touchScreenState);
+        
+    // Has the screen been touched?
+    if (st_touchScreenState.TouchDetected)
+    {
+        // It has been, extract the X and Y coordinates touched
+        u16_screenX = st_touchScreenState.X;
+        u16_screenY = st_touchScreenState.Y;
+        
+        // Call the routine which handles the key press
+        MainHandleKeyPress(u16_screenX, u16_screenY);
+    }
+    
+    // Drive the Moorse Code access functionality, scanning the
+    // push button 10 times a second
+    MainScanPushButton();
+}
+
+// ----------------------------------------------------------------------
+// MainDrawKeypad()
+//
+// This function will set the LCD background to WHITE and set the 
+// default text color to BLUE, thehn it will display information about
+// what theoperator should do, then the keypad is constructed on the
+// display using the information in the keypad map.
+//
+// ----------------------------------------------------------------------
+static void MainDrawKeypad(void)
+{
+    uint8_t u8_keyPadItem = 0;
+    
+    // For the keypad, we want the entire screen to be this color
+    st_lcd.SetBackColor(LCD_COLOR_WHITE);
+    
+    // For the keypad's general text we want characters to be this color
+    st_lcd.SetTextColor(LCD_COLOR_BLUE);
+    
+    // Build the ketypad display
+    st_lcd.DisplayStringAt(1, LINE(0), (uint8_t *)"Enter access code or", CENTER_MODE);
+    st_lcd.DisplayStringAt(1, LINE(1), (uint8_t *)"use the Moorse Code", CENTER_MODE);
+    st_lcd.DisplayStringAt(1, LINE(2), (uint8_t *)"push button to unlock", CENTER_MODE);
+    
+    // Step through the keys to plot until we reach an entry that's zero
+    while(0 != st_KeypadInformation[u8_keyPadItem].u16_screenXLocation)
+    {
+        // Draw the rectangle
+        st_lcd.FillRect(st_KeypadInformation[u8_keyPadItem].u16_screenXLocation,
+            st_KeypadInformation[u8_keyPadItem].u16_screenYLocation,
+            st_KeypadInformation[u8_keyPadItem].u16_keyHeight,
+            st_KeypadInformation[u8_keyPadItem].u16_keyWidth);
+
+        // Display the character near the lower right corner of the rectangle
+        st_lcd.DisplayChar(
+            st_KeypadInformation[u8_keyPadItem].u16_screenXLocation + 
+                (st_KeypadInformation[u8_keyPadItem].u16_keyHeight / 2) + 6,
+            st_KeypadInformation[u8_keyPadItem].u16_screenYLocation + 
+                (st_KeypadInformation[u8_keyPadItem].u16_keyWidth / 2) + 2,
+            st_KeypadInformation[u8_keyPadItem].u8_keyASCIICharacter);
+
+        // Go to the next keypad to create
+        u8_keyPadItem++;
+    }
+}
+
+// ----------------------------------------------------------------------
+// main()
+//
+// This is the main entry called by thembed operating system.
+//
+// This function will:
+//      o Initialize this module
+//      o Call the function that established the LCD screen and
+//        draws the keypad
+//      o Goes in to a forever loop that wakes up 10 times a second
+//      o Calls a function which drives the main task which scans the
+//        LCD touch screen for screen touches
+//
+// ----------------------------------------------------------------------
+int main(void)
+{
+    // Perform local module initialization, if any
+    MainInit();
+    
+    // When we start up we will draw the touch screen keypad
+    MainDrawKeypad();
+
+    // Enter in to a forever loop which wakes up once a second
+    while (true)
+    {
+        // Sleep for a tenth of a second
+        wait(0.1);
+   
+        // Call the main process
+        MainThread();
+    }
+}
+
+// End of file
+