IanHarvey / bluepy

Python interface to Bluetooth LE on Linux
Other
1.58k stars 490 forks source link

waitForNotifications raises error in BTLE #432

Open tbecker8625 opened 3 years ago

tbecker8625 commented 3 years ago

I am using BLE notifications to obtain sensor data from an Arduino Nano 33 BLE Sense. Using the bluepy site example, I created a code snippet to test this function (using Python3.7 on an rPi). My intention was to get the snippet working and then embed it in the larger project.

The code snippet works perfectly! However, once the code snippet is embedded in the larger project I have run into a number of problems. An error is raised in BTLE. That error is initiated when I issue a writeCharacteristic to subscribe to notifications. This write has withResponse=True. In BTLE an error is raised when self.delegate.handleNotification(hnd, data) is executed. The error is "TypeError: handleNotification() missing 1 required positional argument: 'data'".

I have been banging my head against the wall for days trying to figure this one out and now asking for help. Below is the code snippet I am using. Again this code works perfectly. Note that I have not included the larger project code since it is over 1500 lines of code. Also, this code is proprietary to a product that we are working on.

Any help is greatly appreciated.

Code snippet:

import signal import sys from bluepy.btle import Scanner, DefaultDelegate, BTLEException from bluepy import btle

class MyDelegate(DefaultDelegate):

def __init__(self):

    DefaultDelegate.__init__(self)
    self.data_string_found = False

def handleDiscovery(self, dev, isNewDev, isNewData):

    if isNewDev:
        bt_list_addr.append((dev.addr, dev.getValueText(9)))

def handleNotification(self, cHandle, data):

    if cHandle == 12:
        self.o3Value = data.decode("ascii")
    if cHandle == 15:
        self.uvcValue = data.decode("ascii")
        self.data_string_found = True  
    if self.data_string_found == True:
        print("o3Value = ", self.o3Value, " UV Value = ", self.uvcValue)
        self.data_string_found = False

class BlueTh:

def __init__(self):

    self.bt_addr = "C1:B4:C2:91:85:7C"

**

Now we need to open the selected Bluetooth device -

Error on connection is trapped by BTLEException

#

**

def open_bluetooth(self):       

    try:
        print("About to connect to BT device")
        self.controller_device = btle.Peripheral(self.bt_addr)
        self.controller_device.setDelegate(MyDelegate())

        self.bt_open = True
        print("Connected to BT device")    
        self.setup_data = b"\x01\x00"
        self.controller_services = self.controller_device.getServices()
        self.controller_services = self.controller_device.getServiceByUUID("41316a2e-e37a-4f5e-8dd2-ac1bc99fac74")
        self.chars = self.controller_services.getCharacteristics()
        self.notify = self.chars[0]
        print("Characteristic 000d = ", str(self.notify))
        self.notify_handle = self.notify.getHandle() + 1
        self.controller_device.writeCharacteristic(self.notify_handle, self.setup_data, withResponse=True)
        self.notify = self.chars[1]
        print("Characteristic 000e = ", str(self.notify))
        self.notify_handle = self.notify.getHandle() + 1
        self.controller_device.writeCharacteristic(self.notify_handle, self.setup_data, withResponse=True)
        for self.ch in self.chars:
            print(str(self.ch))
            continue

        print("Bluetooth connected")

    except BTLEException as exception:
        print("BT device did not connect - error = ", exception)
        bt_open = False
        print("Bluetooth not connected")

def wait_for_BTData(self):

    try:
        self.controller_device.waitForNotifications(1.0)

    except BTLEException as exception:
        print("BT device error ", exception)
        if exception == "Device disconnected":
            self.open_bluetooth()
            return

def signal_handler(sig, frame):

print("Exit via ctrl-c signal handler")
file.close()
sys.exit(0)

file=open("data.txt",mode='w') signal.signal(signal.SIGINT, signal_handler)

data_string_found = False data_string = "" blueth = BlueTh() blueth.open_bluetooth()

while True: blueth.wait_for_BTData()

tbecker8625 commented 3 years ago

Additional information... the larger project that I embedded the notification code into also uses traditional (non-LE) Bluetooth. I am using module Bluetooth (which I believe is bluez?). My impression from testing the code snippet further is that bluepy and bluez together might have some sort of conflict....

Tom

tbecker8625 commented 3 years ago

In looking at this issue further, I went out on a limb and revised btle as follows:

`def _getResp(self, wantType, timeout=None): if isinstance(wantType, list) is not True: wantType = [wantType]

    while True:
        resp = self._waitResp(wantType + ['ntfy', 'ind'], timeout)
        if resp is None:
            return None

        respType = resp['rsp'][0]
        if respType == 'ntfy' or respType == 'ind':
            hnd = resp['hnd'][0]
            data = resp['d'][0]
            if self.delegate is not None:
                self.delegate.handleNotification(self, hnd, data)
            if respType not in wantType:
                continue
        return resp`

I added "self" to the statement "self.delegate.handleNotification(self, hnd, data), i.e., added self as an operand. This seems to allow my program to operate. I am still looking at whether it is operating correctly or not.

I do not know if adding self in this context changes the relationship of objects or their integrity. I would be interested in commentary or feedback regarding this revision.

Tom

tbecker8625 commented 3 years ago

Is this issue list for talking to yourself?