raspberry pi door bell project: part 2

Success! As of last night, I officially got the doorbell detector working. I can’t tell you what a great feeling that is. Ah, to see your weird ideas come to life.

If you haven’t read part 1 of the series on this project, I suggest you do that just to see where I’m coming from here. Just to recap, the idea is this:

I want to install the SkyBell on the front gate (which we lock sometimes) so we get all the features of the SkyBell, but also tap into the signal from the doorbell so that I can fire events when it happens. Like playing a sound/song/voice inside the house on the SONOS, for example.

To do that, I did a ton of reading and what ended up being the most simple solution was using a little device called an optocoupler. Oh the things I’ve learned on my first Pi project.

What an optocoupler is, put simply, is a way to keep electrical signals separate while still allowing them to communicate. Sort of like that plexiglass barrier they use in prisons to separate the probably dangerous people from the presumably less dangerous people. Inside the optocoupler is a simple setup. On one side — the “dangerous” side you’re signaling from — you’ve got your LED. On the other side, you’ve got your photoresistor (a device that increases resistance with brighter light). All of this is sort of glued together with an epoxy and put inside of an opaque casing with some leads sticking out on either side to provide power and ground.

Credit: WikipediaThis diagram (left) shows all of this happening. On the left side is the LED sending light when there is signal over to the reader/collector on the right side. In my use case, I was able to wire the 12-volt “hot” (non-dashed) input to the LED side (12 volts resisted down to 1.2 volts via three 220-ohm resistors), then attach “ground” (dashed).

On the other side, I wired pin 3 to 3.3 volts from the Pi with another 220-ohm resistor in-between. Then wired pin 4 for GPIO 17 on the Pi. Now one thing you might notice here is that I didn’t wire anything to ground on the Pi, and that’s because I learned about a magical thing called a pull-down/pull-up resistor. Effectively, there are built-in resistors on GPIO ports that you can activate through code.

The reason for the existence of these things is, apparently, because physical connections aren’t either “off” (0) or at varying degrees of “on”; they float! So it’s hard to get a solid reading without assuming an on-state (pull-down resistor) or assuming an off-state (pull-up resistor). Any way you want to think of it, these built-in resistors are a huge space saver and allow for much simpler set-ups.

Oh, and I ended up going with the Sharp PC817 optocoupler/opto-isolator:

Sharp PC817 Optocoupler/Opto-isolator

Optocoupler Diagram, Credit: Wikipedia

To make a long (and very boring) story short, it means I can “read” a 12 volt signal safely into a 3.3 volt input. And that’s exactly what I did:

ASCII Raspberry Pi Doorbell Design

This is a crappy diagram and actually does not match the final “masterpiece,” but it certainly conveys the general idea. I’ve got a 12 volt DC power supply that provides current at 1 amp. Originally, it had a barrel connector (the round kind you stick into all kinds of devices), but I cut that bit off and was left with two wires — one hot and one neutral. As the people at SkyBell recommended, I spliced a 10-ohm/10-watt resistor onto the end of the non-dashed adapter wire and, as you saw in the previous post, hooked that up to the SkyBell (the left portion of the above diagram). I tested this setup with my multimeter and it worked fine, even though I had the positive/negative leads switched. Ultimately, I knew I was seeing the remaining 11-volt output drop to around 0 volts for about 8 seconds when the doorbell was pushed1.

Once I got my Raspberry Pi delivered, I had it set up and hooked up to a break-out board within an hour. I didn’t have a ton of time to play with it, but I was able to set up a simple push-button setup to light up a single LED (just to test it).

The next step was to hook the doorbell up using the photocoupler to actually read the voltage as I’d seen in bits and pieces around the web, but the trouble is that I was freaking out internally. I really did not want to break the SkyBell after forking over $100 for it, and I knew just enough about circuits to be dangerous but not effective. So I bothered my buddy Justin a lot, then to mix things up, I started bothering the people of #raspberrypi on FreeNode (great bunch of people, by the way). After running through the same scenario a number of times, it started to dawn on me that I didn’t need the SkyBell in the mix at all to test whether the drop from 11 volts to 0 volts was readable on GPIO.

So I removed the SkyBell and started hooking the DC wires (with 10-ohm/10-watt resistor) straight onto my breadboard, through the optocoupler, and on the other side, I hooked up 3.3 volts of power to the third pin of the optocoupler, and GPIO pin 17 to the fourth pin.

After some careful adjustments and clamping connections together using paperclips and tiny binder clips, then plugging some code into the Pi to test for high/low signal on the port, I actually saw some success! At that point, I plugged my SkyBell back in and gave it another test. After some more futzing with bad connections (these are all temporary), I had it ready and you can see for yourself what happens:

I actually let this program run overnight and left everything connected as sort of a burn-in test. When I woke up, everything still worked perfectly. Oh, and I will post the current code for coupler.py but first, let me show you an overview of the (really crappy/ramshackle) setup:

As promised, here is the code in all its glory. Please note two things. One, the pull_up_down bit; this is something I may switch back over to use gpiozero since it looks so much cleaner. Two, the gist (which will be versioned as I make updates) is available here.

#!/usr/bin/python

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

PIN = 17
GPIO.setmode(GPIO.BCM)
GPIO.setup(PIN, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
coupler = True
couplerLast = True
startTime = time.time()
elapsedTime = 0

sonosPath = urllib2.quote('/sayall/there is someone at the door')
sonosUrl = 'http://127.0.0.1:5005%s' % sonosPath

print sonosUrl

try:
        while True:
                if GPIO.input(PIN):
                        coupler = True
                else:
                        coupler = False
                sleep(0.1)

                if coupler != couplerLast:
                        if coupler:
                                elapsedTime = time.time() - startTime
                                print "Elapsed time: %01.2f" % elapsedTime
                        else:
                                startTime = time.time()
                                print "DingDong! Sending to SONOS..."
                                req = urllib2.Request(sonosUrl)
                                try: urllib2.urlopen(req)
                                except urllib2.URLError as e:
                                        print e.reason
                        couplerLast = coupler
finally:
        GPIO.cleanup()

After all this was set up, I had to move on to the next step (which, if you’ve looked over the code is already present).

To get that going, I installed an excellent library that is still in beta but worked AMAZINGLY well once I had the right configuration: node-sonos-http-api. More or less, this is all I had to do in order to get it installed:

cd ~/Desktop/
git clone https://github.com/jishi/node-sonos-http-api.git
cd node-sonos-http-api/
curl https://raw.githubusercontent.com/creationix/nvm/v0.23.2/install.sh | bash
source ~/.bashrc
nvm install 4.0.0
nvm alias default 4.0.0
npm install

Then, to configure it for text-to-voice, I had to set up an account at VoiceRSS, then create a settings.json file and put my VoiceRSS API key in like so:

{
	"voicerss":"YOUR_KEY_HERE"
}

Once I did that, I was able to start it up and leave it running even if I log out:

nohup npm start > node-sonos.log &

Assuming your SONOS network is discoverable on the same WiFi network your Pi is connected to, what you’ll notice (or at least, what I saw) from the output of the node-sonos-http-api server was that it immediately detected the SONOS, found my “rooms” (living room), and was ready for use right away. If you have any issues with this setup, I suggest you read over the documentation provided on the repo. In fact, since the repo is changing all the time, I suggest you keep an eye on it either way if you plan to use it!

Now that I have it set up to work off of the SkyBell button press, the script above basically tells the SONOS to say, “There is someone at the door” in a British accent and it does so while temporarily pausing the music that’s playing. How cool is that?

What I have in store next is a magnetic sensor for the gate itself. When that opens/closes, I plan to play wav/mp3 files out of the audio-out jack of the Pi. What will it play, you wonder? Halloween sounds on Halloween, Christmas sounds around Christmas, and random other stuff the rest of the year! That’s the idea anyway. I’ll keep brainstorming.


Footnotes

  1. The 8 seconds is intentional from SkyBell’s end when the “Digital Door Chime” setting is enabled from the SkyBell app. If that’s turned off, the “low” (0-volt) happens for a fraction of a second.