0.1 Rationale

The raspberry pi has always been appealing to me, but I needed a project to really get involved. After discussing with Eric Koncina who made several great applications with Pis, I decided to go for a home surveillance system.

The main objective was to see how often the neighbor cats are coming to our garden, because they are scaring our cat. It’s not a big deal, rather a justification for the pi project.

0.2 Materials

I bought a Pi3 starter budget kit that contains:

Additionally, I purchased:

Was hoping to get some decent pictures / videos with low light. Turned out that IR leds are needed. That goes in the TODO section.

Here is an example of picture with low interior light. Colors are off, but quality is fine to me

0.3 Setting-up the pi

I won’t go into details, I mostly followed the instructions in this tutorial. Briefly, here are the main steps

0.3.1 download raspbian lite

Since I have no screen, no keyboard and the pi comes with a WiFi controller, the stretch lite is sufficient. Image can be found at raspberrypi.org

0.3.2 format SD card

using disk utility, choose MS-DOS FAT file system

0.3.3 install raspbian

Ensure your SD card is the second disk (/dev/disk2), otherwise do adapt to the correct one!

unzip 2017-11-29-raspbian-stretch-lite.zip
sudo dd bs=1m if=2017-11-29-raspbian-stretch-lite.img of=/dev/rdisk2

0.3.4 enable ssh

Once copied, you can enable ssh by creating an empty file at the SD card root

cd /Volumes/boot/
touch ssh

0.3.5 enable wifi

In order to connect to the pi without screen / keyboard, wifi needs to be configured right away. At the same location (/Volumes/boot) add a file named wpa_supplicant.conf

which contains:

network={
        ssid="your_network_ssid"
        psk="xxx"
        key_mgmt=WPA-PSK
}

Of note, I recently acquired a pi zeroWH, for which I had to add 3 lines (StackExchange question).

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=FR
network={
        ssid="your_network_ssid"
        psk="xxx"
        key_mgmt=WPA-PSK
}

0.3.6 connect to pi

once the raspberrypi booted, try to find its IP

nmap -sn 192.168.1.0/24

which gives:

Starting Nmap 7.60 ( https://nmap.org ) at 2017-12-08 22:44 CET
Nmap scan report for 192.168.1.27
Host is up (0.0071s latency).
Nmap scan report for 192.168.1.254
Host is up (0.0048s latency).
Nmap done: 256 IP addresses (2 hosts up) scanned in 11.71 seconds

192.168.1.254 was the rooter, so pi was assigned 192.168.1.27

ssh pi@192.168.1.27 works.

you can also assign a fixed IP to your pi

0.3.7 final configuration

once connected to the pi:

0.4 install the surveillance system

Even if, I’d like to have openCV like in this tutorial, it was way more work. Hence, the choice of motion

0.4.1 motion software

I followed the instructions provided in this great tutorial by Bouvet. With some changes described below.

0.4.1.1 compilation

see from here https://motion-project.github.io/motion_build.html

wget https://github.com/Motion-Project/motion/releases/download/release-4.1.1/pi_stretch_motion_4.1.1-1_armhf.deb
sudo apt-get install gdebi-core
sudo gdebi pi_stretch_motion_4.1.1-1_armhf.deb

0.4.2 run motion

first, as Bouvet suggested, I copied the main config file

mkdir ~/motion && cp /etc/motion/motion.conf ~/motion/

and alter the new copy.

running motion:

motion -c ~/motion/motion.conf

0.4.3 tweaks to the initial tutorial

I choose to get the videos and several other changes that are reported below.

In red the initial values, in green: the replaced ones

diff --git a/motion.conf.orig b/motion.conf
index a03faf0..8101ff5 100644
--- a/motion.conf.orig
+++ b/motion.conf
@@ -78,6 +78,6 @@ flip_axis none
# Image width (pixels). Valid range: Camera dependent, default: 320
width 320640

# Image height (pixels). Valid range: Camera dependent, default: 240
height 240480

@@ -85,3 +85,3 @@ height 240
# Valid range: 2-100. Default: 100 (almost no limit).
framerate 220

@@ -120,3 +120,3 @@ rtsp_uses_tcp on
# Default: Not defined
;mmalcam_name vc.ril.camera

@@ -224,3 +224,3 @@ pre_capture 0
# Number of frames to capture after motion is no longer detected (default: 0)
post_capture 05

@@ -236,3 +236,3 @@ event_gap 60
# When value is exceeded a new movie file is created. (Default: 0 = infinite)
max_movie_time 030

@@ -252,3 +252,3 @@ emulate_motion off
# Can be used as preview shot for the corresponding movie.
output_pictures offbest

@@ -258,3 +258,3 @@ output_debug_pictures off
# The quality (in percent) to be used by the jpeg and webp compression (default: 75)
quality 7580

@@ -267,3 +267,3 @@ picture_type jpeg
# Use ffmpeg to encode videos of motion (default: off)
ffmpeg_output_movies onoff

@@ -345,3 +345,3 @@ snapshot_interval 0
# Set to 'preview' will only draw a box in preview_shot pictures.
locate_motion_mode offpreview

@@ -353,3 +353,3 @@ locate_motion_mode off
# Set to 'redcross' will draw a little red cross to mark center.
locate_motion_style boxredbox

@@ -368,3 +368,3 @@ text_right %Y-%m-%d\n%T-%q
# Text is placed in upper right corner
text_changes offon

@@ -376,3 +376,3 @@ text_changes off
# a unique identifier for each event.
text_event %Y%m%d%H%M%S%Y/%m/%d-%H:%M:%S

@@ -403,3 +403,3 @@ text_double off
# Recommended to use absolute path. (Default: current working directory)
#target_dir /tmp/motiontarget_dir /home/pi/motion/detected

@@ -412,3 +412,3 @@ text_double off
# point to the latest snapshot, unless snapshot_filename is exactly 'lastsnap'
snapshot_filename %v-%Y%m%d%H%M%S-snapshot%v-%Y-%m-%d_%H%M%S-snapshot

@@ -421,3 +421,3 @@ snapshot_filename %v-%Y%m%d%H%M%S-snapshot
# convention for preview shots. See motion guide for details
picture_filename %v-%Y%m%d%H%M%S-%q%v-%Y-%m-%d_%H%M%S-%q

@@ -426,3 +426,3 @@ picture_filename %v-%Y%m%d%H%M%S-%q
# File extensions(.mpg .avi) are automatically added so do not include them
movie_filename %v-%Y%m%d%H%M%S%v-%Y-%m-%d_%H%M%S

@@ -431,3 +431,3 @@ movie_filename %v-%Y%m%d%H%M%S
# File extensions(.mpg .avi) are automatically added so do not include them
timelapse_filename %Y%m%d-timelapse%Y-%m-%d-timelapse

@@ -454,9 +454,9 @@ stream_quality 50
# rate given by stream_maxrate when motion is detected (default: off)
stream_motion offon

# Maximum framerate for stream streams (default: 1)
stream_maxrate 110

# Restrict stream connections to localhost only (default: on)
stream_localhost onoff

@@ -494,3 +494,3 @@ webcontrol_port 8080
# Restrict control connections to localhost only (default: on)
webcontrol_localhost onoff

@@ -608,3 +608,3 @@ quiet on
# To give the filename as an argument to a command append it with %f
;on_picture_save value/home/pi/motion/send_detection.py %f

0.4.4 see live streaming

with this configuration, you should see the live streaming from this URL: http://192.168.1.27:8081

the web control on port 8080 is disabled apart outside the pi, since we’ll use telegram to control motion

0.4.5 detection

Here is an example of my kid being detected. The red rectangle works nicely

But of course, there are false alarms, such as when the light comes in/out suddenly

the parameter lighswitch 80 reduced the issue but it still exists.

0.5 Communication with motion via telegram

Now comes the fun part. Receiving the motion detection by emails is fine, but it can be done via Telegram and the awesome API telepot. Eric told me about telegram bots and it looked promising. Actually, you can even send commands to your pi using your phone using those telegram bots.

The useful feature I implemented are:

0.5.1 create mybot

Eric gave me the link to this tutorial

Of course, I am assuming you already have our own telegram account.

Talk to the BotFather and create mybot, you will receive a private token.

0.5.2 install telepot

back on the pi, install telepot with pip, assuming you installed python and pip.

pip install telepot

0.5.3 test sending message

0.5.3.1 get the bot id

import telepot
bot = telepot.Bot('your-token')
bot.getMe()

returns {u'username': u'mybot', u'first_name': u'cat tracker', u'is_bot': True, u'id': 00000008}

0.5.3.2 get your telegram id:

  • send a messages from telegram to mybot
  • fetch your message on the pi
from pprint import pprint
response = bot.getUpdates()
pprint(response)

your id appears, such as: u’id’: 00000004

0.5.3.3 basic tests

  • for text

bot.sendMessage(00000004, 'Hey!')

  • for picture

bot.sendPhoto(00000004, photo=open('/home/pi/motion/detected/07-2018-01-06_205746-13.jpg', 'rb'), caption='motion detected')

0.5.3.4 create commands for the bot

After sending /setcommands to the BotFather:

time - Returns current time on pi
check - Returns status of the camera
status - Returns status of motion
pause - Pauses the motion detection
resume - Resumes motion detection
snapshot - Returns current image
video - Returns last recorded video

Of note, it doesn’t prevent the bot to receive other commands, it just helps to display commands and select them in telegram.

0.5.3.5 python script that listen

here the script listen_bot.py, derived from the telepot documentation.

Some comments: - the last video when requested is fetched from the sub-folder vids. If we use the main folder of detection, the last video could be an incomplete one form a newer detection. Hence, the command in motion.conf to move a finished video to the vids folder. - I failed to restrict the bot to communicate only with me. Might not be a big deal, but the code if chat_id != 00000008 is not working. - the webcontrol was set in the RAW mode. Then the retrieved text can be directly send to your telegram account

#!/usr/bin/python2.7

import datetime
import telepot
import time
import requests
import os
import glob
from telepot.loop import MessageLoop

def webcontrol(chat_id, type, cmd):
    req = 'http://localhost:8080/0/'+type+'/'+cmd
    res = requests.get(req)
    bot.sendMessage(chat_id, res.text)

def handle(msg):
    chat_id = msg['chat']['id']
    command = msg['text']
    #FIXME does not work
    #if chat_id != 00000008:
    #    bot.sendMessage(chat_id, "Sorry this is a personal bot. Access Denied!")
    #    continue

    print 'Got command: %s' % command

    if command == '/snapshot':
        requests.get('http://localhost:8080/0/action/snapshot')
    elif command == '/status':
        webcontrol(chat_id, 'detection', 'status')
    elif command == '/pause':
        webcontrol(chat_id, 'detection', 'pause')
    elif command == '/resume':
        webcontrol(chat_id, 'detection', 'start')
    elif command == '/check':
        webcontrol(chat_id, 'detection', 'connection')
    elif command == '/time':
        bot.sendMessage(chat_id, 'now is '+str(datetime.datetime.now()))
    elif command == '/video':
        # the most recent video in this particular folder of complete vids
        video = max(glob.iglob('/home/pi/motion/detected/vids/*.mp4'), key=os.path.getctime)
        # send video, adapt the the first argument to your own telegram id
        bot.sendVideo(00000008, video=open(video, 'rb'), caption='last video')
    else:
        bot.sendMessage(chat_id, "sorry, I don't know the command "+command)
# adapt the following to the bot_id:bot_token
bot = telepot.Bot('0000000004:bot_token')

MessageLoop(bot, handle).run_as_thread()
print 'I am listening ...'

while 1:
    time.sleep(10)

Now if the both listen_bot.py and motion -c ~/motion/motion.conf are running, the system should work.

0.5.3.6 run scripts at startup

Here, the following is working, but I am sure this is the right way to do, so use we care.

in /etc/rc.local

add the following lines

# start listening to picatbot
/home/pi/motion/telegrambot.sh &
# start motion
motion -c /home/pi/motion/motion.conf

with telegrambot.sh being

#!/bin/sh
sleep 10
sudo /home/pi/motion/listen_bot.py

0.5.3.7 Screenshots

Here are some examples of the telegram window

  • received a notification and later on, /check if connection is still on.
  • received notification and ask for the corresponding video

this video works as a GIF directly in the window:

gif file

gif file

  • pause the detection, and since no motion can be detected, ask for a snapshot

0.5.4 TODO

Despite a functional system, some improvements I’d like to achieve:

0.6 Conclusion

Beyond the initial goal, catch the neighbor cats coming in, which actually I don’t care about, it was fun to set-up the whole thing. Moreover, telegram offers a great service and offers a great interface for many applications. I knew about the TeleR bot, that is actually easy to set-up.

Don’t hesitate to leave a comment below for any remarks or improvements that I overseen.