I just connected my door buzzer to the Internet

Henry
11 min readDec 30, 2016

--

tl;dr: I built a device that allows you to let yourself in to your building using the Internet. All you need is some old speaker wire, a relay, and a mini-computer.

The outside door to my building is operated by an RFID key fob. You touch it to the door, and it unlocks, allowing you access.

I was faced with a little problem: my building management company would only give me one key fob. I have kids, but there’s only one adult on the lease, so only one key fob. So — without giving my only set of keys to my guests— how could I let them in to my apartment when I wasn’t there.

There were a few solutions: I could clone the RFID card through various online services. CloneMyKey.com wanted $20, but the drawback was that it would mean sending away my keys and struggling to access the building in the meantime. Despite extensive searches, and Reddit questions, I was unable to find a similar service in NYC.

So the obvious solution was to engineer a solution using electronics. With my handy Phillips screwdriver, I whipped the front of my door buzzer in my apartment. It was just a simple handset with a buzzer, and a button to release the catch on the door downstairs.

The metal flap on right-hand side was under the button I pressed to release the front door. Once I closed that circuit with my bare finger, I could hear the door buzzing downstairs over the intercom.

Using my trusty multi-meter, I set about measuring the levels across each pin. Note to self: check voltages before touching it with your bare hands next time.

The system ran at 25VAC, at just a few milliamps. But, the most useful thing I found was that joining pins 5 and 7 completed the circuit to the lobby, and open the door went.

There was a simple solution: I could connect the button downstairs that makes my intercom buzz (using the black solenoid at the top left) straight to the door opening circuit. Instead of causing the solenoid to buzz in my apartment, someone pressing the button downstairs would immediately open the door with no involvement from me. Simple, but not at all safe, and not really the elegant solution I was looking for.

Instead, I looked for a more sophisticated solution— using an Arduino electronic prototyper sitting unused in my draw — and started building. My aim was to have a simple web page, where you could tap a button, and cause the door to buzz for a set number of seconds. This would allow anyone with a particular web address to be able to open the door themselves when coming to visit.

Your first thought might be that this isn’t secure either — and it isn’t really — but at this stage this is about building a proof of concept that’s extensible. I can add a layer of security second once I prove that it can be done.

The Arduino itself is able to handle low voltage DC electricity. However, since the door buzzer circuit was higher voltage, and alternating current, I needed to find a way to indirectly bridge the connection between the pins on the door buzzer’s circuit board. The solution is simple: a relay.

Borrowed from the Internet, this is the circuit I built. The relay breaks the live wire from the AC power source (the buzzer system) heading to the destination (the outside door’s lock). When the relay is actuated by the Arduino—and the computer code within — the circuit completes and the door opens.

In the end, I used some old speaker wire to connect the pins from inside the intercom handset to the relay, and back again. But before I got closed to connecting it to a live circuit, I worked on my solution using the computer. A relay makes an audible “click” when it’s turned on, so I was able to test my code without connecting to the real-world intercom system.

My Arduino circuit board is paired with a WiFi module, which allows me to create a simple web server, and share it on the Internet all in one matchbox-sized unit.

(The full code for this project is available here: https://github.com/digitalhen/arduino-json-api/blob/master/arduino-json-api.ino. Follow along if you can.)

The code does a few simple things:

  • Connect to the WiFi network in my apartment
  • Start a simple web server, and wait for a connection
  • On receiving a connection, break up the address that requested to understand what is being requested
  • If the request isn’t valid, reject it
  • If it is valid, then do what it says, and tell the user

Here’s how I connect to the WiFi:

#include <SPI.h>
#include <WiFi101.h>
char ssid[] = "yourNetwork"; // your network SSID (name)
char pass[] = "secretPassword"; // your network password
int status = WL_IDLE_STATUS;
WiFiServer server(80);
void setup() {
Serial.begin(9600); // initialize serial communication

// check for the presence of the shield:
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println("WiFi shield not present");
while (true); // don't continue
}

// attempt to connect to Wifi network:
while ( status != WL_CONNECTED) {
Serial.print("Attempting to connect to Network named: ");
Serial.println(ssid); // print the network name (SSID);

// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
}
server.begin(); // start the web server on port 80
printWifiStatus(); // you're connected now, so print out the status
}
// simple method for detailing the WiFi connection to the console
void printWifiStatus() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());

// print your WiFi shield's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);

// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
// print where to go in a browser:
Serial.print("To see this page in action, open a browser to http://");
Serial.println(ip);
}

There’s a lot going on here, but essentially this is identical to the sample code from the Arduino themselves. What’s different in my example is how I handle the loop() method when someone connects to the server.

In this example, I’m listening for a connection, and then processing a request from the user. In this case, I’m expecting a request that has this format in the URL: http://domainname.com/ON/2. The intention here being to turn ON pin number 2.

Right now, this code does nothing in particular, except receive the request, and then figure out what is being asked of it:

void loop() {
WiFiClient client = server.available(); // listen for incoming clients

if (client) { // if you get a client,
Serial.println("new client"); // print a message out the serial port
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected()) { // loop while the client's connected
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
if (c == '\n') { // if the byte is a newline character

// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:application/json");
client.println();

// parse the URL to figure out what we're doing, by tokenizing based on the '/'
action = getStringPartByNr(getRequest, '/', 1);
actionPin = getStringPartByNr(getRequest, '/', 2).toInt();

// The HTTP response ends with another blank line:
client.println();

// end the connection to the client;
client.stop();
Serial.println("client disonnected");

// break out of the while loop:
break;
}
else { // if you got a newline, then clear currentLine:
if(currentLine.startsWith("GET ")) {
getRequest = currentLine;
}
currentLine = "";
}
}
else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
}
}
}
}

First, this borrows heavily from the Simple Web Server WiFi sample code. Read that first, and you’ll get the idea of what we’re trying to achieve. I’m less concerned with interacting with the Arduino’s pins at stage, but instead trying to understand what the user is requesting.

The key part is where I read the request from the user. I wait until I’ve reached the end of a line (where we detect a newline with \n). At this point, if the line begins with “GET” — the line with the request to the web server — we stash it in the getRequest variable. We’ll use it later. This is how we do it:

if(currentLine.startsWith("GET ")) {
getRequest = currentLine;
}

Once we have this, we can break it apart to see what the components are. We do this by tokenizing (breaking up) the string like this:

action = getStringPartByNr(getRequest, '/', 1);
actionPin = getStringPartByNr(getRequest, '/', 2).toInt();

Two things are happening here. I’m getting the first bit of the string after the first slash — the bit that says ON in our example. I’ve called that the “action” in this example. Second, I’m extracting the string after the second slash— the bit that says 2 in our example. That’s the pin I want to take action on in the example.

With these two key pieces of information, I can now make something happen in response to the user request. I can make the door buzzer turn on and off!

So, now the code to make that action happen:

// set up the json processing here
DynamicJsonBuffer jsonBuffer; // json buffer
JsonObject & root = jsonBuffer.createObject(); // top level JSON object
JsonArray & pins = root.createNestedArray("data"); // nested array to hold the detail of the pin statuses
// if we have an action, and it's state, and we have a particular allowed pin
if (action.length() > 0 && action == "STATE" && actionPin > 0 && arrayIncludeElement(validPins, actionPin, validPinSize)) {
//client.println("You want the state of pin " + String(actionPin));
pinMode(actionPin, OUTPUT);
int val = digitalRead(actionPin);
root["status"] = "ok";
root["action"] = action;
JsonObject & pin = pins.createNestedObject();
pin["actionPin"] = actionPin;
pin["val"] = val;
root.printTo(client);
// TODO: allow for checking of the state of every valid pin
} else if (action.length() > 0 && action == "STATE" && actionPin == 0) {
root["status"] = "work in progress";
root["action"] = action;
/*
for(int i=0;i<validPinSize;i++) {
pinMode(validPins[i], OUTPUT);
int val = digitalRead(validPins[i]);
JsonObject& pin = pins.createNestedObject();
pin["actionPin"] = validPins[i];
pin["val"] = val;
} */
root.printTo(client);
// turns on the specified valid pin and puts the result back in the json
} else if (action.length() > 0 && action == "ON" && actionPin > 0 && arrayIncludeElement(validPins, actionPin, validPinSize)) {
//client.println("You want to turn on pin " + String(actionPin));
pinMode(actionPin, OUTPUT);
digitalWrite(actionPin, HIGH);
int val = digitalRead(actionPin);
root["status"] = "ok";
root["action"] = action;
JsonObject & pin = pins.createNestedObject();
pin["actionPin"] = actionPin;
pin["val"] = val;
root.printTo(client);
// turns off the specified valid pin and puts the result back in the json
} else if (action.length() > 0 && action == "OFF" && actionPin > 0 && arrayIncludeElement(validPins, actionPin, validPinSize)) {
//client.println("You want to turn off pin " + String(actionPin));
pinMode(actionPin, OUTPUT);
digitalWrite(actionPin, LOW);
int val = digitalRead(actionPin);
root["status"] = "ok";
root["action"] = action;
JsonObject & pin = pins.createNestedObject();
pin["actionPin"] = actionPin;
pin["val"] = val;
root.printTo(client);
// turns on a valid pin for a fixed length of time, currently 5 seconds. we turn off once the client has been disconnected so they don't have to wait around
} else if (action.length() > 0 && action == "BUZZ" && actionPin > 0 && arrayIncludeElement(validPins, actionPin, validPinSize)) {
//client.println("You want to buzz pin " + String(actionPin));
pinMode(actionPin, OUTPUT);
digitalWrite(actionPin, HIGH);
int val = digitalRead(actionPin);
root["status"] = "ok";
root["action"] = action;
JsonObject & pin = pins.createNestedObject();
pin["actionPin"] = actionPin;
pin["val"] = val;
root.printTo(client);
// nothing else matches so it must be a malformed request
} else {
root["status"] = "fail";
root.printTo(client);
}

You also need some variables at the top of the code to capture your choices and list the valid pins:

String action = ""; // what is the action
int actionPin = 0; // which pin are we performing the action on
int validPins[] = {1,2,3,4,6,8,9,10,11,12,13}; // which pins should be allowed for actions by the program (some are reserved for the WiFi shield, so I've excluded them)
int validPinSize = 11; // how long is the array of pins --- this should be updated
String getRequest = "";

What I’m doing here is:

  • Check to see if I have an action, and then check to see which action it is
  • Check to see if I have a pin specified, and then check it against the list of valid pins (some pins are not valid because they are in-use by the WiFi shield)

Once we have both of those, we actuate the pin and set the voltage to high or low, or in a special case we “buzz” it for a few seconds. Then we build a response as a JSON object.

JSON is short for JavaScript Object Notation (if you’ve made it this far, you probably knew that already), but is essentially a way for one piece of code on one computer to get data from another. The intention in the long term is to create a well-designed interface that interacts with my door buzzer.

One final thing. You might notice that I’m handling the buzzing by turning on the pin, but not turning it off. I need this final step to happen after we return a result to the user, otherwise they will have to sit for several seconds waiting for a response.

Here’s how I handle the automatically turning off the buzzing pin, with a five second delay:

// stop a buzz if needed
if (action.length() > 0 && action == "BUZZ" && actionPin > 0) {
delay(5000);
digitalWrite(actionPin, 0);
}

This is the finished product:

A black wire runs from the door buzzer
The relay (connected to the buzzer) and Arduino (connected to WiFi and USB for power) sit on the floor

Future plans are to permanently wire in the automation in to the system, and to provide power to the Arduino directly from the 25VAC running in the door entry system. Also, I want to add code to return the status of multiple pins at once, so I can provide a dashboard to the system as a whole.

Even this temporary solution worked perfectly: for a recent party, I gave my guests a time-limited URL. Every guest buzzed themselves in from their smartphones, and made their way upstairs without calling up.

--

--

Henry

Newsman and daddy in New York City. @digitalhen everywhere.