Last month we set ourselves on the journey of exploring how alarms work. The project, while fully functional, lacked some crucial features to make it useful. This month we will expand on these sub-systems to better understand exactly how to maximise their functionality.
Our alarm system is taking shape, but there are a few core functionalities that make it a bit tricky to use in the real world. Some of these are RFID key learning, and exit/entry delay. While IoT capability isn’t really a critical functionality, it’s certainly nice to have, so we’ve added in the fundamentals of IoT arm/disarm, and a few other cool things too.
THE BROAD OVERVIEW
We need to extend the features that we implemented the first part of this project. For this, we will implement some changes such as adding a learning function to the RFID system to enable the user to add keys/fobs into the system; and adding the Ethernet shield and enabling network connectivity, which allows us to enable IoT functionality and control the alarm with a web app. We’ll also add in an entry and exit delay to this system by defining particular zones to have a no alarm period. And we’ll add a door strike function, which will enable you to remotely unlock any door from your phone, before finally breaking out the unit with screw-headers that will create a much more robust device.
To do all this, we have added three extra libraries: SPI, SD and Ethernet to our code. These are all required to enable the use of the Ethernet shield and its hosted SD card slot. All three are included with the standard installation of Arduino’s Processing.
CIRCUIT DIAGRAM
After Part 1 was published, we had a few requests for a standard schematic to assist with wiring. This is definitely worthwhile, so here it is! It's important to note that the Arduino pins for the Passive and Active Zones are indicative only. Connect Active / Passive zones to any of the configured pins. The key difference is to ensure grounding of passive zones. This is also true for Relay Output and Buzzer. They demonstrate connectivity, however the circuit supports multiples of these systems from different pins.
RFID CODE LEARNING
In part one we implemented a RFID reader that was used to arm and disarm the system. This works quite well for that simple function, but it lacked certain elements that would make it more productive. The primary limitation was that cards and fobs could only be added to the system manually. This wasn’t a terribly difficult process during prototyping, but once the unit is installed it’s more tricky.
We address this by adding in the required functions to make the system learn. One of the advantages in using the Ethernet shield is that in addition to enabling IoT functionality it includes an onboard SD card. We will use this function to store our RFID keys.
In the original version of the code, we created an array and placed our keys as strings in it. We also defined the number of keys we needed. We still have that limitation in that we need to specify the number of keys, but we have set it up to have 16 keys in the system. This could very easily be contracted or expanded as required.
We have added the SD card library and use it to read and write the RFID keys to the SD card. When the array is initialised, all the elements are set to null. We then open the RFID.TXT file located in the root directory of the card.
One limitation with the code learning however, is the lack of ability to “unlearn” codes. The complexity here is that it’s not as simple as a “scan and delete” operation. If you need to remove a tag from the system, there’s a 99% chance you don’t have the tag handy to scan. And if you did, why not just take it from the person who has it, similar to taking back a set of keys? If you cannot get the RFID key back, you would need to remove the RFID file on the SD card and relearn all existing tags.
A Long Press Goodnight
Having a button for every function is not always practical. It is common practice these days for buttons to be held down for a “long press” and a “short press” thus enabling any button to serve two purposes. This reduces hardware complexity but does present a software problem. In searching for an answer, we were able to correlate some quick solutions, but many of them required multiple variables and quickly became quite complicated. In this case, we needed to start the RFID learning mode, but considering how often it would be used we did not feel it warranted another button.
We already had code in the main loop that would check to see if the test button was high and if it was then the test function would run; however, by using the millis() function we can time how long the button has been held down. In this case, we see if the button is still high after 500ms, and if it is then we execute our other function, which in this case is rfidLearn. Where the limitation lies here is that we call a delay for 500ms - half a second. In testing, this was enough time to differentiate between the two; but feel free to experiment as this is quite a handy function to have. The code can easily be expanded to other buttons.
// Button 2 TEST
if (digitalRead(testButton) == HIGH) {
delay(500);
if (digitalRead(testButton) == HIGH) {
rfidLearn();
delay(2000);
} else {
testSystem();
}
delay(200);
}
ENTRY AND EXIT DELAY
What our previous system lacked was the ability to define certain zones as delay zones. The theory behind this is that if your panel is located somewhere on the premise that may take you longer to get out, then the system will give you a defined grace period. Likewise, upon re-entry to the premises, there is a delay through certain zones before you deactivate the alarm.
Also, we need to make sure all zones are OFF; for example, if you defined zone 3 to be a door that has a reed switch on it you would need to close the door, then arm the system. The system will give you grace to break that circuit and re-close the door before it will sound an alarm if the door is opened. It is currently set for 10 seconds. You can change this time using the exitDelay variable. To complement this, we have instigated an entry delay. This works in the same way when you open the door or trigger a PIR that is near or on its way to the disarm panel; the system will give you a grace period in which to disable the alarm. If you do not turn it off, then the alarm activates.
HOW DOES IT WORK?
This sounds quite simple however, it ends up being rather complex. The Arduino system works by creating one main loop that continues until the unit is shut down. This idea works quite well for something like an alarm system. We start the system and arm it, then simply run the loop through, checking all the zones to see if it is broken and start again. Where this becomes complex is when we need the system to multi-task or thread code.
We could simply assign our entry and exit delay zones, then use a delay function to halt the system for a set period. However, this stops the entire main loop from functioning which in turn renders all of our zones as inoperable. We might as well just disarm the system. How do we create a situation in which the main loop will continue, even when certain zones are individually put in a time-limited idle state?
When using the entry and exit delays we a different thread of code while still letting the main loop continue. To achieve this, the system runs through its normal zone checking code; however, instead of simply “tripping” if the line is high, the system now checks to see if this particular zone is contained with the delayZones array. If not then it trips the alarm. If it is then (depending on entry or exit), it flags to the system that the alarm is active but that the delay is in place. Now our issue becomes how do we set a predefined time delay? We can’t use the delay function as this will halt the main loop, and the Arduino does not have a time function without the use of a real time clock (RTC) module. What we do have is the millis() function, which will tell us the total number of milliseconds since the Arduino board began running the current program. Using this we create a timestamp and place it in a variable of type LONG. The code then continues looping and rechecking the other zones. Once the difference between the timestamp and the current time is greater, the delay you have defined the system reverts to its normal state and triggers (on entry) or active idle (on exit).
A NOTE ABOUT THE MILLIS() FUNCTION: The millis function returns the number of milliseconds since boot. Be mindful that this returns an unsigned LONG and if you decided to use it in conjunctions with integers (int) you would likely have erratic arithmetic problems.
INTERNET OF THINGS (IOT) ACCESS
To bring the alarm into modern times, we need to implement some IoT functionality. Using the Ethernet shield, we have created a basic IoT runtime. To set it up you just need to put an IP address into the code. Alternatively leave it empty to let the network assign one.
//IPAddress ip(10, 0, 0, 177);
IPAddress ip();
The server is initiated during setup and calls the function by using chkEthernet() which is called from the main loop. This function retrieves the URL that is sent to the Arduino from the web app. We then go through the CHAR string until we find “?playback”. The provided web app simply pushes a single CHAR with each button press. This could easily be expanded to have more than four functions. The code uses “if” statements to look at the CHAR and then run code based on that evaluation.
if (linebuf[15] == ‘D’) {
disarmSystem();
}
if (linebuf[15] == ‘U’) {
doorStrike();
}
In this instance, we have created the following four functions: Arm/disarm, RFID learn, and remote door strike control.
The arm/disarm function simply executes the armSystem() and disarmSystem() functions when we send “A” or “D” via the web app. The RFIDlearn function calls the rfidLearning function, which put the system into a loop and waits for the user to scan a key.
We are going to use the same type of relay module to control the siren for the door strike. In this case, we’ll tie it to output04 (Pin33) and create a new function called doorStrike(). This function will serve to send the pin high, activating the relay which will then switch on the door strike. Our next problem is how long do we keep the strike active? The code itself is quite straight forward; we just add a delay() in after the pin has gone high. As this delay is something that you may need adjust later, we will add a global variable called strikeDelay. From here we can easily modify.
To control this via the IoT function, we copy the code located in the index.html file in the web app section. To create a new button we go to this section of code and duplicate it. You then title in “Unlock” and use the letter U as the keyCode. In the INO file, we go to the Ethernet function and duplicate the “if” statement for RFID. Change the R to a U and then we change the function to doorStrike.
What is a Door Strike?
A door strike is used to electronically control a door lock. These are often used in banks and hospitals to stop members of the public going where they are not meant to go. They can be controlled from many different locations, require power to be active, and move the mechanism out of the way, allowing the door to open. In our case, we are using an output pin to drive a relay, which in turn switches power to the door strike. In creating the function to control this, we can call it from any point in the program. This same functionality can be used to realistically control any electric lock, automatic door, or other electronically triggered access.
A MOVE FROM BREADBOARD
One of the major hurdles we faced with installation previously, was the unit being on breadboard. We attempted to address this with some basic Veroboard and create screw terminals to allow easy zone wiring and such.
Since most of our breakout is on the far-end header, we can use a 34-way ribbon header to take the pins to Veroboard. The issue here is that this header on the mega is 36-way which is not a standard size. However, the top two pins are just 5V and GND. We can therefore use a 34-way header and an angle or straight adapter to easily plug into the current header. On the Veroboard, we’ll place a socket which then allows us to control the alarm’s I/O. This leaves us only with 5V and GND to deliver to the Veroboard separately. We have added the three buttons and a breakout for the RFID; these are all the items that are connected via the end GPIO pins.
Trying to describe the wiring line-by-line is exhaustive, and describing the soldering of the Veroboard is virtually impossible. Through this process, it has become clear this would be a better job for a custom PCB, which would solve all the problems we have in getting everything reliably broken out with hard connections. A custom PCB may really be the only way to handle this properly; however, if you took the time to construct your own Veroboard edition, it would likely be just as robust (though fault-finding could be a challenge). We are continuing development of resolution that adequately resolves this problem.
WHERE TO FROM HERE?
In preparation for part 3 of this article series, we’ve found a few challenges, which we’ll attempt to overcome. Our end-goal is to create a fully installable item, which can be practically installed with reliable operation.
12V SENSORS : One major request that has been made a few times, is to expand the capabilities to use common PIRs. Generally these run from 12V, so we’ll implement a circuit to allow some versatility of sensor use. This is especially important when you want to replace an existing alarm system, and don’t want to have to replace every PIR in the building.
IoT SECURITY : It’s important to note that we still don’t have any security around this functionality. Anyone who knows the system could send a request to the system to disarm it. We will attempt to overcome this with some basic “public/private key” implementation in part 3 also. This involves creating a basic authentication method to help prevent unauthorised usage.
RFID MANAGEMENT : It makes sense to try and manage your RFID tags via a web interface. The Arduino isn’t really capable of serving a complex page, but expanding on our HTML package might yield some useful abilities.
RFID TIME CONDITIONS : What if you had a cleaner coming each week? Why not set access to allow them to disarm on a Tuesday, but not on other days? Or perhaps you have office staff? Management can disarm the system anytime, but general staff may only do so during daylight hours or on weekdays.
CONTROL PANEL : We are creating a full 3D printed case for the controller. The case is easy enough to design, but how do we feed so many data lines to the controller? We may have to move to I2C for the LCD to reduce the number of cable cores required.