It’s again the time of year when it’s socially acceptable to scare little kids and give them candy. Oh joy I’m here to make your job easier by showing you how to create a simple Raspberry Pi Halloween motion sound box. Here is a demo:
Here’s what you need
Probably the only part you don’t already have is the motion sensor, a small and inexpensive part that you can find at your local micro center or Maplin map.
Raspberry Pi (any model will do).
Motion sensor (~$3).
Wired speaker (most Bluetooth speakers have the ability to use line-in).
3.5mm stereo cable, male to male.
Once you’re done you can add some timed lighting effects. too, but in this lesson we will only talk a little about scary sounds!
We’re using Raspbian Jessie Lite and Python 2.7, but any Linux distribution running on your Pi should be fine. I left it at the default hostname «raspberrypi.local», so start by logging in remotely with SSH (open a terminal window if you’re on a Mac. Here’s how to do it on Windows) — or if you decide to use full Raspbian with desktop GUI, feel free to upgrade.
ssh firstname.lastname@example.org (enter raspberry as the password) sudo apt-get update sudo apt-get install python-pip sudo pip install gpiozero
This installs a simple library for working with GPIO pins in Python with many types of built-in sensors and buttons. Connect a sensor with signal pin on GPIO4, VCC connected to 5V and GND connected to GND. This may vary depending on your specific model, so confirm with a pinout diagram.
Helpfully, my Pimoroni Pi 2 case has the pinout lasered directly onto it.
Now let’s make our motion detection script.
from gpiozero import MotionSensor pir = MotionSensor(4) while True: if pir.motion_detected: print("Motion detected!") else: print ("No motion")
Press CTRL-X then Y to save and exit, then run:
You should see the «no motion» message repeat on the screen until you wave your hand in front of the sensor when it lingers on «Motion detected!»
If the message doesn’t change at all, you’ve wired it incorrectly.
If you want to learn more about this simple GPIOZero library, take a look at this fantastic spreadsheet.
Connect your portable speaker and make sure it is turned on if necessary. We will use the library pygame to play sounds, so let’s install it:
sudo apt-get install python-pygame
First, we need a sound file to play. If you are doing this from a desktop environment, then download the file WAV or OGG from somewhere (I found a good selection of free Halloween sounds here) and put it in your home directory. I would suggest downsampling first and converting to small OGG format anyway.
If you are connecting remotely and using only the command line, we have a little more trouble with some sites because the command wget may not capture the actual file. Instead, we can download it locally to our desktop and use the command scp (secure copy) to copy on the command line. You can read more about scp here, but for now, open a new Terminal tab and type:
scp thunder.ogg email@example.com:
Rename thunder.ogg appropriately, but don’t forget final : (the command will complete without it, but it won’t do what we want). By default, this will move the file to the Pi user’s home directory.
Now let’s modify the script to play the sound. Start by importing some new modules:
import pygame.mixer from pygame.mixer import Sound
Then, right after the existing import statements, we will repeat the same sound over and over again for testing purposes. Leave the rest of your motion sensitive code as is for now — it just won’t run as it will be stuck in that audio loop forever.
pygame.init() pygame.mixer.init() #load a sound file, in the home directory of Pi user (no mp3s) thunder = pygame.mixer.Sound("/home/pi/thunder.ogg") while True: thunder.play() sleep(10) thunder.stop()
Note that when I initially tried this process, the sound refused to play and just clicked instead. File size or bitrate was the culprit: it was 24-bit and over 5MB for a 15-second clip. Scaling to 16-bit using the converter I linked above did everything well and the size was reduced to 260kb!
If you notice an obnoxious hiss from your speakers while running a Python application, but not otherwise, type:
sudo nano /boot/config.txt
And add this line at the end:
Restart for the changes to take effect. Or don’t worry as it still sounded like rain to me.
Finally, let’s modify the main motion test loop to play a sound when motion is detected. We will use a 15 second delay so that we can replay the entire loop and act as a spam buffer when there is a lot of non-stop traffic.
while True: if pir.motion_detected: print("Motion detected!") thunder.play() # ensure playback has been fully completed before resuming motion detection, prevents "spamming" of sound sleep(15) thunder.stop() else: print ("No motion")
We probably want to install this somewhere with a battery and no internet connection, so the script should run on restart without having to open a command prompt. To do this, we will use the simplest possible method: crontab Type of:
sudo crontab -e
If you are running this command for the first time, it will start by asking which editor to use. I chose option 2, for nano. It will load into the editor of your choice, so add the following line:
@reboot python /home/pi/motion.py &
This means that your motion.py script will run on every run, and will run silently (so any output from the script will be ignored). Reboot to try it out.
If nothing plays despite movement, or you only hear a small click, you may not have used the full path to the file, or your file may need to be converted to a lower bit rate and smaller file size.
Add more sounds
Repeating the same effect is a bit boring, so let’s add some randomness to it. Download some more Halloween sounds, remembering to downsize them to a reasonable size and bitrate, then send them to your pi with scp, as before. I added three different types of shout.
Change the code so that instead of defining a single variable pygame.mixer.Sound we actually created array sounds. This is easy with Python, just enclose them in square brackets separated by commas like this:
Now modify the main motion detection loop as follows:
while True: if pir.motion_detected: print("Motion detected!") playSound = random.choice(sounds) playSound.play() # ensure playback has been fully completed before resuming motion detection, prevents "spamming" of sound sleep(15) playSound.stop() else: print ("No motion")
Note a minor change: instead of playing back a single Sound variable, we use the function random.choice, to pick a random sound from our array of sounds and then play it.
Here’s the complete code in case you’re having trouble:
import pygame from pygame.mixer import Sound from gpiozero import MotionSensor from time import sleep import random pygame.init() pygame.mixer.init() #load a sound file, same directory as script (no mp3s) sounds = [ pygame.mixer.Sound("/home/pi/thunder.ogg"), pygame.mixer.Sound("/home/pi/scary_scream.ogg"), pygame.mixer.Sound("/home/pi/girl_scream.ogg"), pygame.mixer.Sound("/home/pi/psycho_scream.ogg") ] pir = MotionSensor(4) while True: if pir.motion_detected: print("Motion detected!") playSound = random.choice(sounds) playSound.play() # ensure playback has been fully completed before resuming motion detection, prevents "spamming" of sound sleep(15) playSound.stop() else: print ("No motion")
With only four samples, there is a high chance of repeating each time, but you can add more samples if that’s annoying.
This is it! Hide it in the bushes with some scary LED monster eyes and you can save some candy since all the kids ran away screaming before they even got to the door. Or hide in the closet because the angry mom is out for blood after you made little Johnny cry.
Disclaimer: Not responsible for any injury that may result from your use of this project!
Will you be making this motion-activated sound box to scare local stunts?Have you set up any scary effects with your Raspberry Pi this Halloween?Please let us know about it in the comments below!