Introduction: Interactive Magic Mirror With Candy Dispenser

For Halloween 2012 I created an interactive Magic Mirror. The Magic Mirror has a sonar (PING)))) sensor that detects when kids are are close. Then the mirror says a few random phrases and automatically drops candy to the bucket. I repurposed my Automatic Candy Dispenser from two years ago. The PING))) sensor, fog and red lights are controlled by an Arduino.

You can text "say [your name]" to a SMS short code and the Magic Mirror will say your name, drop candy,  take a Dropcam snapshot and send it back to your mobile phone (MMS). The SMS/MMS capability was handled by an awesome API from Mogreet, that makes MMS really easy to use. The Magic Mirror can also be controlled by a PHP web application hosted on Apache2 in the Mac Mini.

The Mirror Mirror software is made by the folks at ImaginEERIEing and I bought it two years ago. Last year I used it to do an interactive photobooth (which sadly I did not document!). I highly recommend it. You can get a free version but the $15 version is much nicer and pretty affordable.

Now, before you go dismissing this project as a simple copy of  previous (and great) Instructables like DIY Magic Mirror and others that use the same software. Please consider the interactive features such as the SMS and candy dispenser. And ultimately I hope to provide inspiration for similar and improved mirrors!

Step 1: Materials

Magic Mirror Wall
  • Mirror frame
  • Sheet rock panel
  • 2x4's to build a frame
  • Black, gray and golden spray paint
  • Popcorn ceiling finish
  • Zodiac cutouts
Automatic Candy Dispenser
  • Please refer to my Instructables here to see how I buit it.
  • Arduino Uno
  • Adafruit prototype shield
  • PING))) Paralax sensor
  • Servo motor
Magic Mirror computer
  • Mac or PC.
  • Computer LCD monitor.
Special Effects
  • Mini Fog machine
  • X10 appliance relay
  • Red Flood light
  • Flood light socket

Step 2: Mirror Wall Construction

Construction of the wall was "mostly" simple. I used the thinnest sheet rock I could find and made the required openings. I soon learned that the sheet rock by itself was not going to be enough (It actually fell once and cracked). So I build a frame to give it more support. Sheet rock was not my first choice. I actually first build the wall with a very thin wood panel. This option could work but my initial plan did not include blocking the whole door. I was planning to put it on the side corner. When I realized that It was better to use the whole door I had to retire the wood panel.

Once the sheet rock had its frame I used the popcorn finish and spray paints to give it texture. I printed silhouettes of the 12 zodiac signs and meticulously cut each one of them. Then I used cardboard to cut squares to place the cutouts and then sprayed painted with the golden spray paint.

I also spray painted the mirror frame since it used to be green instead of golden. 

Step 3: Computer Software

An Apache2 webserver running on a older Mac Mini took care of incoming external request to talk (via Web or SMS API). The Magic Mirror voice was generated by the OSX "say" command. This is the PHP Script I used. Note I had to use sudo as I needed the Applescript and Say commands to run as the same user that the Mirror Mirror software was running.
speak.php
<?php
$body = file_get_contents("php://input");
shell_exec("sudo -u [username] echo 0 > state.txt");
shell_exec("sudo -u [username] arch -i386 osascript events.scpt g");
shell_exec("sudo -u [username] say " . $body);
?>
This is the Applescript that took care of down "key downs" to animate the Mirror Mirror puppet. In the PHP script I made sure I "woke up" the puppet with the key press of "g"

events.scpt
on run argv
tell application "System Events"
        key down (item 1 of argv) 
        delay 2
        key up (item 1 of argv)
end tell
end run
In the next step you can see the main Applescript script that received inputs from the Arduino USB Serial connection.

Step 4: Arduino

The Arduino used the following sketch. Look at the comments for the pin connections.

>mirrormirror.ino
#include 
#include 

Servo myservo;             // New instance of Servo.h

int servoPin = 7;          // Servo used to rotate the CD
int pingPin = 4;           // Ping pin
int dtrPin = 3;            // DTR line for C17A - DB9 pin 4
int rtsPin = 2;            // RTS line for C17A - DB9 pin 7

int buttonState = 0;       // Initialize button state to low
int bitDelay = 1;          // mS delay between bits (1 mS OK)    
int counter = 0;          // counter used to change/toogle lcd message

char code[32] = "";
boolean isEngaged = false;
int isitreal = 0;

void setup(){
  myservo.attach(servoPin);
  Serial.begin(9600);
  X10.init(rtsPin, dtrPin, bitDelay);  // Initialize X10 C17A
  myservo.write(0);
  dropCandy();
}

void loop(){
  long duration, cm;
  pinMode(pingPin, OUTPUT);
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin, LOW);
  pinMode(pingPin, INPUT);
  duration = pulseIn(pingPin, HIGH);
  cm = microsecondsToCentimeters(duration);
 
  if (cm < 120){
      isitreal ++;
      if (isitreal = 2){
      Serial.print("A");
        delay(8 * 1000);
        dropCandy();
        delay(15 * 1000);
        isitreal = 0;
      }
  } else  if (cm > 120 && cm < 200){
    X10.sendCmd( hcA, 2, cmdOff ); 
  }
  
  //Listen if computer send signal to drop candy.
  if (Serial.available()) {
      char c = (char)Serial.read();
      if (c == '1'){
        dropCandy();
      }
  }

  
  delay(100);
}

void dropCandy(){
  //Turn on X10 light and fog
  X10.sendCmd( hcA, 1, cmdOn );
  X10.sendCmd( hcA, 3, cmdOn );
  myservo.write(180);
  delay(500);
  myservo.write(0);
  //Wait 30 seconds before next candy

  //Turn on X10 light and fog
  X10.sendCmd( hcA, 1, cmdOff );
  //delay(10 * 1000);
  X10.sendCmd( hcA, 3, cmdOff ); 
 
}


long microsecondsToCentimeters(long microseconds)
{
  return microseconds / 29 / 2;
}

The following Applescript takes care of the Arduino-USB Serial connection. Note that I used Xports to connect Applescript to serial port.
set zonea to {"Who are you", "I like your costume", "Spin around for me","Who is the fairest of them all, you of course.", "What is the magic word?", "Do a dance for me","Trick or treat, Smell my feet"}
set zoneb to {"Happy Halloween!", "Hasta la vista baby", "So long, farewell, auf Wiedersehen, good night", "Thanks for stopping by, happy halloween" }

set onMode to false
set use_port to "/dev/cu.usbmodem1d11"
repeat until (get serialport list) contains use_port
        delay 3
end repeat
if (get serialport list) contains use_port then
        set onMode to true
        set myPort to serialport open use_port bps rate 9600 data bits 8 parity 0 stop bits 1 handshake 0
        delay 1
        if myPort is equal to -1 then
                display dialog "could not open port"
        else
                repeat while onMode is true
                set state to (do shell script "cat '/full/path/of/file/state.txt'")
                if state is equal to "1" then
                        set x to serialport read myPort
                        if x is not equal to "" then
                                if x is equal to "A" then
                                        set randomPhraseA to some item of zonea
                                        say randomPhraseA
                                        do shell script ("sudo -u [username] arch -i386 osascript /full/path/of/file/animate.scpt &> /dev/null &")
                                        delay 10
                                        set randomPhraseB to some item of zoneb
                                        say randomPhraseB
                                end if
                        end if
                else
                        serialport write "1" to myPort
                        delay 5
                        do shell script ("sudo -u [username] echo 1 > /full/path/of/file/state.txt")
                        do shell script ("sudo -u [username] arch -i386 osascript /full/path/of/file/animate.scpt &> /dev/null &")
                end if
                end repeat
        end if
        serialport close myPort
end if

I used the following script to virtually press down/lef/right arrow keys. This made the Mirror Mirror puppet move his head.

>animate.scpt
tell application "System Events"
        key down (ASCII character 31)  
        delay 1 
        key up  (ASCII character 31) 

        key down (ASCII character 28)  
        delay 1
        key up  (ASCII character 28) 

        key down (ASCII character 29)  
        delay 1
        key up  (ASCII character 29) 

end tell

Step 5: SMS/MMS Setup

One of the most exciting features of this project was the ability to allow my neighbors to easily interact with the Magic Mirror. This is not the first time I have done SMS integration (see Automatic Halloween Candy Dispenser), but finally I was able to do it with a short code. Setting up a short code with my previous SMS API (twilio) is prohibitively expensive. But now thanks to Mogreet is pretty easy.

So basically when on of my neighbors send a Text message with the following workd "say [your name] the Mogreet server contacted my callbackurl (halloween.php) which in turn grabbed a snapshot from the front facing Dropcam and send it back to the sender.

Another neat feature I added was a "profanity filter". I just new it could be too tempting for some teenagers to send some inappropriate words.


halloween.php
<?php

$xml = file_get_contents("php://input");
$mo = new SimpleXMLElement($xml , 0, false);

$mobile = $mo->msisdn;
$msg = $mo->message;

$strArray = explode(' ',$msg);
$name = trim($strArray[1]);

$tmpdate = date ("mdy-His");
$filename = $name."-".$tmpdate.".jpeg";

downloadDropcam($filename);

$geturl = 'http://www.wdyl.com/profanity?q='.urlencode($name);
$json_output = json_decode(httpGet($geturl));

$response = $json_output->{'response'};

if ($response == 'false'){   
    $message = "Hello ". $name . ". I am the magic mirror and I have some candy for you. Happy Halloween!";       
    httpPost("http://[Server running on Mac Mini/speak.php", $message);
    echo "Message sent: ".$msg;
}else{
    echo "Sorry. Profanity not allowed!";
}

$url  = "http://[hosted server].com/halloween/".$filename;
$params = "client_id=XXXX&token=XXXXX&campaign_id=XXXXX&to=".$mobile."&message=".urlencode("Happy Halloween")."&content_url=".$url;
$smsResponse = httpGet("https://api.mogreet.com/moms/transaction.send?".$params);



function downloadDropcam($file){
    $url  = "https://nexusapi.dropcam.com/get_image?width=800&uuid=XXXXXXX";
    $path = "halloween/".$file;
 
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $data = curl_exec($ch);
    curl_close($ch);
    file_put_contents($path, $data);
}

function httpGet($url){
    $ch = curl_init(); 
    curl_setopt($ch, CURLOPT_URL, $url); 
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 
    $output = curl_exec($ch); 
    curl_close($ch);    
    return $output;
}

function httpPost($url,$data){
    $ch = curl_init($url); 
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    curl_setopt($ch, CURLOPT_FRESH_CONNECT, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 1);
    $output = curl_exec($ch); 
    curl_close($ch);
    return $output;
}

?>

Step 6: Lights and Music Setup

To add to the ambiance I created a small lights and music show using a Raspberry Pi. Check my Instructables here.

Halloween Decorations Contest

Second Prize in the
Halloween Decorations Contest

DIY Audio

Participated in the
DIY Audio

Make It Glow

Participated in the
Make It Glow