maandag 4 augustus 2014

Pi-files: Doorbell alert with pushmessage and mail with webcam footage

Actually the first 'project' I ever did with a Raspberry Pi was sending a push message to my Iphone. It was 2012, I was lying sick in bed and found a new app on my Iphone called Pushover (what else to do when you're sick?). With Pushover you can send and receive custom made push messages. On the website I found a simple Python script to send messages. I knew the Rpi was able to run Python code, so here my Rpi adventures started. Within 30 minutes I was able to receive 'hello world' on my phone (needless to say I wasn't lying in bed anymore). Seeing 'hello world' on your screen is like the software equivalent of the blinking led, THE coolest feature ever!

I decided to hook up this push message feature with my doorbell. The idea is that every time somebody rings the doorbell, I get a push message that there is somebody at the door. The wires of the doorbell were already connected to a wireless transmitter and I wanted to keep that functionality. I used a relay to combine the transmitter with a switch on the GPIO header of the Raspberry Pi.


The led and 330R resistor can be installed in the actual doorbell, so the person can find the doorbell and is triggered when the button is pressed in case he/she doesn't actual hear the bell (the led turns off when the doorbell is pressed). The 100u capacitor and flyback diode are to limit voltage peaks when using the relay. The 'test' switch is available close to the GPIO header to test the (software) functionality of the doorbell alert. The relay and test switch are connected to pin11 (GPIO0) of the Rpi.


The Python code looks as follows. First the necessary libraries are included and the GPIO header is configured:

#import libs
import RPi.GPIO as GPIO
import httplib, urllib
import time
from time import sleep, localtime, strftime

#display no warnings (on command line)
GPIO.setwarnings(False)
#set GPIO: use RPi board pin numbers
GPIO.setmode(GPIO.BOARD)  #alternative is GPIO.BCM (Broadcom names)
#set pin 11 as input
GPIO.setup(11, GPIO.IN)  #Input: doorbell (relay)

A seperate function is written for the push message. This is mainly copied from the Pushover FAQ section ('How do I send Pushover notifications in Python?'). The token ('push_token') and user ID ('push_user') you get when you sign in on the website. There you can also select the devices on which you want to receive the push messages.

#set up push message (pushover)
def push(text):

   push_token = 'xxx'
   push_user   = 'yyy'

   conn = httplib.HTTPSConnection("api.pushover.net:443")
   conn.request("POST", "/1/messages.json",
                urllib.urlencode({"token": push_token,
                                        "user": push_user,
                                        "message": text,}), 
             {"Content-type": "application/x-www-form-urlencoded" })
   conn.getresponse()

The main loop checks for the GPIO input regularly. I added a tiny bit of delay (sleep) to relieve the CPU. When the doorbell is pressed, it takes the current date and time and adds that to the push message.


while 1:

    sleep(0.1) #relieves CPU load big time!
      
    #if doorbell is pressed...
    if GPIO.input(11):

          #determine date/time and add to message
          timestr_date = strftime("%a %d %b %Y", localtime())  
          timestr_time = strftime("%H:%M:%S", localtime())      

          mess = "Doorbell pressed on {}, {}.".format(timestr_date,timestr_time)
          print "\n"
          print mess


          push(mess)

When this all worked it was time to extend the doorbell alert feature. Not only I want to know that there is someone at the door, I also would like to know who was at the door, especially when I am not at home. I added a Logitech C270 webcam to the setup to capture snapshots and a short movie.



A Python mail script is supposed to send it all to my mailbox. The mail functionality I didn't invent myself, I just relied on the beautiful internet community. On Kutuma's Ramblings I found almost exactly what I needed. The only difference is that I wanted to sent multiple attachments. For that I changed the mail function a little bit:

def mail(to, subject, text, attachments=[]):

   gmail_user = 'XXX@gmail.com'
   gmail_pwd  = 'YYY'

   #add attachments to a list
   assert type(attachments)==list

   msg = MIMEMultipart()

   msg['From']    = gmail_user
   msg['To']      = to
   msg['Subject'] = subject

   msg.attach(MIMEText(text))

   for attach in attachments:
     part = MIMEBase('application', 'octet-stream')
     part.set_payload(open(attach, 'rb').read())
     Encoders.encode_base64(part)
     part.add_header('Content-Disposition',
           'attachment; filename="%s"' % os.path.basename(attach))
     msg.attach(part)

   mailServer = smtplib.SMTP("smtp.gmail.com", 587)
   mailServer.ehlo()
   mailServer.starttls()
   mailServer.ehlo()
   mailServer.login(gmail_user, gmail_pwd)
   mailServer.sendmail(gmail_user, to, msg.as_string())
   mailServer.close()

You can now add the attachments as an comma separated array:

mail("xxx@gmail.com",mess,mess,["screen1.jpg","screen2.jpg","screen3.jpg","movie.avi"])



As you can see I added three snapshots and one movie as attachments. They all come from the Motion package which is a linux package to capture motion on a webcam. To be honest, I actually don't use the motion feature of that package, but I tried several other solutions (like FFMpeg or something) and came across a bunch of issues with compiling, frame rates etc. The Motion package served my purpose the best. 

NB. The Raspberry Pi that I am using is a model A, which is not capable of using the official Rpi camera module with its cool movie and snapshot features.

To install the Motion package, run the following command:
pi@raspberrypi ~ $  sudo apt-get install motion libv4l-0 uvccapture

I added some additional modules to the import section of the Python script described above and added the code below to the main loop. Starting the webcam is done right after the doorbell is pressed. After capturing the snapshots and sending the push message, the program waits 60 seconds until the webcam is stopped. After this the mail is send with the snapshots and movie.

import subprocess
import shutil #high-level file operations

    #if doorbell is pressed...
    if GPIO.input(11):

          #start webcam (motion package)
          print "\n"
          print "Start webcam (Motion)"
          
          #subprocess.call("sudo /etc/init.d/motion start", shell=True)
          subprocess.call("sudo motion", shell=True)

          #copy (save) most recent snapshot of webcam
          print "\n"
          for k in range(3):
               sleep(3)
               sour_path = '/home/pi/rpi/webcam/motion/snapshot.jpg'
               dest_file    = '/home/pi/rpi/webcam/motion/screen%d.jpg' %k
               shutil.copy2(sour_path, dest_file)
               print 'Snapshot captured:  %s' %(dest_file)


          ****** Sending push message (see above) ******

          print "wait 60 seconds"
          sleep(60)
          
          #stop webcam
          print "Stop webcam"
          subprocess.call("sudo /etc/init.d/motion stop", shell=True) 

          #send mail via Gmail
          mail("xxx@gmail.com",mess,mess,
 ["screen1.jpg","screen2.jpg","screen3.jpg","movie.avi"])



 

The motion package has a pretty big configuration file where you can configure all sorts of things. Most of the items speak for their own. The file can be edited with nano:

pi@raspberrypi ~ $  sudo nano /etc/motion/motion.conf

For the doorbell alert let me highlight the most important sections:

# File to store the process ID, also called pid file. (default: not defined)
process_id_file /var/run/motion/motion.pid 

The folder /var/run/motion needs to be present. In my latest Raspbian (Debian Wheezy) OS I encountered a bug (as far as I can tell) that this folder is gone every time I boot up the Rpi. For that reason I added a check and create the folder if necessary:

import os

folder   = "/var/run/motion"

if not os.path.exists(folder):
  print "Folder does not exist. Create folder " + folder
  os.makedirs(folder)


# Image width/height (pixels). Valid range: Camera dependent, default: 352/288
width 1280
height 720
Bigger images won't hurt!

# Maximum number of frames to be captured per second.
# Valid range: 2-100. Default: 100 (almost no limit).
framerate 2

I only use two frames per second to keep the movie size as small as possible.

# Always save images even if there was no motion (default: off)
output_all on

I don't use the motion detection features so I want all images to be saved

# Use ffmpeg to encode a timelapse movie
# Default value 0 = off - else save frame every Nth second
ffmpeg_timelapse 0.5


# Make automated snapshot every N seconds (default: 0 = disabled)
snapshot_interval 1

Save snapshots every second

# Target base directory for pictures and films
# Recommended to use absolute path. (Default: current working directory)
target_dir /home/pi/rpi/webcam/motion

Location where all files are stored

# File path for snapshots (jpeg or ppm) relative to target_dir
snapshot_filename snapshot

All snapshots are given the same name (no timestamp) to save storage space.

# File path for motion triggered ffmpeg films (mpeg) relative to target_dir
movie_filename movie

Each movie is given the same name (no timestamp) to save storage space.

That's it for now! I am already thinking of new features like face recognition, direct videostream, and a call back option to actually talk to the person at the door wherever I am. The sky is the limit! Unfortunately my spare time as well :)

Below a picture of my custom storage box which is located in the meter cupboard (again, is that really the correct translation for the dutch word 'meterkast'?). It houses a Raspberry Pi model A, an interface board (on the right) with the well famous heartbeat led. On the left two boards for the doorbell and one for the front door light which I will explain in another post soon.




18 opmerkingen:

  1. How cool is that! Hackaday.com placed an article of this blog on their site. In my opinion the coolest DIY/custom elecronics/hack site links to my blog!!!

    Check out the article:
    http://hackaday.com/2014/08/06/raspberry-pi-spies-on-your-front-door/

    BeantwoordenVerwijderen
  2. Hi, thanks for producing this guide, i've managed to get it working and sending a single file but i cannot fathom how to pass an array of attachments to the email

    I get this error
    part.set_payload(open(attach, 'rb').read())
    TypeError: coercing to Unicode: need string or buffer, list found

    any help will be appreciated

    BeantwoordenVerwijderen
  3. Deze reactie is verwijderd door een blogbeheerder.

    BeantwoordenVerwijderen
  4. Hi Chris,

    I just found out I forgot to mention that I also changed some code in the mail function itself. I added the code of the mail function to the blog article. Hope this helps!

    Kind regards,
    Sander

    BeantwoordenVerwijderen
  5. Brilliant! i'll try this later.
    Thanks again for making this guide

    BeantwoordenVerwijderen
  6. very cool. Have you considered changing your loop that checks for button presses to an interrupt "event" ?
    It really helps with reliability:
    # setup the pin
    GPIO.setup(2, GPIO.IN)
    #define what you want to happen when the button is pushed
    def alert_action(channel):
    print('Edge detected on channel %s'%channel)

    ## here is the magic
    # add the event listener to the pin (falling or rising) it even includes bounce detection
    GPIO.add_event_detect(2, GPIO.FALLING, callback=alert_action, bouncetime=200)

    #then the while loop becomes ONLY sleeping!
    while True:
    sleep(1)

    http://www.adafruit.com/blog/2013/03/22/how-to-use-interrupts-with-python-on-the-raspberry-pi-and-rpi-gpio/

    BeantwoordenVerwijderen
    Reacties
    1. Andy,
      thanks for the tip. I will definitely check it out!

      Verwijderen
  7. can you post your motion.conf? its going through the motions of taking pictures but doesn't enable the webcam removing the comment from #subprocess.call("sudo /etc/init.d/motion start", shell=True) allows the webcam to work but the email and push do not. Any help would be appreciated

    BeantwoordenVerwijderen
    Reacties
    1. Hi Chris,

      Make sure that the folder /var/run/motion exists. For whatever reason this folder is deleted every time I reboot the Pi, and prevented the webcam from recording. I check for this folder and create it when not existing.

      If I recall correctly using 'sudo /etc/init.d/motion start' runs the webcam in non-daemon mode (equal to using 'sudo motion -n'), and no process ID (motion.pid) is made. This maybe prevents the webcam from stopping correctly and send the mail/push. See if the file /var/run/motion/motion.pid exists when you start the webcam. Try to start/stop the webcam straight from the command line instead of from the Python script.

      You can find the complete motion.conf file here: http://goo.gl/MvFpgH

      good luck!

      Verwijderen
  8. how about giving names to your files and where do they go?

    BeantwoordenVerwijderen
    Reacties
    1. Hi Teddy,
      all the code above belongs to one python (*.py) file. You can store the files everywhere in the \home directory (e.g. \home\rpi\scripts) and run it from there.

      Verwijderen
  9. Man, I would love to see a zip file or image of the whole SD card...so i could have a plug and play of this...modify code is easier than installing it all and getting it to work ( having no linux and ubuntu experience.....)

    BeantwoordenVerwijderen
  10. Deze reactie is verwijderd door een blogbeheerder.

    BeantwoordenVerwijderen
  11. Deze reactie is verwijderd door een blogbeheerder.

    BeantwoordenVerwijderen
  12. Deze reactie is verwijderd door een blogbeheerder.

    BeantwoordenVerwijderen
  13. Thanks for info! Question can you post/send me how I would wire the 16v ac lighted Doorbell button without any of the normal Doorbell transformer or bell. I want to light the Doorbell and connect it directly to the raspberry pi. Just not sure how to keep the ac power from connecting to the raspberry pi.

    BeantwoordenVerwijderen
  14. Thanks for info! Question can you post/send me how I would wire the 16v ac lighted Doorbell button without any of the normal Doorbell transformer or bell. I want to light the Doorbell and connect it directly to the raspberry pi. Just not sure how to keep the ac power from connecting to the raspberry pi.

    BeantwoordenVerwijderen
  15. are you manufacturer of Portable Wireless Doorbell ? i want 2000 peace for sell on amazon.

    BeantwoordenVerwijderen