/* * Open Source RFID Access Controller - v4 Standard Hardware * * 10/214/2015 Version 1.40 * Last build test with Arduino v1.6.4 for Linux * Arclight - arclight@23.org * Danozano - danozano@gmail.com * * 10.14.2015 Removed the "Superuser" feature. * * Notice: This is free software and is probably buggy. Use it at * at your own peril. Use of this software may result in your * doors being left open, your stuff going missing, or buggery by * high seas pirates. No warranties are expressed on implied. * You are warned. * * * For latest downloads, see the Wiki at: * http://www.accxproducts.com/wiki/index.php?title=Open_Access_4.0 * * For the SVN repository and alternate download site, see: * http://code.google.com/p/open-access-control/downloads/list * * Latest update puts configuration variables in user.h * This version supports the new Open Access v4 hardware. * * * This program interfaces the Arduino to RFID, PIN pad and all * other input devices using the Wiegand-26 Communications * Protocol. It is recommended that the keypad inputs be * opto-isolated in case a malicious user shorts out the * input device. * Outputs go to a Darlington relay driver array for door hardware/etc control. * Analog inputs are used for alarm sensor monitoring. These should be * isolated as well, since many sensors use +12V. Note that resistors of * different values can be used on each zone to detect shorting of the sensor * or wiring. * * Version 4.x of the hardware implements these features and emulates an * Arduino Duemilanova. * The "standard" hardware uses the MC23017 i2c 16-channel I/O expander. * I/O pins are addressed in two banks, as GPA0..7 and GPB0..7 * * Relay outpus on digital pins: GPA6, GPA7, GPB0, GPB1 * DS1307 Real Time Clock (I2C): A4 (SDA), A5 (SCL) * Analog pins (for alarm): A0,A1,A2,A3 * Digital input in (tamper): D9 * Reader 1: D2,D3 * Reader 2: D4,D5 * RS485 TX enable / RX disable: D8 * RS485 RX, TX: D6,D7 * Reader1 LED: GPB2 * Reader1 Buzzer: GPB3 * Reader2 LED: GPB4 * Reader2 Buzzer: GPB5 * Status LED: GPB6 * LCD RS: GPA0 * LCD EN: GPA1 * LCD D4..D7: GPA2..GPA5 * Ethernet/SPI: D10..D1313 (Not used, reserved for the Ethernet shield) * * Quickstart tips: * Set the console password(PRIVPASSWORD) value to a numeric DEC or HEX value. * Define the static user list by swiping a tag and copying the value received into the #define values shown below * Compile and upload the code, then log in via serial console at 57600,8,N,1 * */ #include "user.h" // User preferences file. Use this to select hardware options, passwords, etc. #include // Needed for I2C Connection to the DS1307 date/time chip #include // Needed for saving to non-voilatile memory on the Arduino. #include // Allows data to be stored in FLASH instead of RAM #include // DS1307 RTC Clock/Date/Time chip library #include // Wiegand 26 reader format libary #ifdef MCU328 #include // Pcint.h implementation, allows for >2 software interupts. #endif #ifdef MCPIOXP #include // Library for the MCP23017 i2c I/O expander #endif #ifdef AT24EEPROM #include // AT24C i2C EEPOROM library #define MIN_ADDRESS 0 #define MAX_ADDRESS 4096 // 1x32K device #endif #define EEPROM_ALARM 0 // EEPROM address to store alarm triggered state between reboots (0..511) #define EEPROM_ALARMARMED 1 // EEPROM address to store alarm armed state between reboots #define EEPROM_ALARMZONES 20 // Starting address to store "normal" analog values for alarm zone sensor reads. #define EEPROM_FIRSTUSER 24 #define EEPROM_LASTUSER 1024 #define NUMUSERS ((EEPROM_LASTUSER - EEPROM_FIRSTUSER)/5) //Define number of internal users (200 for UNO/Duemillanova) #ifdef HWV4STD // Use these pinouts for the v3 Standard hardware #define DOORPIN1 6 // Define the pin for electrified door 1 hardware. (MCP) #define DOORPIN2 7 // Define the pin for electrified door 2 hardware (MCP) #define ALARMSTROBEPIN 8 // Define the "non alarm: output pin. Can go to a strobe, small chime, etc. Uses GPB0 (MCP pin 8). #define ALARMSIRENPIN 9 // Define the alarm siren pin. This should be a LOUD siren for alarm purposes. Uses GPB1 (MCP pin9). #define READER1GRN 10 #define READER1BUZ 11 #define READER2GRN 12 #define READER2BUZ 13 #define RS485ENA 6 // Arduino Pin D6 #define STATUSLED 14 // MCP pin 14 #define R1ZERO 2 #define R1ONE 3 #define R2ZERO 4 #define R2ONE 5 #endif uint8_t reader1Pins[]={R1ZERO, R1ONE}; // Reader 1 pin definition uint8_t reader2Pins[]={R2ZERO, R2ONE}; // Reade 2 pin definition const uint8_t analogsensorPins[] = {0,1,2,3}; // Alarm Sensors connected to other analog pins /* Global Boolean values * */ bool door1Locked=true; // Keeps track of whether the doors are supposed to be locked right now bool door2Locked=true; boolean doorChime=false; // Keep track of when door chime last activated boolean doorClosed=false; // Keep track of when door last closed for exit delay boolean sensor[4]={false}; // Keep track of tripped sensors, do not log again until reset. /* Global Timers * */ unsigned long door1locktimer=0; // Keep track of when door is supposed to be relocked unsigned long door2locktimer=0; // after access granted. unsigned long alarmDelay=0; // Keep track of alarm delay. Used for "delayed activation" or level 2 alarm. unsigned long alarmSirenTimer=0; // Keep track of how long alarm has gone off unsigned long consolefailTimer=0; // Console password timer for failed logins unsigned long sensorDelay[2]={0}; // Used with sensor[] above, but sets a timer for 2 of them. Useful for logging // motion detector hits for "occupancy check" functions. #define NUMDOORS (sizeof(doorPin)/sizeof(uint8_t)) #define numAlarmPins (sizeof(analogsensorPins)/sizeof(uint8_t)) //Other global variables uint8_t second, minute, hour, dayOfWeek, dayOfMonth, month, year; // Global RTC clock variables. Can be set using DS1307.getDate function. uint8_t alarmActivated = EEPROM.read(EEPROM_ALARM); // Read the last alarm state as saved in eeprom. uint8_t alarmArmed = EEPROM.read(EEPROM_ALARMARMED); // Alarm level variable (0..5, 0==OFF) uint8_t consoleFail=0; // Tracks failed console logins for lockout /* Global values for the Wiegand RFID readers * */ volatile long reader1 = 0; // Reader1 buffer long reader1dec=0; // Separate value for decoded reader1 values long reader2dec=0; // Separate value for decoded reader1 values volatile int reader1Count = 0; // Reader1 received bits counter volatile long reader2 = 0; volatile int reader2Count = 0; int userMask1=0; int userMask2=0; boolean keypadGranted=0; // Variable that is set for authenticated users to use keypad after login //volatile long reader3 = 0; // Uncomment if using a third reader. //volatile int reader3Count = 0; unsigned long keypadTime = 0; // Timeout counter for reader with key pad unsigned long keypadValue=0; // Serial terminal buffer (needs to be global) char inString[64]={0}; // Size of command buffer (<=128 for Arduino) uint8_t inCount=0; boolean privmodeEnabled = false; // Switch for enabling "priveleged" commands /* Create an instance of the various C++ libraries we are using. */ DS1307 ds1307; // RTC Instance WIEGAND26 wiegand26; // Wiegand26 (RFID reader serial protocol) library #ifdef MCPIOXP Adafruit_MCP23017 mcp; #endif #ifdef MCU328 PCATTACH pcattach; // Software interrupt library #endif #ifdef LCDBOARD cLCD lcd; #endif /* Set up some strings that will live in flash instead of memory. This saves our precious 2k of * RAM for something else. */ const unsigned char rebootMessage[] PROGMEM = {"Access Control System rebooted."}; const unsigned char doorChimeMessage[] PROGMEM = {"Front Door opened."}; const unsigned char doorslockedMessage[] PROGMEM = {"All Doors relocked"}; const unsigned char alarmtrainMessage[] PROGMEM = {"Alarm Training performed."}; const unsigned char privsdeniedMessage[] PROGMEM = {"Access Denied. Priveleged mode is not enabled."}; const unsigned char privsenabledMessage[] PROGMEM = {"Priveleged mode enabled."}; const unsigned char privsdisabledMessage[] PROGMEM = {"Priveleged mode disabled."}; const unsigned char privsAttemptsMessage[] PROGMEM = {"Too many failed attempts. Try again later."}; const unsigned char consolehelpMessage1[] PROGMEM = {"Valid commands are:"}; const unsigned char consolehelpMessage2[] PROGMEM = {"(d)ate, (s)show user, (m)odify user "}; const unsigned char consolehelpMessage3[] PROGMEM = {"(a)ll user dump,(r)emove_user ,(o)open door "}; const unsigned char consolehelpMessage4[] PROGMEM = {"(u)nlock all doors,(l)lock all doors"}; const unsigned char consolehelpMessage5[] PROGMEM = {"(1)disarm_alarm, (2)arm_alarm,(3)train_alarm (9)show_status"}; const unsigned char consolehelpMessage6[] PROGMEM = {"(t)ime set "}; const unsigned char consolehelpMessage7[] PROGMEM = {" "}; const unsigned char consolehelpMessage8[] PROGMEM = {"(e)nable - enable or disable priveleged mode"}; const unsigned char consolehelpMessage9[] PROGMEM = {"(h)ardware Test - Run the hardware test"}; const unsigned char consoledefaultMessage[] PROGMEM = {"Invalid command. Press '?' for help."}; const unsigned char statusMessage1[] PROGMEM = {"Alarm armed state (1=armed):"}; const unsigned char statusMessage2[] PROGMEM = {"Alarm siren state (1=activated):"}; const unsigned char statusMessage3[] PROGMEM = {"Front door open state (0=closed):"}; const unsigned char statusMessage4[] PROGMEM = {"Roll up door open state (0=closed):"}; const unsigned char statusMessage5[] PROGMEM = {"Door 1 unlocked state(1=locked):"}; const unsigned char statusMessage6[] PROGMEM = {"Door 2 unlocked state(1=locked):"}; void setup(){ // Runs once at Arduino boot-up Wire.begin(); // start Wire library as I2C-Bus Master mcp.begin(); // use default address 0 pinMode(2,INPUT); // Initialize the Arduino built-in pins pinMode(3,INPUT); pinMode(4,INPUT); pinMode(5,INPUT); mcp.pinMode(DOORPIN1, OUTPUT); mcp.pinMode(DOORPIN2, OUTPUT); pinMode(6,OUTPUT); for(int i=0; i<=15; i++) // Initialize the I/O expander pins { mcp.pinMode(i, OUTPUT); } digitalWrite(RS485ENA, HIGH); // Set the RS485 chip to HIGH (not asserted) /* Attach pin change interrupt service routines from the Wiegand RFID readers */ #ifdef MCU328 pcattach.PCattachInterrupt(reader1Pins[0], callReader1Zero, CHANGE); pcattach.PCattachInterrupt(reader1Pins[1], callReader1One, CHANGE); pcattach.PCattachInterrupt(reader2Pins[1], callReader2One, CHANGE); pcattach.PCattachInterrupt(reader2Pins[0], callReader2Zero, CHANGE); #endif //Clear and initialize readers wiegand26.initReaderOne(); //Set up Reader 1 and clear buffers. wiegand26.initReaderTwo(); mcp.digitalWrite(DOORPIN1, LOW); // Sets the relay outputs to LOW (relays off) mcp.digitalWrite(DOORPIN2, LOW); mcp.digitalWrite(ALARMSTROBEPIN, LOW); mcp.digitalWrite(ALARMSIRENPIN, LOW); //Initialize LEDs //ds1307.setDateDs1307(0,57,0,7,21,9,13); /* Sets the date/time (needed once at commissioning) uint8_t second, // 0-59 uint8_t minute, // 0-59 uint8_t hour, // 1-23 uint8_t dayOfWeek, // 1-7 uint8_t dayOfMonth, // 1-28/29/30/31 uint8_t month, // 1-12 uint8_t year); // 0-99 */ Serial.begin(UBAUDRATE); // Set up Serial output to value in user.h logReboot(); chirpAlarm(1); // Chirp the alarm to show system ready. #ifdef LCDBOARD lcd.begin(16,2); lcd.setCursor(0,0); lcd.print("Open Access "); lcd.print(VERSION); delay(500); lcd.clear(); #endif //Set up the MCP23017 IO expander and initialize #ifdef MCPIOXP mcp.digitalWrite(STATUSLED, LOW); // Turn the status LED green #endif } void loop() // Main branch, runs over and over again { readCommand(); // Check for commands entered at serial console /* Check if doors are supposed to be locked and lock/unlock them * if needed. Uses global variables that can be set in other functions. */ if(((millis() - door1locktimer) >= DOORDELAY) && (door1locktimer !=0)) { if(door1Locked==true){ doorLock(1); door1locktimer=0; } else { doorUnlock(1); door1locktimer=0; } } if(((millis() - door2locktimer) >= DOORDELAY) && (door2locktimer !=0)) { if(door2Locked==true) { doorLock(2); door2locktimer=0; } else { doorUnlock(2); door2locktimer=0; } } #ifdef LCDBOARD lcdStatus(1,door1Locked); lcdStatus(2,door2Locked); #endif /* Set optional "failsafe" time to lock up every night. */ ds1307.getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year); // Get the current date/time if(hour==23 && minute==59 && door1Locked==false){ doorLock(1); door1Locked==true; Serial.println("Door 1 locked for 2359 bed time."); } // Notes: RFID polling is interrupt driven, just test for the reader1Count value to climb to the bit length of the key // change reader1Count & reader1 et. al. to arrays for loop handling of multiple reader output events // later change them for interrupt handling as well! // currently hardcoded for a single reader unit /* This code checks a reader with a 26-bit keycard input. Use the second routine for readers with keypads. * A 5-second window for commands is opened after each successful key access read. */ if(reader1Count >= 26){ // When tag presented to reader1 (No keypad on this reader) reader1dec=decodeCard(reader1); // Format the card data (format can be defined in user.h) logTagPresent(reader1dec,1); // Write log entry to serial port reader1=0; reader1Count=0; /* Check a user's security level and take action as needed. The * usermask is a variable from 0..255. By default, 0 and 255 are for * locked out users or uninitialized records. * Modify these for each door as needed. */ userMask1=checkUser(reader1dec); if(userMask1>=0) { switch(userMask1) { case 0: // No outside privs, do not log denied. { // authenticate only. logAccessGranted(reader1dec, 1); break; } // Example office user class case 20: // "Door stays open on scans after 0500 user { // Any scan after 0500 results in staying unlocked until 1700. ds1307.getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year); if((hour >=5) && (hour <17)){ logAccessGranted(reader1dec, 1); // Log and unlock door 2 alarmState(0); armAlarm(0); // Deactivate Alarm doorUnlock(1); // Set door to stay open door1Locked=false; //chirpAlarm(1); } else { logAccessGranted(reader1dec, 1); // Log and unlock door 1 alarmState(0); armAlarm(0); // Deactivate Alarm door1locktimer=millis(); doorUnlock(1); // Unlock the door. break; } break; } case 255: // Locked out user { Serial.print("User "); Serial.print(userMask1,DEC); Serial.println(" locked out."); break; } default: { logAccessGranted(reader1dec, 1); // Log and unlock door 1 alarmState(0); armAlarm(0); // Deactivate Alarm door1locktimer=millis(); doorUnlock(1); // Unlock the door. break; } } } else { logAccessDenied(reader1dec,1); // No tickee, no laundree chirpAlarm(1); } wiegand26.initReaderOne(); // Reset for next tag scan } // End of Reader 1 check logic if(reader2Count >= 26){ // Tag presented to reader 2 reader2dec=decodeCard(reader2); // Format the card data (format can be defined in user.h) logTagPresent(reader2dec,2); // Write log entry to serial port reader2=0; reader2Count=0; //chirpAlarm(1); // Chirp alarm to show that tag input done // CHECK TAG IN OUR LIST OF USERS. -1 = no match keypadGranted=false; // Reset the keypad authorized variable userMask2=checkUser(reader2dec); if(userMask2>=0){ switch(userMask2) { case 0: // No outside privs, do not log denied. { // authenticate and log only. logAccessGranted(reader2dec, 2); break; } case 10: // Authenticating immediately locks up and arms alarm { // logAccessGranted(reader2dec, 2); runCommand(0x2); break; } case 20: //Limited hours user { ds1307.getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year); if((hour >=5) && (hour <=17)){ logAccessGranted(reader2dec, 2); // Log and unlock door 2 alarmState(0); armAlarm(0); // Deactivate Alarm door2locktimer=millis(); doorUnlock(2); // Unlock the door. keypadGranted=1; } break; } case 255: // Locked out { Serial.print("User "); Serial.print(userMask2,DEC); Serial.println(" locked out."); break; } default: { logAccessGranted(reader2dec, 2); // Log and unlock door 2 alarmState(0); armAlarm(0); // Deactivate Alarm door2locktimer=millis(); doorUnlock(2); // Unlock the door. keypadGranted=1; break; } } } else { logAccessDenied(reader2dec,2); // no tickee, no laundree } wiegand26.initReaderTwo(); // Reset for next tag scan if(READER2KEYPAD == 1) // If Reader2 has a keypad, users can also enter commands { unsigned long keypadTime=0; // Timeout counter for reader with key pad long keypadValue=0; keypadTime=millis(); if(keypadGranted==1) { while((millis() - keypadTime) <=KEYPADTIMEOUT){ // If access granted, open 5 second window for pin pad commands. if(reader2Count >=4){ if(reader2 !=0xB){ // Pin pad command can be any length, terminated with '#' on the keypad. if(keypadValue ==0){ // This 0..9, A..F encoding works with many Wiegand-format keypad or reader keypadValue = reader2; // plus keypad units. } else if(keypadValue !=0) { keypadValue = keypadValue <<4; keypadValue |= reader2; } wiegand26.initReaderTwo(); //Reset reader one and move on. } else break; } } logkeypadCommand(2,keypadValue); runCommand(keypadValue); // Run any commands entered at the keypads. wiegand26.initReaderTwo(); } wiegand26.initReaderTwo(); } } // end of keypad check /* Example "bed time" feature. * Check if open hours and relock doors if BEDTIME hour has passed and door1 is still open. * */ if(BEDTIME_ENABLED == 1) { ds1307.getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year); if((hour >=BEDTIME) && (door1Locked==false)){ lockall(); door1Locked=true; chirpAlarm(1); } } /* Check physical sensors with the logic below. Behavior is based on the current alarmArmed value. 0=disarmed 1=armed 2= 3= 4=door chime only (Unlock DOOR1, Check zone 0/chirp alarm if active) Modify the alarm sequence to meet your needs. */ switch(alarmArmed) { case 0: { break; // Alarm is not armed, do nothing. } case 1: // Alarm is armed { if(alarmActivated==0){ // If alarm is armed but not currently alarming, check sensor zones. if(pollAlarm(0) == 1 ){ // If this zone is tripped, immediately set Alarm State to 2 (alarm delay). alarmState(2); // Also starts the delay timer alarmDelay=millis(); if(sensor[0]==false) { // Only log and save if sensor activation is new. logalarmSensor(0); EEPROM.write(EEPROM_ALARM,0); // Save the alarm sensor tripped to eeprom sensor[0]=true; // Set value to not log this again } } if(pollAlarm(1) == 1 ){ // If this zone is tripped, immediately set Alarm State to 1 (alarm immediate). alarmState(1); if(sensor[1]==false) { // Only log and save if sensor activation is new. logalarmSensor(1); EEPROM.write(EEPROM_ALARM,1); // Save the alarm sensor tripped to eeprom sensor[1]=true; // Set value to not log this again } } if(pollAlarm(2) == 1 ){ // If this zone is tripped, immediately set Alarm State to 1 (alarm immediate). alarmState(1); if(sensor[2]==false) { // Only log and save if sensor activation is new. logalarmSensor(2); EEPROM.write(EEPROM_ALARM,2); // Save the alarm sensor tripped to eeprom sensor[2]=true; // Set value to not log this again } } if(pollAlarm(3) == 1 ){ // If this zone is tripped, immediately set Alarm State to 2 (alarm delay). alarmState(2); // Also starts the delay timer alarmDelay=millis(); if(sensor[3]==false) { // Only log and save if sensor activation is new. logalarmSensor(3); EEPROM.write(EEPROM_ALARM,3); // Save the alarm sensor tripped to eeprom sensor[3]=true; // Set value to not log this again } } } if(alarmActivated==1) { // If alarm is actively going off (siren/strobe) for 10 min (6e5=10min) if(millis()-alarmSirenTimer >=3.6e6) // Check for alarm interval expired and turn off if needed { mcp.digitalWrite(ALARMSIRENPIN,LOW); // Turn on the chime instead mcp.digitalWrite(ALARMSTROBEPIN,HIGH); } } if(alarmActivated==2) { // If alarm is activated on delay, take this action if(millis()-alarmDelay >=60000) // Turn on the siren once delay exceeds 60sec. { alarmState(1); } } break; } case 4: { // Door chime mode if((pollAlarm(3) !=0) && (doorChime==false)) { // Only activate door chime once per opening chirpAlarm(3); logChime(); doorChime=true; } if(pollAlarm(3) ==0){ doorChime=false; } break; } default: { break; } } // Log all motion detector activations regardless of alarm armed state. Useful for "occupancy detection" if(pollAlarm(0) == 1 ){ // If this zone is tripped, log the action only // if(sensor[0]==false) if((millis() - sensorDelay[0]) >=7500) { logalarmSensor(0); sensorDelay[0]=millis(); sensor[0]=true; } // Set value to not log this again for 7.5s } if(pollAlarm(1) == 1 ){ // If this zone is tripped, log the action only // if(sensor[1]==false) if((millis() - sensorDelay[1]) >=7500) { logalarmSensor(1); sensorDelay[1]=millis(); sensor[1]=true; // Set value to not log this again for 7.5s } } } // End of loop() void runCommand(long command) { // Run any commands entered at the pin pad. switch(command) { case 0x1: { // If command = 1, deactivate alarm alarmState(0); // Set global alarm level variable armAlarm(0); chirpAlarm(1); break; } case 0x2: { // If command =2, activate alarm with delay. doorUnlock(1); // Set global alarm level variable door1Locked=false; doorClosed=false; // 200 chirps = ~30 seconds delay if((pollAlarm(3) == 0) && (pollAlarm(2) == 0)) { // Do not arm the alarm if doors are open for(uint8_t i=0; i<30; i++) { if((pollAlarm(3) !=0) && doorClosed==false) { // Set door to be unlocked until alarm timeout or user exits lockall(); doorClosed=true; } mcp.digitalWrite(ALARMSTROBEPIN, HIGH); delay(500); mcp.digitalWrite(ALARMSTROBEPIN, LOW); delay(500); } chirpAlarm(2); armAlarm(1); lockall(); // Lock all doors on exit } else { // Beep the alarm once and exit if attempt made to arm alarm with doors open mcp.digitalWrite(ALARMSTROBEPIN, HIGH); delay(500); mcp.digitalWrite(ALARMSTROBEPIN, LOW); delay(500); lockall(); // Lock all doors anyway } break; } case 0x3: { doorLock(1); // Set door 2 to stay unlocked, and door 1 to be locked doorUnlock(2); door1Locked=true; door2Locked=false; chirpAlarm(3); break; } case 0x4: // Set doors to remain open { armAlarm(4); doorUnlock(1); doorUnlock(2); door1Locked=false; door2Locked=false; chirpAlarm(4); break; } case 0x5: // Relock all doors { lockall(); chirpAlarm(5); break; } case 0x911: { chirpAlarm(9); // Emergency armAlarm(1); alarmState(1); break; } case 0x20: { // If command = 20, do nothing break; } default: { break; } } } /* Alarm System Functions - Modify these as needed for your application. Sensor zones may be polled with digital or analog pins. Unique reader2 resistors can be used to check more zones from the analog pins. */ void alarmState(uint8_t alarmLevel) { //Changes the alarm status based on this flow logalarmState(alarmLevel); switch (alarmLevel) { case 0: { // If alarmLevel == 0 turn off alarm. mcp.digitalWrite(ALARMSIRENPIN, LOW); mcp.digitalWrite(ALARMSTROBEPIN, LOW); alarmActivated = alarmLevel; //Set global alarm level variable break; } case 1: { mcp.digitalWrite(ALARMSIRENPIN, HIGH); // If alarmLevel == 1 turn on strobe lights and siren // mcp.digitalWrite(ALARMSTROBEPIN, HIGH); // Optionally activate yoru strobe/chome alarmSirenTimer=millis(); alarmActivated = alarmLevel; //Set global alarm level variable logalarmTriggered(); break; } case 2: { mcp.digitalWrite(ALARMSTROBEPIN, HIGH); alarmActivated = alarmLevel; break; } case 3: { alarmActivated = alarmLevel; break; } /* case 4: { vaporize_intruders(STUN); break; } case 5: { vaporize_intruders(MAIM); } etc. etc. etc. break; */ default: { // Exceptional cases kill alarm outputs mcp.digitalWrite(ALARMSIRENPIN, LOW); // Turn off siren and strobe // mcp.digitalWrite(ALARMSTROBEPIN, LOW); break; } } if(alarmActivated != EEPROM.read(EEPROM_ALARM)){ // Update eeprom value EEPROM.write(EEPROM_ALARM,alarmActivated); } } //End of alarmState() void chirpAlarm(uint8_t chirps){ // Chirp the siren pin or strobe to indicate events. for(uint8_t i=0; iSENSORTHRESHOLD){ return 1; } else return 0; } void trainAlarm(){ // Train the system about the default states of the alarm pins. armAlarm(0); // Disarm alarm first alarmState(0); int temp[5]={0}; int avg; for(int i=0; i NUMUSERS)) { // Do not write to invalid EEPROM addresses. Serial.print("Invalid user modify attempted."); } else { EEPROM_buffer[0] = uint8_t(tagNumber & 0xFFF); // Fill the buffer with the values to write to bytes 0..4 EEPROM_buffer[1] = uint8_t(tagNumber >> 8); EEPROM_buffer[2] = uint8_t(tagNumber >> 16); EEPROM_buffer[3] = uint8_t(tagNumber >> 24); EEPROM_buffer[4] = uint8_t(userMask); for(int i=0; i<5; i++){ EEPROM.write((offset+i), (EEPROM_buffer[i])); // Store the resulting value in 5 bytes of EEPROM. } Serial.print("User "); Serial.print(userNum,DEC); Serial.println(" successfully modified"); } } void deleteUser(int userNum) // Deletes a user from the local database. { // Users number 0..NUMUSERS int offset = (EEPROM_FIRSTUSER+(userNum*5)); // Find the offset to write this user to logDate(); if((userNum <0) || (userNum > NUMUSERS)) { // Do not write to invalid EEPROM addresses. Serial.print("Invalid user delete attempted."); } else { for(int i=0; i<5; i++){ EEPROM.write((offset+i), 0xFF); // Store the resulting value in 5 bytes of EEPROM. // Starting at offset. } Serial.print("User deleted at position "); Serial.println(userNum); } } int checkUser(unsigned long tagNumber) // Check if a particular tag exists in the local database. Returns userMask if found. { // Users number 0..NUMUSERS // Find the first offset to check unsigned long EEPROM_buffer=0; // Buffer for recreating tagNumber from the 4 stored bytes. int found=-1; logDate(); for(int i=EEPROM_FIRSTUSER; i<=(EEPROM_LASTUSER-5); i=i+5){ EEPROM_buffer=0; EEPROM_buffer=(EEPROM.read(i+3)); EEPROM_buffer= EEPROM_buffer<<8; EEPROM_buffer=(EEPROM_buffer ^ EEPROM.read(i+2)); EEPROM_buffer= EEPROM_buffer<<8; EEPROM_buffer=(EEPROM_buffer ^ EEPROM.read(i+1)); EEPROM_buffer= EEPROM_buffer<<8; EEPROM_buffer=(EEPROM_buffer ^ EEPROM.read(i)); if((EEPROM_buffer == tagNumber) && (tagNumber !=0xFFFFFFFF) && (tagNumber !=0x0)) { // Return a not found on blank (0xFFFFFFFF) entries logDate(); Serial.print("User "); Serial.print(((i-EEPROM_FIRSTUSER)/5),DEC); Serial.println(" authenticated."); found = EEPROM.read(i+4); return found; } } Serial.println("User not found"); delay(1000); // Delay to prevent brute-force attacks on reader return found; } void dumpUser(uint8_t usernum) // Return information ona particular entry in the local DB { // Users number 0..NUMUSERS unsigned long EEPROM_buffer=0; // Buffer for recreating tagNumber from the 4 stored bytes. if((0<=usernum) && (usernum <=199)){ int i=usernum*5+EEPROM_FIRSTUSER; EEPROM_buffer=0; EEPROM_buffer=(EEPROM.read(i+3)); EEPROM_buffer= EEPROM_buffer<<8; EEPROM_buffer=(EEPROM_buffer ^ EEPROM.read(i+2)); EEPROM_buffer= EEPROM_buffer<<8; EEPROM_buffer=(EEPROM_buffer ^ EEPROM.read(i+1)); EEPROM_buffer= EEPROM_buffer<<8; EEPROM_buffer=(EEPROM_buffer ^ EEPROM.read(i)); Serial.print(((i-EEPROM_FIRSTUSER)/5),DEC); Serial.print("\t"); Serial.print(EEPROM.read(i+4),DEC); Serial.print("\t"); if(DEBUG>=2){ Serial.println(EEPROM_buffer,DEC); // Show user in DEC } else { if(EEPROM_buffer != 0xFFFFFFFF) { Serial.print("********");} } } else Serial.println("Bad user number!"); } /* Displays a serial terminal menu system for * user management and other tasks */ long decodeCard(long input) { if(CARDFORMAT==0) { return(input); } if(CARDFORMAT==1) { bool parityHigh; bool parityLow; parityLow=bitRead(input,0); parityHigh=bitRead(input,26); bitWrite(input,25,0); // Set highest (parity bit) to zero input=(input>>1); // Shift out lowest bit (parity bit) /* bool parityTemp0=0; for(unsigned int i=0; i<13; i++) { parityTemp0+=bitRead(input,i); } bool parityTemp1=0; for(unsigned int i=14; i<24; i++) { parityTemp1+=bitRead(input,i); } if( (parityTemp0 != parityLow) && (parityTemp1 != parityHigh) ) { return(input); } else { Serial.print("ParityTemp0: "); Serial.print(parityTemp0); Serial.print(' ');Serial.println(parityLow); Serial.print("ParityTemp1: "); Serial.print(parityTemp1); Serial.print(' ');Serial.println(parityHigh); return(-1); } */ return(input); } } void readCommand() { byte cmds=7; byte cmdlen=9; uint8_t stringSize=(sizeof(inString)/sizeof(char)); char cmdString[cmds][cmdlen]; // Size of commands (4=number of items to parse, 10 = max length of each) uint8_t j=0; // Counters uint8_t k=0; char cmd=0; char ch; if (Serial.available()) { // Check if user entered a command this round ch = Serial.read(); if( ch == '\r' || inCount >=stringSize-1) { // Check if this is the terminating carriage return inString[inCount] = 0; inCount=0; } else{ (inString[inCount++] = ch); } //Serial.print(ch); // Turns echo on or off if(inCount==0) { for(uint8_t i=0; i=5) && (millis()-consolefailTimer<300000)) // Do not allow priv mode if more than 5 failed logins in 5 minute { PROGMEMprintln(privsAttemptsMessage); break; } if (strtoul(cmdString[1],NULL,16) == PRIVPASSWORD) { consoleFail=0; PROGMEMprintln(privsenabledMessage); privmodeEnabled=true; } else { PROGMEMprintln(privsdisabledMessage); privmodeEnabled=false; if(consoleFail==0) { // Set the timeout for failed logins consolefailTimer=millis(); } consoleFail++; // Increment the login failure counter } break; } //privmodeEnabled=true; //Debugging statement case 'a': { // List whole user database if(privmodeEnabled==true) { logDate(); Serial.println("User dump started."); Serial.print("UserNum:"); Serial.print(" "); Serial.print("Usermask:"); Serial.print(" "); Serial.println("TagNum:"); for(int i=0; i<(NUMUSERS); i++){ dumpUser(i); Serial.println(); } } else{logprivFail();} break; } case 's': { // List user if(privmodeEnabled==true) { Serial.print("UserNum:"); Serial.print(" "); Serial.print("Usermask:"); Serial.print(" "); Serial.println("TagNum:"); dumpUser(atoi(cmdString[1])); Serial.println(); } else{logprivFail();} break; } case 'd': { // Display current time logDate(); Serial.println(); break; } case '1': { // Deactivate alarm if(privmodeEnabled==true) { armAlarm(0); alarmState(0); chirpAlarm(1); } else{logprivFail();} break; } case '2': { // Activate alarm with delay. chirpAlarm(20); // 200 chirps = ~30 seconds delay armAlarm(1); break; } case 'u': { if(privmodeEnabled==true) { alarmState(0); // Set to door chime only/open doors armAlarm(4); doorUnlock(1); doorUnlock(2); door1Locked=false; door2Locked=false; chirpAlarm(3); } else{logprivFail();} break; } case 'l': { // Lock all doors lockall(); chirpAlarm(1); break; } case '3': { // Train alarm sensors if(privmodeEnabled==true) { trainAlarm(); } else{logprivFail();} break; } case '9': { // Show site status PROGMEMprint(statusMessage1); Serial.println(alarmArmed,DEC); PROGMEMprint(statusMessage2); Serial.println(alarmActivated,DEC); PROGMEMprint(statusMessage3); Serial.println(pollAlarm(3),DEC); PROGMEMprint(statusMessage4); Serial.println(pollAlarm(2),DEC); PROGMEMprint(statusMessage5); Serial.println(door1Locked); PROGMEMprint(statusMessage6); Serial.println(door2Locked); break; } case 'o': { if(privmodeEnabled==true) { if(atoi(cmdString[1]) == 1){ alarmState(0); // Set to door chime only/open doors armAlarm(4); doorUnlock(1); // Open the door specified door1locktimer=millis(); break; } if(atoi(cmdString[1]) == 2){ alarmState(0); // Set to door chime only/open doors armAlarm(4); doorUnlock(2); door2locktimer=millis(); break; } Serial.print("Invalid door number!"); } else{logprivFail();} break; } case 'r': { // Remove a user if(privmodeEnabled==true) { dumpUser(atoi(cmdString[1])); deleteUser(atoi(cmdString[1])); } else{logprivFail();} break; } case 'm': { // Add/change a user if(privmodeEnabled==true) { dumpUser(atoi(cmdString[1])); addUser(atoi(cmdString[1]), atoi(cmdString[2]), strtoul(cmdString[3],NULL,10)); // Decimal add user defined dumpUser(atoi(cmdString[1])); } else{logprivFail();} break; } case 't': { // Change date/time if(privmodeEnabled==true) { Serial.print("Old time :"); logDate(); Serial.println(); ds1307.setDateDs1307(atoi(cmdString[1]),atoi(cmdString[2]),atoi(cmdString[3]), atoi(cmdString[4]),atoi(cmdString[5]),atoi(cmdString[6]),atoi(cmdString[7])); Serial.print("New time :"); logDate(); Serial.println(); } else{logprivFail();} break; } case 'h': { // Run hardware test hardwareTest(atoi(cmdString[1])); } case '?': { // Display help menu PROGMEMprintln(consolehelpMessage1); PROGMEMprintln(consolehelpMessage2); PROGMEMprintln(consolehelpMessage3); PROGMEMprintln(consolehelpMessage4); PROGMEMprintln(consolehelpMessage5); PROGMEMprintln(consolehelpMessage6); PROGMEMprintln(consolehelpMessage7); PROGMEMprintln(consolehelpMessage8); PROGMEMprintln(consolehelpMessage9); break; } default: PROGMEMprintln(consoledefaultMessage); break; } } // End of 'if' statement for Serial.available } // End of 'if' for string finished } // End of function /* Wrapper functions for interrupt attachment Could be cleaned up in library? */ void callReader1Zero(){wiegand26.reader1Zero();} void callReader1One(){wiegand26.reader1One();} void callReader2Zero(){wiegand26.reader2Zero();} void callReader2One(){wiegand26.reader2One();} void callReader3Zero(){wiegand26.reader3Zero();} void callReader3One(){wiegand26.reader3One();}