Sunday, 15 March 2015

Raspberry Pi Powered Coffee Need Predictor Using Python and API Data

I like coffee.  A lot.  Too much perhaps.  Hence a Geek project that includes coffee is extremely exciting for me!

In a queue in a coffee shop a couple of weeks ago, the Barista recognised my colleague and asked him if he wanted "his usual" coffee.  After commenting on how cool this was we continued to discuss what if a new Barista had started?  How could the coffee shop use technology to provide the same great service?  So we discussed:

  • Perhaps the coffee shop could allow you to register your photo and your preferred coffee online and then use facial recognition as you join the queue to spot you and tell the Barista what your preference is.

...but them, as Geek conversations go, we evolved this to be:

  • Facial recognition plus access to personal API data.  This API data will tell the coffee shop how tired you're likely to be and thus they can recommend the coffee you need rather than the coffee you prefer.

For some reasons the person serving behind the counter wasn't up for this as an idea so I decided to build it myself...

So overall this is based upon the premise that caffeine is a stimulant and the more tired you are the more caffeine you need.  Sounds plausible to me.

First to think about what information is readily available about how tired I might be.  Based upon the Geek work I've done recently I have:

  • Fitbit data based upon the FitBit API.  This will tell me how many steps I did the day before, how many floors I climbed and how well I slept the previous night.
  • Strava data from my Garmin Forerunner that I've accessed using the Strava API.
  • General exercise data I've logged using my "Jerks" system.

The big idea was that I could write a Python script on my Raspberry Pi to:

  1. Gather data from these sources.
  2. Compare the results against pre-defined thresholds.
  3. Accumulate coffee points if the thresholds are breached.
  4. Map the resulting number of points to a coffee (more points is a coffee with more caffeine) and email the result to myself.

There's a full code listing at the bottom but here's a quick summary of the key points.

Fitbit Data
Using Python Fitbit (see my last post on this which tells you how to authenticate and access the API using Python objects).

To get steps and floors I created a Fitbit object and then did:
fitbit_activities = authd_client._COLLECTION_RESOURCE('activities',date=MyDate)

Where MyDate is yesterdays date in the format YYYY-MM-DD.  This is formed using the GetADate() function (see full code listing below for this).  GetADate() takes a parameter to define if it's todays date, yesterdays etc. and then does a date subtraction to provide the required date.

The response is a JSON structure from which you can extract the step count and the number of floors.  JSON parsing is a bit hit 'n' miss for me but through trial and error I found out:
Steps = fitbit_activities['summary']['steps'])
Floors = fitbit_activities['summary']['floors']

This can then be compared with thresholds to see if a "coffee point" is accumulated.  Specifically:
  • More than 10,000 means a coffee point is accumulated.
  • More than 25 floors means a coffee point is accumulated.
(With the floor and steps thresholds being nicely defined as constants).

I got the sleep data by doing:
fitbit_sleep = authd_client._COLLECTION_RESOURCE('sleep')

(No date specified as I wanted today's sleep value).  The specific value wanted can then be extracted from the JSON using:
Sleep = fitbit_sleep['summary']['totalMinutesAsleep']

Then the threshold comparison can be done again:
  • Less than 420 minutes (7 hours) means a point accumulated.
As an aside, the sleep element of the API looks epic.  More investigation is required but it looks like a record is logged for every minute that the Fitbit thinks you're asleep.  More Geek investigation is required I think...

Strava Data
Strava data is easy to access using a direct API plus an access token you get when you register as a developer.  (See here for a refresher on this).

The semi-tricky bit was specifying a date parameter to avoid extracting all data from the API and having to parse it yourself to pick out all action since the day before.  With the Strava API you can specify "before" and "after" parameters in Unix time to limit what is provided by the API.  To do this I:
  1. Used the GetADate function described above to get yesterday's date.
  2. Turned this into Unix time using a function I wrote called ConvertToUnix()
You can then get the API date using methods from the built-in urllib2 module:
StravaText = urllib2.urlopen('https://www.strava.com/api/v3/activities?access_token=' + StravaToken + '&after=' + TheUnixTime).read()

A coffee point is accumulated simply by virtue of having done one or more Strava activities since yesterday.  These can be counted by simply doing:
StravaCount = StravaText.count('tcx')

Where the string 'tcx' appears once and once only for each Strava activity (it's the filename I uploaded from Garmin with the exercise data in it).

Jerks Data
For the Jerks system I created, I log data in a mysql database on my Raspberry Pi.  Hence accessing data is simply a question of running an SQL query.  First create a database object:
db = MySQLdb.connect(dbHost,dbDatabase,dbUsername,dbPassword)
curs=db.cursor()

Then get yesterdays date (same method as above) and form the query:
JerksQuery =  'SELECT * FROM ' + dbMainTable + ' WHERE tdate = "' + MyDate + '";'
Then counting how many Jerks activities I did is a simple case of assessing curs.rowcount.  More than 5 Jerks (my average for the last couple of weeks) means a coffee point is accumulated.

Coffee Point to Coffee Translation
So that just leaves mapping the number of coffee points accumulated to an actual coffee.  This is done in the ChooseACoffee() function and this is the "complex" logic:
  • 0 points means a nice cup of tea.
  • 1 point means a small coffee.
  • 2 points means a medium coffee.
  • 3 points means a large coffee.
  • 4 points means a large coffee and a single espresso.
  • 5 points means a large coffee and a double espresso.
With the coffee in question (Latte, Americano or Cappuccino) selected by creating a random integer from 1 to 3 using the randint function.

Finishing it Off
So having chosen the coffee the script then writes a HTML email (re-using code from my Jerks summary project) then sends it (again re-using the Jerks summary code).

The resulting email is sent automatically as the script is run using a cron job and looks like this:


Improvements
There's much room for improvement here:
  • The overall algorithm is far too simplistic.  For example, if I had 5 minutes sleep or 6 hours 59 minutes sleep this still only equates to 1 coffee point.  Equally a Strava (which could be pretty hard) is given the same weighting as a jerk (which could be relatively easy). 
  • The data structures I use are really lame.  I use multiple lists rather then using a single data structure.  I need to investigate this more.  Comments please on this.
  • The email is a bit boring.  I experimented with adding images to the HTML content of the email but it didn't work.  I think I need to use MIME multipart structures for this but didn't get round to doing it.
  • There's loads of code there; far too much.  I need to streamline it a bit.  Perhaps write it in Java...

The Code
It's all below.  Of course I've blanked out all the keys and passwords!  You'll need to get these yourself.  Happy drinking?

#V1 = First attempt
import fitbit
from datetime import date, datetime, timedelta
from time import time, mktime
import smtplib
import MySQLdb
import urllib2
from random import randint

#MIME multipart stuff
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.MIMEImage import MIMEImage

#Date manipulation related constants
DateToday = 'DateToday'
DateYesterday = 'DateYesterday'
DateFirstDayOfWeek = 'DateFirstDayOfWeek'
DateFirstDayOfMonth = 'DateFirstDayOfMonth'
DateFirstDayOfYear = 'DateFirstDayOfYear'

#Constants - For Fitbit API
CLIENT_KEY = 'lovelovemedo'
CLIENT_SECRET = 'youknowiloveyou'
USER_KEY = 'illalwaysbetrue'
#USER_KEY = 'sopleasepleaseplease'
USER_SECRET = 'lovemedo'

#Constants - Fitbit thresholds
StepThreshold = 10000   #Daily target
FloorThreshold = 25     #Rough average for when I'm not running
SleepThreshold = 420    #7 hours sleep

#Constants - For Strava
StravaToken = 'itsbeenaharddaysnight'
StravaThreshold = 0

#Constants - For Jerks
dbMainTable = 'iamtheeggman'
dbHost = 'theyaretheeggmen'
dbDatabase = 'iamthewalrus'
dbUsername = 'googoogajoob'
dbPassword = 'googoogajoob'

#Constants - Jerks thresholds
JerksThreshold = 5

#Email Constants  
smtpserver = 'in.the.town.where.i.was.born'
AUTHREQUIRED = 1 # if you need to use SMTP AUTH set to 1
smtpuser = 'lived.a.man.who.sailed.to.sa'  # for SMTP AUTH, set SMTP username here
smtppass = 'andhetoldmeofhislife'  # for SMTP AUTH, set SMTP password here
RECIPIENTS = 'in.a.yellow.submarine'
SENDER = 'we.all.live.in.a.yellow.submarine'

##########################################################
#Choose what coffee to drink.  Takes a points count as an input
#and decides what coffee to drink.  Randomises to choose a different coffee type
def ChooseACoffee(CoffPoints):
  #First choose a coffee type
  CoffeeVar = randint(1,3)
  if (CoffeeVar == 1):
    CoffeeType = 'Latte'
  elif (CoffeeVar == 2):
    CoffeeType = 'Cappucino'
  elif (CoffeeVar == 3):
    CoffeeType = 'Americano'

  #Now choose a size based upon the points value sent as a parameter
  if (CoffPoints == 0):
    CoffeeString = 'You need a nice cup of tea.'
  elif (CoffPoints == 1):
    CoffeeString = 'You need a small ' + CoffeeType + '.'
  elif (CoffPoints == 2):
    CoffeeString = 'You need a medium ' + CoffeeType + '.'
  elif (CoffPoints == 3):
    CoffeeString = 'You need a large ' + CoffeeType + '.'
  elif (CoffPoints == 4):
    CoffeeString = 'You need a large ' + CoffeeType + ' and a single espresso.'
  elif (CoffPoints == 5):
    CoffeeString = 'You need a large ' + CoffeeType + ' and a double espresso.'

  return CoffeeString

#Gets a initial timestampt for emails
def GetDateTime():
  #Get the time, format it and return it
  now = datetime.now()
  return str(now.date()) + ' ' + str(now.strftime("%H:%M:%S"))

#Send an email
def SendEmail(TheSubject,TheMessage):
  #Form the message
  #msg = "To:%s\nFrom:%s\nSubject: %s\n\n%s" % (RECIPIENTS, SENDER, TheSubject, TheMessage)
  
  #Start forming the MIME Multipart message
  msg = MIMEMultipart('alternative')
  msg['Subject'] = TheSubject
  msg['From'] = SENDER
  msg['To'] = RECIPIENTS

  # Record the MIME types of both parts - text/plain and text/html.
  #part1 = MIMEText(text, 'plain')
  part2 = MIMEText(TheMessage, 'html')
  
  # Attach parts into message container.
  # According to RFC 2046, the last part of a multipart message, in this case
  # the HTML message, is best and preferred.
  #msg.attach(part1)
  msg.attach(part2)
  
  #print msg

  #Do the stuff to send the message  
  server = smtplib.SMTP(smtpserver,587)
  server.ehlo()
  #server.starttls() 
  server.ehlo()
  server.login(smtpuser,smtppass)
  server.set_debuglevel(1)
  server.sendmail(SENDER, [RECIPIENTS], msg.as_string())
  server.quit()

#Creates a string with a HTML table and a heading based upon parameters sent
#(EmailSubject,ActualsList,ThresholdList,PointsList)
def CreateHTMLTable(InTitle,CatList,AcList,ThreshList,PtList):
  try:
    #Start with the heading
    OutString = '<H1>' + InTitle + '</H1>\r\n'
    
    #Start the table
    OutString = OutString + '<table border="1">\n\r'
    #See if there is anything to write
    for i in range(0,len(AcList)):
      #Add the table opening tag

      #Add the list elements as table rows
      OutString = OutString + '<tr>\r\n'
      #Add the table elements
      OutString = OutString + '<td>' + CatList[i] + '</td>\r\n'
      OutString = OutString + '<td>' + AcList[i] + '</td>\r\n'
      OutString = OutString + '<td>' + ThreshList[i] + '</td>\r\n'
      OutString = OutString + '<td>' + PtList[i] + '</td>\r\n'

      #Close the table row
      OutString = OutString + '</tr>\r\n'    

    #Close the table tag      
    OutString = OutString + '</table>\r\n'
    
    #Return the result
    return OutString
  except:
    return 'Error creating table'
#Returns dates in different formats for different uses based upon the parameter passed
#Returns a date based upon the parameter supplied
def GetADate(DateType):
  #try:
    if (DateType == DateToday):    #Just get and return todays date
      #Get the time, format it and return it
      now = datetime.now()
      return now.strftime("%Y-%m-%d")
    elif (DateType == DateYesterday):
      now = datetime.now()
      TheDateYesterday = now - timedelta(days=1)
      return TheDateYesterday.strftime("%Y-%m-%d")
    elif (DateType == DateFirstDayOfWeek):   #The first day of the current week.  Sunday is 0. Monday is 1 etc.  We want to know how many days from Monday it is
      #Find what day of the week it is
      now = datetime.now()
      DayOfWeek = int(now.strftime("%w"))   #Get the number of the day of the week
      print 'Day of week ->>' + str(DayOfWeek)
      #See what to subtract.  Sunday is a special case
      if (DayOfWeek == 0):
        DayDelta = 6         #Monday was always 6 days ago on a Sunday!
      else:
        DayDelta = DayOfWeek - 1
      print 'Day delta ->>' + str(DayDelta)
      DateOfMonday = now - timedelta(days=DayDelta)
      print 'Monday was ->>' + str(DateOfMonday)
      return DateOfMonday.strftime("%Y-%m-%d")
    elif (DateType == DateFirstDayOfMonth):
      now = datetime.now()
      DayOfMonth =  int(now.strftime("%d"))
      DayDelta = DayOfMonth - 1
      FirstDateOfMonth = now - timedelta(days=DayDelta)
      return FirstDateOfMonth.strftime("%Y-%m-%d")
    elif (DateType == DateFirstDayOfYear):
      now = datetime.now()
      DayOfYear =  int(now.strftime("%j"))
      DayDelta = DayOfYear - 1
      FirstDateOfYear = now - timedelta(days=DayDelta)
      return FirstDateOfYear.strftime("%Y-%m-%d")

#Returns a Unix time based upon the date sent
def ConvertToUnix(InDate):
  #What we'll be sent is a text string in the format yyyy-mm-dd.  Need to change it to a proper date variable
  DateVal = datetime.strptime(InDate,"%Y-%m-%d")
  
  #Now turn into Unix time
  UnixTime = mktime(DateVal.timetuple())
  return int(UnixTime)

#################################################################################################
#Main body of code

#Start a list that we'll use to track all the data
CategoryList = []     #Contains a string showing the category
ActualsList = []      #Contains a string showing actuals
ThresholdList = []    #Contains a string showing thresholds
PointsList = []       #Contains a string showing resulting points

#Add the headings for the resulting table
CategoryList.append('Activity')
ActualsList.append('Value')
ThresholdList.append('Threshold')
PointsList.append('Coffee Points')

#First we do Fitbit
#Create an object to access the Fitbit API
authd_client = fitbit.Fitbit(CLIENT_KEY, CLIENT_SECRET, resource_owner_key=USER_KEY, resource_owner_secret=USER_SECRET)

#Get yesterdays activities - First get a date in format yyyy-mm-dd
#Now access the activities component of the API
MyDate = GetADate(DateYesterday)
fitbit_activities = authd_client._COLLECTION_RESOURCE('activities',date=MyDate)
print 'Activities:'
print fitbit_activities
print '~~~~~~~~~~~~~~~~~~~~'
print 'Steps: ' + str(fitbit_activities['summary']['steps'])
print 'Floors: ' + str(fitbit_activities['summary']['floors'])

#Add the various items to the lists.  Start with Steps
CategoryList.append('Fitbit Steps')
ActualsList.append(str(fitbit_activities['summary']['steps']))
ThresholdList.append(str(StepThreshold))
if (fitbit_activities['summary']['steps'] > StepThreshold):
  PointsList.append('1')
  CoffeePoints = 1
else:
  PointsList.append('0')
  CoffeePoints = 0

#Add the various items to the lists.  Do Floors
CategoryList.append('Fitbit Floors')
ActualsList.append(str(fitbit_activities['summary']['floors']))
ThresholdList.append(str(FloorThreshold))
if (fitbit_activities['summary']['floors'] > FloorThreshold):
  PointsList.append('1')
  CoffeePoints = CoffeePoints + 1
else:
  PointsList.append('0')

#Now for sleep
fitbit_sleep = authd_client._COLLECTION_RESOURCE('sleep')    #No date as it's today's data we want
print '~~~~~~~~~~~~~~~~~~~~~'
print 'Sleep: ' + str(fitbit_sleep['summary']['totalMinutesAsleep'])

#Add the various items to the lists.  Now do sleep
CategoryList.append('Fitbit sleep')
ActualsList.append(str(fitbit_sleep['summary']['totalMinutesAsleep']))
ThresholdList.append(str(SleepThreshold))
if (fitbit_sleep['summary']['totalMinutesAsleep'] < SleepThreshold):
  PointsList.append('1')
  CoffeePoints = CoffeePoints + 1
else:
  PointsList.append('0')

#Second we do Strava
#We want to see if we've done any Strava since first thing yesterday.  So after 00:00:00 yesterday.
#First we get yesterdays date, then we turn into Unix timestamp which can be used as Strava API "after" parameter
MyDate = GetADate(DateYesterday)
TheUnixTime = str(ConvertToUnix(MyDate))

#Access the Strava API using a URL
StravaText = urllib2.urlopen('https://www.strava.com/api/v3/activities?access_token=' + StravaToken + '&after=' + TheUnixTime).read()
print StravaText
#See what we got
StravaCount = StravaText.count('tcx')
if (StravaCount > StravaThreshold):
  print 'You did a Strava!'
else:
  print 'You did no Strava :-('

#Add to the output lists
CategoryList.append('Stravas')
ActualsList.append(str(StravaCount))
ThresholdList.append(str(StravaThreshold))
if (StravaCount > StravaThreshold):
  PointsList.append('1')
  CoffeePoints = CoffeePoints + 1
else:
  PointsList.append('0')

#Now we do Jerks - First connect to the database
db = MySQLdb.connect(dbHost,dbDatabase,dbUsername,dbPassword)
curs=db.cursor()

#Get yesterdays date for the SQL query
MyDate = GetADate(DateYesterday)
JerksQuery =  'SELECT * FROM ' + dbMainTable + ' WHERE tdate = "' + MyDate + '";'
print JerksQuery
curs.execute (JerksQuery)
print "Jerks count: " + str(curs.rowcount)

#Add to the output lists
CategoryList.append('Jerks count')
ActualsList.append(str(curs.rowcount))
ThresholdList.append(str(JerksThreshold))
if (curs.rowcount > JerksThreshold):
  PointsList.append('1')
  CoffeePoints = CoffeePoints + 1
else:
  PointsList.append('0')

for i in range(0,len(CategoryList)):
  print CategoryList[i] + '-' + ActualsList[i] + '-' + ThresholdList[i] + '-' + PointsList[i]

print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
print 'Total coffee points: ' + str(CoffeePoints)

#Get the email subject.  This is the coffee type inferred from the amountof coffee points
EmailSubject = ChooseACoffee(CoffeePoints)
print EmailSubject

#Start forming the HTML
EmailHTML = '<HTML>\r\n<HEAD></HEAD>\r\n<BODY>\r\n<H1>What Coffee do You Need?</H1>\r\n'

#Add the coffee image
#EmailHTML = EmailHTML + '<img src="/coffeecup.jpg" alt="coffee" width="256" height="256">'

#Add the table for the HTML
EmailHTML = EmailHTML + CreateHTMLTable(EmailSubject,CategoryList,ActualsList,ThresholdList,PointsList)

#End the HTML
EmailHTML = EmailHTML + '</BODY>\r\n</HTML>'

#Send the email
SendEmail(EmailSubject + ' ' + GetDateTime(),EmailHTML)






Sunday, 8 March 2015

Sleep Analysis Using the FitBit API on a Raspberry Pi with Python

A couple of weeks ago I bought myself a Fitbit Charge HR.  This was for all the usual reasons, i.e. being more active, but also for the delicious geek data it could provide via the FitBit API!

This was my original post on using OAUTH1.0 to access the Fitbit API.  I've now "upgraded" to use OAUTH2.0; go to this blog post for a how-to guide. 

(As an aside, this chap DC Rainmaker has written an excellent review of the Charge HR, take a look if you're interested in getting one.  DC Rainmaker is always a good read).

It wasn't 100% easy getting access to data for the API, (less than a previous attempt with Strava), so I thought I'd share some of my learnings.  If you read this thinking, "dude that's easy stuff", remember I'm just a home hobbyist, not a professional, and I'm writing this for other similarly minded people.

Step 1 - Register an Application
The first things to do is to go to the Fitbit developer site and register an application.  This assumes that you are a developer wanting to write an app to access other people's data (with their permission).

Select the "REGISTER AN APP" tab and enter some details.  I specified:
  • Application Name - "Fitbit API Practise"
  • Description - "Using Fitbit data for my personal hobby"
  • Application website - http://pdwhomeautomation.blogspot.co.uk
  • Organisation - Geek Dad
  • Organisation website - http://pdwhomeautomation.blogspot.co.uk
  • Application type - Browser
  • Callback URL - http://pdwhomeautomation.blogspot.co.uk
  • Default Access Type - Read&Write
  • Subscriber - Endpoint URL = http://pdwhomeautomation.blogspot.co.uk <No other data entered>
This created an application and, most importantly, gave me some values for use later in the process.  These are:
  • Client Key - (Also known as the Consumer Key)
  • Client Secret - (Also known as the Consumer Secret)
Log these values somewhere.

Step 2 - Get User Key and Secret
I then needed to authenticate to be able to use the API to get user specific data.  The Fitbit documentation tells you that you need to use OAUTH 1.0 to authenticate users and warns you not to try and build this capability yourself but rather use a pre-built module.

A quick search around the internet led me to a Python Fitbit module that someone has written.  The module is here and the documentation is here. All credit to the author of this.

I downloaded and installed it on my Raspberry Pi using this command git clone https://github.com/orcasgit/python-fitbit.git

(I can't recall installing git so I assume it must come with Raspbian, the OS I'm using).

Now this is where I got a bit stuck.  There are a few blogs on the internet where people talking about using python-fitbit to authenticate and get user specific credentials.  They might be right for some but I just couldn't get it to work for me.  After a modicum of fumbling I struck upon this comment from the fitbit-python help page (RTFM is the best advice always!).


So this advises to use a script called "gather_keys_cli.py" to get the user keys.  I searched on my Pi and it wasn't included in fitbit-python but a quick internet search led me to here on Git, i.e. a sub area of python-fitbit.  I copied the script, pasted it into a Nano editor and then ran it, providing my key and secret from registering my app with FitBit.  Here's a step by step description of what happens.  I've:
  • Broken up the flow to show what you need to do at key stages.
  • Changed all the keys and tokens.  I'm not that stupid!
sudo python gather_keys_cli.py 12345fishalive 78910backagain
** OAuth Python Library Example **

* Obtain a request token ...

RESPONSE
{   u'oauth_callback_confirmed': u'true',
    u'oauth_token': u'1234qwetyuiop1234',
    u'oauth_token_secret': u'111222333aaabbbccc'}

* Authorize the request token in your browser

Verifier:

<Pause in flow>
Verifier?  What the heck's a verifier?  At this point the script stops and asks you to enter this value.

Now this is where this script doesn't help too much but I'd learnt from other failed attempts what to do.  So at this point the script's used your consumer secret and consumer key to obtain a temporary request token.  You then add this temporary token to an authorisation URL and paste it into a browser.  This takes you to a FitBit page where you log in and authorise your app to access your data.

What you need to do is take the URL:
http://api.fitbit.com/oauth/authorize?oauth_token=

...and append the oauth token from the step above, thus giving:
http://api.fitbit.com/oauth/authorize?oauth_token=1234qwetyuiop1234

So you've now authorised the app and you get re-directed to the website you specified when you signed up for a developer account.  Here's what I saw:


So the verifier is the last part of the URL.  Copy it from your browser and paste it into the script.

Verifier: abcde1234abcde1234

* Obtain an access token ...

RESPONSE
{   u'encoded_user_id': u'davedeedozybeakymickandtitch',
    u'oauth_token': u'ymcafuntoplay1234',
    u'oauth_token_secret': u'heisdisco1234heisdisco'}

Done.

So now you have all the keys you need to use the API!  The oauth_token is your user key and the oauth_token_secret is your user secret.  Make sure you log them somewhere!

Step 3 - Get Some API Data
So now for a Python script to access the API.  For a hint on how to do this I used this chap's blog.  Note I couldn't get the authorisation stuff he describes to work but the stuff he does to actually use the Python API wrapper did work for me.

In the script at the bottom of this post you see:

  • Me defining all the keys as constants.
  • Creating an object to use to access the API.  (authd_client._COLLECTION_RESOURCE etc.)
  • Using the API to get activities(authd_client._COLLECTION_RESOURCE('activities') etc.)
Here's it in action:


Now to do something with all that delicious Geek data!!

All the code:


import fitbit

#Constants
CLIENT_KEY = '12345fishalive'
CLIENT_SECRET = '78910backagain'
USER_KEY = 'ymcafuntoplay1234'
USER_SECRET = 'heisdisco1234heisdisco'

authd_client = fitbit.Fitbit(CLIENT_KEY, CLIENT_SECRET, resource_owner_key=USER_
KEY, resource_owner_secret=USER_SECRET)

fitbit_stats = authd_client._COLLECTION_RESOURCE('activities')
print 'Activities:'
print fitbit_stats


Sunday, 1 March 2015

Ultrasonic Elephant Cheese-puff Game using Raspberry Pi and Scratch

In two previous posts I've written how my kids and I used an ultra sonic sensor, Raspberry Pi and Scratch to make a press up counter and a cuddly toy theft alarm.  One deficiency in those projects was that the Scratch/GPIO capability could only take an ultra-sonic measurement every one second.  In particular, for the press up counter I had to do the press ups REALLY slowly to get them to count. Too much like hard work!

A bit more investigation on Simple Si's epic Scratch/GPIO site showed that he has created a capability using an "UltraDelay" variable which means you can take a reading every 0.2 seconds.  So this means you can many more measurements and so, racking my brains to think what we could do with this, I had the idea of making the Scratch cat levitate by moving my hand up and down over the ultra sonic sensor.  A extension of this was to create a Scratch game, using the sensor to control a sprite's position.

Here's what we came up with:

  • Have a sprite moving across the screen from right to left at random heights.  When it reaches the left hand side of the screen it returns to the right hand side and re-starts at a new height.
  • Have a sprite that moves up and down the screen based upon the position of your hand over the sensor.  If this sprite touches the right to left sprite then you score points.
  • Have this happening for a fixed period of time and then have the final score read out at the end.

Here's the code for the right to left sprite:

So a very simple block of code:
  • Start when g (for game) is pressed.
  • Set the initial position of the sprite as x=200, y=-100.  This is on the right edge of the screen towards the bottom.
  • Loop 600 times.  
  • For each loop, move the x position of the sprite by -13 steps. 
  • If the sprite gets to an x position of -180 (left hand side of the screen) then move it back to the right hand side of the screen but at a random height that is somewhere between -200 (bottom of screen) and 200 (top).
There was some excellent learning to be had just in this short block of code:
  • x and y coordinates on the screen.
  • Loops, and changing the loop variable to get the game time we needed.
  • Experimenting with the number of steps to move the sprite by for each loop.  The final value of -13 was found to be just the right number to make the game challenging but not too challenging.
  • An if statement and an action taken should the statement be true.

Which leaves the (slightly more complex) code for the second sprite:
Here we:
  • Again start by pressing g.
  • Set the UltraDelay variable to 0.  This means a reading is available every 0.2 seconds.
  • Set a HitCount variable to 0, this was incremented when the sprites touch.
  • Set the sprite to it's initial position.  x = -100 is left of centre meaning there's time to move the sprite into position to hit the right to left sprite.
  • Go into a loop with 600 iterations, the same as for the other sprite.
  • Broadcast for an ultrasonic measurement.
  • Get the response and then set the y position of the sprite to be (value * 10) - 200.  Having -200 meant that when the value is very low (or zero) the sprite moves to the bottom of the screen.  Having * 10 means that 40cm worth of hand movement over the sensor can translate to 400 steps of movement of the sprite.
  • A check to see of the sprite is touching the right-left sprite.  If it is then increment a variable to count the number of touches and play a pop sound.
  • When the main 600 iterations are done, go into a loop that reads out the final score.

So in doing this we learnt:
  • About setting and incrementing variables
  • About maths statements, taking one value and transposing it to another.
  • The simple concept of taking a real world value and turning this into an on-screen analogue.

Which left the important task of choosing the sprite icons.  For this we used:
  • A bowl of cheese puffs for the right to left sprite.
  • An elephant for the up and down sprite.

You ask why?  I say why not!!!

Here's the game in action:


Future improvements:
  • A high score feature.
  • Having only one point scored when the sprites touch (should be able to use a simple boolean for this).





Sunday, 22 February 2015

Ultra Sonic Pinky Pig Anti-Theft Device

Meet Pinky Pig:



Pinky Pig is the most loved soft toy in the Geek household.  He's been owned by my eldest daughter since birth, is a a bit worse for wear these days but is loved all the same.  In fact Pinky Pig rarely leaves the house,such would be the trauma of losing him.

In a search for something new to do with our ultra sonic sensor (see last week's post), we came up with the idea of using the ultra sonic sensor to make a device to detect if Pinky has been stolen.  In simple terms, Pinky's resting place would be on the ultra sonic sensor.  If he was taken then this could be detected and an alarm raised.  

We had a lot of fun (and a lot of learning) coming up with the Scratch code below on one of our Raspberry Pi's.  It was a really good way to focus on the role of a loop and an if statement.



So if the measurement from the sensor is small, all is well and no alarm is raised.  However if the measurement is not small then the (audible) alarm is raised.

We we did learn which was interesting was that Pink Pig (and other cuddly toys) are transparent to ultra sonic sound waves.  Very interesting, this added an extra layer of investigation to this project.  Please comment below if you can tell me why this is the case.  Hence to make the alarm work, Pinky had to be wrapped in an extra layer of paper.  Here's a picture showing this in action.


So last thing, here's a video of it in action (wish the volume on the TV was louder - listen carefully and you can hear the alarm going off).



Monday, 16 February 2015

Raspberry Pi Ultra Sonic Press Up (Push Up) Counting Machine using Scratch

Looking at back at recent posts I've noticed:
  • I've not done any electronics related projects recently.
  • I've not done much programming with my kids recently.
When doing some press ups* recently I lost count and couldn't log them on my Jerks exercise logging system.  Hence this gave me the idea to build a machine to count my press ups.

(*Push ups for you readers in North America)

I saw you can buy one from Amazon and there are some available as smart phone apps but this isn't the Geek Dad way; I wanted to build one with a Raspberry Pi, program it in Scratch and use some electronics.  Ideas:
  • Mount a light dependent resistor - as I go down it blocks out light from the LDR, as I go up it allows light again and counts a press up.
  • Use a camera and a QR code.  As I go down it reads the QR code and counts a press up.
  • Use an ultra sonic sensor to measure distance as I go up and down.
I decided to use an ultra sonic sensor - purely because I'd hadn't used one since a University project about 20 years ago.  Here's the one I bought, an HC-SR04 module which has both a transmitter and a receiver.  They're widely available on the interweb.


To integrate the sensor to my Raspberry Pi I used the utterly amazing, epic and awesome Cymplecy Scratch GPIO system (did I mention that this site is brilliant).  This chap has developed a series of Scratch releases that allow you to integrate with many and various third party boards and bits of electronic kit that you can connect to the GPIO.  There's a specific page on Ultra Sonic module integration; it's available here and the solution only uses one GPIO input/output which is ace.

The resistors shown on the breadboard diagram on Cymplecy's site are essential to stop you blowing up your Raspberry Pi; they create a potential divider to reduce the +5V output from the HC-SR04 to a GPIO friendly 3.3V.  The resistors are:
  • Brown-Black-Red = 1kOhms
  • Red-Red-Red = 2.2kOhms
I won`t do the maths here, but see this site for an explanation of potential dividers.

The system works brilliantly and with this code block you can get Scratch to show the measurement from the ultra sonic sensor and move the Scratch Cat around.



So this worked beautifully, next I needed a robust housing for the press up counting machine to avoid me breaking it.  Enter Lego, the tool of tinkerers everywhere.  Here's a series of before, during and after shots:





So the breadboard is mounted perpendicular in the Lego housing, meaning the sensor points straight up.  Hence as I'm doing press ups I'm moving up and down vertically over the sensor.  The Raspberry Pi sits separate to it.

Here's a quick video:



The basic algorithm for the press up code is as follows:

boolean = false
forever
  Take Measurement
  if Measurement < Low AND boolean = false
    boolean = true
  if Measurement > High and boolean = true
    increment press up count
    boolean = false

So this uses a simple boolean to log when my body is low down (say < 10cm from the sensor).  The spots the bottom part of the press up.  Then when I move up, if my body is high up (say > 30cm) AND I've previously been low down then a press up is counted (and the boolean is reset).

The basic Scratch code is shown on the screen shot below (put your email address in a comment below and I'll send you the file).  This does a good job of counting press ups (although you have to go quite slow as the sampling period is only one second) and getting the Scratch Cat to say them.



What I wanted to do with my daughters is jazz it up a bit.  In preparation for this I recorded a number of different audio messages for Scratch to play out.  I used this site which takes a text string input and provides a downloadable MP3 file.  These files can then be put in the /usr/share/scratch/Media/Sounds directory on your Raspberry Pi.  Armed with this I spent an enjoyable afternoon with my daughters making the press up counter more interesting.

We:
  • Changed the background to make it look better.
  • Added a sound at the start to tell you that "It's press up time!".
  • Added If statements to detect each Press Up (up to 10) and play an audio message with the number ("1" for the first press up, "5" for the fifth etc).
  • Added some motivational (semi-abusive) messages to keep you going.
  • Added a "Well Done" message at the end.

Here's a video of it in action!



Here's a screenshot of the Scratch code (top part). A but hard to see so, again, put your email address in comments below and I'll send you the Scratch file.


...and bottom part: