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
--- /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;
+}