Halloween fright ambush project

I know it has been ages since I posted here so I thought I would put something up for Halloween.  I have a brilliant and simple project here.  It’s easy to understand and, more importantly, it actually does work.  I accidentally scared myself with it once!  So I know it does.

A Raspberry Pi is so small that it can easily be hidden or concealed and that makes it the perfect tool for this kind of project.  The basic idea here is that we’re going to use motion detection to trigger the playback of a scary video clip at high volume.  At the same time we can use the Raspberry Pi camera module to record a video of the victim jumping and then play it back for them to laugh at.

You could set this up with your TV pointing out of a window to get random strangers walking past your house?  I wouldn’t do that near a road though!

As a minimum you will need

  • Raspberry Pi (A or B) with peripherals, USB keyboard, mouse etc
  • Raspberry Pi camera module (this)
  • HDMI TV or Monitor with Speakers
  • At least 3 Female to Female jumper wires (like these)
  • PIR Motion Sensor Module (like this)

I also reccomend

  • RPi Camera Board 360 Gooseneck Mount (like this)

What is a PIR module?

PIR stands for Passive Infra Red.  You’ve all seen these things before, they are tremendously common these days.  Most often found in the corners of rooms for burglar alarm systems.  All objects that are above absolute zero emit radiation in the form of infra red.  Infra red wavelengths are not visible to the human eye but they can be detected by the electronics inside one of these modules.

The sensor is regarded as passive because it doesn’t send out any signal in order to detect movement.  It adjusts itself to the infra red signature of the room it’s in and then watches for any changes.  Any object moving through the room will disturb the infra red signature and will cause a change to be noticed by the PIR module.

We don’t need to worry about its inner workings.  What we’re interested in are those three pins on it.  We can connect those to the Raspberry Pi GPIO pins.  One is for +5 volts, one is for ground and the other is the sensor pin (the middle pin on my one).  That pin will receive power whenever motion is detected by the PIR module.  We can then see that happening on the Raspberry Pi and take action accordingly.

Wiring it up

Let’s do this in stages.  First we’ll wire up the motion sensor and get that working and later we’ll sort out playing the scary clip and recording the victim via the camera.

Have your Raspberry Pi turned off and unplugged when you do this.  Refer to the diagram above for pin numbers.  If you look closely at the pins on your PIR module you’ll see some white text on the PCB near the base of each one.  VCC is for +5 volts in.  Take one of the female to female jumpers and connect the VCC pin to pin 2 (red) on the Pi, this will make the Pi give 5 volts of power to the PIR module.  Use another jumper to connect GND on the module to pin 6 (black) on the Pi, this completes the circuit and allows current to flow back out of the module into the Pi.  Now do the same for the sensor pin (OUT), you can use any of the green pins on the Pi for this but I am going to use pin number 7 (since it’s the first green one).

Note: If you have a different PIR module to my one then the pin layout might be different, this is why I refer you to the labels VCC and GND.

When you’re done you should then have something like this.

Next lets boot up the Pi.  Make sure you’re using a recent version of Raspbian since we need this for the camera board software later on.  If you need to download and burn a new SD card image, do this now.

After you have logged in we need to create two python files.  The first one, listed in full below, is a class that handles the motion detection.  We’ll use this in a second python file which will be our main scare program.  You can either do this using the X desktop and Leafpad or over an SSH connection using something like Putty.  A paste in Putty is done with a right click of the mouse.

Copy the code below and save it as a file named pirDetect.py

import RPi.GPIO as GPIO
import time

class detector(object):
        def __init__(self, sensor):
                self.callBacks = []
                self.sensor = sensor
                self.currState = False
                self.prevState = False

                GPIO.setup(self.sensor, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

        def read(self):
                self.prevState = self.currState
                self.currState = GPIO.input(self.sensor)

        def printState(self):
                print "GPIO pin {0} is {1}".format(self.sensor, "HIGH" if self.currState else "LOW")

        def subscribe(self, callBack):

        def callBack(self, state):
                for fn in self.callBacks:

        def start(self):
                while True:
                        if self.currState != self.prevState:

When you have done that we can create another file which will be the actual scare program.  This file will link into pirDetect.py so we can make use of its code.  Now save the code below into a new file named scare.py

from pirDetect import *

objDetect = detector(7)

Note: We pass the number 7 into the detector class initialise function, this is telling it to watch pin 7 for activity!  Pin 7 is connected to the sensor pin on the PIR module remember.

At the moment all this program will do is show some text saying GPIO pin 7 is HIGH or LOW.  HIGH means there is power on the pin, low means there is not.  So really HIGH means movement has been detected.  This is enough to test the PIR module, so let’s give it a try.

After you have saved scare.py we need to set it to be executable and then we can run it.  Enter the following commands at the command line (or in LX Terminal if you prefer).

sudo chmod +x scare.py
sudo ./scare.py

If you get an error saying RuntimeError: No access to /dev/mem it means you forgot to use sudo.  You must run programs that access the GPIO as root and sudo does this for you.  Think of it as super-user-do.  Press Ctrl – C when you want to exit.


If you keep still you should notice pin 7 will go to LOW.  Then if you start moving or wave it will go HIGH.  Keep on waving and it will stay HIGH and only go back to LOW if you keep still again.  If this is what you have then everything is working correctly.  If not then something is wrong and you need to go back and troubleshoot.

On the PIR module you should have two orange coloured components that look like they take a phillips screwdriver (see above).  These things are called potentiometers and they allow you to adjust the sensitivity of the sensor and the detection time.  I would suggest to have sensitivity set to max and time to min, the choice is yours though.

Scary movie clip

Okay so let’s download a scary movie clip and play it.  Enter the following command at the command line to download the file.

wget https://dl.dropboxusercontent.com/u/14125489/3secondScare.mp4

Next we can use omxplayer to play the file.  Now beware, you know this is going to be scary so don’t jump too much!  Enter the following command.

omxplayer 3secondScare.mp4 -o hdmi

So, what we need to do is to run this command from within our scare.py file when motion is detected.  The detector class that I wrote implements the observable software design pattern.  This allows you to define a function in your code and make the detector class run it whenever movement is detected.  Modify scare.py with the code below.

import subprocess as sp
import time
from pirDetect import *

video = ["omxplayer", "filename", "-o", "hdmi"]
scareFile = "3secondScare.mp4"

def onMotion(currState):
        if currState:
                video[1] = scareFile
                subVideo = sp.Popen(video)
                while subVideo.poll() is None:

objDetect = detector(7)

Note: We define the onMotion function which takes the parameter currState.  This is a boolean value and will be True if HIGH and False if LOW.  If currState is True/HIGH then we use the sub process library to launch omxplayer and wait for it to finish with a while loop.

We then call the subscribe function on the detector class passing in our onMotion function.  This will make the detector class call that function whenever the state of pin 7 changes.

Keep very still and run scare.py from the command line.  When you do some movement the scary clip should play!  Press Ctrl – C to exit.

sudo ./scare.py

I have noticed that occasionally omxplayer captures your Ctrl – C and then messes up the display of the command prompt.  You’ll know if this has happened because you can no longer see what you type and pressing enter causes the prompt to appear on the same line as opposed to the next line.  If you get this then just type reset and that will fix it.

Recording a video of the victim

So we could already start scaring people with what we have now.  But wouldn’t it be better if we could capture the moment of fright using the camera module and then play it back to them?

So first you’ll need to fit the camera module to your Raspberry Pi, here is a video showing how to do it.  Do this now if you have not done so already (shut down your Pi and do this with it unplugged from the mains).


The next thing we need to do is activate the camera software on Raspbian.  So boot the Pi back up and after you log in enter the following command (you can skip this if you’ve already done this part).

sudo raspi-config

One of the options will be Enable Camera.  Select this and press enter and then you can go down to Finish at the bottom to exit.  The Pi should then reboot.  After you log in again enter the following command to test the camera.

 raspivid -t 0

This should just give you an infinite preview of what the camera can see (Ctrl – C to exit).  You should be able to see yourself on screen when you point the camera towards yourself.  If you’re using the gooseneck mount use it to hold the camera in the right position so that its well aimed.

Let’s try recording a 5 second video and playing it back.  Enter the following commands.

raspivid -o testing.h264 -t 5000
omxplayer testing.h264 -o hdmi

You should find that the video is played back to you correctly.  You’ll notice that, while a video records, a preview is also shown on the screen at the same time.  We need to disable this as it will obscure the playback of the scary clip.  If we add the -n parameter to raspivid that will disable the preview.  Try this command.

raspivid -o testing.h264 -n -t 5000

You should find that the video was recorded but no preview was shown.  We’re now ready to incorporate these commands into our program.  Modify scare.py with the following code.

import subprocess as sp
import time
import datetime
from pirDetect import *

def getFileName():
        return datetime.datetime.now().strftime("%Y-%m-%d_%H.%M.%S.h264")

def subProcWait(params):
        sub = sp.Popen(params)
        while sub.poll() is None:

record = ["raspivid", "-o", "filename", "-n", "-t", "5000"]
video = ["omxplayer", "filename", "-o", "hdmi"]
scareFile = "3secondScare.mp4"

def onMotion(currState):
        if currState:
                autoFileName = getFileName() #Get a time stamped file name
                record[2] = autoFileName
                subRecord = sp.Popen(record) #Start recording to capture their fright
                video[1] = scareFile
                subProcWait(video) #Play the video to scare them
                video[1] = autoFileName
                subProcWait(video) #Play back the video we just recorded

objDetect = detector(7)

Note: I have added a couple of utility functions here. One is to generate a file name (getFileName) for the video of the victim and the other is to start a sub process and wait for it to finish (subProcWait).

So now, when motion is detected we generate a time stamped file name for the victim video and use it to start recording.  Then we play the scary clip and wait for it to finish, during which we hopefully caught someone on camera jumping out of their skin!  We then play back the video that we just recorded so the victim gets to laugh at themselves.

Also note that there is a potential race condition here.  The length of the scary clip and the length of the video we record.  If we record for too long then the code will try to play back the recorded video before it has finished recording.  5 seconds is pretty safe, however maybe you can see a way to modify this code to stop that from happening?  Look at the poll method on the subRecord object maybe?

sudo ./scare.py

After some time you may notice a number of videos, with a timestamp filename, have been recorded.  Monitor the size of these to ensure they do not fill up your SD card.  You can clear them out by using the following command.

sudo rm *.h264

That is pretty much it.  All you need to do now is set the trap and wait for a victim to come along.  Turn the volume up for best results.  Good luck and have a safe Halloween!

15 thoughts on “Halloween fright ambush project

  1. I am having a small problem with the “Halloween Fright Ambush Project”.
    Everything worked up until I modified the scare.py code. The program throws an error as follows: “File “./scare.py” SyntaxError: Non-ASCII character “xc2” in file ./scare.py on line 10, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details.

    I looked there but am at sea yet! Halloween is coming on fast and I’d like to use this program but am a tyro at python. Can you point me in the right direction or tell me what’s wrong?

    • It sounds like a bad character made it into your python file when you did your copy and paste of the code from this page.
      The error mentions line 10, have a look on the 10th line down from the top of the file to see what you can see.

        • Thanks for the quick reply. The problem was with me. I did cut and paste the code you supplied but after close examination, I found that a closing parenthetical was not picked up. Once I found that and place it in the right position, everything functioned as it should. Thanks for the effort, I appreciate it!

  2. How about using a normal USB webcam? I have some spare webcams that I could use instead of the pi camera and it would be easier to mount the webcam further away from the raspi.

    • Guess I could just do something like this:

      record = [“avconv”, “-f”, “video4linux2”, “-r”, “25”, “-i”, “/dev/video0″, ” “filename”]


      • oops, need to add the time to it:

        record = [“avconv”, “-f”, “video4linux2”, “-r”, “25”, “-i”, “/dev/video0”, “-t”, “5”, “filename”]

  3. Hey thanks for this awesome tutorial but I cannot get past the first hurdle sadly, I am new to programming and when I type in sudo chmod +x scare.py I get No such file or directory returned. I have saved the files to my desktop. I then tried sudo chmod +x /home/pi/Desktop/scare.py

    That returned a blank command prompt then I typed in sudo ./scare.py and got command not found then I typed in the file path again and got command not found returned.

    Thanks for any help with this.

    • Sorted, just used nano to create files and it read it all from the home folder. Works perfectly thank you however I have a question…..

      is there a way to start with a black screen and then play the video?

        • Great, that’s work for me.
          In terminal:

          sudo nano /etc/profile

          then, at the end of the file, write the follow:


          Now, i’m searching the way to modify code, to get the video in the usb.
          Thank You

  4. hey man, really nice project! Unfortunately the dropbox-link doesn’t work anymore. Any chance of getting the “3secondScare.mp4” video-file? I would like to give it a try 🙂

    Kinds regards

  5. Love this! Works like a charm! What I would like to add is the ability of the trick or treaters to select their scare using two buttons. One button for scary, another for kid friendly. What I’m thinking, is when one button or the other is pressed a different set of videos would play. Any suggestions to make this happen? I am completely new to python and this is my first “project” working and learning/teaching with my young kids. Any help would be appreciated..

  6. Hello,
    I’m trying to do something similar to this but I am a total rookie. I have a 10 minute video that I want to play when motion is detected from the PIR sensor. after the video plays, I have a 20 second video that I want to loop until the next motion is detected and then the 10 minute video plays. Do you have any code recommendations?

Ask a Question or Leave Some Feedback

We will not display your email address. Fields marked with a * must be filled in.