Time to dive into the (bluetooth) specifics of the BS440 scale. When starting this project i was lucky to bump into this post on StackOverflow where user Edmundo was actually trying to acomplish the same. He snooped the bluetooth traffic on his Android phone and thus discovered the commands to send to the scale to make it send the data we need.
Data is presented to us by use of Indications. Indications are messages conveying the data for certain characteristics the device supports. Characteristics have a long (16 byte globally unique UUID) identifier but also a two-byte shortcut for it: the handle. Writing the command 0200 to a handle tells the device you register to the indications of this characteristic. Writing 0000 will stop it. A data packet of this characteristic will report with a handle 1 less than the handle of the characteristic. The BS440 has 3 kinds of interesting characteristics:
Handle UUID Data
0x26 00008a82-0000-1000-8000-00805f9b34fb person, gender, age, size, activity
0x1c 00008a21-0000-1000-8000-00805f9b34fb weight, time, person
0x1f 00008a22-0000-1000-8000-00805f9b34fb time, person, kcal, fat, tbw, muscle, bone
Some notes: person ranges from 1~8 and corresponds with the user selected before stepping on the scale. Or: being a “smart scale” : if the weight roughly corresponds to a known user weight that user is automatically selected. And when multiple users with the same weight are found it even lets you select the appropriate user.
So what does the data look like?
The last 30 measurements per person will be stored in the scale and upon communication the history for this user will be dumped. So you will receive values like this
Nice, but what does it actually mean?
Now the puzzle starts where to find the data we need, but luckily Edmundo helps us out again:
All multibyte values represented in unsigned int, little endian (multi byte values
start with least significant byte).
First byte of string is byte 0.
fixed: byte: 0 [0x1d]
weight: byte: 1 & 2 [kg*100]
timestamp: byte 5-8 Unix timestamp
person: byte 13 [1..8]
fixed: byte 0 [0x6f]
timestamp: byte 1-4 Unix timestamp
person: byte 5 [1..8]
kcal: byte 6 & 7 first nibble = 0xf, [kcal]
fat: byte 8 & 9 first nibble = 0xf, [fat*10]
tbw: byte 10 & 11 first nibble = 0xf, [tbw*10]
muscle: byte 12 & 13 first nibble = 0xf, [muscle*10]
bone: byte 14 & 15 first nibble = 0xf, [bone*10]
fixed: byte 0 [0x84]
person: byte 2 [1..8]
gender: byte 4 (1=male, 2=female) [1|2]
age: byte 5 [year]
size: byte 6 [cm]
activity: byte 8 (0=normal, 3=high) [0|3]
In Python the datastrings can be interpreted simply using struct.unpack using the pattern ‘BxBxBBBxB’ for the 0x25 data, ‘<BHxxIxxxxB’ for the 0x1b data and ‘<BIBHHHHH’ for the 0x1e data. In my program BS440decode.py takes care of the decoding job.
These insights should get us going. Just one more thing. When does the scale start sending data? Data transfer starts after sending 0x02 followed by the unix timestamp to handle 0x23. This is also the way the scale resyncs it’s RTC. Note that this last write must be a write with response (i.e. “write-req” instead of a “write-cmd”).
By now you might start to understand why this project dragged over 3 months to complete. Part 4 will discuss the Python code.