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

main.cpp

Committer:
acesrobertm
Date:
2017-09-25
Revision:
0:a56239ae90c2

File content as of revision 0:a56239ae90c2:

#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;
}