The Making of a Smart Doorbell

Gabriel Aboy
Jan 15 · 7 min read
Photo by Louis Reed on Unsplash

This article will annotate the creation of a smart doorbell, using a Raspberry Pi. Enlightened by the internet of things (IoT) technology sprouting in our neighborhoods lets take this opportunity to develop our own IOT device given all the hype. Hopping on the wagon lets begin with an intuitive prototype of a smart doorbell. The concept design should utilize a push button to text my cell phone a web address to view a video stream channeled by the Raspberry Pi.

Prerequisites:

  1. Downloading Rasbian
  2. Configure Internet Connection
  3. Python3
  4. Twilio Account with project SID, the authorization token, and a Twilio phone number

Before we get started it is important to update your Linux distribution before installing any other packages. For simplicity, the following examples use nano for the creation and editing of the python scripts. There are several methods for simpler ways to edit code on a remote machine, the guide won’t affect those who deviate from nano.

Commands for Installing Nano

sudo apt-get update #Make sure that the device is up to date
sudo apt-get upgrade
sudo apt-get install nano #Install text editor

Devices used in this project

  • Raspberry Pi Model 3 B+
  • Raspberry Pi Camera Board v2–8 Megapixels
  • Tactile Button Switch

Helpful commands

sudo shutdown -h now # Safely powers off the Raspberry Pi
sudo shutdown -r now # Safely restarts the Raspberry Pi
ifconfig # Find your IP address
cd
ls
mkdir
nano <FILE_NAME> #Creates file in current directory

Creating a Web Server

Let us begin by installing dependencies from the Linux package manager by running the following command. To make sure that we do not corrupt our OS distribution update and upgrade your Raspbian before any installation.

sudo apt-get install apache2 -y

Let’s test the web server, Apache already creates an HTML file in the web folder. The default page is hosted on http://localhost/ or http://<IP_ADDRESS>/. This web server will later host the live video stream feed through a Python script. You can find your IP address by taking a look at the helpful commands posted earlier.

Connecting the Pi Camera

Refer to the Raspberry Pi board schematic throughout the guide.

Connect the 8-megapixel camera into the I/O port. In order for the Raspberry Pi to register the device, restart the system using the restart command from the helpful command list above. Once we have established an SSH connection with the Pi, camera software must be enabled. Enter the Raspberry Pi configuration menu to toggle the authorization. In the terminal type the following to enter the Raspberry Pi configuration menu and follow the subsequent images.

sudo raspi-config

Once the camera module has been enabled, create a file that will leverage the camera to stream to a web server. The code below exports a function that starts a video stream to port 8000. The reason for exporting it as a function is to embed the stream into its own thread in the main executable. To save the file press Ctrl+X and type Y and then Enter.

nano rpi_camera_live_stream.py

#create file by typing into console=> nano rpi_camera_live_stream.py
import io
import picamera
import logging
import socketserver
from threading import Condition
from http import server
def startCamera():
# Web streaming example
# Source code from the official PiCamera package
# http://picamera.readthedocs.io/en/latest/recipes2.html#web-streaming
PAGE="""\
<html>
<head>
<title>Raspberry Pi - Surveillance Camera</title>
</head>
<body>
<center><h1>Raspberry Pi - Surveillance Camera</h1></center>
<center><img src="stream.mjpg" width="640" height="480"></center>
</body>
</html>
"""
class StreamingOutput(object):
def __init__(self):
self.frame = None
self.buffer = io.BytesIO()
self.condition = Condition()
def write(self, buf):
if buf.startswith(b'\xff\xd8'):
# New frame, copy the existing buffer's content and notify all
# clients it's available
self.buffer.truncate()
with self.condition:
self.frame = self.buffer.getvalue()
self.condition.notify_all()
self.buffer.seek(0)
return self.buffer.write(buf)
class StreamingHandler(server.BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/':
self.send_response(301)
self.send_header('Location', '/index.html')
self.end_headers()
elif self.path == '/index.html':
content = PAGE.encode('utf-8')
self.send_response(200)
self.send_header('Content-Type', 'text/html')
self.send_header('Content-Length', len(content))
self.end_headers()
self.wfile.write(content)
elif self.path == '/stream.mjpg':
self.send_response(200)
self.send_header('Age', 0)
self.send_header('Cache-Control', 'no-cache, private')
self.send_header('Pragma', 'no-cache')
self.send_header('Content-Type', 'multipart/x-mixed-replace; boundary=FRAME')
self.end_headers()
try:
while True:
with output.condition:
output.condition.wait()
frame = output.frame
self.wfile.write(b'--FRAME\r\n')
self.send_header('Content-Type', 'image/jpeg')
self.send_header('Content-Length', len(frame))
self.end_headers()
self.wfile.write(frame)
self.wfile.write(b'\r\n')
except Exception as e:
logging.warning(
'Removed streaming client %s: %s',
self.client_address, str(e))
else:
self.send_error(404)
self.end_headers()
class StreamingServer(socketserver.ThreadingMixIn, server.HTTPServer):
allow_reuse_address = True
daemon_threads = True
with picamera.PiCamera(resolution='640x480', framerate=24) as camera:
output = StreamingOutput()
#Uncomment the next line to change your Pi's Camera rotation (in degrees)
#camera.rotation = 90
camera.start_recording(output, format='mjpeg')
try:
address = ('', 8000)
server = StreamingServer(address, StreamingHandler)
server.serve_forever()
finally:
camera.stop_recording()
#Uncomment the next line if you want to demo the stream
#startCamera()

Demoing the Video Stream

It is helpful to test each segment individually before layering the final product. This will help debug errors more effectively, by reducing the number of dependencies. To run the video stream, un-comment the last line of the rpi_camera_live_stream.py, this will invoke the deployment function. To run the python script run the following command.

python3 rpi_camera_live_stream.py

Once the Python script has successfully run, you will have access to the web server that is hosting the camera stream. Open any browser (Chrome and Firefox were tested successfully) and go to http://localhost:8000.

Text Me

For this segment, we will incorporate the communication between the device and the “homeowners” cell phone. Leveraging Twilio for their Python SMS service. Install the package by entering the following line into the console.

pip3 install twilio

Once that is installed let us check that everything was installed correctly by checking the version. Run the next command, and expect a positive response with a version number over 9.0.0 unless your installation specified otherwise.

pip3 --version

# For more information on Twilio https://www.twilio.com/docs/python/install# Import Twilio
from twilio.rest import Client
# Your Account Sid and Auth Token from twilio.com/console
account_sid = 'ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
auth_token = 'your_auth_token'
client = Client(account_sid, auth_token)
# This function will be invoked from our button event
def text_user():
message = client.messages.create(
# Message sent to user
body="http://10.245.203.106:8000/index.html",
from_='<Youre_Twilio_Number>',
to='+<Receiving_Number>'
)
# If a GUID is logged then the message was sent successfully sent#Message id for debugging on the Twilio event logs
print(message.sid)
#text_user()
#Uncomment the line above to demo

Tactile Buttons Are Ringing!

The finale joins all the other files into separate threads to avoid interrupts and blocks. Python supports simultaneous run executions with their multiprocessing library. In order to host the web server and listen to button events, this library will be crucial to support the prototype architectures integrity. Without separating the python modules into their own threads the program will interrupt older processes at run time.

Let’s begin by configuring the circuit board to the schematic below. On one side of the switch, we connect to GPIO pin 10. The other side of the switch needs to connect to the 5V pin (Refer to your Raspberry Pi GPIO layout) interrupted by a 10k ohm current limiting resistor to protect the input pin. In production pin 10 will be at 0V until the button is pressed closing the circuit sending 5V. GPIO pin 10 is constantly listening for input voltages, once the event fires the button_callback function is invoked. Using the send_SMS module written earlier to send a text message to the phone number specified.

# Import startCamera function from the video stream file
from rpi_camera_demo import startCamera
# Import function to send SMS from the send_SMS.py file we created earlier
from send_SMS import text_user
# Import Process from the multiprocessing module in Python standard library
from multiprocessing import Process
# Import Raspberry Pi's GPIO library
import RPi.GPIO as GPIO
def button_callback(channel):
text_user()
print("Button was pushed!")
GPIO.setwarnings(False) # Ignore warning for now
GPIO.setmode(GPIO.BOARD) # Use physical pin numbering
GPIO.setup(10, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # Set pin 10 to be an input pin and set initial value to be pulled low (off)
GPIO.add_event_detect(10,GPIO.RISING,callback=button_callback) # Setup event on pin 10 rising edge# Define function to start the video stream
# This is necessary to initialize multi processing
def Start_Stream():
print('Starting thread for video stream')
startCamera()
# Assures that the entry point of the program begins here
if __name__ == '__main__':
Thread_Target=Process(target=Start_Stream)
#Starts Process
Thread_Target.start()
#Block the calling thread until the Thread_Target process terminates
Thread_Target.join()
message = input("Press enter to quit\n\n") # Run until someone presses enter
GPIO.cleanup() # Clean up

To finally demonstrate the final product make sure that all the independent modules are not invoking themselves. Then use python3 to invoke the button module.

python3 button_event.py

Resources

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade