The Holidays are always a pleasant time of year where families can get together and unwind, but for one person this is their busiest time.
Santa Claus that well known bringer of presents is busy preparing everything that our hearts desire. He travels the world in one night but no one has a real picture of him, well all this is about to change with the Element 14 Santa Trap, cunningly disguised as a decorative Christmas tree.
So what are we going to build?
A Christmas tree which will flash lights and play music via integrated LEDs and a speaker. This is triggered via an ultrasonic sensor which will detect when a person is within 1 metre of the tree. Sneakily there is also a camera to capture Santa as he delivers our presents.
What kit do we need?
- Ultrasonic Sensor (HC-SR04)
- 1x 1k Resistor
- 16x 220ohm Resistor (Optional)
- Male to Male Jumper Wires
- Male to Female Jumper Wires
- Female to Female Jumper Wires
- Hook up wire
- Green Card
- Box For Raspberry Pi
Download the code
All of the code for this project can be found on Github
Building the Christmas tree
For our tree we upcycled cardboard from our local grocery store using colored card and glue. We simply created a single side to illustrate the project clearly. You can build your tree using any materials that you see fit, apart from metal as that will conduct the power from your LEDs.
Wiring the LEDs
To save GPIO pins we have seven banks of LEDs. Each bank connects to one GPIO pin. We can safely power five LEDs from one GPIO pin as the current that they draw is within the Raspberry Pi limit. When using LED with the Raspberry Pi it is recommended that you use a current limiting resistor and these would be placed in line from the GPIO pin to the LEDs. Your LEDs will appear less bright but your Raspberry Pi and LED will be protected from too much current. In this project I chose not to use them as I wanted a brighter effect.
To wire the LEDs together we used female to male jumper cables and connected them to a breadboard stuck to the rear of the tree. Each bank of LEDs had their own spot on the breadboard enabling us to power banks individually and identify them. The wiring for a bank is as per the diagram below.
Wiring the RGB LED
For the outer most parts of the tree we used some special RGB LED that can display red, green and blue colors. We used a common cathode version of the LED, which means that there is one common ground for the RGB and that all of the other pins require 3.3V of power to pick a color. When using LED with the Raspberry Pi it is recommended that you use a current limiting resistor and these would be placed in line from the GPIO pin to the LEDs. Your LEDs will appear less bright but your Raspberry Pi and LED will be protected from too much current. In this project I chose not to use them as I wanted a brighter effect.
The pin out for the RGB LED is as per this diagram, which also shows how we can wire many LED together. Grouping them by color so that when the correct GPIO pin is triggered we can pick any of the three colors or cycle through them.
Wiring the HC-SR04 Ultrasonic Sensor
The HC-SR04 Ultrasonic sensor is a wonderful piece of kit. It emits a pulse of ultrasonic sound that will reflect off any nearby surfaces. When a reflection occurs an echo is sent to the sensor which is registered. Using a little basic maths and Python programming we can work out the distance between the sensor and the object.
Wiring up the sensor is straightforward requiring only four pins to be connected.
Pin 1 is 5V power, the HC-SR04 uses 5V logic and will happily accept the 5V power supply from the GPIO pin 2. The Trigger pin is next and this can be directly connected to pin 26 of your Raspberry Pi. The echo pin requires us to use an in-line 1k Ohm resistor as the sensor uses 5V logic which can damage the GPIO. By using a resistor we protect the GPIO pin from damage. Our last pin is GND otherwise known as Ground, this is connected to the Raspberry Pi Ground pin.
Building the project
Wiring the project is relatively simple, please follow the diagram to see how we wired our project for one bank. Test each bank individually before moving on to the next bank. In total we had seven banks of single colored LEDs and one bank of six RGB LED, with each of their corresponding pins being connected to a specific GPIO pin.
Installing our PiNoir
The PiNoir camera works especially well in low light conditions, so is well suited to capturing Santa.
To install the camera locate the black connector marked CAMERA between the HDMI and Ethernet ports.
Carefully lift the top and bottom edges of the connector vertically, they will gently slide up and then stop when in place.
Remove your camera from the box and slide the ribbon connector into the CAMERA connector, ensure that the blue edge faces the ethernet port. Be careful handling the camera it is rather fragile. With the ribbon inside the connector gently push the connector edges back down, locking the ribbon in place.
With the camera in place, boot up your Raspberry Pi and in a terminal run
At the menu navigate to Enable Camera and press enter. In the next screen select Enable, and then navigate to Finish, which will prompt you to reboot your Raspberry Pi. Do this and allow the Pi to reboot, thus enabling your camera.
Programming the trap!
Our Santa Trap follows a simple logic
- Person triggers the sensor
- A photograph is taken
- The LEDs light up
- Music is played
This is then looped so that the sequence is repeated if the sensor is triggered.
The libraries Pygame and PiCamera do not come installed on your Raspberry Pi and will necessitate you installing them via the terminal.
Open LXTerminal and type in the following, remember to press enter at the end of each line.
With the external libraries installed we can now safely close the LXTerminal window and refer back to the project.
Let's step through the code. Starting with importing the modules that will enhance our code.
We import modules that enable us to control the speed of our project (time), permit Python to work with real times and dates (datetime) and enable us to use the GPIO pins in our project (RPi.GPIO) You will have noticed that the GPIO import is different to those before it, by importing a module as something we can rename it from the rather difficult RPi.GPIO into a more friendly GPIO.
The last three imports start with a module to play sound in our project, and this is called Pygame. Pygame is a module for those that want to build games using Python. Next we import the picamera module which enables the official Raspberry Pi Camera and the new Pi Noir camera to be used with Python.
Our last import is rather special as rather than importing the entire module or renaming it like we did the RPi.GPIO we shall just import one function from the module. The random module contains lots of great functions that can be used to add a few chaotic elements to your code, such as random numbers and choice. In this project it would be fun to introduce a random selection to our music choice, and in order to do that we will import the choice function from the random library.
With the imports completed lets move on to configuring Pygame.
In order to use Pygame and it's audio mixer we must first initialise both Pygame and it's audio mixer, and this is done via the two lines above.
Next we need to configure the project to work with the Raspberry Pi GPIO pins.
Our first line is an instruction that reduces the verbosity of any errors, in general use the GPIO warnings can be quite noisy so by setting them to False, in other words Off, we can minimise the noise. If you would rather see the warnings, for debugging purposes then you add a “#” to the start of that line, effectively commenting out the line and instructing Python to ignore it.
Next we set the GPIO to use a logical board numbering system that is really easy to work with. For a clear reference see the diagram below. For reference, the diagram contains all of the pins present on the Raspberry Pi B+, if you are using a model A or B, the GPIO will stop at pin 26.
Variables are an important part of programming. We can use them to store information like a container will hold water and variables will hold any data types that we wish to use. Strings, floats and integers can be stored and the input can be generated manually or via a multitude of sensors.. Where they are most useful is in storing data and giving it a more useful reference. For example which pin is used for the first string of LEDs? Well I can store that pin number in a variable called LED1 and then just use that reference through my code.
You can see in the code above that the first variable created is “global distance” and this differs to those after it. A global variable is a special kind of variable that can be used both inside and outside of a function. The global distance variable is used inside of our ultrasonic sensor detection function, that we will go into more detail with later.
Our other functions contain, integers which represent the GPIO pins used for our LED, strings which represent the location of the audio files played when the sensor is triggered, and lastly we store a string which will call a function for our camera. This is stored in the camera variable, and using the variable makes it really easy to use.
With our variables created, we will now use them to setup our project to use the GPIO pins. To use a GPIO pin we first need to setup what each pin does, for example LED1 (pin 8) is setup to be an output, in other words current will flow from the Raspberry Pi to the LEDs attached to the pin. We do this for all of the LED variables that we created earlier, and to the trigger pin, which will be used for initiating the ultrasonic pulse.
The same setup is done for our RGB LED, with each leg of the LED having it's own pin to denote the colour that will be chosen.
Our last GPIO setup is the pin which will be connected to the receiver component of the ultrasonic sensor. Rather than sending current from the GPIO pin, in this case the pin will be receiving current from the ultrasonic sensor. So for this pin we set it up as an input (GPIO.IN).
Creating a function for our Ultrasonic Sensor
The HC-SR04 is a cheap and simple sensor unit, it sends a pulse of ultrasonic sound and then awaits the return of the reflected pulse. The best way to use this sensor with Python is via a function which will contain all of the code needed to handle the sensors operation.
With functions, we first define the name of them in this case it is called “ultra”, the word (sensor) is an argument. This code is expandable so that we can add more ultrasonic sensors to the project. The (sensor) argument is passed the number of the sensor, with the first (and only sensor in this example) being sensor 0. Our next line instructs the function that we wish to use the global variable (distance) in this function.
We now create the start of an if and else conditional statement, starting with choosing the correct sensor, in this case 0. With the sensor chosen we now delay the code by 0.3 seconds, enough time to settle the sensor before use. We then create two variables, signalon and signaloff and we set them to 0. Next we instruct the trigger pin to send current to our ultrasonic sensor which will trigger a pulse to be sent.
With the pulse being triggered we want to control how long the pulse will be, so that are readings are accurate, so we use a delay of 0.00001 seconds, just enough to get a reliable result. We turn off the pulse by instructing Python to turn off the trigger pin on our GPIO.
Still inside of our if condition, we now create two while conditions. These conditions handle the capture of the time that a pulse is sent and received. So while no echo is received, we save the current time as a variable called signaloff, but when the echo is received, denoted by the number 1 in the second while condition, the current time is saved as another variable called signalon.
We now come to the last three lines of our if statement, and here we see the maths that will give us the answer we need. We create a new variable called timepassed and in there we store the answer to the equation, signalon minus signaloff. The answer is then used in another equation which will give us the distance that we need, so in this equation we ask what the answer will be for timepassed multiplied by 17000 which is the speed of sound in centimetres. With the distance found we then print the speed to the terminal for debug purposes. The else part of the conditional statement is rather simple, in that it just prints to the shell if there is an error.
Creating more functions
Functions are a great way to keep our code neat and tidy, and to help us localise any issues that we may face when we are debugging. After creatng the function to handle the ultrasonic sensor, we now create more functions to handle the other functionality that we require.
We earlier imported the pygame library, and initialised it to work as an audio player. In order to play audio we need to create a function to handle the selection and playback of audio, and the best way to do this is via a function which takes an argument.
We first create a function called music, and this function takes an argument, in this case it is represented by x, but x will be replaced with the audio file that we wish to play. The music function consists of two lines of code, the first loads up the audio file that we wish to play, and the second line plays the audio file once.
Flashing our LED
On our tree we have two types of LED, the first being simple LEDs of a single colour and this function handles all of those LEDs at once! Flashing an LED with the GPIO is relatively straightforward, requiring us to turn on the power for a set amount of time and then turn off that power at the required time. But to do that to the many LEDs at once is a big job, made a lot easier via this handy function.
Just like our music function, the flash function can also take arguments, in this case being the variables used to identify the many LEDs in our project. In the code example below there are 7 different strings of LED that can be addressed, a,b,c,d,e,f,g, are the argument placeholders, but when used we will use the LED variables that we created earlier.
For easy reference
a == LED1
b == LED2
c == LED3
d == LED4
e == LED5
f == LED6
g == LED7
The function turns on LED1, LED2 off, LED3 on and LED4 off, LED5 on, LED6 off and lastly LED7 onthen waits for 0.2 seconds then reverses each of the LED so what was on previously is now off. It waits for a further 0.2 seconds and then the sequence is run again.
Flashing the RGB LED
Our RGB LED has four pins, the longest pin (pin 2) is the common ground (cathode) while the others refer to a particular color. By sending power to pins 1,3,4 of the RGB LED we can trigger the correct color that we require. In the script below we firstly trigger the red LED to light up, then blue, green and lastly we turn all of them off.
Resetting our LEDs
To keep things neat and tidy, we're created a simple little function that ensure that all of the LEDs are turned off at the start of our sequence.
Putting it all together
We now move on to the main body of our code.
We start by playing an audio file that tells us that the program has started correctly, I chose sleigh bells, but you can of course change this to suit your needs.
Now we start an infinite loop (while True) that will trigger our ultra function to ping out ultrasound and measure the distance travelled. Next we create a list (known in other languages as an array) labelled songlist that contains the three variables song1,song2,song3. Each of these variables contains the path to the audio files that we chose to play. In the next line we create a new variable called chosen, that will store the result of choosing a random entry from the songlist list. The function choice is part of the random module that we imported earlier.
We now create a new if else conditional statement, and this time we are evaluating the distance between the ultrasonic sensor and any persons in the room, if the distance is less than 30cm then the following takes place.
We create a variable called a, and in there we store the current time, we then convert the time into a string using a helper function called str() which converts anything inside the brackets into a string. Further work is needed with our a variable, the full time captured is rather too big, so we use something called string slicing to cut away the text that we do not need. We are only interested in the first 20 characters so using a[0:19] will cut this out and save it as the variable.
We now create a variable called alert, and in there we create a string that alerts us to Santa's presence, along with the time of the detection, which we created and saved as our variable a.
We print the alert to the shell and then move straight on to creating another variable that contains the time of the intrusion, again from the variable a, and we join the string .jpg to it, creating a filename that contains the time of the intrusion.
Our next section of code handles the taking of a picture using the Raspberry Pi Pi Noir camera, first of all we set the resolution of the picture to 1024 by 768 pixels, a nice size for a quick snap. We then capture a picture, and use the filename that we created earlier. Lastly we play the music that had been chosen randomly earlier.
Inside of the if conditional statement we now nest a for loop, that will run our two Led functions flash() and rgb() 100 times.
Out of the for loop, but still inside of our if statement we now instruct the code to wait for 1 second, thus ending the if statement.
We now move to the last part of the code, the else statement which simply prints “Waiting for Santa” and then waiting for 1 second.
We have now built our own Santa Trap, which is also rather pleasing to both our eyes and ears.
Good luck trying to catch Santa!