8

I am developing a Linux application using python that will connect to my BLE Device and get the data by notifying characteristic. I am using pygatt for BLE Communications. I can successfully connect and bond to device and read from/write to characteristics. Even I can subscribe to notify characteristic, but the problem is, my BLE device is a custom machine and have 4 counters inside it, every time one of the counter's data changed, it sets the corresponding flag for notification, thus, with the onDataChanged-like methods I can read the counter's data from reading characteristic. In Python using pygatt, I can subscribe to notify characteristic with:

class_name.device.subscribe(uuid.UUID(notify_characteristic),callback=notifyBle)

and the notifyBle is:

def notifyBle(self,handle,data):
    read_data = class_name.device.char_read(uuid.UUID(read_characteristic))
    print(read_data)

When I run the program, first I scan the devices and connect to my device and bond with it, then I discover characteristics and list them. All is successful. After listing the characteristics, I write to write characteristic to clear notification flags, it is successful too. Last I subscribe to notify characteristic it is successful.

After all these processes, I increase my device's counters physically(there are buttons on the device for increasing counters). When I press the button program goes to the notifyBle method and it gives the error, which is:

Exception in thread Thread-3:
Traceback (most recent call last):
  File "/usr/lib/python3.5/threading.py", line 914, in _bootstrap_inner
    self.run()
  File "/usr/local/lib/python3.5/dist-packages/pygatt/backends/gatttool/gatttool.py", line 137, in run
    event["callback"](event)
  File "/usr/local/lib/python3.5/dist-packages/pygatt/backends/gatttool/gatttool.py", line 479, in _handle_notification_string
    self._connected_device.receive_notification(handle, values)
  File "/usr/local/lib/python3.5/dist-packages/pygatt/device.py", line 226, in receive_notification
    callback(handle, value)
  File "/home/acd/Masaüstü/python_workspace/ble.py", line 54, in notifyBle
    read_data = bleFunctions.dev.char_read(uuid.UUID(bleFunctions.read_characteristic))
  File "/usr/local/lib/python3.5/dist-packages/pygatt/backends/gatttool/device.py", line 17, in wrapper
    return func(self, *args, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/pygatt/backends/gatttool/device.py", line 40, in char_read
    return self._backend.char_read(self, uuid, *args, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/pygatt/backends/gatttool/gatttool.py", line 53, in wrapper
    return func(self, *args, **kwargs)
  File "/usr/local/lib/python3.5/dist-packages/pygatt/backends/gatttool/gatttool.py", line 519, in char_read
    self.sendline('char-read-uuid %s' % uuid)
  File "/usr/lib/python3.5/contextlib.py", line 66, in __exit__
    next(self.gen)
  File "/usr/local/lib/python3.5/dist-packages/pygatt/backends/gatttool/gatttool.py", line 180, in event
    self.wait(event, timeout)
  File "/usr/local/lib/python3.5/dist-packages/pygatt/backends/gatttool/gatttool.py", line 154, in wait
    raise NotificationTimeout()
pygatt.exceptions.NotificationTimeout

Any help would be appreciated.

PS: I wrote the exact same program in Android, and in Windows UWP. With python, I am aiming to run this on raspberry pi 3.

PSS: I am using raspberry pi 3 with Ubuntu Mate installed for developing this program in python.

Alican Uzun
  • 349
  • 1
  • 6
  • 20
  • Looking into the test code for pygatt I see there is a receive_notification function for the BLEDevice class. See https://github.com/peplin/pygatt/blob/master/pygatt/device.py and https://github.com/peplin/pygatt/blob/master/tests/test_device.py. From the error trace I think the problem is that the module times out when trying to read from the device after you change the device you test. Is your device properly listing its characteristics after you make the change? – BoboDarph Jul 07 '17 at 08:12
  • @BoboDarph I didn't try it, since you tell me to try, now I tried it and it says Characteristic discovery failed, when it hit the notifyBle method. – Alican Uzun Jul 07 '17 at 08:24
  • Can you check that the UUID of your device doesnt change when you modify it? Or that your device is available for discovery with the same UUID imediately after you change it? That would be a very interesting thing to discover. If it does, then most likely self._save_charecteristic_callback will fail and hit an AttributeError exception possibly because the old UUID of your device wasnt available for querying. – BoboDarph Jul 07 '17 at 09:03
  • @BoboDarph what do you mean by UUID of device? services and characteristics have UUID and they can be changed with the core code inside eeprom. – Alican Uzun Jul 07 '17 at 10:13
  • 1
    From your code I understand you're trying to read by a UUID generated from your read_characteristic. From the pygatt implementation and error you listed, I deduced that the call might fail because that UUID either doesnt exist or somehow gets changed when you flip the switch. Sorry if I can't be of more help. – BoboDarph Jul 07 '17 at 10:33
  • @BoboDarph I understand what you mean now, but UUID's are constant and can not be changed. I can read manually again and again, I am not familiar with python, so I can't fully understand the source code of the methods that are provided by the author of pygatt. From other languages I just now how BLE Communication works. – Alican Uzun Jul 07 '17 at 10:42

1 Answers1

4

Firstly, create event class like below,

class Event:
    def __init__(self):
        self.handlers = set()

    def handle(self, handler):
        self.handlers.add(handler)
        return self

    def unhandle(self, handler):
        try:
            self.handlers.remove(handler)
        except:
            raise ValueError("Handler is not handling this event, so cannot unhandle it.")
        return self

    def fire(self, *args, **kargs):
        for handler in self.handlers:
            handler(*args, **kargs)

    def getHandlerCount(self):
        return len(self.handlers)

    __iadd__ = handle
    __isub__ = unhandle
    __call__ = fire
    __len__  = getHandlerCount

then, create a ble class

import pygatt
from eventclass import Event

class myBle:
    ADDRESS_TYPE = pygatt.BLEAddressType.random
    read_characteristic = "0000xxxx-0000-1000-8000-00805f9b34fb"
    write_characteristic = "0000xxxx-0000-1000-8000-00805f9b34fb"
    notify_characteristic = "0000xxxxx-0000-1000-8000-00805f9b34fb"
    def __init__(self,device):
        self.device = device
        self.valueChanged = Event()
        self.checkdata = False

    def alert(self):
         self.valueChanged(self.checkdata)

    def write(self,data):
        self.device.write_char(self.write_characteristic,binascii.unhexlify(data))

    def notify(self,handle,data):
        self.checkdata = True

    def read(self):
        if(self.checkdata):
            self.read_data = self.device.char_read(uuid.UUID(self.read_characteristic))
            self.write(bytearray(b'\x10\x00'))
            self.checkdata = False
            return self.read_data
    def discover(self):
        return self.device.discover_characteristics().keys()

when you get notification, you will set the boolean value to true and the alert method will notify the boolean value is changed.You will listen to alert method with

def triggerEvent(checkdata):
   print(str(checkdata))

ble = myBle(device)
ble.valueChanged += triggerEvent
ble.alert()

you can call the read method to get characteristics value with triggerEvent method.

Daniel F
  • 13,684
  • 11
  • 87
  • 116
mr.
  • 679
  • 12
  • 30
  • 1
    Thanks for answer, it seems like it will work. I am going to try it immediately and will notify you. – Alican Uzun Jul 18 '17 at 13:23
  • 1
    I tried the code you post and it worked. Thank you for solving this weird problem. – Alican Uzun Jul 18 '17 at 13:42
  • Hi Alican Uzun, i am trying to use pygatt to subscribe to a characteristic, but i am not able to get it working. Here is a link to my problem https://stackoverflow.com/questions/60888220/pygatt-unable-to-execute-device-subscribe . Can you please share your solution? – Sandrocottus Mar 29 '20 at 08:28