Halloween is one of my favorite holidays here in the US, and so much so that I spent a few years of my life thinking up and building smart Halloween props and animatronics for the haunted attraction industry. I won’t go too deep into the details of the business, but a few friends and myself founded a company a few years back with the “help” of some investors. It was my first tech startup, and like many tech companies, we had the hardware and software to revolutionize a decades stagnant industry that quite honestly did not want to change. To make a long story short, none of the original founders are part of that business anymore, with myself backing out in late 2015.
One of my duties in the company was to brainstorm and prototype new and innovative props that utilized modern technology, while remaining easy enough to use for the aging haunted house owners to be able to program. Often this was accomplished by making props that just worked once powered up, other times this involved utilizing our custom Raspberry Pi based animatronic / whole scene controller unit on the finished prop. However, during the prototyping phase, I would always develop the project using a bare Raspberry Pi or Arduino, and I loved this part about the business the most. The thrill of coming up with a concept, and then building and presenting a working prototype during our weekly all-hands meeting was exhilarating. This is why I love creating Halloween projects every year here at Element14. It gives me the perfect excuse to build some of those concepts that I never got around to prototyping when I was co-owner of the company.
One of those product ideas that I never got around to building was a smart-mirror that was fully functional while hiding a mind and body jarring jump scare that would be triggered when someone stopped in front of the mirror for more than a few seconds. So when I was asked to come up with a second Halloween project this year, I instantly thought of the smart-mirror. What I did not anticipate was the level of frustration and failure I would experience while building it. Don’t worry though, in the end, I managed to work up something that works 90% of how I wanted it to, and I am going to continue refining this project over the next couple of months, but for now, the smart mirror does work, it just lacks many of the features I wanted it to have. Before we get into the build, I would like to take a moment to talk about what failures I encountered during this project in hopes that a reader may be able to help with the javascript programming when I reboot this project in a month or two.
Experiencing Failure
As I said in the paragraph above, this project was one of the most frustrating, and stress inducing projects I have ever encountered. My issues began when building the wood frame that would house the mirror in its final form, but I am not here to rant about that, because I simply got a measurement wrong at some point in my CAD design. This was easily solved, and I only lost an hour or three rebuilding it. The real frustration kicked in shortly after deciding to use the MagicMirror2 software to power the magic mirror portion of the project.
MagicMirror2 is an amazing smart-mirror package if you are just wanting to build a feature-rich, highly functional smart-mirror. I really like this software, and it appears to be regularly updated, and has a very active community behind it. I can not recommend it enough if you are building a normal smart-mirror. The deficiencies begin to show themselves when one wants to detour from the traditional functionality that most smart-mirror builders desire. To be short, attempting to play a full-screen video, display a full-screen .GIF, or simply display a static .jpg in full screen mode on top of the MagicMirror2 display is quite difficult, if not impossible all together.
I spent three 12-16 hour days trying to get a video via OMX Player, HTML5, and various other raspberry-pi based video players to work when a GPIO Pin is pulled high by a PIR sensor. After I realized I was just not skilled enough in JavaScript programming to do this on my own, I asked a friend who is a great programmer for help, and six hours later we were still stumped. So I reached out via GitHub to one of the MagicMirror2 module developers, and he attempt to help me figure it out for several hours as well. In the end, the general consensus was that at the moment, without some extensive JavaScript coding, and a deep understanding of how MagicMirror2 and Node.JS work, that it was not possible in time to get this project published on time.
So after missing my deadline, and feeling like a complete failure, I picked myself up, tossed MagicMirror2 and all of my code into the garbage and went searching for a new approach. After a few hours of searching, I happened to come across a repository on GitHub that contained a smart-mirror program that was written in Python. This was the best possible outcome for me after the failure as Python is a language I can easily write, and understand. I really would like to make this work with the MagicMirror2 software as it is much more feature rich, and you can do some really cool stuff with Node.js, so if you would like to help me figure that out, please get in touch! Ok, enough about my failures, let's get into the actual project.
Parts Required
Hardware
Raspberry Pi 3 With NoobsRaspberry Pi 3 With Noobs
PIR SensorPIR Sensor
HT-255D Crimp ToolHT-255D Crimp Tool
Crimp ConnectorsCrimp Connectors
HDMI DisplayHDMI Display
HDMI CableHDMI Cable
3D Printing Files
Software
Setting Up Your Raspberry Pi and Downloading The Code
Before we begin installing the software packages we will need to make our spooky smart-mirror, you will need to install the latest version of Raspbian onto the SD card that will go into your Raspberry Pi. If you are using a fresh, empty SD card, then you can follow the video above to learn how to install the latest version of Raspbian to the SD card. If you already have an SD card with Raspbian installed, we can update and upgrade Raspbian from the command line. To do this remotely from the command line on your computer, connect to your Pi to your network via WiFi or a network cable, and then log in (my preferred method) via a terminal app such as Terminal, Putty, or CMDR and then enter the following commands.
sudo apt-get update
Then select “Yes” if prompted
When the update is finished running, its time to check to see if there is an upgrade available, and install it. To do this, run the following command. This could take a while, so sit back and watch some YouTube videos, or check out my Design Challenge Weekly Updates while you wait.
sudo apt-get upgrade
Then select “Yes” if prompted
Once everything is up to date, shutdown the Pi and connect a HDMI monitor, or the TV you will be using for your mirror. I did my initial development using the official Raspberry Pi 7” Touch Screen. Now restart the Pi, and access it once again from a terminal program on your computer.
Before any of the fun happens, we need to install my fork of the Smart-Mirror software. To do this you will need to use Git. If you do not have Git installed, or you have never used it before, here is a helpful tutorial. The Magic Book-bag portion of the tutorial is not relevant to this project, but it does help you understand how to use Git better.
The Smart Mirror Code
Once Git is installed, and you have your SSH key saved in your Git settings, navigate to the home/pi directory again and run the following commands. This will clone my Smart-Mirror-With-Halloween-Jump-Scare repository to the Raspberry Pi.
cd /home/pi git clone git@github.com:CharlesJGantt/Smart-Mirror-With-Halloween-Jump-Scare.git
Navigate to the folder for the repository
cd Smart-Mirror
Install the Smart-Mirror software’s dependencies (Make sure you have pip (https://pip.pypa.io/en/stable/installing/) installed before doing this.)
sudo pip install -r requirements.txt sudo apt-get install python-imaging-tk
Select “Yes” if prompted
At the moment, the weather widget is broken due to an API change, but for what we are doing with this project at the moment, that does not matter much. With that said, you should still register a free developers account at darksky.net and enter your API key in the smartmirror.py file as pictured above. To do this, enter the following commands.
sudo nano smartmirror.py
And edit line 23 with your API key. Then exit nano with ctrl+x or cmd+x and press enter to keep the same file name.
Screen Orientation and Cleanup
Before we can test the Smart-Mirror install, we need to take care of some minor, but required task. The first task is to rotate the display by 90 degrees so that our smart mirror can hang in portrait orientation. To do this, enter the following commands, and edit the config file.
sudo nano /boot/config.txt
Arrow down to the bottom of the file, and add the following line of code. Then save the file by pressing ctrl+x or cmd+x and press enter to keep the same file name. If your screen's rotation is 180 degrees off after this rotation, change the number to 3 instead of 2.
lcd_rotate=2
Now we need to hide the taskbar, and the only way to do this from the command line is to edit another config file. Enter the following command to edit the necessary file.
sudo nano /home/pi/.config/lxpanel/LXDE-pi/panels/panel
In the “Global” section at the top of the file, you need to modify the following parameters:
autohide=0 heightwhenhidden=2
Replace that line with the following line
autohide=1 heightwhenhidden=0
With those small task taken care of you can now test the Smart-Mirror install by running the following command. Note that you will have to run this from the terminal app on the Pi itself for it to work properly. This is because TKinter will only open if ran natively. There are ways to run this command remotely, but I have found them to be buggy.
sudo python smartmirror.py
An error may pop up about the weather module, but ignore it, and the screen should turn black with white clock and news text appearing on the screen.
The Jump Scare Code
Ok, now that we have the Smart-Mirror running, it's time to connect the PIR sensor to the Raspberry Pi’s GPIO header. Follow the diagram below paying close attention to the Power and GND wires.
- PIR Sensor VCC Pin to RPi 5V
- PIR Sensor Data Pin to RPi GPIO5
- PIR Sensor GND Pin to RPi GND
Now let's take a look at the jumpscare.py file that is inside of the Smart-Mirror directory. You might be wondering why I did not just include this code in the smartmirror.py code, and my reason is just because I expect that file to be updated soon by its creator to fix the weather API, and I also like the idea of being able to turn off the jump scare feature by killing the jumpscare process.
Open the jumpscare.py file in Nano.
sudo nano jumpscare.py
Starting at the top of the file we import the following libraries:
import RPi.GPIO as GPIO import time import os import sys from subprocess import Popen
Next we have to set which GPIO numbering schema we will be using. I always use the BCM schema. There are two different numbering schemes for the GPIO pins on the Pi. The Broadcom chip-specific pin numbers (BCM) and P1 physical pin numbers (BOARD.
Here’s a reference showing all the pins on the P1 header, along with their special functions and both BCM and BOARD numbers:
GPIO.setmode(GPIO.BCM)
Now we need to setup the GPIO and tell the Pi which pins are what. In the first line we are telling the Pi to set GPIO pin 5 as an input, and to attach a pulldown resistor to it. The second line sets up GPIO pin 26 as an output. I left this in the code so that you could connect an LED to pin 26 to use for troubleshooting motion triggers.
GPIO.setup(5, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) GPIO.setup(26, GPIO.OUT)
Before we get into the loop, we need to declare a variable called "motionDetected". We can use this variable to count motion triggers in our loop. We also need to tell the Pi where the video we want to play is located. Since you cloned this repository from my Github, the zombie.mp4 file will be in the Smart-Mirror directory.
motionDetected = 0 movie1 = ("/home/pi/python_programs/zombie.mp4")
I’m going to break the loop down line by line to better help you understand what's going on. In the line below we are defining our loop and stating that while True do this.
while True:
Here we are telling the Pi to watch the state of GPIO 5
input_state = GPIO.input(5)
In the next block of code we are telling the Pi that if the input state of GPIO equals True (high) then print “Motion Detected” in the terminal, increment the motionDetected variable by one, and then wait for 0.2-seconds before moving on to the next line of code.
if input_state == True: print('Motion Detected') motionDetected += 1 time.sleep(0.2)
Finally, we finish things up with another if statement that says if motionDetected equals 1 then set GPIO pin 26 HIGH, then make sure no instance of OMXplayer is running, then open a video player with the video that was defined earlier in the code. Next we tell the code to wait for 60 seconds before continuing with the loop, resetting the motionDetected variable to 0, and set GPIO pin 26 low to turn off the debugging LED. Note that you can delay or speed up how frequent the jump scare triggers by adjusting the time.sleep(60) setting.
if motionDetected == 1: GPIO.output(26, GPIO.HIGH) os.system('killall omxplayer.bin') omxc = Popen(['omxplayer', '-b', '-o', 'local', movie1]) player = True time.sleep(60) motionDetected = 0 GPIO.output(26, GPIO.LOW)
The full code is listed below.
# This Code Triggers a video to play on # the raspberry pi when motion is detected # via a PIR sensor on BCM pin 5. # Written By # Charles Gantt 2017 # http://www.themakersworkbench.com # & http://www.youtube.com/c/themakersworkbench # https://github.com/CharlesJGantt/Smart-Mirror-With-Halloween-Jump-Scare import RPi.GPIO as GPIO import time import os import sys from subprocess import Popen GPIO.setmode(GPIO.BCM) movie1 = ("/home/pi/Smart-Mirror-With-Halloween-Jump-Scare/zombie.mp4") GPIO.setup(5, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) GPIO.setup(26, GPIO.OUT) motionDetected = 0 while True: input_state = GPIO.input(5) if input_state == True: print('Motion Detected') motionDetected += 1 time.sleep(0.2) if motionDetected == 1: GPIO.output(26, GPIO.HIGH) os.system('killall omxplayer.bin') omxc = Popen(['omxplayer', '-b', '-o', 'local', movie1]) player = True time.sleep(5) motionDetected = 0 GPIO.output(26, GPIO.LOW)
With all of the code finished up, let's set both python programs to run on boot. To do this we are going to write a simple bash script that tells the Pi to run both of the python files in the background.
cd /home/pi/Smart-Mirror-With-Halloween-Jump-Scare nano launcher.sh
Now type in this script
#!/bin/sh # launcher.sh # navigate to home directory, then to this directory, then execute python scripts, then back home cd / cd /home/pi/Smart-Mirror-With-Halloween-Jump-Scare sudo python smartmirror.py & sleep 10 sudo python jumpscare.py & cd /
We need to make the launcher script an executable file. To do this, enter the following command.
chmod 755 launcher.sh
Since we will be using crontab to trigger this script. We need to make a directory to log any errors that may occur. This will help with troubleshooting. Enter the following commands:
cd mkdir logs
Now lets add the script to the crontab. Add the following line to the very bottom of the crontab file.
@reboot sh /home/pi/bbt/launcher.sh >/home/pi/logs/cronlog 2>&1
Now you can reboot the Pi to see if it worked. Enter the following command to reboot the Pi.
sudo reboot
When the Pi finishes booting, you should see the GUI load, then the smart mirror window open. If you wave your hand in front of the PIR sensor, the jumpscare.py script should trigger the zombie.mp4 video, and once finished, the smart mirror screen should reappear.
The Smart Mirror
With our code finished, it’s time to make our smart mirror. This is the part of the project where my end result may differ from yours. I chose to order a new 32” LED TV from Amazon, and try my hand at creating the two way mirror from window tint film and standard plate glass. I also wanted to create a wooden frame to house the TV inside so that it had the appearance of a hand-crafted mirror. Fortunately, I have a complete, fully stocked woodworking shop here at home, and whipping up a frame was a few hour process. As I mentioned at the beginning of this project, I did get my math wrong and made the first version of the frame incorrectly, and the TV screen did not fit. I was able to correct this, but if you do not have an abundance of time-saving tools, and extra wood to work with, take time, and measure your screen’s dimensions carefully. The only advice I can really offer is to leave about 1/16” clearance around the edge of the screen to account for expansion as the steel frame of the TV as it warms up.
I am not going very in depth here about the process I used to build the frame because how you frame the mirror is arbitrary and not very relevant to getting the mirror to work. You could even just tape a 2-way mirror to the front of the TV and the effect would be the same. Some people even create these little mirrors from 15” laptop screens, or HDMI monitors. You do not have to use an actual TV. I simply used a brand new 32” TV because I will be rebuilding this mirror, with a much more refined frame that will be built from exotic hardwood. I do however plan on making a video on that build with a complete step by step guide for my YouTube channel. So if you would like to check that out, it should be out sometime towards the end of the year.
I didn’t get many photos of the glass cutting or tinting process as that was another major issue I ran into during this project. Initially I decided that I would cut my own glass, as it is something I have done in the past, and it saves a good bit of money. My mistake was thinking that the glass I bought from a big box home repair store would be of a high enough quality to actually be easy to cut with the standard score and snap method. I broke $38 worth of glass before I gave up defeated, and called a local glass shop that explained to me that the glass quality that big box home improvement stores sell is just too low quality, and it's not annealed very well giving it a harder surface that is prone to flaking in the scoring process. The higher quality glass that glass shops stock is designed to be cut with laser sharp accuracy, and to minimize errant cracks in the cutting process. They showed me how quick and easy a good, high-quality piece of glass is to cut, and $17.38 later I was on my way home with the glass.
That afternoon, I attempted to tint the glass by myself and while I came very close to succeeding, I botched the tinting process twice. This was 100% my fault, and instead of following the poorly written directions that came with the mirror tint window film, I watched a few window tinting tutorials on YouTube, and realized that by adding a little dish soap to the water I was spraying the glass and film with, the process was much easier, and provided a better result. Living in a home with four dogs, and a couple of cats as well as an attached woodworking shop did end up haunting me a bit during the tinting process though. I spent a lot of time picking specks of dust and animal hair out of the wet tint, and still managed to trap a few dust particles and hair under the tint. Since this is a Halloween prop, I am not to bothered by that. When I rebuild this into a proper smart mirror, I will order a piece of chemically tinted 2-way mirror glass to avoid these issues all together. Another advantage to chemically treated glass is that your mirror can be made from tempered safety glass which means there is a much less chance of injury if it does shatter or fall off the wall and break.
The one thing that I made that may help you along with your build is the corner brackets I designed and 3D Printed that hold the mirror and TV firmly to the frame’s bezel. These brackets take about 20-minutes to print each on a Prusa i3 MK2s at a 0.2mm resolution. If you would like to use these brackets in your project, you can download them from my Thingiverse by clicking here.
So without going into too much detail, here are some photos of the frame build.
Now that we have the frame built and it’s hanging cable attached, it's time to attach the Raspberry Pi to the back of the TV. If you have room, and a 3D Printer, you can print this handy dandy VESA mount Rasberry Pi case bottom that I found on Thingiverse. If you want to print the top piece as well, that is just fine. I only printed the bottom as I wanted my Pi to have good airflow. Unfortunately on the TV I am using, the VESA mount was only part of the rear plastic, so I ended up attaching the Pi to a single screw hole. When I rebuild the mirror, I will print a custom case with mounting points that fit the screw locations on the grey steel backing plate.
As you can see, it mounts to the tv with standard M4 machine screws. Then the Pi attaches to it with small 3mm screws. Then all that is required is to attach a USB cable from the Raspberry Pi 3, and the TV’s USB port.
Finally, we need to attach the PIR Sensor to the top of the frame. To do this, I found a nice and compact PIR sensor case on Thingiverse, which I remixed, and designed a small extension arm for. Download it here. This is held together with M3 machine screws and nuts. To mount it to the top of the frame, I just used more small screws like I used on the corner brackets.
To finish up the PIR sensor mounting I needed to make up a cable that would connect it to the Raspberry Pi. Using a pin and crimp kit, I made the cable about three inches longer than it needed to be to add some strain-relief and prevent the cable from putting too much tension on the pins of the Pi.
Now all that is left is to test the smart mirror and jump scare out, and to do this, I simply stood it up on my workbench. Check out the demo video above, and I hope it shows off the jump scare well enough. This was the final point of frustration for me during this project. I shot a nice video with my DSLR, and lavaliere microphone, but it appears that something is broken in my brand new camera as it will not record audio from its microphone jack. Thankfully, Canon has a spectacular warranty department, and it will be fixed in a couple of weeks. I plan on taking the smart mirror to a friend's Halloween party, and will update this post with a video of some people getting scared if that happens.
So, what are my final thoughts on this project? Well I can honestly say that even after all of the frustration, stress, and unfortunate events, that I am, for the most part, proud of how it turned out. There are things I wish I would have done a different way, and some features that I left off simply because of time constraints. As I mentioned earlier, I am going to continue developing this project over the coming months, and hope a few people will join me in that journey, but for now, I have a working smart mirror that also features a cool jump scare, so I will call this one a win. Albeit, a small win, but a win nonetheless. I guess that the take away from my experience on this project would be that perseverance always pays off, and as long as you refuse to give up, anything is possible. Thanks for taking the time out of your day to read this tutorial. If you would like to see me create more cool stuff like this, please leave a comment below, and hopefully I will get assigned more projects like this! I will see you on the next one, and until then, remember to Hack The World and Make Awesome!