Introduction: Raspberry Pi Smart Target

The Raspberry Pi Smart Target was designed to be hit by the now famous Flying Monkey, but It can be hit by any other light object such as small ball. When the Target is hit the following events happen:
  1. A random sound effect is played through a small set of speakers connected to the Raspberry Pi.
  2. A "congratulations" message is displayed in the front LCD screen.
  3. The Raspberry Pi grabs a snapshot from a network camera (Dropcam) and is posted to a social network.
  4. A random message is posted along with the picture taken by the Dropcam.
  5. The whole action is immortalized in the interwebz.
 I built the target to showcase the Oracle Social Network Public API, which is a RESTful based API.  I used the Target during the Oracle Open World 2012 conference. It was setup at the Oracle Technology Network Lounge, so people stopped by throughout the day and gave the Target a shot (pun intended). If someone hit the Target they also got to keep the monkey as a keepsake.

Since the Oracle Social Network is not released at this point I posted a modified version posting to Twitter.

Step 1: Materials

These are the basic materials I used to build the Smart Target:
  1. Raspberry Pi (Model B) / Wireless USB dongle / SD Card
  2. Adafruit Pi Box
  3. Adafruit Prototyping Pi Plate Kit
  4. 2 10K Resistors
  5. 1 Push button
  6. Wifi USB dongle
  7. Select Pine Lumber
  8. Switch with roller
  9. Driveway reflectors
  10. 10'' Cake Board
  11. Springs
  12. Door hinges
  13. ioBridge LCD 2x16 screen
  14. Plexiglass
  15. Red Paint
  16. Dropcam
  17. Speakers

Step 2: Build

The Target base is 12in long, 7in wide and 10 in tall. The Target arm is 24 in. Overall with the Target with circle installed it stands at 2 ft 1/2.

Once all the pieces were cut, I assembled them and used two springs to keep the arm always at a 90 degree position. This forced the switch with a roller to be NC (normally closed) by the pressure the arm against it. The switch is installed on the front top left area (check picture #2 for a better visual).

Once assembled I used the red spray paint to give it a nice finish. Also I mounted the Cake holder using four machine screws and used the red driveway reflector in the middle.

Step 3: Connect

The Target is using 2 GPIO ports. One for the switch with a roller, and another for shutdown button.
The switch terminal NC (normally closed) is connected to GPIO port 123 using a 10k resistor in between. The switch "COMMON" pin is connected directly to the Raspberry PI. The shutdown button is connected the same way. The LCD screen is simple connected to the Raspberry Pi TX pin and is using 5v and ground.
  1. Switch with roller
    1. Raspberry Pi GPIO17 pin --> 10k Resistor --> NC terminal
    2. Raspberry Pi Ground --> Switch Common
  2. Shutdown switch
    1. Raspberry Pi GPIO23 pin --> 10k Resistor --> Push button
    2. Raspberry Pi Ground --> Push button
  3. ioBridge LCD Screen
    1. Raspberry Pi TX --> LCD Data
    2. Raspberry Pi 5v --> LCD 5v pin
    3. Raspberry Pi ground --> LCD ground
Speakers are connected to the Raspberry Pi audio out mini plug.

Step 4: Code

The Raspberry Pi is running "headless" meaning there is no need to have a Monitor, Keyboard or Mouse.  In order to start things automatically once I connected the RPI, I used a Linux init script (/etc/init.d/target) to start the Java program that listens for action as well as a shutdown program to listen for the shutdown button. The script also shows the current IP address in the LCD screen during boot.

#!/bin/bash
#/etc/init.d/target

export HOME
case "$1" in
  start)
     if [ ! -e "/dev/ttyS1" ]; then
       ln /dev/ttyAMA0 /dev/ttyS1
     fi     
     /home/pi/lcd.py `ifconfig wlan2 | grep inet | awk '{print $2}' | sed 's/addr://'` 
     java -cp .:/home/pi:/home/pi/lib/* Target >> /home/pi/target.log &
     /home/pi/shutdown-listener.py &
     ;;
  stop)
     LCD_PID=`ps auxwww | grep Target | head -1 | awk '{print $2}'`
     kill $LCD_PID   
     ;;
  *)
    echo "Usage: /etc/init.d/target {start|stop}"
    exit 1
    ;;
esac


exit 0

Shutdown script

#!/usr/bin/env python
 
from time import sleep
import os
import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BCM)
OFF_SWITCH = 23
GPIO.setup(OFF_SWITCH, GPIO.IN)

while True:
        if ( GPIO.input(23) == False ):
                os.system('/home/pi/lcd.py " Shutting down.    Goodbye :)"')
                os.system('shutdown -h now')
                break
        sleep(1);
print 'Shutting down'


Lcd Python script driving the ioBridge LCD screen

#!/usr/bin/env python
import serial
import string
import sys

test=serial.Serial("/dev/ttyAMA0",9600)
test.open()
line = sys.argv[1]
byte = chr(0xFE)
try:
        test.write(byte)
        test.write("B")
        test.write("9")
        test.write(byte)
        test.write("Z")
        test.write(line)
                
        test.write(byte)
        test.write("T")
        test.write("0")
        delay = chr(0x10)
        test.write(delay)

                
except KeyboardInterrupt:
    pass # do cleanup here

test.close()

Now here is the Java source. Note I'm using twiiter4j library and rpi-gpio-java (http://code.google.com/p/rpi-gpio-java/)


import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import java.io.OutputStream;
import be.doubleyouit.raspberry.gpio.Boardpin;
import be.doubleyouit.raspberry.gpio.Direction;
import be.doubleyouit.raspberry.gpio.GpioGateway;
import be.doubleyouit.raspberry.gpio.impl.GpioGatewayImpl;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.activation.MimetypesFileTypeMap;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.FileEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import twitter4j.StatusUpdate;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.conf.ConfigurationBuilder;

import java.util.Random;

public class Target {
    static OutputStream output;
    static GpioGateway gpio;
    static SerialPort port;
    static CommPortIdentifier portId;

    static HttpClient httpclient = new DefaultHttpClient(); 
    static String replyURL;
    static String message;
    static boolean ready = true;
    static String dropcam = "https://nexusapi.dropcam.com/get_image?width=800&uuid=XXXX";
    static String osnImg;
    public static Date now;
    static Random generator = new Random();
    
    public static String[] messages = { 
	"Lucky! Looks like you will be taking me home tonight!",
	"It paid off to stop by the OTN Lounge tonight.",
	"Looks like someone has been practicing.",
	"Not bad for your first try. Now go ahead and brag to your friends.",
	"I love hanging out at the OTN Lounge, but It looks like you will be taking me home tonight!",
	"That was nice. No go and tell your friends to stop by the OTN Lounge.",
	"I just love to scream and fly at the OTN Lounge.", 
	"Roses are red and Oracle too. Now you can take me home too.",
	"I just wanna fly.",
	"Thanks for stopping by. Looks like you got lucky tonight.",
	"I just love to scream and fly at the OTN Lounge."
	};

    public static void main(String[] args) throws Exception{
        AddShutdownHookSample kill = new AddShutdownHookSample();
        kill.attachShutDownHook();
        

        try{
            portId = CommPortIdentifier.getPortIdentifier("/dev/ttyS1");
            port = (SerialPort)portId.open("Raspi LCD", 4000);
            output = port.getOutputStream();
            port.setSerialPortParams(9600,SerialPort.DATABITS_8, SerialPort.STOPBITS_1,SerialPort.PARITY_NONE);
            

            gpio = new GpioGatewayImpl();
            gpio.unexport(Boardpin.PIN11_GPIO17);
            gpio.export(Boardpin.PIN11_GPIO17);
            gpio.setDirection(Boardpin.PIN11_GPIO17, Direction.IN);
	    
            while (true){

        	    brightness("9");
        	    clear();
        	    print("     Ready      ");
        	    delayBacklight("0",5);
        	    int i = 0;
		//remove negation!
                while (!gpio.getValue(Boardpin.PIN11_GPIO17)){
                   i = 1; 
                }

                if (ready){
                    //System.out.println("start");
                    ready = false;
                    int rnd = generator.nextInt(10);
                    Process proc = Runtime.getRuntime().exec(new String[]{"/usr/bin/mpg321", "-q","/home/pi/"+rnd+".mp3"});
                    brightness("9");
                    clear();
                    print(" Congratulations");
                    //Login
                    downloadPicture();
                    uploadPicture(messages[rnd]);	

                    clear();
                    delayBacklight("0",5);
                    Thread.sleep(2 * 5000);
                    ready = true;
                    //System.out.println("end");
                }

            }    
        }catch (Exception e){
            System.out.println(e.toString());
        }
    }




    public static void downloadPicture() throws IOException{
        now = new Date();
        String formatDate = new SimpleDateFormat("MMddyyyy-hhmmss").format(now);
        osnImg = "OTN-" + formatDate + ".jpeg";
        
        HttpGet httpget = new HttpGet(dropcam);
        HttpResponse response = httpclient.execute(httpget);
        HttpEntity entity = response.getEntity();
        //System.out.println("downloadPicture: " +response.getStatusLine());
        if (entity != null) {

            byte[] bytes = EntityUtils.toByteArray(entity);
            File file = new File("/var/www/" + osnImg);
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(bytes);
            fos.close();     
               
        }    
        EntityUtils.consume(entity);
        
    }    

    public static void uploadPicture(String message) throws TwitterException {
        ConfigurationBuilder cb = new ConfigurationBuilder();
        cb.setOAuthConsumerKey("XXXX");
        cb.setOAuthConsumerSecret("XXXX");
        cb.setOAuthAccessToken("XXXX");
        cb.setOAuthAccessTokenSecret("XXXX");

        StatusUpdate status = new StatusUpdate(message);

        File imageFile = new File ("/var/www/" +  osnImg); 
        status.setMedia(imageFile);
                
        TwitterFactory tf = new TwitterFactory(cb.build());
        Twitter twitter = tf.getInstance();
            
                
        try {
            twitter.updateStatus(status);
        } catch ( Exception ex ) {
            ex.printStackTrace();
        }

    }


    public static void delayBacklight(String level, int delay ) throws Exception{
        startCommand();
        output.write("T".getBytes());
        output.write(level.getBytes());
        output.write(delay);
    }

    public static void brightness(String level) throws Exception{
        startCommand();
        output.write("B".getBytes());
        output.write(level.getBytes());
    }

    public static void print(String message) throws Exception{
        output.write(message.getBytes());
    }

    public static void clear() throws Exception{
        startCommand();
        output.write("C".getBytes());
    }

    public static void startCommand() throws Exception{
        output.write(254);
    }

}

//http://www.javabeat.net/2010/11/runtime-addshutdownhook/
class AddShutdownHookSample {
  void attachShutDownHook() {
  Runtime.getRuntime().addShutdownHook(new Thread() {
   @Override
   public void run(){
    try{
        System.out.println("Shuting down target");
        File file = new File("/var/lock/LCK..ttyS1");
        Lcd.httpclient.getConnectionManager().shutdown();
        if(file.delete()){
            System.out.println(file.getName() + " is deleted!");
        }
    }catch(Exception e){
        e.printStackTrace();
    }
   }
  });
    //System.out.println("Shut Down Hook Attached.");
 }
}

Step 5: Camera Setup

I've got a Dropcam HD a few months ago and so far I love it. While designing the Target I looked into different options to capture the whole shooting action. One of the obvious options was to use a USB camera connected to the Raspberry PI right? Well in my initial tests I realized that it was too late to capture a snapshot of the shooting. That's why I opted to use my Dropcam. Knowing that there will be a 1-2 second delay from it, it prove to be the perfect image source. Plus I was able to position the camera wherever I wanted.

The quality of the snapshot is not the best, compared to the beautiful 720p image feed it can offer but It will do. The Dropcam needs to be set to "Public". Once is public just grab the number after "/watch/" when viewing your camera "https://www.dropcam.com/watch/XXXXX" Then just plug that numer here https://nexusapi.dropcam.com/get_image?width=800&uuid=XXXXX and thats it! I

You see this URL call im my Java code.

Step 6: In Action