How to Build a Smart Magic Mirror Using a Raspberry Pi Zero [Tutorial]

Build a Smart Mirror

Ever wanted to see check the weather is in Tripoli and read randomized Seinfeld quotes while you’re doing your hair in the morning? Smart mirrors are the DIY project to make that happen. This easy-to-follow tutorial covers how to build a small magic mirror using a Raspberry Pi Zero and a few other bits and pieces.

Smart mirrors (also sometimes known as “magic mirrors”) can display live information right in the reflection on a mirror – it looks cool and is actually kind of useful. You can set them up to display whatever information you want – calendars, weather, quotes, photos, reminders – it’s totally up to you.

Smart mirrors are pretty easy to make, but everyone seems to make big ones, so let’s make a small one for your desk or bedside table instead.

In keeping with the Raspberry Pi Palmtop article, I’ll avoid anything that requires special tools or 3d printers, or exact measurements.

The completed smart mirror

The completed mirror and a definitely not staged stage for it.

Materials

Hardware

Magic Mirror Hardware (1/2)

Magic Mirror Hardware (1/2)

Magic Mirror Hardware (2/2)

Magic Mirror Hardware (2/2)

Here’s the hardware used to put our project together. You might recognize some of the parts from the Raspberry Pi Palmtop project – everything gets recycled here.

Here are the must-haves:

  • Ikea picture frame
  • Raspberry Pi Zero + SD Card
  • Small LCD Monitor
  • USB, HDMI Cables
  • 2-way Mirror Acrylic Sheet (Mine is 3mm thick, A5 size)
  • A USB keyboard to get the Pi set up
  • Adhesives – double-sided tape, glue, etc. (if Apple are content to make their devices unrepairable by filling them with glue, so are we)

And in the second picture, some optional extras:

  • Plastimorph
  • Buttons and wire
  • If you’re adding a button and wire and don’t have any pre-soldered, you’ll need a soldering iron (but again – optional!)

Everything above can be found on your favorite multi-colored logo bearing online marketplace.

My smart mirror’s innards will also include a USB hub as I don’t have the OG Raspberry Pi Zero and will need an extra USB port for a WiFi adapter. The newer Raspberry Pi Zero W has built-in WiFi and no need for this, so I’ve left it out.

Adhesives Warning

Don’t glue or tape over or near exposed electronics (capacitors, transistors, resistors, chips) on circuit boards – they could short or overheat! Always have the board properly mounted in a plastic case – then stick things to that.

Software

This project will be a good way to explore using Linux packages as well as Bash and Python scripts:

Layout and Fit

Layout and Fit

Layout and Fit

Layout and Fit

I’ve marked the screen corners, so I know not to place any tape over where it will show through on the other side.

Plastimorph

Plastimorph

Plastimorph! You put the granules in boiling water (which is what’s in that jar), wait a minute, and in return, you get plastic you can shape, which will set hard. It’s the poor man’s 3D Printer.

Melted Plastimorph

Melted Plastimorph

The heat melts the plastic granules into a clump, which you can then shape with your hands.

Plastimorph Brackets

Plastimorph Brackets

I’ve used the plastimorph to create some brackets to hold the screen onto the perspex sheet, which is simply taped to the shroud, which usually sits inside the picture frame.

Making It Fit

Making It Fit

Making It Fit

Here it all is put together and sitting in the frame.

As much as I’m a fan of being untidy, this is going a bit too far. Adjustments need to be made.

In the frame

In the frame

The plastimorph was regrettably abandoned, and more tape was used instead. Much better.

plastic riser

The plastic riser, which ran around the inside edge of the frame, was removed so that everything can sit flush with the front glass.

Blu-tak

Blutack was used to hold the internals (now all stuck together) to the frame. It may not seem like a permanent solution, but it will hold – and be easy to remove if you want to make any adjustments.

Looking svelte!

Looking svelte!

Push Button Soldered

Push Button Soldered

I’ve kind of skipped over the button you can see in the above photos – it’s just a basic push button with a couple of wires soldered to it.

It’s connected to GPIO pin 21 and ground. My Raspberry Pi only has a few headers soldered because I am both too lazy to solder the ones I’m yet to use and terrible at soldering.

You can buy buttons and Raspberry Pis with pre-soldered headers if you want to avoid soldering at all.

Back and stand re-attached.

Back and stand re-attached.

Finally, the back and stand are to be re-attached. You can use the metal fasteners that the picture frame already has built-in, or in my case, because the USB hub makes everything too fat, I used a copious amount of adhesives.

If it works, it works. Just make sure you can still get to the internals to plug in a keyboard for the software setup.

Make sure you leave a space for the button too.

Raspberry Pi OS and SSH

Next task – get an operating system on the SD card and enable SSH so that it can be accessed remotely from the network. You’ll need a keyboard attached to the Raspberry Pi, and you might want to have it connected to your computer monitor, so things are easy to read.

Installing Raspberry Pi OS Lite

Download the Raspberry Pi OS Lite image from:

https://www.raspberrypi.org/software/operating-systems/

I won’t reproduce the steps for flashing the Raspberry Pi OS SD card image – you can follow our article on Installing Ubuntu on a Raspberry Pi and ignore everything but the Setting up your SD Card With the Ubuntu MATE Image section – substitution the Ubuntu MATE image with the Raspberry Pi OS Lite image downloaded earlier.

Connect to WiFi and Enable SSH

Once you’re up and running with Raspberry Pi OS, plug a keyboard into your Raspberry Pi, log in and update your software:

sudo apt update
sudo apt upgrade

Then run the configuration tool:

sudo raspi-config
sudo raspi-config

sudo raspi-config

You’ll get the following configuration screens – run through them to connect to your WiFi network by navigating:

System Options -> Wireless LAN 

And enter your network name and passphrase.

Connecting via SSH from another computer

Run raspi-config again and navigate to:

Interface Options -> SSH 

And enable SSH access.

Follow this guide to get access to the Raspberry Pi over the network.

You can now unplug your pi from the keyboard and re-attach it to the insides of your smart mirror.

Installing MagicMirror Software

The Smart Mirror functionality we seek will be provided by the software package MagicMirror. It’s free, and it works, so let’s use it.

If you’re using a Raspberry Pi 2 or above, you can skip all of this and just use a pre-built SD card image from

https://docs.magicmirror.builders/getting-started/installation.html#magicmirroros

Just scroll down to the MagicMirrorOS option and go from there

There are no pre-built images for the Raspberry Pi Zero, so we’ll have to set things up ourselves – here’s how.

Installing Node JS on a Raspberry Pi Zero

You might have spotted the instructions for Magic Mirror at

https://docs.magicmirror.builders/getting-started/installation.html#manual-installation

and be tempted to try them. Please don’t do it! You’ll hit this error:

Installing Node JS on a Raspberry Pi Zero

Installing Node JS on a Raspberry Pi Zero

You can’t install NodeJS via this method on the Pi Zero, as support for the ARMv6 CPU they use has been dropped, so follow the advice the console spits out and download the binary tarball using wget, and install manually:

wget https://nodejs.org/dist/latest-v10.x/node-v10.23.0-linux-armv6l.tar.gz
sudo mkdir -p /usr/local/lib/nodejs
sudo tar -xvf node-v10.23.0-linux-armv6l.tar.gz -C /usr/local/lib/nodejs

NodeJS version 10 is the last version to support the ARMv6 processor.

See this article for more information about using wget

Edit your Bash ~/.profile using the *nano* text editor:

nano ~/.profile

…and add:

# Nodejs
VERSION=v10.23.0
DISTRO=linux-armv6l
export PATH=/usr/local/lib/nodejs/node-v10.23.0-linux-armv6l/bin:$PATH

Refresh profile by running:

. ~/.profile

NodeJS is now installed! Confirm it with:

node -v

You should get a version number printed out to confirm everything is there.

node -v

node -v

Downloading MagicMirror using GIT

After that diversion, the MagicMirror software can be installed. The code will be pulled directly from Github using Git, so let’s install it:

sudo apt install git

We’re using apt rather than apt-get – here’s why!

If asked if you wish to install -answer ‘yes’!

Clone the MagicMirror repository using Git:

git clone https://github.com/MichMich/MagicMirror

 

Downloading MagicMirror using GIT

Downloading MagicMirror using GIT

Now run the MagicMirror installation process – this will download all of the dependencies and libraries MagicMirror requires internally. It’ll take a while, and it might complain a bit – but so long as it’s only warning not producing errors, it’s no big deal:

cd MagicMirror 
npm install -arch=armv7l

Some of MagicMirrors’ dependencies don’t have prebuilt armv6 compatibility – so we’ll trick it into thinking we’re on ARMv7 (using the -arch=armv7l option) just to get through the install process – and deal with compatibility issues later.

If you didn’t include the -arch=armv7l option, you’d get the below error:

Error without -arch=armv7l option

Error without -arch=armv7l option

Create the configuration file by copying the sample:

cp config/config.js.sample config/config.js

If for some reason you’re still referring to the MagicMirror installation documentation, you’d be tempted to run:

npm run start

Again, please don’t do it! You’ll get more errors. This is because of that ARMv7 trick above – we can’t use the built-in web browser that MagicMirror includes for displaying content because it’s not supported on the Pi Zero.

Installing Chromium Browser

To get around this, use a browser that does work on ARMv6 – Chromium (It’s pretty much Google chrome without some extras). Install it:

sudo apt install chromium-browser xinit xorg matchbox unclutter

Also being installed is the X Windows Systemmatchbox window manager, and unclutter – a tool to hide the mouse cursor.

Now that we have a browser, we need to set it to start up automatically.

Auto Login

Run the configuration tool again to enable auto-login:

sudo raspi-config

Navigate to:

System Options -> Boot / Auto Login -> Console Autologin

Select finish, no need to reboot yet.

Automatically Launching MagicMirror

This command writes out a configuration file for Chromium to stop it from prompting or throwing up any message boxes when it is launched – just paste it into the terminal (after reading through it to make sure you know it’s not doing anything malicious):

sudo touch /etc/chromium-browser/customizations/01-disable-update-check;echo CHROMIUM_FLAGS=\”\$\{CHROMIUM_FLAGS\} –check-for-update-interval=31536000\” | sudo tee /etc/chromium-browser/customizations/01-disable-update-check

Return to the home directory:

cd ~

Create a Bash script using the nano text editor to start the MagicMirror server:

nano start_magic.sh

Enter the following before saving:

#!/bin/bash
cd ~/MagicMirror
node serveronly &
sleep 30
xinit /home/pi/start_chromium.sh

Create another Bash script to start Chromium in the matchbox window manager, running in kiosk mode (hiding the window border and address bar), and loading the MagicMirror software now running at http://localhost:8080/:

nano start_chromium.sh

Enter the following before saving:

#!/bin/sh
unclutter &
xset -dpms # disable DPMS (Energy Star) features.
xset s off # disable screen saver
xset s noblank # don’t blank the video device
matchbox-window-manager &
chromium-browser --incognito --kiosk http://localhost:8080/ # MagicMirror runs on 8080 by default

Make the scripts executable:

chmod a+x start_*

We have our start scripts, and we have auto login – let’s set the scripts to start after auto login by adding them to the Bash profile.

nano ~/.profile
#Start mirror on profile load, but only if this isn't an ssh session
if [ -n "$SSH_CLIENT" ] || [ -n "$SSH_TTY" ]; then
    echo "Hello, SSH!" # SSH session, do not try to launch SmartMirror, or it will make configuring things difficult
else
    sh ~/start_magic.sh # Not an SSH session, run SmartMirror
fi

Some may say .profile isn’t the best place for that, but it’s convenient, and this isn’t a mission-critical scenario. Now reboot.

sudo reboot

It might take a minute or two for MagicMirror to load.

MagicMirror loaded.

MagicMirror loaded.

The default MagicMirror content gives out all sorts of compliments.

Everything is a bit cluttered on this particular screen, but the layout and configuration can be changed later.

Adding a Sleep/Wake Button

You don’t have to wire in a sleep/wake button, but we have, so let’s use it.

Installing Python and Other Dependencies

You don’t have to – Raspberry Pi OS already includes Python and the required libraries to interact with the GPIO pins the button is attached to.

A Bash Script to Toggle The Display

Create the script:

nano display_toggle.sh

And enter the following commands to toggle the display before saving:

#!/bin/bash
if [ $(vcgencmd display_power) = "display_power=1" ]; then
    vcgencmd display_power 0
else
    vcgencmd display_power 1
fi

Make it executable:

chmod +x display_toggle.sh

Cool, right? Now create the Python script to trigger this Bash script when the green button is pressed:

nano watch_sleep_button.py

Enter the following before saving:

import RPi.GPIO as GPIO
import os

# Set which pin the button is wired to
gpio_pin=21

def buttonFunction():

        # Monitor the pin for changes
        try:
                GPIO.setmode(GPIO.BCM) # Use GPIO numbering rather than pin position number so the number above points to the right pin
                GPIO.setup(gpio_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Set pin as an input and set it high so it can be grounded
                GPIO.wait_for_edge(gpio_pin, GPIO.FALLING) # See if the pin has been pressed (ie grounded, connected to ground)
                os.system("sh ~/display_toggle.sh")
                print("Display state changed!")
                GPIO.cleanup() # Revert all GPIO pins to normal state, just in case something else wants to use them
        except:
                pass


while True: # An infinite loop so this function will just keep on running, continually checking for a button press!
        buttonFunction()

Make it executable:

chmod +x watch_sleep_button.py

Now to make this Python script execute in the background whenever the Pi starts.

In the Linux shell, adding a & to the end of a command will run it in the background!

Add the following BEFORE starting the mirror in your .profile

nano ~/.profile

BEFORE the line:

sh ~/start_magic.sh # Not an SSH session, run SmartMirror

Add the following line:

python ~/watch_sleep_button.py & # Watch the sleep button

And do a final reboot:

sudo reboot

Configuring Mirror Content

You’ll probably want to change what is shown on the mirror. There’s a lot of different things you can add, covered over on the MagicMirror docs:

https://docs.magicmirror.builders/getting-started/configuration.html

(*Be sure you don’t change the port number or address in the configuration, or nothing will load)

Results!

Completed Smart Mirror (1/2)

Completed Smart Mirror (1/2)

Completed Smart Mirror (2/2)

Completed Smart Mirror (2/2)

It’s not going to win any design awards, but it looks alright! It’s a bit hard to get the full effect in photos, but the effect is very cool in person.

Of course, there’s already grit under the glass – living in central London, 86% of the air is diesel particulates, so it’s to be expected.

That’s it! Our smart mirror took a bit of extra hacking to get together, but it’s working. Make one for your mum for Christmas, and check out our other Raspberry Pi project articles!.

SHARE:
nv-author-image

Brad Morton

I'm Brad, and I'm nearing 20 years of experience with Linux. I've worked in just about every IT role there is before taking the leap into software development. Currently, I'm building desktop and web-based solutions with NodeJS and PHP hosted on Linux infrastructure. Visit my blog or find me on Twitter to see what I'm up to.

Leave a Reply

Your email address will not be published. Required fields are marked *