Tag Archives: sl4a

The Robot Photo Booth

One of my imaginary robots

Can I take your photo?

I had an idea some time ago to create a lightweight photo-booth client using just an android phone and Python running in SL4A. The functionality is as follows:

  1. Speak a greeting to the user
  2. Scan a QR code containing the user’s twitter name
  3. Take their photo
  4. Tweet the photo @ them
  5. Say thank you and repeat!

Added to this, I wanted to give the booth an ‘attitude’ by creating a script of random things for it to say (at random time intervals). The most difficult part of the project was finding a way to run the attitude script when the main process is sitting waiting for a barcode to scan. I tried various approaches, including using multiprocess/threads, however none of these were able to create a timeout in the scanBarCode() function. The simple solution was to create two scripts, one to provide the random talking script running as a background process and another to handle the main grunt work of scanning barcodes, taking photos and tweeting. The development platform was a Samsung Galaxy Pocket, because it’s very cheap and has all the necessary functionality.

The script runs in SL4A r6 and Python r5 (NB that I wasted a significant quantity of time running Python r1… which wouldn’t run any of the scripts I found on the web for tweeting), with the Tweepy library to tweet. The Tweepy library is a special fork that supports the status_update_with_media method, and can be found here. Thanks to Dr Drang for his post on how to tweet images using regular Python. I installed the tweepy plug-in by downloading the main .egg file and then replacing the .py files from the fork with media, seems to work fine.

Here is the code for the barcode scanning and photo taking portion. Note that the QR codes shouldn’t have the preceding @ for twitter names – although this can easily be modified. I’ve been using goqr.me to generate my standard code:

Image

Here is the main code (which I called shoot.py)

import android
import tweepy
import time

droid=android.Android()
droid.wakeLockAcquirePartial()
CONSUMER_KEY = ‘key here’
CONSUMER_SECRET = ‘key here’
ACCESS_KEY = ‘key here’
ACCESS_SECRET = ‘key here’
droid.makeToast(“Starting Auth”)
auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
droid.makeToast(“Starting Authset”)
auth.set_access_token(ACCESS_KEY, ACCESS_SECRET)
droid.makeToast(“Starting API”)
api = tweepy.API(auth)

picdir = ‘/sdcard/pbooth/’
pictemp = picdir + ‘%05i.jpeg’
piccount = 1

runcontinuous = True

while (runcontinuous == True):
time.sleep(10)
droid.ttsSpeak(“Hello, I’m the robot photo booth! Please show me your QR code”)
code = droid.scanBarcode()
droid.ttsSpeak(“Smile! Taking picture now.”)
time.sleep(1)
droid.cameraCapturePicture(pictemp % piccount)
tweet = “@” + code[1][‘extras’][‘SCAN_RESULT’] + ” here is your picture. Have a nice day!”
api.status_update_with_media(pictemp % piccount, status=tweet)
droid.ttsSpeak(“All finished. Your picture has now been tweeted. Thank you”)
#time.sleep(10))
piccount += 1

Picture file names are incremented, so the directory will be over-written each time you restart the script. The talking script is here:

import android, time, random
runcontinuous = True
droid=android.Android()

dialogues = {0 : “Hello, I’m the robot photo booth! Please show me your QR codes and I will take your picture and tweet it back to you. Please put your QR code in front of the camera now”,
1: “I can’t see your QR code properly, is it upside down?”,
2: “I’m bored. Please come and play with me”,
3: “Sorry, it’s nearly my bedtime but I’ll just take one more picture”,
4: “Help! Help! Someone has unplugged me”
}

while (runcontinuous == True):
droid.ttsSpeak(dialogues[random.randrange(0, 4, 2)])
time.sleep(random.randrange(20, 70, 2))

The dictionary of phrases can be added to in the format number:”text”,. Be careful not to paste in non-ascii characters if using quotes and jokes pasted from the web.

When setting up with twitter, go to dev.twitter.com and register your new application. Remember to select Read and Write access for your application, otherwise you will waste a fair amount of time as I did.

You can find the output of my photobooth on twitter here: http://www.twitter.com/RobotPhotoBooth

The remaining parts of the project are to print out a nice A0 size robot, including some instructions and sellotape the camera on the back of it. Job done!

Tagged , , , , , , , , , , , , , , ,

High Altitude Ballooning on a budget!

Almost exactly a year ago, I finally set out on a journey to realise my dream of taking photos from the edge of the world. A video of the whole adventure is up on Youtube here. Here’s how I did it (with a little help from my friends too!)

This photo was taken right at the top of the flightpath

Things you will need (the total cost as launched was 199CHF, excluding helium):

  • A balloon
  • A parachute
  • An android phone
  • A single use handwarmer
  • Transparent plastic box (for waterproofing)
  • A polystyrene box (for insulation)
  • Some helium
  • String + sellotape
  • A tarpaulin
  • Friends (who are as crazy as you are)

How to get started:

Hardware

Buy the things you need, I purchased my balloons from Random Solutions and the parachute from Spherachutes in the USA. For the android phone I purchased a Samsung Galaxy Mini GT-S5570 on prepay from my local mobile phone store. Obviously the phone you use should have a camera and a GPS, a reasonable battery life is also a good idea. I picked up the handwarmers from a local sports shop, they were in a box by the counter.

Software

The Galaxy Mini runs Android 2.3.4 with some Samsung ‘stuff’ on the top. I first rooted the phone using Superuser, then configured it to have the lowest screen brightness (maximise battery life), turned off the wifi, bluetooth and anything else non-critical that will eat precious power. Next install SL4A to allow the use of scripting languages such as Python, don’t forget that you will also need to install the scripting package for your chosen language, which if you want to use Python you can find here.

Once you have installed the SL4A environment and Python, you can run a script to do what you need for the High Altitude Balloon Payload:

  • Take Pictures at a regular interval (I chose 7 seconds)
  • Log the GPS coordinates of the phone’s current position
  • Log all the extra data that Android makes available (battery life, magnetometer readings, accelerometer readings etc.)
  • Send you text messages at regular intervals with the GPS position so that you can find the phone on landing!

The source code for the script is at the end of this blog post.

Practice:

Before the launch day it’s a good idea to do a test-assembly run of the payload package. Put the android phone (best to test it) and the handwarmer (no need to activate it until the real thing) inside the transparent plastic box, sellotape the edges of the box to make sure no water will get in. Then mount the box inside the polystyrene outer box – this is a good opportunity to check that the camera can see clearly. I did a battery test by putting the whole lot in my freezer (remember it’s -50C in the stratosphere so temperature performance is an issue) to see how long it will run for (about 5 hours in my case). Doing a test also gives a good indication that the script is working correctly.

Pre-assembly and testing is highly recommended to make sure the camera gets a good view

Prediction:

In the week leading up to your planned launch it’s a good idea to run a number of simulations using the fantastic CUSF Landing Predictor tool. I did a number of simulations with various assumptions for gas fill, take-off time (because as I’m usually late) and launch position for each planned launch day. Remember to archive the KML of your predictions if you want to compare them with the actual performance! It’s also a good idea to keep a close eye on the weather. For the launch day you want a clear dry day with low surface winds.

Make sure that the flight time doesn’t exceed the expected battery life of the phone and remember to keep a margin of safety as the script will only send a text message every 20 minutes – so if you land with only 19 minutes of battery life remaining you may never get it back!

On launch day:

When the launch day comes, start early (we got up at 5am), drive to your chosen launch site and start filling the balloon. Spread the tarpaulin out on the ground, folded in half. Unroll the balloon between the folds of the tarp and start to fill it very slowly. Trying to fill the balloon too fast will cause the helium to cool significantly, as I discovered to my cost with two failed launch attempts. On the final, successful attempt we used a string tied round the balloon inflation nozzle on the helium canister to stabilise the volume of gas going into the balloon, such that the fill time was about 1 hour for about 2m3 of helium. Keeping the balloon folded inside the tarp during inflation will make sure it doesn’t blow away.

The balloon fully filled with 1.4m3 of helium

Whilst the balloon is filling, assemble the payload package, activate the handwarmer and start the script. Unmodified, the script will send a text message with the location approximately every 20 minutes, so it’s a good idea to wait until you have the first message received before letting go of the balloon. Also be sure to open the handwarmer, otherwise the phone will freeze and you will never get it back! Just in case something does go wrong, it’s a good idea to film your final assembly so that you can check it was done correctly. Once the payload box is correctly assembled, securely attach the parachute (a landscape orientation is best if you want to make a movie out of the photos later). Finally attach a 2m length of string to the top of the parachute and use this to connect it to the balloon. This string needs to be reasonably long (and well secured at each end) as it prevents the balloon from getting tangled up in the parachute.

Ioannis, Morag, Manohar and me!

Ready for take off:

Once the balloon is filled, close the neck using a cable tie. On our first attempt I tied the neck of the balloon in the same way you would tie a party balloon, with the payload attached on a string through the knot. This didn’t work and the balloon came undone at about 3000m, hence the use of cable ties. After the first cable tie is secure, form the remainder of the balloon neck into a U shape, tie the payload on to bottom of the U and close it with another cable tie. Make sure that everything is correctly and securely attached, otherwise the flight might end prematurely!

Make sure that everything if firmly attached before letting go!

Take off!

Once everything is attached to the filled balloon, cross your fingers and let go.

Letting go of the balloon!

A great shot from the top 1/3rd of the flight.

Recovery.

The re-entry was very rapid.

Assuming everything has worked correctly, you should get a text message from your balloon after 3-4 hours with the coordinates of the landing spot. For collection it’s a good idea to have your hiking gear, and a walking GPS. Load the coordinates into the hiking GPS and go! Be careful not to follow it blindly into anywhere dangerous!

The payload landed face down in the grass half way up a mountain!

Download

The best bit. If your phone still has any battery remaining you will be able to see the photos immediately.  Google Picassa is great for composing time-lapse photos.

Legal Stuff

You should have insurance in case the balloon causes damage when it lands (as the prediction isn’t an exact science), you should also have permission from the land-owner for the place where you launch, and from where you recover (if it isn’t a public right of way). We launched in Switzerland, where the following rules apply and we complied with them.

Source code – use at your own risk!

import os
import time
import android

phoneNumber=”123456″ #Insert your phone number here, complete with international code 00
period = 5 # seconds
gpsperiod = 1 #take a gps position every 5 seconds
sensorperiod = 1 #take a sensor snap every second
picperiod = 2 #take a photo every 20 seconds
smsperiod = 190 #text me every 20 minutes

# FOR TESTING
runcontinuous = True #run in a loop
gpsflag = True #take gps data
sensorflag = True #record the sensor values
smsflag = True #text the cell number
picflag = True #take photos
logmode = ‘a’ # write mode FOR TESTING (for reply use ‘a’)
bufsize = 0

droid = android.Android()
droid.startSensingTimed(1, 5000)
droid.wakeLockAcquirePartial()
droid.startLocating(5000) # period ms, dist
droid.batteryStartMonitoring()

sensorlog = open(‘/sdcard/balloon/devicelog.txt’, logmode, bufsize)
picdir = ‘/sdcard/balloon/pics/’
pictemp = picdir + ‘%05i.jpg’
lastloc = ”
droid.wakeLockPartial()
tick = 0
piccount = 1

#send an initial text with the position of the phone
#time.sleep(300)
#r = droid.readLocation()
#loc = r.result

#packet = loc[‘gps’]
#al = packet.get(‘altitude’,0.0)
#lo = packet.get(‘longitude’,0.0)
#ti = packet.get(‘time’,0)
#la = packet.get(‘latitude’,0.0)
#sp = packet.get(‘speed’,0)
#ac = packet.get(‘accuracy’,0)
#lastloc = “Payload online: 1, %i, http://maps.google.com/maps?q=%.5f,+%.5f,hl=en,t=h, %.5f, %.5f, %i” % (ti, la, lo, al, sp, ac)
batlevel = droid.batteryGetLevel()
droid.smsSend(phoneNumber, “Payload online. Battery level %i” % batlevel.result)

#time.sleep(900)
#batlevel = droid.batteryGetLevel()
#droid.smsSend(phoneNumber, “Houston we are go for launch. Battery level %i” % batlevel.result)

while (runcontinuous == True ):
logline = “”
if ((tick % sensorperiod) == 0) and sensorflag:

# Prepare the log linelogline=””
available_sensors=””

# Log magnetometer *ONLY* if the read values are valid
# (Otherwise we are probably missing a magnitometer or it’s in a weird state)
magr = droid.sensorsReadMagnetometer()
mag = magr.result
if (not (mag is None)) and (type(mag) is list) and (len(mag) == 3) and (not (mag[0] is None)):
available_sensors += “M”
logline += “, %.5f, %.5f, %.5f” % (mag[0], mag[1], mag[2])
else:
logline += “,,,”

# Log accelerometer *ONLY* if the read values are valid
# (Otherwise we are probably missing an accelerometer or it’s in a weird state)

accr = droid.sensorsReadAccelerometer()
acc = accr.result
if (not (acc is None)) and (type(acc) is list) and (len(acc) == 3) and (not (acc[0] is None)):
available_sensors += “A”
logline += “, %.5f, %.5f, %.5f” % (acc[0], acc[1], acc[2])
else:
logline += “,,,”

# Log orientation *ONLY* if the read values are valid
# (Otherwise we are probably missing the sensor or it’s in a weird state)
orir = droid.sensorsReadOrientation()
ori = orir.result
if (not (ori is None)) and (type(ori) is list) and (len(ori) == 3) and (not (ori[0] is None)):
available_sensors += “O”
logline += “, %.5f, %.5f, %.5f” % (ori[0], ori[1], ori[2])
else:
logline += “,,,”

# Log battery *ONLY* if the read values are valid
# (Otherwise we are probably missing the sensor or it’s in a weird state)
bathealth = droid.batteryGetHealth()
batlevel = droid.batteryGetLevel()
battemp = droid.batteryGetTemperature()
if ((not (bathealth is None)) and (not (batlevel is None)) and (not (battemp is None))):
available_sensors += “B”
logline += “, %i, %i, %i” % (bathealth.result, batlevel.result, battemp.result)
else:
logline += “,,,”

# Write down the line
sensorlog.write(“SENSORS, %i, %s, %s\n” % ( time.time(), available_sensors, logline ))

if ((tick % picperiod) == 0) and picflag:
#droid.webcamAdjustQuality(3, 20)
#droid.cameraStartPreview(5000,100,picdir)
#droid.cameraStart(5000,100,picdir)
#time.sleep(1)
#droid.cameraStop()
droid.cameraCapturePicture(pictemp % piccount, True)
piccount += 1
if ((tick % gpsperiod) == 0) and gpsflag:
r = droid.readLocation()
loc = r.result
#print “LOC:”, loc
if ‘gps’ in loc:
if not (loc[‘gps’] is None):
packet = loc[‘gps’]
al = packet.get(‘altitude’,0.0)
lo = packet.get(‘longitude’,0.0)
ti = packet.get(‘time’,0)
la = packet.get(‘latitude’,0.0)
sp = packet.get(‘speed’,0)
ac = packet.get(‘accuracy’,0)
lastloc = “1, %i, http://maps.google.com/maps?q=%.5f,+%.5f, %.5f, %.5f, %i” % (ti, la, lo, al, sp, ac)
sensorlog.write(“GPS, %i, 1, %.5f, %.5f, %.5f, %.5f, %i\n” % (ti, la, lo, al, sp, ac))
elif ‘network’ in loc:
if not (loc[‘network’] is None):
packet = loc[‘network’]
al = packet.get(‘altitude’,0.0)
lo = packet.get(‘longitude’,0.0)
ti = packet.get(‘time’,0)
la = packet.get(‘latitude’,0.0)
sp = packet.get(‘speed’,0)
ac = packet.get(‘accuracy’,0)
lastloc = “0, %i, http://maps.google.com/maps?q=%.5f,+%.5f, %.5f, %.5f, %i” % (ti, la, lo, al, sp, ac)
sensorlog.write(“GPS, %i, 0, %.5f, %.5f, %.5f, %.5f, %i\n” % (ti, la, lo, al, sp, ac))
if ((tick % smsperiod) == 0) and smsflag:
#print “SMS:”, lastloc
if lastloc != “”:
try:
droid.smsSend(phoneNumber, lastloc)
sensorlog.write(“SMS-OK, %i, \”%s\”\n” % ( time.time(), lastloc ))
except:
sensorlog.write(“SMS-FAIL, %i, \”%s\”\n” % ( time.time(), lastloc ))
time.sleep(period)
tick += 1

Tagged , , , , , , , , , , , , , , , ,