Thursday, April 11, 2013

Hand of PI (Twitter controlled Robot Hand)

OBJECTIVE:  Use the Raspberry PI to monitor a Twitter feed and control a mechanical device.

If you are not interested in the details of the build and just want to see the result you can watch the vid below.  

-----
RESULT:  Success!!!  You can control the "The Hand of PI" by sending a tweet to @OurCatDoor.   If  your tweet includes any of the text below, the "Hand of PI" obeys your command.  Valid commands are (lowercase):
  • one (holds up one finger)
  • peace (shows the two finger peace sign)
  • three (three fingers up)
  • hookem (if you are a Texas Longhorn fan this one makes sense)
  • fist (the Hand of PI gets ready to fight)
  • open (ready for a 'high five')
  • finger (well...  this will be the most tweeted command)
Go ahead, try it!!!  Send a tweet command to @OurCatDoor to let us know you were here.
-----
Basically what you are seeing is the Raspberry PI running a Python script searching any tweet sent to @OurCatDoor.  In the video, an iPad sends a tweet to @OurCatDoor that has the command "finger" in it.  It takes a few seconds, but the Raspberry PI finds the tweet, parses it, and find the "finger" command.  The Python script then sets the PI's GPIO ports High/Low.  The PI GPIO is connected to a PICAXE 18M2 (via a HC7404 buffer).  The PICAXE 18M2 reads the PI's GPIO to control five servo motors.  "Hand of PI" reacts with the appropriate gesture.  Watch closely and you can see the text on the screen update as the "finger" command is found and the "Hand of PI" gestures.   There's a lot going on here.  Confused?  This diagram should help (click to see full size):
Of course this isn't full schematic, but it lays out all the I/O to align with the source code you see below.  Really, the interconnects and 5VDC to the servos, PI, PICAXE, and HC7404 is something anyone wanting to duplicate the project should easily understand given the block diagram and source code.
-----
Let's show a few more pics and action videos of the rig before we get into the source code:
 
-----
This video is a bit long but demonstrates all the gestures of the "Hand of PI".  The screen in the background shows output from the Python script.  The screen is not needed, but I included it in the video to show the tweets as they are captured.  Note the "Hand of PI" reacts when a new tweet command is found.
Everyone wants to see the "Hand of PI" flip the bird; that is the last gesture if you want to skip to the end...
----
If you are still with us, enjoy some source code for your reading pleasure.

First the program that is running on the PICAXE 18M2.  It's job is to read the Raspberry PI's GPIO output and control the five servo motors on the "Hand of PI".

' PICAXE 18M2 for RaspPI intergration to Tweeter Controlled Hand Gesture Robot APRIL 2013
'"THE HAND OF PI"
' www.whiskeytangohotel.com
' NOTE: PICAXE Program Editor Rev newer than 5.3.6 causes servo jitter***
' Other than the minium PICAXE 18M2 'keep alive' 22K R & 10K R
' no other R, C, L, etc need for the project.
' Everything on PICAXE powered by 4.5VDC
' The PICAXE drives the servos straight from the chip.
' See pinouts in comments

' 0 is Thumb (PICAXE pin 6)
' 1 is Pointer (PICAXE pin 7)
' 2 is Middle (PICAXE pin 8)
' 3 is Ring (PICAXE pin 9)
' 4 is Pink (PICAXE pin 10)
' Normally Open Button Switch is PICAXE pin 18 (pulled HIGH with 10K)
; this button will not be used for the PI intergration

' PI GPIO 11 connected to c.0 (PICAXE pin 17)
' PI GPIO 13 connected to c.7 (PICAXE pin 16)
' PI GPIO 15 connected to c.6 (PICAXE pin 15)

symbol RaspPI11 = pinc.0
symbol RaspPI13 = pinc.7
symbol RaspPI15 = pinc.6

'Define Servo values to fully EXtend/Open finger
Symbol Ex_Thumb = 60
Symbol Ex_Pointer = 60
Symbol Ex_Middle = 245
Symbol Ex_Ring = 60
Symbol Ex_Pink = 60

'Define Servo values to fully CLose finger
Symbol CL_Thumb = 225
Symbol CL_Pointer = 240
Symbol CL_Middle = 50
Symbol CL_Ring = 240
Symbol CL_Pink = 240

'Init the servos
servo 0, Ex_Thumb
servo 1, Ex_Pointer
servo 2, Ex_Middle
servo 3, Ex_Ring
servo 4, Ex_Pink

pause 400

'Gesture Subroutines are (2^3 = 8 can be PI Callable)
' Valid Tweet commands are: one, peace, three, hookem, fist, finger, wave

'Insure Open_Hand position at program start
gosub Open_Hand
pause 500

main:  'This loops until hell freezes over

'Read the RasPI GPIO bus and  jump to gesture sub routine

If RaspPI15 = 0 and RaspPI13 = 0 and RaspPI11 = 0 then
gosub Open_Hand
end if

If RaspPI15 = 0 and RaspPI13 = 0 and RaspPI11 = 1 then
gosub One
end if

If RaspPI15 = 0 and RaspPI13 = 1 and RaspPI11 = 0 then
gosub Peace
end if

If RaspPI15 = 0 and RaspPI13 = 1 and RaspPI11 = 1 then
gosub Three
end if

If RaspPI15 = 1 and RaspPI13 = 0 and RaspPI11 = 0 then
gosub Hook_em
end if

If RaspPI15 = 1 and RaspPI13 = 0 and RaspPI11 = 1 then
gosub Fist
end if

If RaspPI15 = 1 and RaspPI13 = 1 and RaspPI11 = 0 then
gosub F_You
end if

'If RaspPI15 = 1 and RaspPI13 = 1 and RaspPI11 = 1 then
' gosub Wave  'wave is pretty hard on the servos, so we commented it
'end if

pause 5
goto main

' Gesture Subroutines below:
Open_Hand:
servopos 0, Ex_Thumb
servopos 1, Ex_Pointer
servopos 2, Ex_Middle
servopos 3, Ex_Ring
servopos 4, Ex_Pink
return ' Open_Hand

Hook_em:
servopos 0, CL_Thumb
servopos 1, Ex_Pointer
servopos 2, CL_Middle
servopos 3, CL_Ring
servopos 4, Ex_Pink
return 'Hook_em

F_you:
servopos 0, CL_Thumb
servopos 1, CL_Pointer
servopos 2, Ex_Middle
servopos 3, CL_Ring
servopos 4, CL_Pink
return 'F_you

One:
servopos 0, CL_Thumb
servopos 1, Ex_Pointer
servopos 2, CL_Middle
servopos 3, CL_Ring
servopos 4, CL_Pink
return 'One

Peace:
servopos 0, CL_Thumb
servopos 1, Ex_Pointer
servopos 2, Ex_Middle
servopos 3, CL_Ring
servopos 4, CL_Pink
return 'Two

Three:
servopos 0, CL_Thumb
servopos 1, Ex_Pointer
servopos 2, Ex_Middle
servopos 3, Ex_Ring
servopos 4, CL_Pink
return 'Three

Four:
servopos 0, CL_Thumb
servopos 1, Ex_Pointer
servopos 2, Ex_Middle
servopos 3, Ex_Ring
servopos 4, Ex_Pink
return 'Four

Fist:
servopos 0, CL_Thumb
servopos 1, CL_Pointer
servopos 2, CL_Middle
servopos 3, CL_Ring
servopos 4, CL_Pink
return 'Fist

Wave:  'waves the fingers
servopos 0, CL_Thumb
pause 70
servopos 1, CL_Pointer
pause 70
servopos 2, CL_Middle
pause 70
servopos 3, CL_Ring
pause 70
servopos 4, CL_Pink
pause 70

servopos 0, Ex_Thumb
pause 70
servopos 1, Ex_Pointer
pause 70
servopos 2, Ex_Middle
pause 70
servopos 3, Ex_Ring
pause 70
servopos 4, Ex_Pink
return 'Wave
----
Now for the Python script running on the Raspberry PI.  It's job is to search any tweet sent to @OurCatDoor and parse it for a "Hand of PI" command, then set the PI's GPIO for input to the PICAXE 18M2.



# WhiskeyTangoHotel.com - APRIL 2013   (special thanks to @Rob_Bishop)
# Error traps entered due to json hitting web site that was down etc.
# For next added to end of prog to blink LED to show program is running.

# Import the urllib library to read data from webpages
import urllib

# Import the simplejson library to  decode the data read from the webpage
import simplejson

# Import the time library for delay and lepse time tracking
import time
CurrentTime = time.time()

# Import the Raspberry Pi GPIO libraries
import RPi.GPIO as GPIO

# Set-up the GPIO pins
# Clear the current set-up
GPIO.cleanup()

# Set up the GPIO library to use Raspberry Pi board pin numbers
GPIO.setmode(GPIO.BOARD)

# Set pin 11, 13, 15  on the GPIO header to be an output
GPIO.setup(11,GPIO.OUT)  #PIXACE leg 17 (c.0)
GPIO.setup(13,GPIO.OUT)  #PIXACE leg 16 (c.7)
GPIO.setup(15,GPIO.OUT)  #PICAXE leg 15 (c.6)
GPIO.setup(7,GPIO.OUT)   #Blinkie LED to let us know the prog is running

# Start with Open Hand
GPIO.output(11,GPIO.LOW)
GPIO.output(13,GPIO.LOW)
GPIO.output(15,GPIO.LOW)
Last_gesture = "open"
Error_hit = 0
print "Hand open.  Waiting for Tweet...","\n"

# Function to take Twitter handle (e.g. @Raspberry_Pi) as an argument and return the most recent tweet

# Define the function name and show the arguments
def Latest_Tweet_to_Twitter_Handle(twitter_handle):
try:
# Get the results of a search on Twitter for tweets containing the given hand$
Twitter_search_results = urllib.urlopen("http://search.twitter.com/search.json?q="+twitter_handle)

# Decode the data that we got from the webpage to form a list of tweets
result_list = simplejson.loads(Twitter_search_results.read())

# The function returns the first result in the list
return result_list["results"][0]["text"]
except:
pass

# Main body of the program - Get the latest tweet and check if it contains certain words
# Loop to run forever

#Twitter commands the hand understands are:
#one, two, three, hookem, fist, finger, wave

while(True):
try:
#Time since program start in seconds
DeltaTime = int(time.time() - CurrentTime)

# Function gets the latest tweet mentioning the handle given in next line
Tweet=Latest_Tweet_to_Twitter_Handle("@OurCatDoor")

# START TEST(open): Check if tweet contains the word given in quotation marks
if "open" in Tweet: # and Last_gesture != "open":
Last_gesture = "open"
# If it did contain the word then print out the tweet along with a message
print DeltaTime,"seconds:",Tweet," - Gesture OPEN HAND","\n"
# Turn on the LED
GPIO.output(11,GPIO.LOW)
GPIO.output(13,GPIO.LOW)
GPIO.output(15,GPIO.LOW)
#---END TEST(open)---

# START TEST(one): Check if tweet contains the word given in quotation marks
if "one" in Tweet: # and Last_gesture != "one":
Last_gesture = "one"
# If it did contain the word then print out the tweet along with a message
print DeltaTime,"seconds:",Tweet," - Gesture ONE","\n"
# Set the PICAXE inputs
GPIO.output(11,GPIO.HIGH)
GPIO.output(13,GPIO.LOW)
GPIO.output(15,GPIO.LOW)
#---END TEST(one)---

# START TEST(peace): Check if tweet contains the word given in quotation marks
if "peace" in Tweet: # and Last_gesture != "peace":
Last_gesture = "peace"
# If it did contain the word then print out the tweet along with a message
print DeltaTime,"seconds:",Tweet," - Gesture PEACE","\n"
# Set the PICAXE inputs
GPIO.output(11,GPIO.LOW)
GPIO.output(13,GPIO.HIGH)
GPIO.output(15,GPIO.LOW)
#---END TEST(peace)---

# START TEST(three): Check if tweet contains the word given in quotation mar$
if "three" in Tweet: # and Last_gesture != "three":
Last_gesture = "three"
# If it did contain the word then print out the tweet along with a message
print DeltaTime,"seconds:",Tweet," - Gesture THREE","\n"
# Set the PICAXE inputs
GPIO.output(11,GPIO.HIGH)
GPIO.output(13,GPIO.HIGH)  
GPIO.output(15,GPIO.LOW)
#---END TEST(three)---

# START TEST(hookem): Check if tweet contains the word given in quotation mar$
if "hookem" in Tweet: # and Last_gesture != "hookem":
Last_gesture = "hookem"
# If it did contain the word then print out the tweet along with a message
print DeltaTime,"seconds:",Tweet," - Gesture HOOK EM HORNS","\n"
# Set the PICAXE inputs
GPIO.output(11,GPIO.LOW)
GPIO.output(13,GPIO.LOW)
GPIO.output(15,GPIO.HIGH)
#---END TEST(hookem)---

# START TEST(fist): Check if tweet contains the word given in quotation mar$
if "fist" in Tweet: # and Last_gesture != "fist":
Last_gesture = "fist"
# If it did contain the word then print out the tweet along with a message
print DeltaTime,"seconds:",Tweet," - Gesture FIST","\n"
# Set the PICAXE inputs
GPIO.output(11,GPIO.HIGH)
GPIO.output(13,GPIO.LOW)
GPIO.output(15,GPIO.HIGH)
#---END TEST(fist)---

# START TEST(finger): Check if tweet contains the word given in quotation mar$
if "finger" in Tweet: # and Last_gesture != "finger":
Last_gesture = "finger"
# If it did contain the word then print out the tweet along with a message
print DeltaTime,"seconds:",Tweet," - Gesture FINGER F_YOU","\n"
# TSet the PICAXE inputs
GPIO.output(11,GPIO.LOW)
GPIO.output(13,GPIO.HIGH)
GPIO.output(15,GPIO.HIGH)
#---END TEST(finger)---

# START TEST(wavewave): Check if tweet contains the word given in quotation mar$
if "wavewave" in Tweet: # and Last_gesture != "wave":
Last_gesture = "wavewave"
# If it did contain the word then print out the tweet along with a message
print DeltaTime,"seconds:",Tweet," - Gesture WAVE","\n"
# Set the PICAXE inputs
GPIO.output(11,GPIO.HIGH)
GPIO.output(13,GPIO.HIGH)
GPIO.output(15,GPIO.HIGH)
#---END TEST(wavewave)---

for x in range(0, 10):
# Wait for xx seconds before repeating
# Blinkie LED to let us know the program is running
GPIO.output(7,GPIO.HIGH)
time.sleep(.1)
GPIO.output(7,GPIO.LOW)
time.sleep(1)
except:
pass


-----
If you are still awake, thanks for checking out the build.  Send a tweet to @OurCatDoor to let us know you were here.
-----