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
Revision 0:a56239ae90c2, committed 2017-09-25
- 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
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