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:
Since the Oracle Social Network is not released at this point I posted a modified version posting to Twitter.
- A random sound effect is played through a small set of speakers connected to the Raspberry Pi.
- A "congratulations" message is displayed in the front LCD screen.
- The Raspberry Pi grabs a snapshot from a network camera (Dropcam) and is posted to a social network.
- A random message is posted along with the picture taken by the Dropcam.
- The whole action is immortalized in the interwebz.
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:
- Raspberry Pi (Model B) / Wireless USB dongle / SD Card
- Adafruit Pi Box
- Adafruit Prototyping Pi Plate Kit
- 2 10K Resistors
- 1 Push button
- Wifi USB dongle
- Select Pine Lumber
- Switch with roller
- Driveway reflectors
- 10'' Cake Board
- Springs
- Door hinges
- ioBridge LCD 2x16 screen
- Plexiglass
- Red Paint
- Dropcam
- 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.
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.
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.
-
Switch with roller
- Raspberry Pi GPIO17 pin --> 10k Resistor --> NC terminal
- Raspberry Pi Ground --> Switch Common
-
Shutdown switch
- Raspberry Pi GPIO23 pin --> 10k Resistor --> Push button
- Raspberry Pi Ground --> Push button
-
ioBridge LCD Screen
- Raspberry Pi TX --> LCD Data
- Raspberry Pi 5v --> LCD 5v pin
- Raspberry Pi ground --> LCD ground
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.
Lcd Python script driving the ioBridge LCD screen
Now here is the Java source. Note I'm using twiiter4j library and rpi-gpio-java (http://code.google.com/p/rpi-gpio-java/)
#!/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 0Shutdown 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.
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.