Skip to content

Commit

Permalink
Parse more data types
Browse files Browse the repository at this point in the history
  • Loading branch information
Benoit Allard committed Jul 17, 2012
1 parent 5d67de7 commit 6f0248a
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 12 deletions.
112 changes: 111 additions & 1 deletion doc/fitbit_data.rst
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,90 @@ set. The second byte gives the active score, and the third the steps.
Example
.......

.. _bank1r:

bank1
-----

This bank is about the **daily statistics**. However, a new record
with the current data is appended each time the bank is being read, or
the clock switch to another day (midnight).

Data format
...........

Each record is 16 bytes long and starts with a timestamp on four
bytes. Follows then XX, steps, distance and 10 x floors. Both steps
and distance are stored on four bytes, the first one (probably
something with calories) and the floors are on two bytes. The unit
used for the distance is the centimeter.

.. note:: On the first version of the fitbit tracker (**not** Ultra),
the records are 14 bytes long as they miss the last two
bytes about the amount of floors climbed.

.. note:: The midnight records, are registered with the date of the
next day. A record for ``2012-07-07 00:00:00`` is actually
about the 6th of July.

Example:
........

::

60 A0 05 50 9C 41 53 19 00 00 31 E5 49 00 1E 00

Which can be interpreted as follow:

- ``0x5005a060``: 2012-07-17 19:26:56
- ``0x419c``: 16796 : approximately 1845 calories (*.1103 - 7)
- ``0x00001953``: 6483 steps
- ``0x0049e531``: 4.842801km
- ``0x001E``: 3 floors
bank2
-----

This bank is about **recorded activities**.

Data format
...........

Records are 15 bytes long, they are prefixed with a timestamp on four bytes.

The record can be categorized in multiple kinds. Their kind is decided
by the value of the byte 6.

Value of 1:

This is a stop of the recorded activity. the record will contain
information about the length of the activity, the steps, the
floors, and more.

Value of 2:

This one seems to always go in pair with the value of 3.

Value of 3:

This one seems to always go in pair with the value of 2, A record
with a value of 2 usually follow two seconds after the record with
a value of 3.

Value of 4:

Not found yet

Value of 5:

This means a start of the activity if all fields are set to 0,
else, the meaning is still to be discovered.

Example
.......



bank3
-----

Expand Down Expand Up @@ -191,6 +269,13 @@ This bank is the same as `bank0w`_.
bank5
-----

* This bank is one of the few without timestamps
* This bank is not related to the amount of steps.
* An erase has an effect on the data, however, the values don't go
to 0.
* This bank is always 14 bytes long.


bank6
-----

Expand Down Expand Up @@ -247,7 +332,32 @@ bank0
-----

This bank always receives 64 bytes. Those bytes are about the device
settings as set on the web page *Device Settings*.
settings as set on the web page *Device Settings* and *Profile
Settings*.

Data format
...........

The following information is to prefix with a big **it looks
like... ** as those are only estimation based on different seen
values.
* First four bytes are always zero
* Then are some bytes about yourself:
- length
- preferred unit
- stride length
- gender
- time zone
* what should be displayed on the tracker
- greeting on/off
- left hand / right hand
- clock format
* 7 zeros
* options displayed on tracker
* the greeting name (10 bytes), right padded with 2 zeros
* the three chatter texts (3 x 10 bytes). each right padded with 2
zeros

bank1
-----
Expand Down
54 changes: 45 additions & 9 deletions python/fitbit.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,20 +322,46 @@ def parse_bank0_data(self, data):
time_index = time_index + 1

def parse_bank1_data(self, data):
banklen = {(4, 14):16}.get((self.app_major_version, self.app_minor_version), 14)
ultra = self.hardware_version >= 12
banklen = {12:16}.get(self.hardware_version, 14)
for i in range(0, len(data), banklen):
print ["0x%.02x" % x for x in data[i:i+13]]
d = data[i:i+banklen]
# First 4 bytes are seconds from Jan 1, 1970
daily_steps = data[i+7] << 8 | data[i+6]
record_date = datetime.datetime.fromtimestamp(data[i] | data[i + 1] << 8 | data[i + 2] << 16 | data[i + 3] << 24)
print "Time: %s Daily Steps: %d" % (record_date, daily_steps)
maybe_calories = d[5] << 8| d[4]
daily_steps = d[9] << 24 | d[8] << 16 | d[7] << 8 | d[6]
daily_dist = (d[13] << 24 | d[12] << 16 | d[11] << 8 | d[10]) / 1000000.
daily_floors = 0
if ultra:
daily_floors = (d[15] << 8 | d[14]) / 10
record_date = datetime.datetime.fromtimestamp(d[0] | d[1] << 8 | d[2] << 16 | d[3] << 24)
print "Time: %s %d Daily Steps: %d, Daily distance: %fkm Daily floors: %d" % (
record_date, maybe_calories, daily_steps, daily_dist, daily_floors)

def parse_bank2_data(self, data):
banklen = {(4, 14):16}.get((self.app_major_version, self.app_minor_version), 14)
for i in range(0, len(data), 13):
print ["0x%.02x" % x for x in data[i:i+13]]
ultra = self.hardware_version >= 12
banklen = {12:15}.get(self.hardware_version, 13)
for i in range(0, len(data), banklen):
d = data[i:i+banklen]
# First 4 bytes are seconds from Jan 1, 1970
print "Time: %s" % (datetime.datetime.fromtimestamp(data[i] | data[i + 1] << 8 | data[i + 2] << 16 | data[i + 3] << 24))
print "Time: %s" % (datetime.datetime.fromtimestamp(d[0] | d[1] << 8 | d[2] << 16 | d[3] << 24))
if d[6] == 1:
elapsed = (d[5] << 8) | d[4]
steps = (d[9]<< 16) | (d[8] << 8) | d[7]
dist = (d[12] << 16) | (d[11]<< 8) | d[10]
foors = 0
if ultra:
floors = ((d[14] << 8) | d[13]) / 10
print "Activity summary: duration: %s, %d steps, %fkm, %d floors" % (
datetime.timedelta(seconds=elapsed), steps, dist / 100000., floors / 10)
else:
print ' '.join(['%02X' % x for x in d[4:]])


def parse_bank4_data(self, data):
assert len(data) == 64
print ' '.join(["0x%.02x" % x for x in data[:24]])
print "Greeting : ", ''.join([chr(x) for x in data[24:24+8]])
print "Chatter: ", ', '.join([''.join([chr(x) for x in data[i:i+8]]) for i in range(34, 64, 10)])

def parse_bank6_data(self, data):
i = 0
Expand All @@ -351,6 +377,16 @@ def parse_bank6_data(self, data):
tstamp = d[3] | d[2] << 8 | d[1] << 16 | d[0] << 24
i += 4

def write_settings(self, options ,greetings = "", chatter = []):
greeting = greeting.ljust( 8, '\0')
for i in range(max(len(chatter), 3)):
chatter[i] = chatter[i].ljust(8, '\0')

self.write_bank(0, payload)

def write_bank(self, index, data):
self.run_opcode([0x25, index, len(data), 0,0,0,0], data)

def main():
#base = DynastreamANT(True)
base = FitBitANT(debug=True)
Expand Down
15 changes: 13 additions & 2 deletions python/ifitbit.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def print_help():

from antprotocol.bases import FitBitANT
from fitbit import FitBit
import time

base = None
tracker = None
Expand Down Expand Up @@ -58,7 +59,7 @@ def init(*args):
def close():
global base, tracker
if base is not None:
print "Closing connctions"
print "Closing connection"
base.close()
base = None
tracker = None
Expand Down Expand Up @@ -95,11 +96,21 @@ def pprint(data):
1: tracker.parse_bank1_data,
2: tracker.parse_bank2_data,
# 3: tracker.parse_bank3_data,
# 4: tracker.parse_bank4_data,
4: tracker.parse_bank4_data,
# 5: tracker.parse_bank5_data,
6: tracker.parse_bank6_data,
}.get(idx, pprint)(data)

@command('pr5', 'Periodic read 5')
@checktracker
def pr5(sleep = '5', repeat = '100'):
sleep = int(sleep)
repeat = int(repeat)
while repeat > 0:
read_bank(5)
time.sleep(sleep)
repeat -= 1

@command('erase', 'Erase data bank')
@checktracker
def erase_bank(index, tstamp=None):
Expand Down

0 comments on commit 6f0248a

Please sign in to comment.