0

I have a AS97 fitness-watch and want to receive the activity data with a raspberry pi using pygatt.

I have to write to the custom services uuids.

I need device.char_write to uuid ff01 and receive the indication from ff02.

If I just use device.char_read, I only get the first indication. I tried the function device.subscribe but this doesn't work.

#!/usr/bin/env python3
import pygatt
import logging
from binascii import hexlify
from time import sleep
        
logging.basicConfig()
logging.getLogger('pygatt').setLevel(logging.DEBUG) # Logger to see the debug
        
watch = "C4:C1:88:F8:58:59"   # watch bluetooth-adress
adapter = pygatt.GATTToolBackend()
        
adapter.start() # start bluetooth
device = adapter.connect(watch, address_type=pygatt.BLEAddressType.random, timeout=20)  
#connects with watch
        
try:
    device.char_write('7905ff01-b5ce-4e99-a40f-4b1e122d00d0',
                      bytearray([0xBE, 0x02, 0x01, 0xFE, 0x07, 0xE6, 0x01, 0x19, 0x00, 
                      0x00]))  #command bytes - 07e6 year - 01 month 
                      # - 19 day - 0000 - minute start
    activity_data = device.char_read('7905ff02-b5ce-4e99-a40f-4b1e122d00d0')
except:
    print("Can't read informations")
    adapter.stop()
        
print(activity_data)
adapter.stop()

I received the first indication:

bytearray(b'\xde\x02\x01\xfe\x07\xe6\x01\x19\x00\x00\x0bz\x00\x00\x07M\x00\xc8\x00')

Custom UUID Write/Indications

Get activity data

Get activity data 2nd 3rd indication

I guess the read function didnt work well because the characteristic just use write and indication.

Thanks all for help!

I tried also something like:

def data_handler_cb(handle, value):
    print("Data: {}".format(value.hex()))
    print("Handle: {}".format(handle))
            
try:
    device.char_write('7905ff01-b5ce-4e99-a40f-4b1e122d00d0',
                       bytearray([0xBE, 0x02, 0x01, 0xFE, 0x07, 0xE6, 0x01, 0x19, 0x00, 0x00]))
        
           
    device.subscribe("7905ff02-b5ce-4e99-a40f-4b1e122d00d0",  callback=data_handler_cb, indication = True)
    print("Wait for callback")
    sleep(3)      
finally:
    adapter.stop()

I also switched the order and first subscribe, but then it waits

DEBUG:pygatt.device:Looking up handle for characteristic 7905ff02-b5ce-4e99-a40f-4b1e122d00d0
DEBUG:pygatt.device:Found <Characteristic uuid=7905ff02-b5ce-4e99-a40f-4b1e122d00d0 handle=26>
DEBUG:pygatt.backends.gatttool.gatttool:Sending cmd=char-write-req 0x1b 0200

and breaks with an Error:

ERROR:pygatt.backends.gatttool.gatttool:No response received

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
leon
  • 1
  • 2
  • Is there a particular reason you are using the pygatt library? It appears to be based on `gatttool` which has been [deprecated](https://git.kernel.org/pub/scm/bluetooth/bluez.git/commit/?id=b1eb2c4cd057624312e0412f6c4be000f7fc3617). If you do want to use it then you need to use the [subscribe](https://github.com/peplin/pygatt#notifications-example) functionality to receive data from a characteristic that has the `indicate` flag set. – ukBaz Jan 26 '22 at 14:53
  • No there is no reason i am using pygatt, but i don't found other libraries for python 3.x on raspberry. May you can help me either to choose another library or with this subscribe problem? – leon Jan 26 '22 at 16:37

2 Answers2

1

I would break the problem up into three stages to help debug where the issue might be.

  1. Prove that you can get the data off the watch using a generic Bluetooth Low Energy scanning and exploration tool on a phone (e.g. nRF connect ).

  2. Use the bluetoothctl command line tool on the RPi to prove it can be done with the RPi in its current configuration.

  • Open two terminals and have a bluetoothctl running in both.

  • connect C4:C1:88:F8:58:59 in one bluetoothctl will connect in both.

  • menu gatt in both terminals.

  • select-attribute 7905ff02-b5ce-4e99-a40f-4b1e122d00d0 in one

  • In the same one notify on

  • select-attribute 7905ff01-b5ce-4e99-a40f-4b1e122d00d0 in terminal two

  • In terminal 2 notify on

  • In terminal 2 write "0xBE 0x02 0x01 0xFE 0x07 0xE6 0x01 0x19 0x00 0x00"

    You should see notification appear after the write.

  1. If both of the above experiments have worked it has demonstrated the watch and the RPi are working as expected. This allows you to be confident the issue is with the Python. There is an alternative example of how to create a BLE Central device with Python (not using pygatt) at: https://stackoverflow.com/a/63751113/7721752 . This uses the BlueZ D-Bus API which are documented at: https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc

With those experiments it should help you narrow down where the issues might be.

ukBaz
  • 6,985
  • 2
  • 8
  • 31
  • The first experiment was my first try and solution to test if the watch works without any specific application. And it still work. But if i try it on two terminals, it always disconnected from the watch by writing the command for `notify on`. I try to use DBus, but i guess it's a problem you need to write and subscribe at the same time. – leon Feb 01 '22 at 13:09
0

The Code with subscribe is right. The problem was the authorization from the server(bluetooth watch) to indicate. On a phone you were ask for a passkey, but on the RPi not. Just connect the watch for the first time with the terminal, with bluetoothctl then agent KeyboardOnly and after that pair the device. Now you will be asked for a passkey. Once the RPi is authorized, then you can use the function as well with Python.

import pygatt
import logging
import time
from binascii import hexlify

def handle_data(handle, value):
    """
    handle -- integer, characteristic read handle the data was received on
    value -- bytearray, the data returned in the notification
    """
    print("Received data: %s" % hexlify(value))

logging.basicConfig()
logging.getLogger('pygatt').setLevel(logging.DEBUG)
watch = "C4:C1:88:F8:58:59"
adapter = pygatt.GATTToolBackend()


try:
    time.sleep(5)
    adapter.start()
    time.sleep(5)
    device = adapter.connect(watch,address_type=pygatt.BLEAddressType.random,timeout=20)

    time.sleep(5)
    for uuid in device.discover_characteristics().keys():
        print("Read UUID %s" % (uuid))
        #read all characteristic uuids
    device.subscribe("7905ff02-b5ce-4e99-a40f-4b1e122d00d0",
                     callback=handle_data, indication = True)
    time.sleep(5)
    device.char_write('7905ff01-b5ce-4e99-a40f-4b1e122d00d0',
                  bytearray([0xBE, 0x02, 0x01, 0xFE, 0x07, 0xE6, 0x02, 0x01, 0x00, 0x00]))

    while 1:
        pass
finally:
    print("Adapter Stopped")
    adapter.stop()
leon
  • 1
  • 2