An access controller for man doors at our facility. It receives Wiegand signals from a keypad/card reader and activates a relay to open the door. Access codes are stored in EEPROM. The active code list is updated from TFTP on a local server.

Dependencies:   24LCxx_I2C CardReader USBHOST

Files at this revision

API Documentation at this revision

Comitter:
acesrobertm
Date:
Mon Sep 25 19:02:40 2017 +0000
Commit message:
in process of moving networking code to non-blocking format

Changed in this revision

24LCxx_I2C.lib Show annotated file Show diff for this revision Revisions of this file
AsciiArt.cpp Show annotated file Show diff for this revision Revisions of this file
CardReader.lib Show annotated file Show diff for this revision Revisions of this file
CodeMemory.cpp Show annotated file Show diff for this revision Revisions of this file
CodeMemory.h Show annotated file Show diff for this revision Revisions of this file
ConfigurationManager.cpp Show annotated file Show diff for this revision Revisions of this file
ConfigurationManager.h Show annotated file Show diff for this revision Revisions of this file
DHCPOptions.cpp Show annotated file Show diff for this revision Revisions of this file
DHCPOptions.h Show annotated file Show diff for this revision Revisions of this file
NTPClient.cpp Show annotated file Show diff for this revision Revisions of this file
NTPClient.h Show annotated file Show diff for this revision Revisions of this file
TFTPClient.cpp Show annotated file Show diff for this revision Revisions of this file
TFTPClient.h Show annotated file Show diff for this revision Revisions of this file
USBHOST.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed-os.lib Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r a56239ae90c2 24LCxx_I2C.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/24LCxx_I2C.lib	Mon Sep 25 19:02:40 2017 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/acesrobertm/code/24LCxx_I2C/#5df584fbabfe
diff -r 000000000000 -r a56239ae90c2 AsciiArt.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AsciiArt.cpp	Mon Sep 25 19:02:40 2017 +0000
@@ -0,0 +1,46 @@
+
+#include <cstdio>
+
+using namespace std;
+
+void asciiAces()
+{
+    //printf("      .o.         .oooooo.   oooooooooooo  .oooooo..o \n");
+    //printf("     .888.       d8P'  \`Y8b  \`888'     \`8 d8P'    \`Y8 \n");
+    //printf("    .8\"888.     888           888         Y88bo.      \n");
+    //printf("   .8' \`888.    888           888oooo8     \`\"Y8888o.  \n");
+    //printf("  .88ooo8888.   888           888    \"         \`\"Y88b \n");
+    //printf(" .8'     \`888.  \`88b    ooo   888       o oo     .d8P \n");
+    //printf("o88o     o8888o  \`Y8bood8P'  o888ooooood8 8\"\"88888P'  \n");
+    
+    return;
+}
+
+void asciiFlag()
+{
+    printf("  _\n");
+    printf(" (_)\n");
+    printf("(___)\n");
+    printf(" | |______\n");
+    printf(" | |* * * )\n");
+    printf(" | | * * (_________\n");
+    printf(" | |* * * |* *|####)\n");
+    printf(" | | * * *| * |   (______________\n");
+    printf(" | |* * * |* *|####|############|\n");
+    printf(" | | * * *| * |    |            |\n");
+    printf(" | |* * * |* *|####|############|\n");
+    printf(" | |~~~~~~| * |    |            |\n");
+    printf(" | |######|* *|####|############|\n");
+    printf(" | |      |~~~\'    |            |\n");
+    printf(" | |######|########|############|\n");
+    printf(" | |      |        |            |\n");
+    printf(" | |######|########|############|\n");
+    printf(" | |~~~~~~|        |            |\n");
+    printf(" | |      |########|############|\n");
+    printf(" | |      \'~~~~~~~~|            |\n");
+    printf(" | |               |############|\n");
+    printf(" | |               \'~~~~~~~~~~~~~\n");
+    printf(" | |\n");
+
+    return;
+}
diff -r 000000000000 -r a56239ae90c2 CardReader.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CardReader.lib	Mon Sep 25 19:02:40 2017 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/acesrobertm/code/CardReader/#47807713b37d
diff -r 000000000000 -r a56239ae90c2 CodeMemory.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CodeMemory.cpp	Mon Sep 25 19:02:40 2017 +0000
@@ -0,0 +1,326 @@
+
+#include "CodeMemory.h"
+
+void CodeMemory::EEPROMClear()
+{
+  for (int i = 0 ; i < EEPROM_LENGTH ; i += 4)
+  {
+    _eeprom->Write(i, (int)0, C24LCXX_I2C::LittleEndian);
+  }
+}
+
+unsigned short CodeMemory::ReadAccessCode(unsigned short address)
+{
+  // Read two bytes at the specified address
+  short code;
+  _eeprom->Read((short)address, &code, C24LCXX_I2C::LittleEndian);
+ 
+  return code; 
+}
+
+void CodeMemory::WriteAccessCode(unsigned short address, unsigned short value)
+{
+  //Write the bytes into the eeprom memory.
+  _eeprom->Write(address, (short)value, C24LCXX_I2C::LittleEndian);
+  
+  return;
+}
+
+unsigned int CodeMemory::ReadEEPROMULong(unsigned short address)
+{
+  // Read four bytes at the specified address.
+  int value;
+  _eeprom->Read((short)address, &value, C24LCXX_I2C::LittleEndian);
+ 
+  return value; 
+}
+
+void CodeMemory::WriteEEPROMULong(unsigned short address, unsigned int value)
+{
+  _eeprom->Write((short)address, (int)value, C24LCXX_I2C::LittleEndian);
+  
+  return;
+}
+
+// Find the address of the specified code
+int CodeMemory::FindAccessCode(unsigned short code)
+{
+  return FindAccessCode(code, 0, CODE_TABLE_SIZE);
+}
+
+int CodeMemory::FindAccessCode(unsigned short code, unsigned short startAddress, unsigned short stopAddress)
+{
+  int address = startAddress;
+  while ((address < stopAddress) && (ReadAccessCode(address) != code))
+  {
+    address += 2;
+  }
+  
+  if (address >= stopAddress)
+  {
+    address = -1;
+  }
+  
+  return address; 
+}
+
+// **************************************************
+// EEPROM Access Code Functions
+// **************************************************
+
+int CodeMemory::ActivateAccessCode(unsigned short value)
+{
+  int address = FindAccessCode(value);
+  if (address >= 0)
+  {
+    return -1; // The code already exists in the list.
+  }
+  
+  // Find the address of the next available 16 bit code slot (zero valued)
+  address = FindAccessCode(0);
+  if (address >= 0)
+  {
+    WriteAccessCode(address, value);
+  }
+  else // Memory Full
+  {
+    address = -2;
+  }
+  
+  return address;
+}
+
+int CodeMemory::DeactivateAccessCode(unsigned short value)
+{
+  int address = FindAccessCode(value);
+  if (address < 0)
+  {
+    return -1; // The code was not found
+  }
+  else
+  {
+    // Erase the two bytes of data from eeprom
+    _eeprom->Write(address, (short)0, C24LCXX_I2C::LittleEndian);
+  }
+  
+  return address;
+}
+
+void CodeMemory::PrintAllAccessCodes()
+{
+  int address = 0;
+  while (address < CODE_TABLE_SIZE + SPECIAL_CODE_TABLE_SIZE)
+  {
+    unsigned int code = ReadAccessCode(address);
+    if (code > 0)
+    {
+      printf("%d - %d\n", address, code);
+    }
+    address += 2;
+  }
+  
+  return;
+}
+
+void CodeMemory::SyncAccessCodes(unsigned short* codeList, unsigned short codeCount)
+{
+    // Deactivate codes that don't exist in the new set.
+    for (int address = 0; address < CODE_TABLE_SIZE; address += 2)
+    {
+        unsigned short code = ReadAccessCode(address);
+        
+        if (code > 0)
+        {
+            // Search the new code list for this code.
+            unsigned short* codePtr = std::find(codeList, codeList + codeCount, code);
+            if (codePtr == codeList + codeCount)
+            {
+                // Code not found.
+                WriteAccessCode(address, 0);
+            }
+        }
+    }
+    
+    // Activate all codes in the code list. Duplicates will be ignored.
+    for (int codeIndex = 0; codeIndex < codeCount; codeIndex++)
+    {
+        ActivateAccessCode(codeList[codeIndex]);
+    }
+    
+    return;
+}
+
+// **************************************************
+// EEPROM Special Command Code Functions
+// **************************************************
+
+bool CodeMemory::IsSpecialCode(unsigned short specialCodeIndex, unsigned short code, unsigned short defaultCode)
+{
+  // Check for code index out of range
+  if (specialCodeIndex >= (SPECIAL_CODE_TABLE_SIZE / 2))
+  {
+    return false;
+  }
+  
+  // Check test code value against the default code or eeprom custom code
+  else
+  {
+    unsigned short eepromValue = ReadAccessCode(CODE_TABLE_SIZE + (specialCodeIndex * 2));
+    return ((eepromValue == 0) && (code == defaultCode)) || (code == eepromValue);
+  }
+}
+
+bool CodeMemory::SetSpecialCode(unsigned short specialCodeIndex, unsigned short code)
+{
+  // Don't allow duplicate codes
+  if (FindAccessCode(code, CODE_TABLE_SIZE, CODE_TABLE_SIZE + SPECIAL_CODE_TABLE_SIZE) < 0)
+  {
+    printf("Changing special code at index %d to %d\n", specialCodeIndex, code);
+    
+    WriteAccessCode(CODE_TABLE_SIZE + (specialCodeIndex * 2), code);
+    return true;
+  }
+  else
+  {
+    printf("Failed to change special code: Code Exists\n"); 
+    return false; // Code already exists in the special code table
+  }
+}
+
+// **************************************************
+// EEPROM Event Log
+// **************************************************
+
+int CodeMemory::LogPointerAddress()
+{
+  return CODE_TABLE_SIZE + SPECIAL_CODE_TABLE_SIZE;
+}
+
+int CodeMemory::FirstEventLogAddress()
+{
+  return LogPointerAddress() + EVENT_LOG_POINTER_SIZE + EVENT_LOG_TIME_BASE_SIZE;
+}
+
+int CodeMemory::EventLogEndAddress()
+{
+  return EEPROM_LENGTH;
+}
+
+void CodeMemory::PrintEvent(unsigned short eventAddress)
+{
+  unsigned char memByte;
+  _eeprom->Read((short)eventAddress, &memByte);
+  printf("%u:%u:%u\n", ReadEEPROMULong(eventAddress + 3), memByte, ReadAccessCode(eventAddress + 1));
+  
+  return;
+}
+
+int CodeMemory::GetLatestEventAddress()
+{
+  return ReadAccessCode(LogPointerAddress()); // Get the location of the last event
+}
+
+int CodeMemory::GetNextEventAddress()
+{
+  return GetNextEventAddress(GetLatestEventAddress());
+} 
+
+int CodeMemory::GetNextEventAddress(unsigned short startAddress)
+{
+  int address = startAddress;
+  
+  if (address < FirstEventLogAddress())
+  {
+    // Empty log, so point to the first slot 
+    address = FirstEventLogAddress();
+  }
+  else if ((address + EVENT_SIZE + (EVENT_SIZE - 1)) < EventLogEndAddress())
+  {
+    // No wrap necessary
+    address += EVENT_SIZE;
+  }
+  else
+  {
+    // Wrap to the beginning of the log
+    address = FirstEventLogAddress();
+  }
+  
+  return address;
+}
+
+int CodeMemory::GetPreviousEventAddress(unsigned short startAddress)
+{
+  int address = startAddress;
+  
+  if ((address - EVENT_SIZE) >= FirstEventLogAddress())
+  {
+    // No wrap necessary
+    address -= EVENT_SIZE;
+  }
+  else // Wrap to the end of the log
+  {
+    int eventLogLength = ((EventLogEndAddress() - FirstEventLogAddress()) / EVENT_SIZE);
+    address = FirstEventLogAddress() + ((eventLogLength * EVENT_SIZE) - EVENT_SIZE); // Last event address
+  }
+  
+  return address;
+}
+
+void CodeMemory::WriteEvent(unsigned char eventType, unsigned short eventValue)
+{
+  unsigned int currentTime = time(NULL);
+  
+  // Determine the location of the next event
+  int eventAddress = GetNextEventAddress();
+  WriteAccessCode(LogPointerAddress(), eventAddress);
+  
+  // Write the event type byte
+  _eeprom->Write(eventAddress, eventType);
+  
+  // Write the event value integer
+  WriteAccessCode(eventAddress + 1, eventValue);
+  
+  // Write the timestamp
+  WriteEEPROMULong(eventAddress + 3, currentTime);
+  
+  // Send the event code to the supervisor for SQL storage
+  printf("Evt:%lu:%u:%u\n", currentTime, eventType, eventValue);
+  
+  return; 
+}
+
+void CodeMemory::PrintEventLog()
+{
+  int oldestEventAddress = GetNextEventAddress();
+  int eventAddress = oldestEventAddress;
+  
+  // Print all of the events, oldest to newest
+  do
+  {
+    PrintEvent(eventAddress);
+    
+    // Increment to the next event in the log
+    eventAddress = GetNextEventAddress(eventAddress);
+  } while (eventAddress != oldestEventAddress);
+  
+  return;
+}
+
+void CodeMemory::PrintRecentEvents()
+{
+  PrintRecentEvents(10);
+  return;
+}
+
+void CodeMemory::PrintRecentEvents(unsigned short numEvents)
+{
+  int eventAddress = GetLatestEventAddress();
+
+  for (int eventIndex = 0; eventIndex < numEvents; eventIndex++)
+  {
+    PrintEvent(eventAddress);
+    eventAddress = GetPreviousEventAddress(eventAddress); 
+  }
+
+  return; 
+}
\ No newline at end of file
diff -r 000000000000 -r a56239ae90c2 CodeMemory.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CodeMemory.h	Mon Sep 25 19:02:40 2017 +0000
@@ -0,0 +1,64 @@
+
+#ifndef CODE_MEMORY_H_
+#define CODE_MEMORY_H_
+
+#include <algorithm>
+#include "24LCxx_I2C/24LCxx_I2C.h"
+
+#define EEPROM_LENGTH 1024
+#define CODE_TABLE_SIZE 200 // bytes (codes * 2)
+#define SPECIAL_CODE_TABLE_SIZE 20 // bytes (codes * 2)
+
+#define EVENT_LOG_POINTER_SIZE 2 // bytes
+#define EVENT_LOG_TIME_BASE_SIZE 4 // bytes
+#define EVENT_SIZE 7 // bytes per event |1:event type|2:event value|4:ms timestamp|
+
+// Event Types
+#define EVENT_CARD_ACCESS_DENIED 1
+#define EVENT_CARD_ACCESS_GRANTED 2
+#define EVENT_KEYPAD_ACCESS_DENIED 3
+#define EVENT_KEYPAD_ACCESS_GRANTED 4
+#define EVENT_SPECIAL_CODE 5
+#define EVENT_SPECIAL_CHANGE 6
+#define EVENT_GATE_STATE_CHANGED 7 // 0 = Open, 1 = Closed
+#define EVENT_ALARM_ACTIVE 8 // (Bit Array) 1:Power On, 2:East Tamper, 4:West Tamper, 8:Comms Loss
+#define EVENT_ALARM_RESET 9
+
+class CodeMemory
+{
+private:
+    C24LCXX_I2C* _eeprom;
+
+    int LogPointerAddress();
+    int FirstEventLogAddress();
+    int EventLogEndAddress();
+    void PrintEvent(unsigned short eventAddress);
+    int GetLatestEventAddress();
+    int GetNextEventAddress();
+    int GetNextEventAddress(unsigned short startAddress);
+    int GetPreviousEventAddress(unsigned short startAddress);
+
+public:
+    CodeMemory(){_eeprom = new C24LCXX_I2C(D14, D15, 0, NC, 1000000);}
+    ~CodeMemory(){delete _eeprom;}
+    void EEPROMClear();
+    unsigned short ReadAccessCode(unsigned short address);
+    void WriteAccessCode(unsigned short address, unsigned short value);
+    unsigned int ReadEEPROMULong(unsigned short address);
+    void WriteEEPROMULong(unsigned short address, unsigned int value);
+    int FindAccessCode(unsigned short code);
+    int FindAccessCode(unsigned short code, unsigned short startAddress, unsigned short stopAddress);
+    int ActivateAccessCode(unsigned short value);
+    int DeactivateAccessCode(unsigned short value);
+    void PrintAllAccessCodes();
+    void SyncAccessCodes(unsigned short* codeList, unsigned short codeCount);
+    bool IsSpecialCode(unsigned short specialCodeIndex, unsigned short code, unsigned short defaultCode);
+    bool SetSpecialCode(unsigned short specialCodeIndex, unsigned short code);
+    
+    void WriteEvent(unsigned char eventType, unsigned short eventValue);
+    void PrintEventLog();
+    void PrintRecentEvents();
+    void PrintRecentEvents(unsigned short numEvents);
+};
+
+#endif
\ No newline at end of file
diff -r 000000000000 -r a56239ae90c2 ConfigurationManager.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ConfigurationManager.cpp	Mon Sep 25 19:02:40 2017 +0000
@@ -0,0 +1,386 @@
+
+#include "ConfigurationManager.h"
+
+void ConfigurationManager::remove_all_chars(char* str, char c) {
+    char *pr = str, *pw = str;
+    while (*pr) {
+        *pw = *pr++;
+        pw += (*pw != c);
+    }
+    *pw = '\0';
+}
+
+bool ConfigurationManager::isValidIpString(char* ip)
+{
+    bool isValid = false;
+    int strLength = strlen(ip);
+    int numPeriods = 0;
+    
+    // Verify that the length is within bounds.
+    if (strLength >= IP_STRING_MIN_LENGTH && strLength <= IP_STRING_MAX_LENGTH)
+    {
+        // Verify that there are three periods in the string.
+        for (int charIndex = 0; charIndex < strLength; charIndex++)
+        {
+            numPeriods += (ip[charIndex] == '.');
+        }
+        
+        isValid = (numPeriods == 3);
+    }
+    
+    return isValid;
+}
+
+int ConfigurationManager::parse_tftp_config(char* data)
+{
+    int dataLength = strlen(data);
+    int paramStart = 0;
+    int paramNameLength = 0;
+    int paramValueLength = 0;
+    char paramName[CONFIG_PARAM_NAME_MAX_LENGTH + 1] = "";
+    char paramValue[CONFIG_PARAM_VALUE_MAX_LENGTH + 1] = "";
+    bool paramComplete = false;
+    
+    // Get the parameter name
+    for (int charIndex = 0; charIndex <= dataLength; charIndex++)
+    {
+        // Check for end of the parameter name.
+        if (data[charIndex] == '=')
+        {
+            paramNameLength = charIndex - paramStart;
+        }
+        
+        // Check for the end of the parameter value.
+        if (data[charIndex] == '\r' || data[charIndex] == '\n' || data[charIndex] == ' ' || charIndex == dataLength)
+        {
+            int paramValueStart = (paramStart + paramNameLength + 1);
+            paramValueLength = charIndex - paramValueStart;
+            
+            // Handle oversized input strings.
+            if (paramNameLength > CONFIG_PARAM_NAME_MAX_LENGTH || paramValueLength > CONFIG_PARAM_VALUE_MAX_LENGTH)
+            {
+                paramComplete = true;
+            }
+            
+            if (!paramComplete && paramNameLength > 0 && paramValueLength > 0)
+            {
+                // Get the name and value strings.
+                memcpy(paramName, data + paramStart, paramNameLength);
+                paramName[paramNameLength] = 0;
+                memcpy(paramValue, data + paramValueStart, paramValueLength);
+                paramValue[paramValueLength] = 0;
+                
+                printf("  %s=%s\n", paramName, paramValue);
+                
+                // Store the parameter value.
+                if (!strcmp(paramName, configName_enableAccess))
+                {
+                    config_enableAccess = (paramValue[0] == '1');
+                }
+                else if (!strcmp(paramName, configName_ipAddress) && isValidIpString(paramValue))
+                {
+                    strcpy(config_ipAddress, paramValue);
+                }
+                else if (!strcmp(paramName, configName_netMask) && isValidIpString(paramValue))
+                {
+                    strcpy(config_netMask, paramValue);
+                }
+                else if (!strcmp(paramName, configName_gateway) && isValidIpString(paramValue))
+                {
+                    strcpy(config_gateway, paramValue);
+                }
+                else if (!strcmp(paramName, configName_codeFileName) && strlen(paramValue) <= CONFIG_PARAM_VALUE_MAX_LENGTH)
+                {
+                    strcpy(config_codeFileName, paramValue);
+                }
+                else if (!strcmp(paramName, configName_ntpServer) && isValidIpString(paramValue))
+                {
+                    strcpy(config_ntpServer, paramValue);
+                }
+                else if (!strcmp(paramName, configName_usbBaud))
+                {
+                    config_usbBaud = atoi(paramValue);
+                }
+            }
+            
+            paramComplete = true;
+        }
+        
+        // Reset for the next parameter.
+        if (data[charIndex] == '\n')
+        {
+            paramStart = charIndex + 1;
+            paramNameLength = 0;
+            paramValueLength = 0;
+            paramName[0] = 0;
+            paramValue[0] = 0;
+            paramComplete = false;
+        }
+    }
+    
+    return 0;
+}
+
+int ConfigurationManager::parse_tftp_codes(char* tftp_data)
+{
+    unsigned short codeList[CODE_TABLE_SIZE / 2] = {0};
+    unsigned int dataLength = strlen(tftp_data);
+    int codeStart = 0;
+    int codeLength = 0;
+    int codeIndex = 0;
+    char codeString[ACCESS_CODE_MAX_LENGTH + 1] = "";
+    bool codeComplete = true;
+    
+    // Get the parameter name
+    for (int charIndex = 0; charIndex <= dataLength; charIndex++)
+    {
+        if (tftp_data[charIndex] >= '0' && tftp_data[charIndex] <= '9')
+        {
+            if (codeComplete)
+            {
+                codeStart = charIndex;
+                codeLength = 0;
+            }
+            
+            codeLength += 1;
+            codeComplete = false;
+        }
+        else // not a number character
+        {
+            codeComplete = true;
+            
+            // Ensure that the code is of the correct size.
+            if (codeIndex < (CODE_TABLE_SIZE / 2) && codeLength >= ACCESS_CODE_MIN_LENGTH && codeLength <= ACCESS_CODE_MAX_LENGTH)
+            {
+                memcpy(codeString, tftp_data + codeStart, codeLength);
+                codeString[codeLength] = 0;
+                unsigned short codeValue = atoi(codeString);
+                
+                // Store the code in a temporary list to use when updating the EEPROM.
+                codeList[codeIndex] = codeValue;
+                
+                // Format and print the list of access codes with multiple codes per line.
+                //printf(ACCESS_CODE_PRINT_FORMAT, codeValue);
+                codeIndex++;
+                
+                // New line of access codes
+                if (codeIndex % ACCESS_CODE_NUM_COLS == 0)
+                {
+                    //printf("\n");
+                }
+            }
+        }
+    }
+    
+    printf("Downloaded %d codes.\n\n", codeIndex);
+    
+    // Update the EEPROM with the current network list of codes.
+    mCodeMem->SyncAccessCodes(codeList, codeIndex);
+    
+    return 0;
+}
+
+// Set the IP address options from the configuration parameters and connect to the network.
+int ConfigurationManager::connectEthernet(char* mac_addr, char* ip_addr, const bool withDhcp, const bool printStats)
+{
+    int connectResult;
+    
+    mEth->set_dhcp(withDhcp);
+    
+    // Reconfigure the network for a static address if specified.
+    if (!withDhcp && isValidIpString(config_ipAddress) && isValidIpString(config_netMask) && (strlen(config_gateway) == 0 || isValidIpString(config_gateway)))
+    {
+        mEth->set_network(config_ipAddress, config_netMask, config_gateway);
+    }
+    
+    // Attempt network connection
+    connectResult = mEth->connect();
+    
+    if (connectResult >= 0)
+    {
+        strcpy(mac_addr, mEth->get_mac_address());
+        strcpy(ip_addr, mEth->get_ip_address());
+        
+        if (printStats)
+        {
+            printf("Network Connected:\n");
+            printf("  mac:  %s\n", mac_addr);
+            printf("  ip:   %s\n", ip_addr);
+            printf("  mask: %s\n", mEth->get_netmask());
+            printf("  gate: %s\n", mEth->get_gateway());
+        }
+    }
+    else
+    {
+        printf("Failed to connect to the network. %d\n", connectResult);
+    }
+    
+    return connectResult;
+}
+
+void ConfigurationManager::updateClock()
+{
+    // Set the real time clock using NTP
+    printf("\nSending NTP query to %s...\n", config_ntpServer);
+    int ntp_result = _ntp.setTime(mEth, config_ntpServer);
+    if (ntp_result < 0)
+    {
+        printf("Failed to send.\n");
+    }
+    
+    lastUpdatedTimeSeconds = time(NULL);
+    return;
+}
+
+void ConfigurationManager::setupNetwork()
+{
+    printf("\nEstablishing DHCP network connection...\n");
+    
+    // Ethernet Setup: Connect to the netowrk using DHCP
+    _tftp = new TFTPClient(mEth);
+    int connectResult = connectEthernet(_mac_addr, _ip_addr);
+    if (connectResult >= 0)
+    {
+        // Reconfigure the network settings for a temporary static IP address
+        // This is required to free port 68 for the DHCP option query in the next step.
+        strcpy(config_ipAddress, mEth->get_ip_address());
+        strcpy(config_netMask, mEth->get_netmask());
+        strcpy(config_gateway, mEth->get_gateway());
+        mEth->disconnect();
+        
+        connectResult = connectEthernet(_mac_addr, _ip_addr, 0, 1);
+        
+        // Get the IP address of the TFTP server via DHCP offer message.
+        printf("\nGetting the TFTP server address via DHCP...\n");
+        DHCPOptions ops(mEth);
+        int optionResult = ops.getOptions(config_tftpServer, _mac_addr);
+        
+        if (optionResult >= 0)
+        {
+            //updateConfiguration();
+        }
+        else
+        {
+            printf("Failed to get options from DHCP: %d\n", optionResult);
+        }
+    }
+    else if (connectResult == NSAPI_ERROR_DHCP_FAILURE)
+    {
+        // Retry DHCP until it works.
+        // If the error is not due to DHCP, then continue without network features.
+        printf("Resetting...\n");
+        wait(RESET_PAUSE_SECONDS);
+        NVIC_SystemReset();
+    }
+}
+
+void ConfigurationManager::update()
+{
+    // Periodically update the controller configuration from TFTP.
+    if ((time(NULL) - lastUpdatedConfigSeconds) > CONFIG_UPDATE_INTERVAL_SECS)
+    {
+        mUpdateStep = UPDATE_STEP_INITIATE;
+        lastUpdatedConfigSeconds = time(NULL);
+    }
+    
+    if (mUpdateStep == UPDATE_STEP_INITIATE)
+    {
+        // Build TFTP filename from the MAC address.
+        char tftp_filename[MAC_STRING_LENGTH + 1] = "";
+        strcpy(tftp_filename, _mac_addr);
+        remove_all_chars(tftp_filename, ':');
+        strcat(tftp_filename, ".cnf");
+        printf("Getting config file %s from %s...\n", tftp_filename, config_tftpServer);
+        
+        int tftpResult = _tftp->readFile(_tftp_data, config_tftpServer, tftp_filename);
+        if (tftpResult < 0)
+        {
+            printf("  TFTP config file request failed: %d\n", tftpResult);
+        }
+        else
+        {
+            mUpdateStep = UPDATE_STEP_CONFIG_FILE_REQUEST_SENT;
+        }
+    }
+    else if (mUpdateStep == UPDATE_STEP_CONFIG_FILE_REQUEST_SENT)
+    {
+        // Wait for the TFTP response from the server.
+        int tftp_result = _tftp->update();
+        if (tftp_result == 1)
+        {
+            printf("TFTP Success.\n\n");
+            mUpdateStep = UPDATE_STEP_CONFIG_FILE_RECEIVED;
+        }
+        else
+        {
+            printf("  Failed to retrieve configuration file: %d\n", tftp_result);
+        }
+    }
+    else if (mUpdateStep == UPDATE_STEP_CONFIG_FILE_RECEIVED) // retrieve file succeeded
+    {
+        int currentUsbBaud = config_usbBaud;
+        
+        parse_tftp_config(_tftp_data);
+        
+        if (config_usbBaud > 0 && config_usbBaud != currentUsbBaud)
+        {
+            printf("\nSetting USB Serial Baud: %d\n                     \n", config_usbBaud); // Leave extra space because setting baud rate deletes some characters.
+            //usbUART.baud(config_usbBaud);
+        }
+        
+        printf("\nConfiguring static network connection...\n");
+        mEth->disconnect();
+        connectEthernet(_mac_addr, _ip_addr, 0, 1);
+        
+        printf("\nGetting code list file %s from %s...\n", config_codeFileName, config_tftpServer);
+        // Get list of access codes from the specified file on the TFTP server.
+        int tftpResult = _tftp->readFile(_tftp_data, config_tftpServer, config_codeFileName);
+        if (tftpResult < 0)
+        {
+            printf("Failed to send config file request.\n");
+        }
+        else
+        {
+            mUpdateStep = UPDATE_STEP_CODES_FILE_REQUEST_SENT;
+        }
+    }
+    else if (mUpdateStep == UPDATE_STEP_CODES_FILE_REQUEST_SENT)
+    {
+        // Wait for the TFTP response from the server.
+        int tftp_result = _tftp->update();
+        if (tftp_result == 1)
+        {
+            printf("TFTP Success.\n\n");
+            mUpdateStep = UPDATE_STEP_CODES_FILE_RECEIVED;
+        }
+        else
+        {
+            printf("  Failed to retrieve code list: %d\n", tftp_result);
+        }
+    }
+    else if (mUpdateStep == UPDATE_STEP_CODES_FILE_RECEIVED)
+    {
+        parse_tftp_codes(_tftp_data);
+        mUpdateStep = UPDATE_STEP_IDLE;
+    }
+    
+    
+    
+    // Periodically update the RTC via NTP.
+    if ((time(NULL) - lastUpdatedTimeSeconds) > TIME_UPDATE_INTERVAL_SECS)
+    {
+        updateClock();
+    }
+    
+    // Check the status of any ongoing NTP updates.
+    int ntp_result = _ntp.update();
+    if (ntp_result < 0)
+    {
+        printf("NTP query failed.\n\n");
+    }
+    else if (ntp_result > 0)
+    {
+        unsigned int ctTime = time(NULL);
+        printf("Time is now (UTC): %s\n\n", ctime(&ctTime));
+    }
+}
diff -r 000000000000 -r a56239ae90c2 ConfigurationManager.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ConfigurationManager.h	Mon Sep 25 19:02:40 2017 +0000
@@ -0,0 +1,97 @@
+
+#ifndef CONFIGURATION_MANAGER_H_
+#define CONFIGURATION_MANAGER_H_
+
+#include "CodeMemory.h"
+#include "NTPClient.h"
+#include "TFTPClient.h"
+#include "DHCPOptions.h"
+
+#define RESET_PAUSE_SECONDS            1.5
+#define ACCESS_CODE_MAX_LENGTH         5
+#define ACCESS_CODE_MIN_LENGTH         3
+#define ACCESS_CODE_PRINT_FORMAT       "%-7u"
+#define ACCESS_CODE_NUM_COLS           10
+#define USB_RX_MAX_LENGTH              80
+#define CONFIG_PARAM_NAME_MAX_LENGTH   8
+#define CONFIG_PARAM_VALUE_MAX_LENGTH  30
+#define IP_STRING_MAX_LENGTH           15
+#define IP_STRING_MIN_LENGTH           7
+#define DEFAULT_USB_BAUD               9600
+
+// Periodic Timers
+#define CONFIG_UPDATE_INTERVAL_SECS    30
+#define TIME_UPDATE_INTERVAL_SECS      30//86400 // Once per day
+
+#if 1
+//Enable debug
+#define __DEBUG__
+#include <cstdio>
+#define DBG(x, ...) std::printf("[NTPClient : DBG]"x"\r\n", ##__VA_ARGS__); 
+#define WARN(x, ...) std::printf("[NTPClient : WARN]"x"\r\n", ##__VA_ARGS__); 
+#define ERR(x, ...) std::printf("[NTPClient : ERR]"x"\r\n", ##__VA_ARGS__); 
+
+#else
+//Disable debug
+#define DBG(x, ...) 
+#define WARN(x, ...)
+#define ERR(x, ...) 
+#endif
+
+// TFTP Configuration Parameters
+const char configName_enableAccess[] = "ENABLE";
+const char configName_ipAddress[] = "IP";
+const char configName_netMask[] = "MASK";
+const char configName_gateway[] = "GATE";
+const char configName_codeFileName[] = "CODEFILE";
+const char configName_ntpServer[] = "NTPSRV";
+const char configName_usbBaud[] = "BAUD";
+
+bool config_enableAccess = 1;
+char config_ipAddress[IP_STRING_MAX_LENGTH + 1] = "";
+char config_netMask[IP_STRING_MAX_LENGTH + 1] = "";
+char config_gateway[IP_STRING_MAX_LENGTH + 1] = "";
+char config_codeFileName[CONFIG_PARAM_VALUE_MAX_LENGTH + 1] = "door-access-codes.cnf";
+char config_tftpServer[IP_STRING_MAX_LENGTH + 1] = "0.0.0.0";
+char config_ntpServer[IP_STRING_MAX_LENGTH + 1] = "0.0.0.0";
+int config_usbBaud = DEFAULT_USB_BAUD;
+
+enum ConfigUpdateSteps
+{
+    UPDATE_STEP_IDLE = -1,
+    UPDATE_STEP_INITIATE = 0,
+    UPDATE_STEP_CONFIG_FILE_REQUEST_SENT = 1,
+    UPDATE_STEP_CONFIG_FILE_RECEIVED = 2,
+    UPDATE_STEP_CODES_FILE_REQUEST_SENT = 3,
+    UPDATE_STEP_CODES_FILE_RECEIVED = 4
+};
+
+class ConfigurationManager
+{
+public:
+    ConfigurationManager(EthernetInterface* stack, CodeMemory* codeMem): mEth(stack), mCodeMem(codeMem), mUpdateStep(0), lastUpdatedConfigSeconds(0), lastUpdatedTimeSeconds(0){}
+    void update();
+    
+private:
+    void remove_all_chars(char* str, char c);
+    bool isValidIpString(char* ip);
+    int parse_tftp_config(char* data);
+    int parse_tftp_codes(char* tftp_data);
+    int connectEthernet(char* mac_addr, char* ip_addr, const bool withDhcp = true, const bool printStats = 1);
+    void setupNetwork();
+    void updateClock();
+    
+    EthernetInterface* mEth;
+    CodeMemory* mCodeMem;
+    char* _mac_addr;
+    char* _ip_addr;
+    NTPClient _ntp;
+    TFTPClient* _tftp;
+    char _tftp_data[MAX_TFTP_FILE_SIZE + 1];
+    int mUpdateStep;
+    // Periodic Timers
+    time_t lastUpdatedConfigSeconds;
+    time_t lastUpdatedTimeSeconds;
+};
+
+#endif
\ No newline at end of file
diff -r 000000000000 -r a56239ae90c2 DHCPOptions.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DHCPOptions.cpp	Mon Sep 25 19:02:40 2017 +0000
@@ -0,0 +1,142 @@
+
+#include "DHCPOptions.h"
+
+int DHCPOptions::getOptions(char* tftp_server, const char* mac_address, const uint32_t timeout)
+{
+    char mac_bin[DHCP_CHADDR_SIZE + 1] = {0}; // Initialize the hardware address array to zeros.
+    int bind_result;
+    DHCPPacket pkt, rec_pkt;
+    
+    // Generate binary version of the MAC string.
+    macStrToBin(mac_bin, mac_address);
+    
+    SocketAddress dhcp_addr(DHCP_BROADCAST_ADDRESS, DHCP_SERVER_PORT);
+    m_sock.open(m_eth);
+    m_sock.set_timeout(timeout);
+    bind_result = m_sock.bind(DHCP_CLIENT_PORT);
+    
+    if (bind_result < 0)
+    {
+        printf("DHCP Port Binding Failed: %d\n", bind_result);
+        return -1; // DHCP port bind failed.
+    }
+    
+    // Construct a DHCP DISCOVER packet
+    pkt.op = DHCP_OP_REQUEST;
+    pkt.htype = DHCP_HTYPE;
+    pkt.hlen = DHCP_HLEN;
+    pkt.hops = DHCP_HOPS;
+    pkt.xid = htonl(rand());
+    pkt.secs = htons(DHCP_SECS);
+    pkt.flags = htons(DHCP_FLAGS);
+    pkt.ciaddr = htonl(DHCP_CIADDR);
+    pkt.yiaddr = htonl(DHCP_YIADDR);
+    pkt.siaddr = htonl(DHCP_SIADDR);
+    pkt.giaddr = htonl(DHCP_GIADDR);
+    // MAC address with padding - 16 bytes total
+    memcpy(pkt.chaddr, mac_bin, DHCP_CHADDR_SIZE);
+    memset(pkt.sname, 0, DHCP_SNAME_SIZE);
+    memset(pkt.file, 0, DHCP_FILE_SIZE);
+    // Options List
+    pkt.magic = htonl(DHCP_MAGIC_COOKIE);
+    pkt.op_mtype = 53;
+    pkt.mtype_len = 1;
+    pkt.mtype = 1;
+    char discover_options[] = {DHCP_PREQLIST, 1, DHCP_OPTION_TFTPSERVER, DHCP_OPTION_STOP};
+    memcpy(pkt.options, discover_options, sizeof(discover_options));
+    
+    // Send read request
+    int bytes_sent = m_sock.sendto(dhcp_addr, (char*)&pkt, sizeof(DHCPPacket));
+    if (bytes_sent < 0)
+    {
+        printf("DHCP Discover Failed: %d\n", bytes_sent);
+        return -2; // DHCP Discover send failed.
+    }
+    else
+    {
+        //printf("Sent: %d\n", bytes_sent);
+    }
+    
+    // Get data packet from the DHCP server
+    int bytes_received = m_sock.recvfrom(&dhcp_addr, (char*)&rec_pkt, sizeof(DHCPPacket));
+    if (bytes_received < 0)
+    {
+        printf("DHCP Offer Failed: %d\n", bytes_received);
+        return -3; // UDP packet recevie failed.
+    }
+    else
+    {
+        //printf("Received: %d\n", bytes_received);
+    }
+    
+    if (decodeOptions(tftp_server, rec_pkt) < 0)
+    {
+        printf("Failed to find the TFTP server option.\n");
+        return -4; // Failed to find the TFTP server name option from the DHCP offer response packet.
+    }
+    
+    m_sock.close();
+    return 0;
+}
+
+int DHCPOptions::decodeOptions(char* tftp_server, DHCPPacket& pkt)
+{
+    char* dhcp_options = pkt.options;
+    int optionIndex = 0;
+    int optionLength = 0;
+    
+    while (optionIndex < DHCP_OPTIONS_SIZE && dhcp_options[optionIndex] != DHCP_OPTION_STOP)
+    {
+        // Get the length of the current DHCP option field.
+        optionLength = dhcp_options[optionIndex + 1];
+        
+        // Check for the TFTP server option code.
+        if (dhcp_options[optionIndex] == DHCP_OPTION_TFTPSERVER)
+        {
+            // Copy the TFTP server string to the output string.
+            memcpy(tftp_server, dhcp_options + optionIndex + 2, optionLength);
+            return 0;
+        }
+        
+        // Move to the next option.
+        optionIndex += 2 + optionLength;
+    }
+    
+    return -1; // Failed to find the DHCP option.
+}
+
+int DHCPOptions::macStrToBin(char* macBin, const char* macStr)
+{
+    if (strlen(macStr) != MAC_STRING_LENGTH)
+    {
+        return -1; // Invalid MAC string
+    }
+    else
+    {
+        for (int byteIndex = 0; byteIndex < 6; byteIndex++)
+        {
+            macBin[byteIndex] = (charToNum(macStr[byteIndex * 3]) * 16) + charToNum(macStr[(byteIndex * 3) + 1]);
+        }
+    }
+    
+    return 0;
+}
+
+unsigned char DHCPOptions::charToNum(const char input)
+{
+    if(input >= '0' && input <= '9')
+    {
+        return input - '0';
+    }
+    if(input >= 'A' && input <= 'F')
+    {
+        return input - 'A' + 10;
+    }
+    if(input >= 'a' && input <= 'f')
+    {
+        return input - 'a' + 10;
+    }
+
+    return 0;
+}
+    
\ No newline at end of file
diff -r 000000000000 -r a56239ae90c2 DHCPOptions.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DHCPOptions.h	Mon Sep 25 19:02:40 2017 +0000
@@ -0,0 +1,86 @@
+
+#ifndef DHCPOPTIONS_H_
+#define DHCPOPTIONS_H_
+
+#include <cstdint>
+#include "EthernetInterface.h"
+#include "NetworkStack.h"
+#include "UDPSocket.h"
+#include <inet.h>
+
+using std::uint8_t;
+using std::uint16_t;
+using std::uint32_t;
+
+#define MAC_STRING_LENGTH 17
+#define DHCP_BROADCAST_ADDRESS "255.255.255.255"
+#define DHCP_SERVER_PORT 67
+#define DHCP_CLIENT_PORT 68
+#define DHCP_DEFAULT_TIMEOUT 1000
+#define DHCP_PACKET_SIZE 576
+
+#define DHCP_OP_REQUEST 1 // Request op
+#define DHCP_HTYPE 1 // Ethernet
+#define DHCP_HLEN 6 // MAC 6 octet
+#define DHCP_HOPS 0
+#define DHCP_SECS 0
+#define DHCP_FLAGS 0
+#define DHCP_CIADDR 0
+#define DHCP_YIADDR 0
+#define DHCP_SIADDR 0
+#define DHCP_GIADDR 0
+#define DHCP_MAGIC_COOKIE 0x63825363 // See, it is magic after all!
+#define DHCP_PREQLIST 55
+#define DHCP_OPTION_TFTPSERVER 66
+#define DHCP_OPTION_STOP 255
+
+#define DHCP_CHADDR_SIZE      16
+#define DHCP_SNAME_SIZE       64
+#define DHCP_FILE_SIZE        128
+#define DHCP_OPTIONS_SIZE     312
+
+class DHCPOptions
+{
+public:
+    DHCPOptions(EthernetInterface *stack) : m_eth(stack) {}
+
+    /* Get DHCP Options from the server. Blocks until completion. */
+    int getOptions(char* tftp_server, const char* mac_address, const uint32_t timeout = DHCP_DEFAULT_TIMEOUT); //Blocking
+
+private:
+    EthernetInterface *m_eth;
+    UDPSocket m_sock;
+    
+    struct DHCPPacket
+    {
+        uint8_t op;
+        uint8_t htype;
+        uint8_t hlen;
+        uint8_t hops;
+        uint32_t xid;
+        uint16_t secs;
+        uint16_t flags;
+        uint32_t ciaddr;
+        uint32_t yiaddr;
+        uint32_t siaddr;
+        uint32_t giaddr;
+        char chaddr[DHCP_CHADDR_SIZE];
+        char sname[DHCP_SNAME_SIZE];
+        char file[DHCP_FILE_SIZE];
+        
+        //Options
+        uint32_t magic; // magic cookie
+        uint8_t op_mtype; // message type
+        uint8_t mtype_len;
+        uint8_t mtype;
+        char options[DHCP_OPTIONS_SIZE];
+        
+    } __attribute__ ((packed));
+    
+    int decodeOptions(char* tftp_server, DHCPPacket& pkt);
+    int macStrToBin(char* macBin, const char* macStr);
+    unsigned char charToNum(const char input);
+};
+
+#endif /* DHCPOPTIONS_H_ */
+
diff -r 000000000000 -r a56239ae90c2 NTPClient.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NTPClient.cpp	Mon Sep 25 19:02:40 2017 +0000
@@ -0,0 +1,208 @@
+/* NTPClient.cpp */
+/* Copyright (C) 2012 mbed.org, MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#if 1
+//Enable debug
+#define __DEBUG__
+#include <cstdio>
+#define DBG(x, ...) std::printf("[NTPClient : DBG]"x"\r\n", ##__VA_ARGS__); 
+#define WARN(x, ...) std::printf("[NTPClient : WARN]"x"\r\n", ##__VA_ARGS__); 
+#define ERR(x, ...) std::printf("[NTPClient : ERR]"x"\r\n", ##__VA_ARGS__); 
+
+#else
+//Disable debug
+#define DBG(x, ...) 
+#define WARN(x, ...)
+#define ERR(x, ...) 
+#endif
+
+#include "NTPClient.h"
+
+#include "mbed.h" //time() and set_time()
+
+NTPClient::NTPClient() : m_sock()
+{
+    reset();
+    
+    mQuerySent = false;
+    mResponseReceived = false;
+    mConversationComplete = true;
+    mStartTime = 0;
+}
+
+void NTPClient::reset()
+{
+    if (outEndpoint)
+    {
+        delete outEndpoint;
+    }
+    if (inEndpoint)
+    {
+        delete inEndpoint;
+    }
+    outEndpoint = NULL;
+    inEndpoint = NULL;
+    
+    m_sock.close();
+}
+
+void NTPClient::process_result()
+{
+    //Correct Endianness
+    pkt.refTm_s = ntohl( pkt.refTm_s );
+    pkt.refTm_f = ntohl( pkt.refTm_f );
+    pkt.origTm_s = ntohl( pkt.origTm_s );
+    pkt.origTm_f = ntohl( pkt.origTm_f );
+    pkt.rxTm_s = ntohl( pkt.rxTm_s );
+    pkt.rxTm_f = ntohl( pkt.rxTm_f );
+    pkt.txTm_s = ntohl( pkt.txTm_s );
+    pkt.txTm_f = ntohl( pkt.txTm_f );
+    
+    //Compute offset, see RFC 4330 p.13
+    uint32_t destTm_s = (NTP_TIMESTAMP_DELTA + time(NULL));
+    int64_t offset = ( (int64_t)( pkt.rxTm_s - pkt.origTm_s ) + (int64_t) ( pkt.txTm_s - destTm_s ) ) / 2; //Avoid overflow
+    DBG("Received @%ul", pkt.txTm_s);
+    DBG("Offset: %lld", offset);
+    //Set time accordingly
+    set_time( time(NULL) + offset );
+    
+    #ifdef __DEBUG__
+    time_t ctTime;
+    ctTime = time(NULL);
+    DBG("Time is now (UTC): %s", ctime(&ctTime));
+    #endif
+}
+
+int NTPClient::update()
+{
+    int ntp_result = NTP_WAIT;
+    bool conversationComplete = true;
+    
+    if (mQuerySent && !mConversationComplete)
+    {
+        // Listen for a response from the NTP server.
+        int ret = m_sock.recvfrom( inEndpoint, (char*)&pkt, sizeof(NTPPacket) ); //FIXME need a DNS Resolver to actually compare the incoming address with the DNS name
+        
+        if (ret == NSAPI_ERROR_WOULD_BLOCK)
+        {
+            //DBG("Waiting...");
+            
+            // Check for response timeout.
+            if ((time(NULL) - mStartTime) < mTimeout)
+            {
+                conversationComplete = false;
+            }
+            else
+            {
+                DBG("Timeout.\n");
+                ntp_result = NTP_TIMEOUT;
+            }
+        }
+        else if(ret < 0)
+        {
+            ERR("Could not receive packet");
+            ntp_result = NTP_CONN;
+        }
+        else if(ret < sizeof(NTPPacket)) //TODO: Accept chunks
+        {
+            ERR("Received packet size does not match");
+            ntp_result = NTP_PRTCL;
+        }
+        else if( pkt.stratum == 0)  //Kiss of death
+        {
+            ERR("Kissed to death!");
+            ntp_result = NTP_PRTCL;
+        }
+        else
+        {
+            process_result();
+            ntp_result = NTP_OK;
+        }
+        
+        if (conversationComplete)
+        {
+            reset();        
+            mConversationComplete = true;
+        }
+    }
+    
+    return ntp_result;
+}
+
+NTPResult NTPClient::setTime(EthernetInterface* stack, const char* host, uint16_t port, uint32_t timeout)
+{
+    mQuerySent = false;
+    mResponseReceived = false;
+    mConversationComplete = false;
+    
+    mStartTime = time(NULL);
+    mTimeout = timeout;
+    
+#ifdef __DEBUG__
+  time_t ctTime;
+  ctTime = time(NULL);
+  DBG("Time is set to (UTC): %s", ctime(&ctTime));
+#endif
+
+  //Create & bind socket
+  m_sock.open(stack);
+  DBG("Binding socket: %d", m_sock.bind(NTP_CLIENT_PORT)); //Bind to a random port
+  m_sock.set_blocking(false); //Set not blocking
+
+  //Now ping the server and wait for response
+  DBG("Ping");
+  //Prepare NTP Packet:
+  pkt.li = 0; //Leap Indicator : No warning
+  pkt.vn = 4; //Version Number : 4
+  pkt.mode = 3; //Client mode
+  pkt.stratum = 0; //Not relevant here
+  pkt.poll = 0; //Not significant as well
+  pkt.precision = 0; //Neither this one is
+
+  pkt.rootDelay = 0; //Or this one
+  pkt.rootDispersion = 0; //Or that one
+  pkt.refId = 0; //...
+
+  pkt.refTm_s = 0;
+  pkt.origTm_s = 0;
+  pkt.rxTm_s = 0;
+  pkt.txTm_s = htonl( NTP_TIMESTAMP_DELTA + time(NULL) ); //WARN: We are in LE format, network byte order is BE
+
+  pkt.refTm_f = pkt.origTm_f = pkt.rxTm_f = pkt.txTm_f = 0;
+
+  outEndpoint = new SocketAddress(host, port);
+  inEndpoint = new SocketAddress(host, 0);
+  
+  //Set timeout, non-blocking and wait using select
+  DBG("Send packet to %s:%d", host, port);
+  int ret = m_sock.sendto(*outEndpoint, (void*)&pkt, sizeof(NTPPacket));
+  if (ret < 0 )
+  {
+    ERR("Could not send packet %d\n", ret);
+    m_sock.close();
+    return NTP_CONN;
+  }
+  DBG("Sent...");
+  
+  mQuerySent = true;
+
+  return NTP_OK;
+}
+
+
diff -r 000000000000 -r a56239ae90c2 NTPClient.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NTPClient.h	Mon Sep 25 19:02:40 2017 +0000
@@ -0,0 +1,103 @@
+/* NTPClient.h */
+/* Copyright (C) 2012 mbed.org, MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef NTPCLIENT_H_
+#define NTPCLIENT_H_
+
+#include <cstdint>
+#include "EthernetInterface.h"
+#include "NetworkStack.h"
+#include "UDPSocket.h"
+#include <inet.h>
+
+using std::uint8_t;
+using std::uint16_t;
+using std::uint32_t;
+
+#define NTP_DEFAULT_PORT 123
+#define NTP_DEFAULT_TIMEOUT 3
+#define NTP_CLIENT_PORT 0 //Random port
+#define NTP_TIMESTAMP_DELTA 2208988800ull //Diff btw a UNIX timestamp (Starting Jan, 1st 1970) and a NTP timestamp (Starting Jan, 1st 1900)
+
+///NTP client results
+enum NTPResult
+{
+  NTP_DNS = -1, ///<Could not resolve name
+  NTP_PRTCL = -2, ///<Protocol error
+  NTP_TIMEOUT = -3, ///<Connection timeout
+  NTP_CONN = -4, ///<Connection error
+  NTP_OK = 1, ///<Success
+  NTP_WAIT = 0
+};
+
+/** NTP Client to update the mbed's RTC using a remote time server
+*
+*/
+class NTPClient
+{
+public:
+    NTPClient();
+    NTPResult setTime(EthernetInterface *stack, const char* host, uint16_t port = NTP_DEFAULT_PORT, uint32_t timeout = NTP_DEFAULT_TIMEOUT); //Blocking
+    int update();
+
+private:
+    void process_result();
+    void reset();
+    
+    struct NTPPacket //See RFC 4330 for Simple NTP
+    {
+        //WARN: We are in LE! Network is BE!
+        //LSb first
+        unsigned mode : 3;
+        unsigned vn : 3;
+        unsigned li : 2;
+        
+        uint8_t stratum;
+        uint8_t poll;
+        uint8_t precision;
+        //32 bits header
+        
+        uint32_t rootDelay;
+        uint32_t rootDispersion;
+        uint32_t refId;
+        
+        uint32_t refTm_s;
+        uint32_t refTm_f;
+        uint32_t origTm_s;
+        uint32_t origTm_f;
+        uint32_t rxTm_s;
+        uint32_t rxTm_f;
+        uint32_t txTm_s;
+        uint32_t txTm_f;
+    } __attribute__ ((packed));
+    
+    struct NTPPacket pkt;
+    UDPSocket m_sock;
+    bool mQuerySent;
+    bool mResponseReceived;
+    bool mConversationComplete;
+    int mStartTime;
+    int mTimeout;
+    SocketAddress* outEndpoint;
+    SocketAddress* inEndpoint;
+};
+
+
+#endif
+
diff -r 000000000000 -r a56239ae90c2 TFTPClient.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TFTPClient.cpp	Mon Sep 25 19:02:40 2017 +0000
@@ -0,0 +1,143 @@
+
+#include "TFTPClient.h"
+
+bool TFTPClient::readFile(char* data, const char* host, const char* filename, const uint16_t port, const uint32_t timeout)
+{
+    printf("TFTP read file\n");
+    m_data = data;
+    
+    if (m_tftp_addr)
+    {
+        delete m_tftp_addr;
+        m_tftp_addr = NULL;
+    }
+    m_tftp_addr = new SocketAddress(host, port);
+    
+    printf("IP: %d\n", m_eth);
+    
+    m_sock.open(m_eth);
+    printf("TFTP before blocking\n");
+    m_sock.set_blocking(false);
+    printf("TFTP before bind\n");
+    m_sock.bind(0);
+    
+    printf("TFTP port bound\n");
+    
+    char chunk[TFTP_MAX_PACKET_SIZE];
+    uint16_t tftp_opcode = htons(RRQ);
+    char tftp_mode[] = "octet";
+    
+    // Construct read request packet
+    memcpy(chunk, &tftp_opcode, 2);
+    memcpy(chunk + 2, filename, strlen(filename) + 1);
+    memcpy(chunk + 2 + (strlen(filename) + 1), tftp_mode, strlen(tftp_mode) + 1);
+    int packet_size = 2 + strlen(filename) + 1 + strlen(tftp_mode) + 1;
+    
+    // Debug output request data.
+    printf("TFTP RRQ packet: ");
+    fwrite(chunk, 1, packet_size, stdout);
+    printf("\n");
+    
+    // Send read request
+    m_bytes_received = 0;
+    m_block_number = 1;
+    m_bytes_sent = m_sock.sendto(*m_tftp_addr, chunk, packet_size);
+    
+    if (m_bytes_sent == packet_size)
+    {
+        m_download_in_progress = true;
+    }
+    else
+    {
+        printf("TFTP read request failed. %d\n", m_bytes_sent);
+    }
+    
+    m_next_data_byte_index = 0;
+    
+    return m_download_in_progress;
+}
+
+int TFTPClient::update()
+{
+    int return_value = -1;
+    char chunk[TFTP_MAX_PACKET_SIZE];
+    
+    if (m_download_in_progress)
+    {
+        // Get data packet from the TFTP server
+        m_bytes_received = m_sock.recvfrom(m_tftp_addr, chunk, TFTP_MAX_PACKET_SIZE);
+        if (m_bytes_received <= 0)
+        {
+            return_value = m_bytes_received; // UDP packet recevie failed.
+        }
+        else
+        {
+            // Convert the opcode and block back into host endian format.
+            uint16_t temp_opcode, temp_block;
+            memcpy(&temp_opcode, chunk, 2);
+            memcpy(&temp_block, chunk + 2, 2);
+            temp_opcode = ntohs(temp_opcode);
+            temp_block = ntohs(temp_block);
+            
+            if (temp_opcode == ERR)
+            {
+                printf("  TFTP Error: %u, %s\n", temp_block, chunk + 4);
+                return_value = -2; // Error response from server.
+            }
+            else if (temp_opcode == DATA)
+            {
+                if (temp_block == m_block_number)
+                {
+                    int chunk_size = m_bytes_received - 4;
+                    // Debug output for incoming chunks of data.
+                    //printf("  TFTP Chunk Received: %u\n", chunk_size);
+                    //fwrite(chunk, 1, m_bytes_received, stdout);
+                    
+                    // Strip the TFTP info from the beginning of the data buffer.
+                    memcpy(m_data + m_next_data_byte_index, chunk + 4, chunk_size);
+                    m_next_data_byte_index += chunk_size;
+                    // Terminate the data string.
+                    m_data[m_next_data_byte_index] = 0;
+                    
+                    // Send an ACK for the current data block
+                    uint16_t tftp_opcode = htons(ACK);
+                    temp_block = htons(temp_block);
+                    memcpy(chunk, &tftp_opcode, 2);
+                    memcpy(chunk + 2, &temp_block, 2);
+                    int m_bytes_sent = m_sock.sendto(*m_tftp_addr, chunk, 4); // Send
+                    
+                    m_block_number++;
+                }
+                else
+                {
+                    return_value = -3; // Incorrect data block number.
+                    m_download_in_progress = false;
+                }
+            }
+            else
+            {
+                return_value = -4; // Wrong opcode
+                m_download_in_progress = false;
+            }
+            
+            if (m_bytes_received != TFTP_MAX_PACKET_SIZE)
+            {
+                m_download_in_progress = false;
+                
+                if (m_bytes_received > 0)
+                {
+                    // Finished download.
+                    return_value = 1;
+                }
+            }
+        }
+    }
+    
+    if (!m_download_in_progress)
+    {
+        m_sock.close();
+    }
+    
+    return return_value;
+}
+    
\ No newline at end of file
diff -r 000000000000 -r a56239ae90c2 TFTPClient.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TFTPClient.h	Mon Sep 25 19:02:40 2017 +0000
@@ -0,0 +1,76 @@
+/* TFTPClient.h */
+
+/** \file
+TFTP Client header file
+*/
+
+#ifndef TFTPCLIENT_H_
+#define TFTPCLIENT_H_
+
+#include <cstdint>
+#include "EthernetInterface.h"
+#include "NetworkStack.h"
+#include "UDPSocket.h"
+#include <inet.h>
+
+using std::uint8_t;
+using std::uint16_t;
+using std::uint32_t;
+
+#define TFTP_DEFAULT_PORT 69
+#define TFTP_DEFAULT_TIMEOUT 4000
+#define TFTP_MAX_DATA_SIZE 512
+#define TFTP_MAX_PACKET_SIZE TFTP_MAX_DATA_SIZE+4
+#define MAX_TFTP_FILE_SIZE 30000 // bytes
+
+enum TFTPOpcode
+{
+  RRQ = 1, // Read request
+  WRQ, // Write request
+  DATA, // Data packet
+  ACK, // Acknowledgement
+  ERR // Error
+};
+
+enum TFTPError
+{
+    NOT_DEFINED, // Not defined, see error message (if any).
+    FILE_NOT_FOUND, // File not found.
+    ACCESS_VIOLATION, // Access violation.
+    DISK_FULL, // Disk full or allocation exceeded.
+    ILLEGAL_OP, // Illegal TFTP operation.
+    UNKNOWN_TID, // Unknown transfer ID.
+    FILE_EXISTS, // File already exists.
+    NO_USER, // No such user.
+};
+
+class TFTPClient
+{
+public:
+    TFTPClient(EthernetInterface *stack) : m_eth(stack){
+        m_download_in_progress = false;
+        m_tftp_addr = NULL;
+    }
+    
+    /**Get current time (blocking)
+    Retrieve file from TFTP server. Non-blocking
+    @param host TFTP server IPv4 address or hostname (will be resolved via DNS)
+    @param filename File to retrieve from the server.
+    */
+    bool readFile(char* data, const char* host, const char* filename, const uint16_t port = TFTP_DEFAULT_PORT, const uint32_t timeout = TFTP_DEFAULT_TIMEOUT);
+    int update();
+
+private:
+    EthernetInterface *m_eth;
+    UDPSocket m_sock;
+    SocketAddress* m_tftp_addr;
+    char* m_data;
+    int m_bytes_sent;
+    int m_bytes_received;
+    int m_block_number;
+    bool m_download_in_progress;
+    int m_next_data_byte_index;
+};
+
+#endif /* TFTPCLIENT_H_ */
+
diff -r 000000000000 -r a56239ae90c2 USBHOST.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USBHOST.lib	Mon Sep 25 19:02:40 2017 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/teams/ST/code/USBHOST/#d3ac9e1c0035
diff -r 000000000000 -r a56239ae90c2 main.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Mon Sep 25 19:02:40 2017 +0000
@@ -0,0 +1,381 @@
+#if !FEATURE_LWIP
+    #error [NOT_SUPPORTED] LWIP not supported for this target
+#endif
+
+#include <sstream>      // std::istringstream
+#include <string>
+#include "mbed.h"
+#include "EthernetInterface.h"
+#include "TCPServer.h"
+#include "TCPSocket.h"
+#include "CardReader/ReaderWiegand.h"
+#include "CodeMemory.h"
+#include "ConfigurationManager.h"
+
+#define MAX_TCP_CONNECTIONS            5
+#define HTTP_SERVER_PORT               80
+#define DATA_SERVER_PORT               500 
+#define RELAY_PULSE_MS                 2000
+#define UNLATCH_FLASH_MS               50
+#define UNLATCH_NUM_BEEPS              5
+
+#define HTTP_STATUS_LINE "HTTP/1.0 200 OK"
+#define HTTP_HEADER_FIELDS "Content-Type: text/html; charset=utf-8"
+#define HTTP_MESSAGE_BODY ""                                     \
+"<html>" "\r\n"                                                  \
+"  <body style=\"display:flex;text-align:center\">" "\r\n"       \
+"    <div style=\"margin:auto\">" "\r\n"                         \
+"      <h1>East Center Carport Door</h1>" "\r\n"                 \
+"      <p>Data Server Port: %d</p>" "\r\n"     \
+"      <p>Number of button presses: %d</p>" "\r\n"               \
+"    </div>" "\r\n"                                              \
+"  </body>" "\r\n"                                               \
+"</html>"
+
+#define HTTP_RESPONSE HTTP_STATUS_LINE "\r\n"   \
+                      HTTP_HEADER_FIELDS "\r\n" \
+                      "\r\n"                    \
+                      HTTP_MESSAGE_BODY "\r\n"
+                      
+using namespace std;
+
+DigitalOut qx_led1(LED1);
+DigitalOut qx_led2(LED2);
+DigitalOut qx_relay(D12);
+DigitalOut qx_redReaderLed(D6);
+DigitalOut qx_greenReaderLed(D7);
+DigitalOut qx_readerBuzzer(D8);
+
+InterruptIn ix_usrBtn(USER_BUTTON);
+InterruptIn ix_tamper(D5);
+InterruptIn ix_doorLatched(D13);
+
+EthernetInterface* _eth;
+char* _mac_addr;
+char* _ip_addr;
+
+int button_presses = 0;
+
+char httpReq[100];
+char httpResponse[100];
+
+ReaderWiegand wgReader(D2, D4);
+CodeMemory codeMem;
+ConfigurationManager configManager(_eth, &codeMem);
+
+Timeout mRelayTimer;
+Timeout unlatchFlashTimer;
+
+RawSerial usbUART(USBTX, USBRX);
+volatile int rx_idx = 0;
+char usb_rx_buffer[USB_RX_MAX_LENGTH + 1];
+
+// Interupt Routine to read in data from serial port
+void Rx_interrupt() {
+    while ((usbUART.readable()) && rx_idx < USB_RX_MAX_LENGTH) {
+        usb_rx_buffer[rx_idx] = usbUART.getc();
+        rx_idx++;
+    }
+    
+    return;
+}
+
+void relayTimeout()
+{
+    qx_led2 = 0;
+    qx_relay = 0;
+}
+
+void relayTimerRestart()
+{
+    mRelayTimer.detach();
+    mRelayTimer.attach_us(&relayTimeout, RELAY_PULSE_MS * 1000);
+}
+
+void usr_btn_pressed()
+{
+    button_presses++;
+    
+    // Manual strike release
+    relayTimerRestart();
+    qx_led2 = 1;
+    qx_relay = 1;
+}
+
+void handle_tamper()
+{
+    qx_greenReaderLed = !qx_greenReaderLed;
+    qx_led2 = !qx_led2;
+}
+
+void handle_latch()
+{
+    qx_redReaderLed = !qx_redReaderLed;
+}
+
+void unlatch_flash()
+{
+    static int flash_count = 0;
+    
+    if (qx_relay)
+    {
+        if (qx_redReaderLed)
+        {
+            qx_redReaderLed = 0;
+            qx_greenReaderLed = 1;
+            
+            if (flash_count++ < UNLATCH_NUM_BEEPS)
+            {
+                qx_readerBuzzer = 0;
+            }
+        }
+        else
+        {
+            qx_redReaderLed = 1;
+            qx_greenReaderLed = 0;
+            qx_readerBuzzer = 1;
+        }
+        
+        // Delay and call this function again.
+        unlatchFlashTimer.detach();
+        unlatchFlashTimer.attach_us(&unlatch_flash, UNLATCH_FLASH_MS * 1000);
+    }
+    else
+    {
+        qx_redReaderLed = 1;
+        qx_greenReaderLed = 1;
+        qx_readerBuzzer = 1;
+        flash_count = 0;
+    }
+}
+
+void unlatchStrike()
+{
+    relayTimerRestart();
+    
+    qx_led2 = 1;
+    qx_relay = 1;
+    
+    unlatch_flash();
+    
+    return;
+}
+
+bool commandHasArg(string testString, string commandName)
+{
+    int commandLength = commandName.length();
+    return ((testString.length() >= (commandLength + 2)) && (testString.find(" ") == commandLength) && testString.compare(0, commandLength, commandName) == 0);
+}
+
+unsigned short stringToCode(string codeStr)
+{
+    unsigned short codeValue;
+    istringstream iss(codeStr);
+    
+    iss >> codeValue;
+    
+    return codeValue;
+}
+
+void submitSerialCommand(string command)
+{
+    command = command.substr(0, command.length() - 1); // Remove the new line character from the end of the command.
+      
+    // Remote access code commands
+    if (!command.compare("help"))
+    {
+        //printProgStr(helpMessage);
+        printf("This is the help command.\n");
+    }
+      
+    else if (!command.compare("list"))
+    {
+        codeMem.PrintAllAccessCodes();
+    }
+    
+    else if (!command.compare("log"))
+    {
+        codeMem.PrintRecentEvents();
+    }
+    else if (commandHasArg(command, "log"))
+    {
+        string arg = command.substr(command.find(" ") + 1);
+    
+        if (!arg.compare("full"))
+        {
+            codeMem.PrintEventLog();
+        }
+    }
+        
+    else if (!command.compare("clear eeprom"))
+    {
+        printf("Clearing EEPROM...\n"); 
+        codeMem.EEPROMClear();
+        printf("EEPROM Erased\n");
+    }
+  
+    else if (commandHasArg(command, "activate"))
+    {
+        unsigned short code = stringToCode(command.substr(command.find(" ") + 1));
+        int address = codeMem.ActivateAccessCode(code);   
+    
+        if (address >= 0)
+        {
+            printf("Code Activated: Address %d\n", address);
+        }
+        else if (address == -1)
+        {
+            printf("Failed to Activate: Code Already Exists\n");
+        }
+        else if (address == -2)
+        {
+            printf("Failed to Activate: Code Table Full\n");
+        }
+        else
+        {
+            printf("Unknown Error\n");
+        }
+    }
+  
+    else if (commandHasArg(command, "revoke"))
+    {
+        unsigned short code = stringToCode(command.substr(command.find(" ") + 1));
+        int address = codeMem.DeactivateAccessCode(code);
+    
+        if (address < 0)
+        {
+            printf("Failed to Revoke: Code Not Found\n");
+        }
+        else
+        {
+            printf("Code Revoked: Address %d\n", address);
+        }
+    }
+  
+    else if (commandHasArg(command, "setsc"))
+    {
+        int afterFirstSpace = command.find(" ") + 1;
+        int afterSecondSpace = command.find_last_of(" ") + 1;
+        unsigned short specialIndex = stringToCode(command.substr(afterFirstSpace, afterSecondSpace - 1));
+        unsigned short specialCode = stringToCode(command.substr(afterSecondSpace));
+    
+        // Ensure that there are at least two arguments
+        if (afterSecondSpace > afterFirstSpace)
+        {
+            codeMem.SetSpecialCode(specialIndex, specialCode);
+        }
+    }
+  
+    // Remote door commands
+    else if (commandHasArg(command, "door"))
+    {
+        string arg = command.substr(command.find(" ") + 1);
+    
+        if (!arg.compare("status"))
+        {
+            if (ix_doorLatched)
+            {
+                printf("Door Closed\n");
+            }
+            else
+            {
+                printf("Door Open\n");
+            }
+        }
+        else if (!arg.compare("open"))
+        {
+            unlatchStrike();
+            printf("Unlatching Strike...\n");
+        }
+    }
+    
+    return;
+}
+
+int main()
+{
+    char mac_addr[MAC_STRING_LENGTH + 1] = "";
+    char ip_addr[IP_STRING_MAX_LENGTH + 1] = "";
+    _mac_addr = mac_addr;
+    _ip_addr = ip_addr;
+
+    usbUART.attach(&Rx_interrupt, Serial::RxIrq);
+
+    // I/O Setup
+    qx_redReaderLed = 1;
+    qx_greenReaderLed = 1;
+    qx_readerBuzzer = 1;
+    
+    ix_tamper.mode(PullUp);
+    ix_tamper.fall(&handle_tamper);
+    ix_doorLatched.mode(PullDown);
+    ix_doorLatched.rise(&handle_latch);
+    ix_usrBtn.fall(&usr_btn_pressed);
+    
+    printf("+------------------------------------------------+\n");
+    printf("|  American Control & Engineering Service, Inc.  |\n");
+    printf("|             Door Access Controller             |\n");
+    printf("+------------------------------------------------+\n");
+
+    // Print some art to the debug terminal, because America
+    //printf("\n");
+    //asciiAces();
+    //asciiFlag();
+    
+    TCPServer srv;
+    TCPSocket clt_sock;
+    SocketAddress clt_addr;
+    
+    srv.open(_eth);
+    srv.bind(_eth->get_ip_address(), HTTP_SERVER_PORT);
+    srv.listen(MAX_TCP_CONNECTIONS);
+    
+    printf("Beginning Main Loop\n");
+    
+    while (true) {
+        if (wgReader.isNew())
+        {
+            uint16_t code = wgReader.card();
+            
+            printf("RAW: %llu COUNT: %d CARD: %u\n", wgReader.bits(), wgReader.bitCount(), code);
+            wgReader.old();
+            
+            if (config_enableAccess && codeMem.FindAccessCode(code) >= 0)
+            {
+                unlatchStrike();
+                codeMem.WriteEvent(EVENT_KEYPAD_ACCESS_GRANTED, code);
+            }
+        }
+        
+        // Check for commands from the USB port.
+        if (usb_rx_buffer[rx_idx - 1] == '\n')
+        {
+            usb_rx_buffer[rx_idx] = 0;
+            string serial_command = usb_rx_buffer;
+            
+            submitSerialCommand(serial_command);
+            
+            rx_idx = 0;
+        }
+        
+        configManager.update();
+        
+        //srv.accept(&clt_sock, &clt_addr);
+        //printf("accept %s:%d\n", clt_addr.get_ip_address(), clt_addr.get_port());
+        
+        // Get browser request.
+        //clt_sock.recv(httpReq, 100);
+        //printf(httpReq);
+        
+        // Send status page to the browser.
+        //sprintf(httpResponse, HTTP_RESPONSE, DATA_SERVER_PORT, button_presses);
+        //clt_sock.send(httpResponse, strlen(httpResponse));
+        //clt_sock.close();
+        
+        wait(0.01);
+    }
+    
+    delete _eth;
+    
+    return 0;
+}
diff -r 000000000000 -r a56239ae90c2 mbed-os.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-os.lib	Mon Sep 25 19:02:40 2017 +0000
@@ -0,0 +1,1 @@
+https://github.com/ARMmbed/mbed-os/#fc1836545dcc2fc86f03b01292b62bf2089f67c3