Connecting to a Bluetooth smart/LE weight scale with bluez/bluetoothctl/gatttool

Edmundo Del Gusto picture Edmundo Del Gusto · Dec 14, 2015 · Viewed 10.2k times · Source

What I want to do:

I want to connect my Raspberry Pi 2 to a Bluetooth Smart Weight Scale (Medisana BS440) and receive my Data.

What I know:

There is just one interesting Primary Service with 5 Characteristics:

 - handle: 0x001a
     -  properties: 0x20 (Indication), char value handle: 0x1b uuid:00008a21-0000-1000-8000-00805f9b34fb
 - handle: 0x001d
     -  properties: 0x20 (Indication), char value handle: 0x1e uuid:00008a22-0000-1000-8000-00805f9b34fb
 - handle: 0x0020
     -  properties: 0x02 (Read-Only),  char value handle: 0x21 uuid:00008a20-00..
 - handle: 0x0022
     -  properties: 0x08 (Write-Only), char value handle: 0x23 uuid:00008a81-00..
 - handle: 0x0024
     -  properties: 0x20 (Indication), char value handle: 0x25 uuid:00008a82-00..

I used the HCI-Snoop-Developer-Funktion of my Android-Phone, to see how the corresponding app communicates to my scale.

  1. Write 0200 -> 0x1f (enable Indication 0x1d)
  2. read 0x21 -> 0x21 (value: 37fb)
  3. write 0200 -> 0x1c (enable Indication 0x1a)
  4. write 0200 -> 0x26 (enable Indication 0x24)
  5. write 02a31e2c0b -> 0x23 (I don't fully understand this here, but I know if you take the bytes after 02 (a3 1e 2c 0b -> 0b 2c 1e a3 -> this is the current Unix-timestamp but for the year ?1975?)

after step 4. there is the first Indication (handle 0x25) which give me the stored personal data of me (my height, gender, age etc)

after step 5. there are some Indications (handle 0x1b and handle 0x1e) which should transfer my measured data. (Didn't analyze the hex-values at this time)

What I did:

I installed bluez.5.32 on my raspi (kernel 4.1.13), and did step 1 - 5 with gatttool and everything works fine till step 5. I don't get any Indication-messages from handle 0x1b and 0x1e) Nothing happens after step 5.

gatttool -t random -b DE:70:4A:XX:XX:XX -I
char-write-cmd 0x1f 0200
char-read-hnd 0x21 (37fb)
char-write-cmd 0x1c 0200
char-write-cmd 0x26 0200
char-write-cmd 0x23 0000000000

(I even do the thing with unix-timestamp-for 1975.. doesnt worked out)

After billions of hours I was getting bluetoothctl work on my raspi (there was a dbus-problem) and I tried the same with bluetoothctl. I enabled all Indications and write 0000000000 to hnd=0x23. Switched to handle 0x1a and it worked! I receive many hex-values that should be the data I'm searching for.

So whats the problem? I want to use gatttool for my purpose or at least I want to understand, why it doesnt worked out with gatttool

When I use bluetoothctl, I just can select and watch one attribute, and after receiving the data, my scale automatically disconnects to my raspberry. So when I select characteristic 0x1a, I cant see the indication-messages of characteristic 0x01d et vice versa.

Is there another connection between my Pi and my Scale when I'm using gatttool or when I'm using bluetoothctl? Or is there a difference in the way, how they communicate to my scale?

Answer

Keptenkurk picture Keptenkurk · Jan 1, 2016

The scale appears to be connectable just for a short period of time after the weight etc. has been measured I used the non-interactive mode of gatttool in a bash script like this:

gatttool -t random -b F1:37:57:XX:XX:XX --char-write --handle 0x001f -n 0200
gatttool -t random -b F1:37:57:XX:XX:XX --char-read --handle 0x0021
gatttool -t random -b F1:37:57:XX:XX:XX --char-write --handle 0x001c -n 0200
gatttool -t random -b F1:37:57:XX:XX:XX --char-write --handle 0x0026 -n 0200
gatttool -t random -b F1:37:57:XX:XX:XX --char-write-req --handle 0x0023 -n 0000000000 --listen

Info was taken from Pratik Sinha here. To get the response displayed the --listen explicitly needed to be given after which some 25 lines of data are received (alternating 1b and 1e responses). Thanks for your info, saved me days of work!

Happy New Year!

EDIT:

Using Python and the pygatt module this boils down to:

import pygatt.backends
from binascii import hexlify

def printIndication(handle, value):
    print('Indication received {} : {}'.format(hex(handle), hexlify(str(value))))

adapter = pygatt.backends.GATTToolBackend()
adapter.start()
# wait for someone to step on the scale
while True:  
    try:
        device = adapter.connect('f1:37:57:xx:xx:xx', 5, 'random')
        break
    except pygatt.exceptions.NotConnectedError:
        print('Waiting...')
device.subscribe('00008a22-0000-1000-8000-00805f9b34fb', callback = printIndication, indication = True)
device.subscribe('00008a21-0000-1000-8000-00805f9b34fb', callback = printIndication, indication = True)
device.subscribe('00008a82-0000-1000-8000-00805f9b34fb', callback = printIndication, indication = True)
try:
    device.char_write_handle(0x23, [02,00,00,00,00], True)
except pygatt.exceptions.NotificationTimeout:
    pass
device.disconnect()
adapter.stop()

NOTES:

  • Reading handle 0x21 serves no purpose in my experience
  • Writing handle 0x23 with 02 followed by the unix timestamp syncs the RTC of the scale
  • Use the multibyte modified pygatt (pull 34) to be able to receive multiple bytes in an Indication
  • Although writing to 0x23 will not produce any Notification it is required to wait for a Notification to be able to receive the 0x1b and 0x1e Indications. When the last Indication was received the NotificationTimeout exception is received.