Introduction: Make a Nike FuelBand! Sort Of...
Back in October of 2012 I won a Nike FuelBand during a fundraiser at my CrossFit gym*. I'm really into data collection, so I was excited to have a 24-hour activity logger. I had no idea how the FuelBand worked. I didn't see any galvanometer probes (perspriation), IR LEDs (heart rate), or any kind of restrictive elastic (blood pressure), so clearly it wasn't taking biometric readings. Since I'm fairly up to date on common sensor technology, that left only accelerometers and gyroscopes. If I'm missing anything, please email!
I immediately noticed the first thing most everyone notices: the more you shake the fuel band, the more the metric of NikeFuel increases. Obviously this isn't a sophisticated measurement, and their use of the arbitrary NikeFuel metric seems to be an indication of this. I realize the limits of wearable telemetry, so I view this product not as a scientific device designed to measure caloric expenditure or VO2max to five decimals, but rather a way to make exercise quantifiable, and providing a mechanism to develop a routine for folks who need that extra nudge.
This led me to think: how could I capture more detailed metrics about my workout. I came up with about seven different sensors I could build that would be non intrusive and build on just motion data alone. But first I had to actually make my own wearable platform with a very basic motion detector.
This instructable uses a 3D accelerometer mounted to an Arduino Uno, flash memory for data recording, and a fairly rough user interface to control calibration, logging, and file management. All on a small wrist-mounted package. (I like the Uno because of its size, the prototype shields give way more space than the smaller devices.)
Bill of materials:
SparkFun 3D Acceleromtoer (and some pin standoffs to solder it)
DFRobot LCD Keypad Shield
DFRobot Uno Prototype Board
5x 3.3V Zener Diodes
SPST Slide Switch
8mbit W2Q80 WinBond Flash Chip
Four 1/2" standoffs & 8 screws that fit an Uno footprint
A small quantity of 20ga solid strand wire
4xAAA Battery Holder
* Sadly, my fuel band was consumed by a friend's dog a few weeks later. Unfortunately it did not survive the journey through the canine's gastrointestinal tract, not that if it had survived I would still want to use it.
Teachers! Did you use this instructable in your classroom?
Add a Teacher Note to share how you incorporated it into your lesson.
Step 1: Wire the Accelerometer
Pick a spot on the shield and solder an 8 pin header down. Place the 3D sensor on the header and solder it down too. Make sure you leave enough room to solder down the flash chip in the next step.
The sensor has eight pins. Two are for power and ground. One puts the device to sleep. Three are the X, Y and Z analog outputs, and the last two are mode configuration.
The mode selector allows you to tune the sensitivity of the device to 1.5g (00), 2g (01), 4g (10) or 6g (11). I opted for 1.5g, and only exceed that rating on a few movements. The tradeoff here is fine-grained fidelity versus dynamic range.
Wire VCC to 5V, ground to ground, sleep to ground (so the device is always on), and both config pins to ground. Wire the X, Y, and Z pins to the shield's analog 1, 2 and 3 pins, respectively.
Step 2: Wire the Flash
There are a few spots on the board that have places for DIP layouts, make use of those for ease of connectivity.
For the flash chip, the WP# (write protect), HOLD#, and VCC go directly to the 5V rail. Connect them to the rail with the Zener diodes in reverse bias to ground so that the 5V is clamped to 3.3V. The chip is 3.3V only, so we need to protect any inputs driven to 5V. You will also put 3.3V Zeners in parallel to ground on CLK, DI, DO and CS (chip select). I completely forgot to add small load resistors in the circuit, but everything worked fine. Were I to do this again I would do it correctly, like this: http://hades.mech.northwestern.edu/images/7/74/Zen...
The SPI library expects the following pin assignments
SPI MOSI (Master Out / Slave In) goes to Flash pin DI (data in); shield pin 11 goes to DIP pin 5.
SPI MISO (Master In / Slave Out) goes to Flash pin DO (data out); shield pin 12 goes to DIP pin 2.
SPI SCK (clock) goes to Flash pin CLK (clock); shield pin 13 goes to DIP pin 6.
SPI SS (select) goes to Flash pin CS# (chip select); shield pin 10 goes to DIP pin 1.
* I actually wired CS# to pin 3, I have no idea why, but note the #define in the code if you change this
Step 3: Final Assembly
The next few steps are rather ad hoc.
First, layer up some duct tape to make a wrist band (or find a 3" wide band that fits on your wrist). Attach some velcro to keep your spiff wristband from falling off.
Drill four small holes in the band that match the Arduino Uno standoff holes. Screw the standoffs into the wrist-band and into the Uno.
Sandwich the prototype board with the flash memory and accelerometer on top of the Uno.
Attach the LCD keypad shield on top of that.
Glue, tape, or somehow affix the battery pack to the wrist band. I used some Gorilla Glue. That stuff is fantastic.
Solder the switch in series with the V+ red wire output of the battery clip, and solder the output of that wire to the VIN pin on the display shield. Solder the ground/black wire from the battery clip to the GND pin on the display shield.
Insert some batteries.
Turn on the switch to make sure all of the LEDs on every component in the stack lights up.
Step 4: Upload the Software
A. Present the user with a basic UI to select whether to:
- Display the X, Y, Z readings
- Record the X, Y, Z readings
- Erase the flash chip
- Show how many 256-byte pages have been written to the flash chip
C. Write the data to Flash
The bulk of the code is dedicated to the UI. It was a fun process learning how to use the LCD library that comes with the top shield. The six buttons on the shield send an analog voltage to the analog 0 pin of the Uno. This is a pretty slick alternative to debouncing pushbuttons (if you don't know why that is challenging, you will some day!). The control code is not that difficult to get your head around. Up/Down cycle through menu options, Right selects, and Reset just reboots the Arduino. Really simple.
In display mode, a string is sent to the LCD with the X, Y and Z readings in milli-gs. The convert() function scales the readings from 0-5V to gs. Since 0g is defined as 1.65V by the manufacture, and the total Arduino analog fidelity from 0-5V is measured in 1024 increments, the equation is 5V/1024 * X - 1.65V = g.
In record mode, the display is not updated. Instead, the raw analog data is written to the flash memory using a pagebuffer class. Every time 256 bytes are sent to the class, it fires off a page write to the flash device and swaps in a new buffer. This class is an improvement over my previous code in my anemometer instructable.
Erase mode simply erases the flash chip.
Display Pages Used Mode
Pages used shows how many pages have been filled in the flash device. Since the first byte of every page is always set to 0, the code can fast-forward to a new fresh page after reset. This allows you to turn the device on and off and still continue writing where it left off.
* Note: In addition to the Arduino core library (for things like SPI and digitalWrite()), you will need the LCD keypad library. It is available on DFRobot website.
** Note #2: I have not explained how to download the data off of the flash chip. That is coming in another instructable. I have a dedicated board with a USB connection that reads data off this chips. Some day I'll move to SD cards and make my life easier.
Step 5: Data Collection and Closing Thoughts
By this point I was really eager to see the different acceleration patterns from various movements. I was well aware that a single scalar value (the 3D vector magnitude of the acceleration) would not be sufficient to ascertain any useful positional data, but getting ahead of oneself make it hard to learn and progress, amiright?
I strapped on my ghetto FuelBand and went to the gym.
I performed three basic movements:
a) 135# back squat -- rack a barbell above the shoulder blades below the cervical vertebrae, keep the core tight and the back straight, squat down until the hip crease passes through the plane of the knees, drive the knees up and the hips forward and stand up; repeat
b) 20# wall-ball -- standing 1-2' from a wall, facing it, hold a 20# medicine ball chest height; squat down until the hip crease passes below the knees; drive up and at the top of the movement add to the momentum of the ball using the arms to throw it at a target 10' high on the wall; as the ball returns, perform the reverse movement to receive the ball; repeat.
c) 95# olympic snatch -- oh this is too complicated, just look it up.
Each movement involves motion of the arms and torso, but the weights are significantly different. I suspected acceleration alone could not be an indicator of the load lifted, but I wanted to see for myself.
I collected the data for several repetitions of each movement. I post-processed the data to generate a magnitude equal to the square root of the sum of the square of each axis' acceleration; e.g., mag = sqrt(x^2 + y^2 + z^2). I plotted the results and smoothed the data with a running average. Also note that even though any one axis may exceed 1.5g, I never exceeded even 1g simultaneously on two or more sensors, so the magnitudes were always pretty low. I say this because at first I thought the data was wrong when the magnitudes were all less than gravity!!!
It looks pretty cool. The complex compound motion of the movement is only hinted at by the overall acceleration measured on the wrist. I can distinctly see the fast and slow phases of the movements, especially the explosive pull of the snatch. My suspicions were correct, it is impossible to note large differences in exertion between one movement (the squat) which moved a total of ~1000lbs of weight while another (wallball) moved only ~200lbs. Clearly a single acceleration value is not enough to provide a sufficient metric of effort exerted, but this whole process led me to develop a platform that I can expand upon with new sensors. I can see multiple sensors using an idle / rest position, and then reconstructing overall movement, but that would still not capture the weight. But I have some more ideas...
Stay tuned and good luck! I hope this inspires you to improve on exercise telemetry!