Monday 11 November 2019

House Alarm with a Pi: The Smart Door


Following on from my previous post, I decided that for my next Smarthome project it would be a good idea to build a sensor for the front door, that way I would be able to detect whether the door is open or closed.
Now I could have brought a full alarm system but I find it more enjoyable to build my own, I even get to learn something new in the process.

Setting the scene

It was a cold dark evening in late December 2018, it was warm inside and I was slouching on the sofa watching the light dancing around on the living room floor, shining through the slats in the window blind I could just make out the red and blue christmas lights from next door as they gently swayed in the wind…. If only! What really happened was definitely not as interesting.

And so I now present my next post in the series, I should mention here that everything is my own work and also my own opinions, if you don't like it stop reading. On the other hand though if you have ways I could improve then let me know.

The Smart door

I have always wanted a house alarm for when we are out but due to not owning my own home, getting an alarm fitted professionally has never been a viable option, though I still wanted to be notified if the house was broken into when I'm out or on holiday. Thus I only really had two options buy a wireless system or build a system and as I normally do I decided that it would be more fun to build, so I set about researching alarm systems, how they work and design considerations. After a heavy research session that left my head fuzzy and spinning, I finally had enough information to start planning how the design would work in practice.

Alarm Sensors

The basic door and window sensors use a reed switch and act just like a light switch, except these switches use magnets to control whether they are on or off rather than using a human finger. Two types exist on the market "normally open" (NO) and "normally closed" (NC), now "normally closed" sensors allow current to travel when the magnet is within a centimeter of them (just like turning on a light switch) and if you then move the magnet more than a couple of centimetres away they open (turn off the light), on the other hand "normally open" sensors work in the opposite way.

For my system I decided that a normally closed sensor would be the better choice, with the main reason being that the circuit is considered complete and will report an "on" state when the door is closed, then when the door is later opened the circuit will be broken and report an "off" state, additionally to this if the wires are cut it also acts like the door has been opened, due to the circuit being broken.

Now that sounds like a "two birds with one stone" problem to me, except…. As I'm sure anyone with an electrical background would be able to see, this doesn't take into account that the wires could be striped and joined together to create a new unbroken circuit which is unable to report any other state, being bypassed like this means the door would never be reported as opened and we would have effectively disabled the door sensor.
Good for criminals but not so good for us.
The common approach seems to be that the circuit is monitored for a particular voltage by the alarm system, to help with this a resistor is placed either "next to" or "in line with" the door sensor and then when the door sensor is bypassed the alarm system sees the difference due to the resistor being cut out of the loop setting off the alarm.
Which would be really helpful if I had brought an alarm system.

Circuit design

I'm not an electrical engineer and neither am I a circuit designer, so what happened next was lots and lots of trial and error, over the course of about 3 months I designed at least 9 circuits with lots of failures and tested 4 circuits on a breadboard, all of them had issues or problems preventing them from working properly. Most of the issues were related to correctly detecting the voltage difference both with and without the resistor being part of the door sensor and as I'm sure you'll see my electronics knowledge leaves a lot to be desired.

The goal (I had chosen to accept) was simple I had to be able to detect when the circuit was open and closed (this was a door sensor after all), and as previously mentioned the added bonus of using a door sensor was that the open circuit would also trigger when the door was open. I also had to be able to detect when the sensor had been bypassed by placing a resistor in line with the sensor which could be used to detect the voltage difference and this is where my problems started.

Also I didn't have a proper door alarm sensor to hand, so I improvised and pulled apart an old "wirefree door alarm" I had from when the children were little. I tore it apart keeping just the reed switch and the magnet, then I fixed the magnet to the door and the reed switch to the frame with blu tac, after attaching the wires I ended up with something that looked like this.

Yes I know, blu tac! But it works and it's only for testing, I will replace this with something better once I get a proper door sensor.

Design 1

The first design was simple and with very few components it would have been cheap to produce in large numbers it also worked perfectly in the circuit simulator, but unfortunately that's where the benefits end. The design suffered from random radio and people interference (possibly mobile phone related) and was too temperamental to be of any actual use, it had numerous false positives and the fault pin never triggered in any real world testing.


So with my broken circuit, lack of electrical knowledge and a slightly better understanding of what not to do I set to work on the next design.

Design 2

Still not really sure what I was doing I started work on circuit 2. Now design number 2 was another failed attempt to use resistors but after further research I decided that with this attempt I wanted a large voltage difference between the "on" (2V or more) and the "off" (under 1V) states. After testing in two different circuit simulators I decided that this also wouldn't work as I expected in the real world and promptly dropped this attempt.


Designs 3, 4 and 5

With a successful design in the circuit simulators I wired these attempts up on a breadboard and tested them whilst connected to the door sensor. Unfortunately these also suffered from interference and never proved stable enough that they would be of any use.


By this point in time I had started researching voltage dividers and op amps to work around this problem, I had also found something called a zener diode.
Note: A zener diode acts a bit like digital children that push against each other with the strongest winning except in the real world the children are replaced with electric current that changes direction once the voltage goes above a set value.
The problem with this approach was that I didn't have a zener diode and didn't really want to spend money on something that I might give up on, so I persevered reading and designing more circuits as I went.

Further research led me to believe that I could be picking up radio interference on the wire between the sensor and the Pi due to it being made from thin strands, so to combat this I cut the ends off a 5 meter network (cat5) cable and wired it up to the door sensor.
Once I am happy the sensor works as expected I plan on replacing this cable with proper alarm cable.

Design 6, 7, 8

These designs never made it past the circuit simulator as I was never able to get the voltage sent to the GPIO pin to be less than 1 volt in an "off" state and more than 2 volts during an "on" state. After more failed circuits I decided that the only way to get this working as desired was to use transistors (after all they are used in computers with great success). I had previously added transistors to some of the circuits but without truly appreciating their abilities I mistakenly dismissed them as not being useful to my goals, which brings me onto the last circuit.


Design 9

Design number 9 was a bit of an "ah ha" moment, as the flow of electrons and how they reacted in the presence of resistors and transistors finally started to make some kind of sense, lighting up the cobwebs in my head. I tested again in two separate circuit simulators before attempting to build this on a breadboard, once built I also checked for the voltage difference at both the door state and fault pins using a voltmeter.


Happy that things looked good in the real world, I set about connecting the breadboard circuit to both the reed switch on the door and the Pi that had previously been setup for my smart doorbell project.

Now that I had the door sensor wired in, I fired up my laptop and used SSH to connect to the Raspberry Pi, once the connection had been established I checked that I could read both GPIO pins using the command line as follows.
Note: If your not used to using the Linux terminal and bash, the { } brackets in the below command might seem confusing. Put simply they force bash to expand the arguments once for each number, just remember to add commas to separate them.

$ echo "27" > /sys/class/gpio/export
$ echo "22" > /sys/class/gpio/export

$ echo "in" > /sys/class/gpio/gpio27/direction
$ echo "in" > /sys/class/gpio/gpio22/direction

$ cat /sys/class/gpio/gpio{27,22}/value

The commands listed above enable the pins so that they can be accessed as if they were a standard file (making terminal access eaiser), next we set the direction for both pins to "in" as we want to read their state and then we perform the actual read of the current pin state using the cat command.

Having verified that the door pin correctly reads a "1" when the door was closed and "0" when open, I set about testing the fault pin. The fault pin is actually supposed to read "0" when the resistor is in place and a "1" when the wires have been shorted (resistor removed), I was therefore very pleased that testing also confirmed this. With the initial testing completed I setup the following quick and dirty monitor to watch the pin state over a few days, with the intention of detecting false triggers. To make this easier I added a timestamp to the loop, this caused the time to be sent to the log file along with the pin number and of course, I included the pin state also.

#!/bin/bash

echo “27” > /sys/class/gpio/export
echo “in” > /sys/class/gpio/gpio27/direction

echo “22” > /sys/class/gpio/export
echo “in” > /sys/class/gpio/gpio22/direction

while [ 1 ]; do
    date;
    echo -n "pin 27: ";
    cat /sys/class/gpio/gpio27/value;
    echo -n "pin 22: "
    cat /sys/class/gpio/gpio22/value;
done > ./logfile.txt
I saved the script as ./gpio-check and made the script active before starting it in the background with:

#make script executable
$ chmod +x ./gpio-check

#and run in background
$ sudo nohup ./gpio-check &

After a few days I logged back into the Pi to check the times of the triggered pins, first I stopped the script with

$ sudo kill $(pidof sudo)
Then using grep I filtered down the results to a single pin number at a time by adding "-C1" to the grep below command allowed me to view the line above and the line below the matching pin number, this way I could see the date/time that the pin changed as well as the state of both pins, using the following few lines I checked that the fault pin didn't false trigger and also that the door never opened when everyone was in bed asleep.

#default pin should always show "0", we search for "1" to make sure it didn’t happen
$ grep -C1 "pin 22: 1" logfile.txt

#sensor pin should show "1" when the door is closed and "0" when open, 
# so we search for "0" as the door should never open when the house is asleep
$ grep -C1 "pin 27: 0" logfile.txt
With the logs looking clean of phantom triggers I copied my doorbell script that I made in the previous blog post editing the URL and pin number so that it would trigger when the door was opened.
After changing the script to look like the below

#!/usr/bin/python3

import requests
import RPi.GPIO as GPIO

input_pin = 27
url = 'http://example.com/api/frontdoor'

GPIO.setmode(GPIO.BCM)
GPIO.setup(input_pin, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)

def button_press(channel):
    state = GPIO.input(channel)
    if state == 1:
        req = requests.post(url, data = "on", headers = {"Content-type": "text/plain", "Accept-Encoding":""})
    return

GPIO.add_event_detect(input_pin, GPIO.BOTH, callback = button_press, bouncetime = 1)

try:
    while True:
        pass
finally:
    GPIO.cleanup()
I saved it as ./frontdor.py, made it executable and copied it to the /usr/local/bin/ folder

$ chmod +x ./frontdoor.py
$ sudo cp ./frontdoor.py /usr/local/bin/
And finally I created a new service file called “frontdoor.service” like this

[Unit]
Description=frontdoor service
After=network.target

[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/frontdoor.py
Restart=always

[Install]
WantedBy=multi-user.target

then moved the file to the correct location and started the service like so

$ sudo mv ./frontdoor.service /etc/systemd/system/
$ sudo systemctl enable --now frontdoor.service
If you have been following along you will notice that have I also removed the sleep delay from the doorbell script, this was to try and improve the delays between the event in the real world and the trigger firing in the Pi's world.

After a few weeks of testing I finally decided that the pin checks still weren't quick enough for me and I also wasn't happy with having to create a separate script and service every time I wanted to use another pin, as it would make maintenance awkward and scalability horrible.

And this will bring me nicely onto the topic of my next post where I go in search of something faster. As always until next time, happy tinkering!

Saturday 2 November 2019

Smart House Alarm with a Pi: The Dumb Doorbell

For some reason last year I decided it would be a good idea to build a smart home alarm system using a Raspberry Pi, part of the reason for this was the ease of integration into my existing smart lights but also because it would allow me to learn more about electronics. As a result, this is my venture into the world of Raspberry Pi's, electronics, GPIOs and software programming, I will of course, also include a nice sprinkling of frustration, name calling and hopefully some nice flashy lights.
The first stop on this not so magical tour is the humble doorbell, I'm not going to add cameras or intercoms yet as this is a planned improvement for the future, plus it will serve as a good learning curve for me.
I should note that I currently have 4 Raspberry Pi's in use around my home with a Pi Zero being the latest addition, I'm also currently using a recent version of openhab (https://www.openhab.org) that is setup to control my lights.
The basic doorbell is just a push button switch making my first project relatively easy to achieve, to start with, I created the following simple circuit using falstad’s circuit simulator (https://www.falstad.com/circuit/) that would pull the GPIO high. This means that when the switch is closed (doorbell is pressed) the GPIO pin is presented with a voltage of more than 2 volts, thus causing the pin to read a high state.

Note: The pin numbers listed in the image above are physical board numbers
Next I wired up the circuit on a breadboard and connected a spare Raspberry Pi that I had laying about, I then set about checking that I could actually read the GPIO state using these simple bash commands.


echo "17" > /sys/class/gpio/export                  
echo "in" > /sys/class/gpio/gpio17/direction
cat /sys/class/gpio/gpio17/value
The commands listed export the GPIO pin to be in a read state, in this case pin 17 (I use BCM numbering for all source code), then setting the direction in or out (read or write respectively) and finally by reading the value using the cat command.
Once reading of the pin state had been verified and after checking that I was able to detect the pin did in fact change state, I set about writing the following python script which waits for a specific GPIO pin to be pulled high (has over 2 volts readable) before calling the http post request.
The script is simple and most importantly it works, as such it became an important tool during my testing.
But first was the task of getting the excellent raspberry-gpio-python (https://sourceforge.net/projects/raspberry-gpio-python/) project installed, created by croston it is an absolute breeze to use. Luckily, it is also easy to setup as long as you install the correct packages first, so without further ado I present the 2 commands needed to install the GPIO module:

sudo apt-get install python3-pip python3-setuptools python3-wheel python3-dev
sudo pip3 install RPi.GPIO==0.7 requests==2.2
The modules should install without error, and as can be seen from the script below both "input_pin" and "url" can be changed to reflect the gpio pin connected on the Pi and also the website address to call when the pin is triggered

#!/usr/bin/python3

import requests
import RPi.GPIO as GPIO

input_pin = 17
url = 'http://example.com/api/doorbell'

GPIO.setmode(GPIO.BCM)
GPIO.setup(input_pin, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)

def button_press(channel):
        state = GPIO.input(channel)
        if state == 1:
            req = requests.post(url, data = "on", headers = {"Content-type": "text/plain", "Accept-Encoding":""})
        return

GPIO.add_event_detect(input_pin, GPIO.BOTH, callback = button_press, bouncetime = 1)

try:
        while True:
                sleep(1);

finally:
        GPIO.cleanup()
Saving the script as ~/doorbell.py and making it executable with

chmod +x ~/doorbell.py
Allowed me to run the script directly, now that the wiring on my Pi was tested and the script working to a satisfactory level I began the process of making the script into a service by copying the file to the /usr/local/bin folder with the command:

sudo cp ~/doorbell.py /usr/local/bin/
Copying the file like this places it in a location that is accessible to systemd. I then set about creating the required systemd service file with the following contents

[Unit]
Description=doorbell service
After=network.target

[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/doorbell.py
Restart=always

[Install]
WantedBy=multi-user.target
saving the file as /etc/systemd/service/doorbell.service I simply had to enable and start the service with

sudo systemctl enable --now doorbell.service
The next step was to clip the case back on the Pi and attach the breadboard to the Pi case, using elastic bands to hold the breadboard in place I set up the Pi in a convenient position and wired in my doorbell, unfortunately the use of a camera eluded me at this point so no pictures were taken during these steps, instead you will have to use your imagination to decide what it looked like.
After checking for errors I quickly ran outside to make sure that the doorbell did indeed work as I was expecting.
Note: This step would have been a great achievement but in my haste I neglected to setup the trigger and response actions in my home controller software (openHAB)
With the response actions finally setup I had a working doorbell that flashed the living room lights when pressed, albeit with a slight delay of around 2 seconds.
I tidied up the cabling and secured the Pi in its new location near the door then sat back and waited for a valid press of the doorbell.
Fortunately or unfortunately, depending on your point of view it was only around an hour that had passed before the lights flashed in my living room signalling that the button had been pressed.
I eagerly jumped up and checked outside to see who was at the door and was surprised to find that the space in front of the door was empty, slightly confused I went and sat back down when about two minutes later the lights flashed again.
Realising that it was probably an issue with my dodgy wiring I rechecked all the cables again making sure that the connections to the doorbell and back to the breadboard were good, at this point I also connected to the Pi from my laptop using SSH so I could monitor the state of the pin as the trigger fired.
To assist with the debugging process I added a few print statements to the python code to display the state of the pin and to tell me when the URL request was sent, restarting the service I monitored it using

sudo journalctl -f --unit=doorbell
I then sat back and waited for my phantom trigger to fire again, but after waiting for most of the day I finally gave up watching the pin and shutdown down my laptop.
That evening whilst watching TV the lights flashed, deciding that this definitely wasn't going to be an easy fix I shutdown the Pi until I would have more time to look at it.
Plenty of research ensured over the preceding few days and it all seemed to point to radio signals causing the interference, from what I was reading the recommended solution was to either; add another resistor to the circuit or introduce a capacitor both of which would reduce the false triggers.
After carefully weighing up the options I decided on the resistor (I didn't have a capacitor to hand so couldn't use that option anyway).
Following the recommendation of the internet at large I added another resistor to the circuit and after two failed attempts with different value resistors I finally chose one that was large enough to stop the interference, thus giving me a fully working doorbell that looked something like the circuit image below.

The doorbell stayed up for around two weeks and I never received a phantom trigger but as will probably become a common theme I did have other issues.
In fact this project had two issues one was with timing, which unluckily for me raised its ugly head once I added sound to the bell event. What I discovered in this instance was that the sound played as soon as the button event was received and if you pressed the button two or three times in quick succession the music would play over the top of the already playing tune, this sounded horrible at worst and had a weird sounding echo at its best, this definitely needed fixing!
Further to this I was also experiencing issues with the WiFi connection in my house which caused the wireless connection to drop and for some reason it would not automatically reconnect, the easy solution was to restart the Pi from the main plug but again this was not ideal as I wasn't around all the time during the day and night to restart the Pi.
As it turned out, fixing the WiFi disconnection issue was relatively easy as I was able to monitor the connection and restart the Pi through a bash script that looks like the below with a simple service file that was similar to the one above.

#!/bin/bash

echo "allowing network to activate"
sleep 10m

while true; do
        echo "checking network status"
        curl --silent http://example.com/index.html > /dev/null;
        if [[ $? == 0 ]]; then
                sleep 5m
        else
                echo "network lost, restarting!
                reboot
        fi
done
Note: I have recently reinstalled Raspbian on this Pi and since the reinstall the WiFi connection has had much better stability.
In the previous python script the "button_press" function is called over and over again all the time the button is being held down, so to fix the timing issue I added a simple sleep delay with a countdown that only checks the pin state when the countdown has expired. Once the doorbell is pressed the countdown is reset back to 5 and then it is reduced by one every second, when the countdown hits 0 the pin state is checked again if the pin is still being held high then the address pointed to by "url" is called and the countdown is set back to 5 again. Thus the cycle continues, the script below explains it much better than I ever could.

#!/usr/bin/python3

import requests
import RPi.GPIO as GPIO
from time import sleep

GPIO.setmode(GPIO.BCM)
countdown = 0
INPUT_PIN = 17
url = 'http://example.com/api/doorbell'

GPIO.setup(INPUT_PIN, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

def button_press(channel):
        global countdown
        state = -1
        if countdown == 0:
     print("countdown is 0")
                state = GPIO.input(channel)
                print("reading pin state: %s"%(state))
        if state == 1:
                print('url: %s'%(url))
                req = requests.post(url, data = "on", headers={"Content-type": "text/plain", "Accept-Encoding""})
                if req.status_code != requests.codes.ok:
                        req.raise_for_status()
                countdown = 5
        return

GPIO.add_event_detect(INPUT_PIN, GPIO.BOTH, callback=button_press, bouncetime=1)

try:
        while True:
                if countdown > 0:
                        countdown = countdown - 1;
                sleep(1);

finally:
        GPIO.cleanup()
After a few weeks of testing I finally considered this part of the project a success, I added the ability to send tweets out once the bell was pressed and also added other lights in the house so that they flashed, I also changed the sound that played finally settling on a lions roar, because, well, why not!
I will of course write about these other attempts in upcoming posts but until then, happy tinkering :)