Friday, March 2, 2012

Wii Weights: An Internet Enabled Scale Using the Wii and Linux

The other day I came across an advertisement for an internet enabled scale that saved your weights and allowed you to view your stats and history online. Being a programmer and not wanting to spend $150 on a scale, I started looking into a way to make my own version. What I found was a creative method using a simple Python script and the Wii Balance Board.

For the purposes of my project, I decided a few steps would be required:

1) Connection would need to be made to the balance board from a computer
2) The sensors would need to have their data sent to the computer
3) Combine the 4 sensors of the balance board into a single weight amount
4) Data needs to be uploaded to Google Docs.

These specific instructions are for Ubuntu 11.4.

Before starting the project, you need to install all required packages. Ubuntu makes this extremely simple with the adt-get command. The entire list of packages you need is as follows:

sudo apt-get install autoconf 
autogen automake gcc bluetooth 
libbluetooth3-dev libgtk2.0-dev pkg-config 
python2.7-dev flex bison git-core 
libbluetooth-dev python-pygame python-tk

At this point, I like to make a new directory to store all of the necessary files. In order to interface with the Wii Remote and Balance Board, I am using an excellent python library called cwiid (pronounced Seaweed) The code for this is stored in a git repository, and needs to be compiled.

mkdir ~/wii
cd ~/wii
git clone http://github.com/abstrakraft/cwiid.git
cd cwiid
aclocal
autoconf
./configure
make
sudo make install
cd python
sudo python setup.py install
git clone git://github.com/videntity/python-omhe.git
cd python-omhe
sudo python setup.py install

With all of this installed, you are ready to start programming using the Wii Remote or Balance Board. Even if you do not want to program for the Wii Hardware, you can use the included program wmgui to see in real time what you are doing with the Wii Remote. Before I get into the actual code, I want to add the ability to save data to a Google Document Spreadsheet. In order to do this I chose to again make a new directory, then downloaded the latest library available from here. Once it is downloaded, installation is as simple as:


sudo python setup.py install

For my actual program, I combined code available from Matt Cutts and the demo from cwiid With some slight modifications. My final code was:

#!/usr/bin/python

import sys
sys.path.insert(0, '/home/anthony/wii/svn/trunk/python/build/lib.linux-i686-2.5/')
import cwiid
import sys
import time
import gdata.spreadsheet.service

def main():
 print 'Put Wiimote in discoverable mode now (press 1+2)...'
 global wiimote
        #if there is a MAC address provided, use it, otherwise make a new connection
 if len(sys.argv) > 1:
  wiimote = cwiid.Wiimote(sys.argv[1])
 else:
  wiimote = cwiid.Wiimote()

 wiimote.rpt_mode = cwiid.RPT_BALANCE | cwiid.RPT_BTN
 wiimote.request_status()

 if wiimote.state['ext_type'] != cwiid.EXT_BALANCE:
  print 'This program only supports the Wii Balance Board'
  wiimote.close()
  return -1

 balance_calibration = wiimote.get_balance_cal()
 named_calibration = { 'right_top': balance_calibration[0],
    'right_bottom': balance_calibration[1],
    'left_top': balance_calibration[2],
    'left_bottom': balance_calibration[3],
   }

 exit = False
 while not exit:
  print "Type q to quit, or anything else to report your weight"
  c = sys.stdin.read(1)
  if c == 'q':
   exit = True
  wiimote.request_status()
  logweight(calcweight(wiimote.state['balance'], named_calibration))

 return 0

def calcweight( readings, calibrations ):
 weight = 0
 for sensor in ('right_top', 'right_bottom', 'left_top', 'left_bottom'):
  reading = readings[sensor]
  calibration = calibrations[sensor]
  if reading > calibration[2]:
   print "Warning, %s reading above upper calibration value" % sensor
  if reading < calibration[1]:
   weight += 1700 * (reading - calibration[0]) / (calibration[1] - calibration[0])
  else:
   weight += 1700 * (reading - calibration[1]) / (calibration[2] - calibration[1]) + 1700

 return ((weight / 100.0) * 2.2)
 
def logweight(weight):
        #replace with your google username, password, and the spreadsheet key available from the spreadsheet URL
 email = 'username@gmail.com'
 password = 'password'
 spreadsheet_key = '6BsFAFER464657d0NlFQdlc0WH45thYUjiaGg2U1E'
 worksheet_id = 'od6'

 spr_client = gdata.spreadsheet.service.SpreadsheetsService()
 spr_client.email = email
 spr_client.password = password
 spr_client.source = 'Weight Logger'
 spr_client.ProgrammaticLogin()

        #Dict keys are the lowercase versions of your column headers
 dict = {}
 dict['date'] = time.strftime('%m/%d/%Y')
 dict['time'] = time.strftime('%H:%M:%S')
 dict['weight'] = weight

 entry = spr_client.InsertRow(dict, spreadsheet_key, worksheet_id)
 if isinstance(entry, gdata.spreadsheet.SpreadsheetsList):
  print "Insert row succeeded."
 else:
  print "Insert row failed."

if __name__ == "__main__":
 sys.exit(main())

This code could easily be modified to achieve many different functions. For example, instead of entering any character to take your weight, it could be changed to take a name, so that multiple users can track their weights. Or maybe you want to set a timer so you don't have to hit a key on your keyboard to save your weight. The possibilities are endless.