Tag Archives: android

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 , , , , , , , , , , , , , , ,

Building a distributed cosmic ray detector in a weekend at CERN Webfest

This weekend I made a Android/Arduino based web enabled Muon (Cosmic ray) detector. I have to give a tonne of thanks to the team who did it with me (Ramviyas, Olof, Brad, Justin and Hugo) who deserve full credit for making it happen. I should also thank the ERGO Telescope team for thinking up the whole scheme of a distributed cosmic ray observatory in the first place and sending us the missing elements for our project.

Our Muon Source

What?

One ERGO pixel unit (which I hacked)

One Geiger-Muller tube and amp/PSU board (which we didn’t use in the end)

One Arduino MEGA ADK

Arduino Mega ADK

One Android Phone (with GPS, running 2.3.4 or better) – we used a Samsung Galaxy Mini GT-S5570

CNY17 optocoupler

Some Arduino Code, some Java and some web scripting

Muon Detector (ERGO Pixel)

How:

1) We hooked up the GM tube (in the end I had to hack the LED on the front of the ERGO box) to the Arduino MEGA ADK via a CNY-17 Opto-isolator. The signals out of the Geiger-Muller tube PCB were a bit too noisy to use straight up, and I didn’t manage to build myself a suitable high impedance device/amp to read them directly. We did the whole project in a weekend, so there wasn’t much time for anything!

Writing code…

2)We wrote some Arduino code that will hook up to an android phone and log events with an accuracy of 0.000212s. This is effectively a polling loop which checks a single input pin repeatedly and sends a signal to the Android when it sees a logical high on the input. The code will take any input on pin2 and output a packet with the relative timestamp to the Android. With this method we can resolve to approx 4700 loop cycles within the Arduino, using a counter (declared as a simple int that we add to). The counter is also re-set each 1s by a signal from the Android GPS clock. Counter values at reset are logged to use for a rolling timing calibration, but we didn’t get chance to implement this in the weekend.
3)We wrote an android app that reads NMEA sentences to get the raw time out of the GPS chipset (tested on Samsung phones running Android 2.3.4 and cyanogenmod) and sends a reset to the Arduino every second based on this value. The app also receives signals from the Arduino with the local timestamp (the loop counter in the Arduino) and adds this to the Navstar time (being the correct name for GPS time) We also correct the Arduino timestamp by multiplication with the constant of 0.000212 (our measured resolvable time interval) which bring the Arduino timestamp into seconds. We couldn’t get the date out of the GPS, so the leapsecond (Navstar time is 13 seconds behind UTC at the moment) correction will be necessary to datestamp it correctly – unless we figure out how to get the date from GPS directly.
4) For the moment we’re pushing it via HTTP post to  http://posttestserver.com/data/2012/08/05/populous/  Eventually we will send it to the ERGO database, once we have done some more precise measurements of the timing accuracy.

The Android App in action

We have a few things that would also be nice to add:
The Arduino code could do with some optimisation – we can probably increase the timing accuracy significantly (but this will obviously take more time than we had over the weekend) to go beyond the 212 microsecond resolution. It’s also running as a polling loop without any de-bounce, so duplicate readings are a distinct possibility. Using interrupts caused problems with the Arduino crashing, I’ll put it on my list to figure out when I have time.

Our system timing and architecture drawing

Timing calibration has been worked out on the back of an envelope, with some work the Android could do a rolling calibration on the Arduino (so that the 4700 loop cycles are adjusted based on observed performance).

As for the GM tube interface (which I struggled with), SEEED studio make a geiger shield that at first glance it looks like it would plug straight in fine – the design is open source as well. It can be found here: http://www.seeedstudio.com/depot/grove-geiger-counter-p-867.html?cPath=190

A great team!

That’s everything for the moment –  here are some links for the resources/websites we used/made:
Source code (arduino and android): https://github.com/jussy/cern-webfest
Pirate pads (where we did the working out..) http://piratepad.net/ep/pad/view/ro.-NYTlmpQZIl/latest
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 , , , , , , , , , , , , , , , ,