title

YouTube player

This project consists of a movement sensor (PIR) plus a camera and a Wi-Fi connected device (Arduino Yún was my choice, but it can be easily substituted with a Raspberry Pi). Optionally, speakers can be connected to reproduce an alarm sound.

The purpose is clear. You don’t want your kids to steal your food from the cupboard, or from the fridge, or someone to open your locker, or you want to take pictures of your pet stealing food, or you are Dwight Schrute and you want to finally unmask the coworker that puts your stuff into jelly… so you hide the device in the cupboard/fridge/locker, and when the device detects  movement, it will take a picture and post it right to your email! And if you use IFTTT, then you can automatically post the picture of the thief to Facebook or Twitter and show the thief’s face to the entire world!

The project is very simple, it doesn’t require any soldering/electronics skills, and it can be assembled in minutes.

As I said before, my platform choice is Arduino Yún, because it’s very easy to use, and it’s easy to configure the Wi-Fi settings. It’s a bit more expensive than the Raspberry Pi, but if you use the Yún you can give you project to someone else as a gift, and anyone can configure the Wi-Fi settings, which may be a difficult task with the Raspberry Pi, without connecting it to a monitor/mouse/keyboard. But if you like it, a Raspberry Pi version is simple.

totalone

Project Steps

1. Arduino Basic Configurations

First of all, you need to configure your Arduino Yún network settings. This should be pretty easy following this guide.

Then test the connection to your Arduino: in your browser type http://arduino.local, you will see the Arduino web interface (doesn’t appear to be working currently).

If it works, open SSH session.

$ssh root@arduino.local

the default password is “arduino”.

Then, let’s install some useful packages.

$opkg update
$opkg install openssh-sftp-server

Why the openssh-sftp-server? Because it would be easier for you to upload and download files from/to the Arduino: now you can use a generic SFTP client (FileZilla, Transmit, or Cyberduck) instead of SCP command.

Then, we have have to install the SSL support package for Python (thanks sbkirby!) Because it’s not included in the default packages.

$opkg install python-openssl

If you have one, I highly recommend to put a micro SD card into the Arduino Yún. It will be automatically mounted in /mnt/sda1.

Then, we will install the USB camera and USB sound card on the Yún.

2. Installing and Testing the USB Camera

The UVC package is already available for Linino via opkg, so installing the camera is easy, just connect to your Yún via SSH and type

$ opkg install kmod-video-uvc

We also need a software for taking pictures, I used fswebcam that is super small and very easy to use

$ opkg install fswebcam

3. Take Your First Picture

Be sure to use the SD card for storage or you will quickly fill the Arduino internal storage. If you are using an SD card, you should see it mounted in /mnt/sda1. If it’s not, put an FAT32 formatted micro SD card into the SD card slot, and reboot your Arduino.

Now plug the camera to the USB host port, and type

$ cd /mnt/sda1
$ fswebcam test.png

If everything is OK, you took your first picture! Take a look at your SD card contents.

This means that now we can take pictures from our Arduino sketch, via the Yún’s Bridge library, in this way

Process.runShellCommand("fswebcam /mnt/sda1/test.png");

4. Install the Sound Card (optional)

Open an SSH session to the Yún, and type:

$opkg install kmod-usb-audio
$opkg install madplay

Now you have sound support on the Arduino Yún. I only tried MP3 playback, but you could try recording audio.

To test it just copy an MP3 audio file to the SD card, and type

$cd /mnt/sda1
$madplay yoursound.mp3

This means that now we can take play sounds from our Arduino sketch, via the Yún’s Bridge library, in this way

Process.runShellCommand("madplay /mnt/sda1/test.mp3");

5. The Email Script

Now we can take pictures and play sounds… but we want more from our Arduino! We want to send them via email.

So, how can we do it? The answer is very easy… Python script! Even if the Arduino Yún integrates the Temboo library, I wanted this project to be as portable as possible, so we will use a very simple Python script to encode the image file and send it to our email.

I’m assuming that you’re using Gmail, but the script can be easily adapted to any SMTP server.

Create a new file, call it “sendemail.py” and paste this code into it

# coding=utf-8

# Copyright (C) 2014  Stefano Guglielmetti

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import smtplib, os, sys
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email.Utils import COMMASPACE, formatdate
from email import Encoders

#From address, to address, subject and message body
from_address    = 'FROM_ADDRESS@EMAIL.COM'
to_address      = ['YOUR_ADDRESS@EMAIL.COM']
email_subject   = 'Alert!!! Zombies!!! Ahead!!!'
email_body      = 'A non dead intruder has been detected and needs to be eliminated!'

# Credentials (if needed)
username = 'EMAIL_LOGIN'
password = 'EMAIL_PASSWORD'

# The actual mail send
server = 'smtp.gmail.com:587'

def send_mail(send_from, send_to, subject, text, files=[], server="localhost"):
    assert type(send_to)==list
    assert type(files)==list

    msg = MIMEMultipart()
    msg['From'] = send_from
    msg['To'] = COMMASPACE.join(send_to)
    msg['Date'] = formatdate(localtime=True)
    msg['Subject'] = subject

    msg.attach( MIMEText(text) )

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

    smtp = smtplib.SMTP(server)
    smtp.starttls()
    smtp.login(username,password)
    smtp.sendmail(send_from, send_to, msg.as_string())
    smtp.close()

send_mail(from_address, to_address, email_subject, email_body, [sys.argv[1]], server) #the first command line argument will be used as the image file name

Now you have to customize the email settings.

#From address, to address, subject and message body
from_address    = 'FROM_ADDRESS@EMAIL.COM'
to_address      = ['YOUR_ADDRESS@EMAIL.COM']
email_subject   = 'Alert!!! Zombies!!! Ahead!!!'
email_body      = 'A non dead intruder has been detected and needs to be eliminated!'

# Credentials (if needed)
username = 'EMAIL_LOGIN'
password = 'EMAIL_PASSWORD'

# The actual mail send
server = 'smtp.gmail.com:587'

Change the from address, to address, and so on to match your settings.

Now you can upload the script to the Arduino Yún SD card via SFTP (or SCP, if you prefer), then open an SSH session to the Yún and test it, typing

$cd /mnt/sda1
$python sendemail.py test.png

And in a few seconds you will receive an email with the picture attachment… amazing!

6. Let's Build the Circuit!

Don’t worry, this is super easy, there is nothing to solder, just the assembly of a few parts.

I used this PIR sensor, because it’s reliable and easy to use.

Just follow this schematic:

image

I’ve connected the PIR sensor, and an LED that will turn on when the device detects movement.

7. Now it’s Time to Add the Arduino Sketch

Copy this code to the Arduino IDE and upload it to your Yún:

/* 
 * Switches a LED, takes a picture and sends it via email
 * according to the state of the sensors output pin.
 * Determines the beginning and end of continuous motion sequences.
 *
 * @author: Stefano Guglielmetti / stefano (at) mikamai (dot) com / http://jeko.net
 * @date:   feb 5, 2014  
 *
 * based on the example by Kristian Gohlke / krigoo (_) gmail (_) com / http://krx.at
 * http://playground.arduino.cc/Code/PIRsense
 *
 * stefano guglielmetti (cleft) 2014 
 *
 * released under a creative commons "Attribution-NonCommercial-ShareAlike 2.0" license
 * http://creativecommons.org/licenses/by-nc-sa/2.0/de/
 *
 *
 * The Parallax PIR Sensor is an easy to use digital infrared motion sensor module. 
 * (http://www.parallax.com/detail.asp?product_id=555-28027)
 *
 * The sensor's output pin goes to HIGH if motion is present.
 * However, even if motion is present it goes to LOW from time to time, 
 * which might give the impression no motion is present. 
 * This program deals with this issue by ignoring LOW-phases shorter than a given time, 
 * assuming continuous motion is present during these phases.
 *  
 */

#include <Bridge.h>

/////////////////////////////
//VARS
//the time we give the sensor to calibrate (10-60 secs according to the datasheet)
int calibrationTime = 10;        

//the time when the sensor outputs a low impulse
long unsigned int lowIn;         

//the amount of milliseconds the sensor has to be low 
//before we assume all motion has stopped
long unsigned int pause = 5000;  

boolean lockLow = true;
boolean takeLowTime;  

int pirPin = 6;    //the digital pin connected to the PIR sensor's output
int ledPin = 13;

Process p;
String imageName;

/////////////////////////////
//SETUP
void setup(){
  Bridge.begin();
  Serial.begin(9600);
  pinMode(pirPin, INPUT);
  pinMode(ledPin, OUTPUT);
  digitalWrite(pirPin, LOW);

  //give the sensor some time to calibrate
  Serial.print("calibrating sensor ");
  for(int i = 0; i < calibrationTime; i++){
    Serial.print(".");
    delay(1000);
  }
  Serial.println(" done");
  Serial.println("SENSOR ACTIVE");
  delay(50);
}

////////////////////////////
//LOOP
void loop(){

  if(digitalRead(pirPin) == HIGH){
    digitalWrite(ledPin, HIGH);   //the led visualizes the sensors output pin state
    if(lockLow){  
      //makes sure we wait for a transition to LOW before any further output is made:
      lockLow = false;            
      Serial.println("---");
      Serial.print("motion detected at ");
      Serial.print(millis()/1000);
      Serial.println(" sec"); 

      imageName = uniqueFileName("png"); //generate a new, uniqe file name

      p.runShellCommand("fswebcam /mnt/sda1/" + imageName); //takes the picture
      while(p.running()); //wait till the process ends
      p.runShellCommand("madplay /mnt/sda1/sounds/sirena.mp3"); //play the siren sound
      while(p.running()); //wait till the process ends
      p.runShellCommand("python /mnt/sda1/sendemail.py /mnt/sda1/" + imageName); //sends the picture via email
      while(p.running()); //wait till the process ends

      delay(50);
    }         
    takeLowTime = true;
  }

  if(digitalRead(pirPin) == LOW){       
    digitalWrite(ledPin, LOW);  //the led visualizes the sensors output pin state

    if(takeLowTime){
      lowIn = millis();          //save the time of the transition from high to LOW
      takeLowTime = false;       //make sure this is only done at the start of a LOW phase
    }
    //if the sensor is low for more than the given pause, 
    //we assume that no more motion is going to happen
    if(!lockLow && millis() - lowIn > pause){  
      //makes sure this block of code is only executed again after 
      //a new motion sequence has been detected
      lockLow = true;                        
      Serial.print("motion ended at ");      //output
      Serial.print((millis() - pause)/1000);
      Serial.println(" sec");
      delay(50);
    }
  }
}

/* A simple function to generate unique timestamp based filenames */

String uniqueFileName(String ext){
  String filename = "";
  p.runShellCommand("date +%s");
  while(p.running()); 

  while (p.available()>0) {
    char c = p.read();
    filename += c;
  } 

  filename.trim();
  filename += "." + ext;

  return filename;
}

After 10 seconds (calibration time) it will start working and making pictures!!!

This is the assembled project:

assembled

All the files of this project are available in my GitHub account.

I really hope you enjoyed this project!

Cheers, Stefano