Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BLE disconnectes when writeCharacteristic() is called in the notify example #253

Open
grahaminnovations opened this issue Jan 22, 2018 · 5 comments

Comments

@grahaminnovations
Copy link

@grahaminnovations grahaminnovations commented Jan 22, 2018

Hi,
The code I used is based on the ble notify example.
I have a separate function to deal with the GPIO button interrupt response.
It will call the writeCharacteristic and send a short command to the peripheral device.
The peripheral device will send back an ACK notification.
Sometimes it cause the ble to disconnect. Any idea why is that?

from bluepy import btle
import time
import RPi.GPIO as GPIO  
GPIO.setmode(GPIO.BCM)  
 
GPIO.setup(5, GPIO.IN)

def notify_Write(data):
    global svc, p
    ch_write = svc.getCharacteristics()[0]
    p.writeCharacteristic(ch_write.valHandle, data)
        
def my_callback(channel):
    print("falling edge detected on %s" % channel)
    notify_Write(b'\x01')
    
 
class MyDelegate(btle.DefaultDelegate):
    def __init__(self):
        btle.DefaultDelegate.__init__(self)

    def handleNotification(self, cHandle, data):
        # ... perhaps check cHandle
        # ... process 'data'
        print(data)

GPIO.add_event_detect(5, GPIO.FALLING, callback=my_callback, bouncetime=300)

# Initialisation  -------

p = btle.Peripheral("ee:6c:c1:cf:fe:f9", "random")
p.setDelegate( MyDelegate() )

# Setup to turn notifications on, e.g.
svc = p.getServiceByUUID("47491010-1011-537e-4f6c-d104768a1001")
ch_notify = svc.getCharacteristics()[1]
p.writeCharacteristic(ch_notify.valHandle+1, b'\x01\x00', withResponse=True)

# Main loop --------

while True:
    if p.waitForNotifications(1.0):
        # handleNotification() was called
        continue

    print("Waiting...")
    # Perhaps do something else here
@grahaminnovations grahaminnovations changed the title BLE disconnected when call writeCharacteristic in notify example BLE disconnectes when writeCharacteristic() is called in the notify example Jan 23, 2018
@grahaminnovations

This comment has been minimized.

Copy link
Author

@grahaminnovations grahaminnovations commented Jan 23, 2018

The error message:

Traceback (most recent call last):
  File "/home/pi/GI-bleNotify/demo.py", line 111, in <module>
    if p.waitForNotifications(1.0):
  File "/usr/local/lib/python3.4/dist-packages/bluepy/btle.py", line 516, in waitForNotifications
    resp = self._getResp(['ntfy','ind'], timeout)
  File "/usr/local/lib/python3.4/dist-packages/bluepy/btle.py", line 369, in _getResp
    resp = self._waitResp(wantType + ['ntfy', 'ind'], timeout)
  File "/usr/local/lib/python3.4/dist-packages/bluepy/btle.py", line 337, in _waitResp
    raise BTLEException(BTLEException.INTERNAL_ERROR, "Unexpected response (%s)" % respType)
bluepy.btle.BTLEException: Unexpected response (wr)```
@Thomas-Dimos

This comment has been minimized.

Copy link

@Thomas-Dimos Thomas-Dimos commented Mar 23, 2018

I received the same error and as it looks when we are waiting for notifications and in my case another thread is going to write a value to a characteristic, a response is produced, as the error says ,and in the _waitResp() in the source code of btle.py which is called from waitForNotifications(),the last else is fired and produces this error.The thing is that in my case the write operation is always executed but then this error comes up.I don't know if it is too simple but maybe in the last else of this function if the response type is 'wr' then you can just skip it and wait again for the correct response instead of throwing an exception,but since we can't modify the code we hope it will be fixed

@ronnymajani

This comment has been minimized.

Copy link

@ronnymajani ronnymajani commented Feb 26, 2019

If you're using multithreading or some setup where multiple BLE functions can be called in "parallel"
then what can happen is that the write() function will send a write command, and then wait to receive the write response "wr".
One possible problem is, if there's another thread that's also waiting for a response (like calling waitForNotifications()) then when that response 'wr' is received, it can mistakenly get passed to waitForNotifications instead, and that function isn't expecting such a response type and will throw an error.
One way to workaround this is to call write() with the withResponse argument set to True (.write(value, withResponse=True) ), which will queue the write request instead so you get everything in the right order? (not exactly sure what's going on in this case, I didn't bother to go deeper than this).

If you instead update the code to "ignore" the 'wr' response, then you could potentially block your code indefinitely.

@ronnymajani

This comment has been minimized.

Copy link

@ronnymajani ronnymajani commented Feb 26, 2019

Oh wait, no no ignore that I found the problem:
int the btle.py
the following code snippet has an indentation error
replace:

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(hnd, data)
                if respType not in wantType:  # [ERROR] indented one more level than it should be
                    continue  # [ERROR] this will never happen
            return resp

with:

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(hnd, data)
            if respType not in wantType:
                continue
            return resp

the part that's suppose to continue the loop if the expected response isn't received yet, is mistakenly indented one more tab, making it a redundant statement that never gets executed.
Just make that fix and all will be good.
I'm gonna submit a pull request soon.

@ronnymajani

This comment has been minimized.

Copy link

@ronnymajani ronnymajani commented Feb 26, 2019

Ok, well it seems that my first comment was more accurate,
the issue still presists, and it definitely seems to be a multithreading (non linear code execution, in your case the use of interrupts) issue where the 'wr' response is propagated to waitForNotifications() instead of .write().
Like I said, just set withResponse to True and you should be fine. The only downside is a slowdown due to using write with response.

edwios added a commit to edwios/bluepy that referenced this issue Mar 29, 2019
…d respType

Line 417, 418  if respType not in wantType: continue never got executed due to wrong tab used.
IanHarvey#253
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants
You can’t perform that action at this time.