IanHarvey / bluepy

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

Cannot get waitForNotifications() work #314

Closed spachner closed 5 years ago

spachner commented 5 years ago

Hi,

I'am new to BLE and bluepy and playing around with my device which run a BLE demo (https://github.com/NordicSemiconductor/Android-nRF-Blinky).

I am happy to be able to read()/write() data from/to by BLE demo device which is great. Many thanks for the library. I also tried with the delegate to avoid calling read() in a loop. I just wonder why waitForNotifications() will not call my delegate at all despite I am able to receive data manually by read().

My question is: I am not sure whether my device is sending an indication or a notification. Will both types be received by waitForNotifications()?

regards

spachner

spachner commented 5 years ago

Hi again,

meanwhile I am sure that my device is sending notifications and no indications since the data of this characteristic is sent with type BLE_GATT_HVX_NOTIFICATION and not with BLE_GATT_HVX_INDICATION. Still wondering why waitForNotifications() is not triggering. Here is my code.

#!/usr/bin/env python3

from __future__ import print_function
import argparse
import binascii
import os
import sys
import time
import struct
from bluepy import btle

class MyDelegate(btle.DefaultDelegate):
    def __init__(self):
        btle.DefaultDelegate.__init__(self)
        print("handleNotification init")

    def handleNotification(self, cHandle, data):
        print("handleNotification handle 0x%04X, data %d" % (cHandle, data))

LED_ON  = bytes([0x01])
LED_OFF = bytes([0x00])
ble_bt840_eval_board_max = "f9:39:3a:5b:f8:1a"
print("Connecting to BLE device MAC: " + ble_bt840_eval_board_max)

per = btle.Peripheral("f9:39:3a:5b:f8:1a", btle.ADDR_TYPE_RANDOM)
svc = per.getServiceByUUID("00001523-1212-EFDE-1523-785FEABCD123") # service

print("connected")

led_characteristic    = svc.getCharacteristics("00001525-1212-EFDE-1523-785FEABCD123")[0] # LED
button_characteristic = svc.getCharacteristics("00001524-1212-EFDE-1523-785FEABCD123")[0] # button

button_handle = button_characteristic.getHandle()
per.withDelegate(MyDelegate())
print("button_handle 0x%X" % button_handle)
print("button_state %d" % int.from_bytes(button_characteristic.read(), byteorder='little'))
sec = 0
led = 0
while True:
    if per.waitForNotifications(0.5):
        print("Notification received")
        continue

    print("Waiting for notifications...")
    if led == 0:
        print("Tx LED ON")
        led_characteristic.write(LED_ON,  withResponse=False)
        print("button_state %d" % int.from_bytes(button_characteristic.read(), byteorder='little'))
    else:
        print("Tx LED OFF")
        led_characteristic.write(LED_OFF, withResponse=False)
        print("button_state %d" % int.from_bytes(button_characteristic.read(), byteorder='little'))
    led ^= 1
    sec += 1
    if sec >= 5:
        break

per.disconnect()
print("disconnected")

I appreciate any hint what I am doing wrong. Thanks

spachner

tautology0 commented 5 years ago

You need to set the CCCD for the characteristic to \x01\x00. The CCCD is normally at characteristic handle +1 (though not always)).

spachner commented 5 years ago

Hi tautology0,

what a great help, many thanks. Now it works:

Here is the working code for reference:

#!/usr/bin/env python3

from __future__ import print_function
import argparse
import binascii
import os
import sys
import time
import struct
from bluepy import btle
from binascii import hexlify

print("Hello")

class MyDelegate(btle.DefaultDelegate):
    def __init__(self, hndl):
        btle.DefaultDelegate.__init__(self)
        print("handleNotification init")
        self.hndl = hndl;

    def handleNotification(self, cHandle, data):
        if (cHandle == self.hndl):
            print("handleNotification handle 0x%04X, data %s" % (cHandle, str(hexlify(data))))
        else:
            print("handleNotification handle 0x%04X unknown" % (cHandle))

LED_ON  = bytes([0x01])
LED_OFF = bytes([0x00])

ble_bt840_eval_board_max = "f9:39:3a:5b:f8:1a"
print("Connecting to BLE device MAC: " + ble_bt840_eval_board_max)

per = btle.Peripheral("f9:39:3a:5b:f8:1a", btle.ADDR_TYPE_RANDOM)
svc = per.getServiceByUUID("00001523-1212-EFDE-1523-785FEABCD123") # service
print(svc)

print("connected")

led_characteristic    = svc.getCharacteristics("00001525-1212-EFDE-1523-785FEABCD123")[0] # LED
print(led_characteristic)
button_characteristic = svc.getCharacteristics("00001524-1212-EFDE-1523-785FEABCD123")[0] # button
print(button_characteristic)
button_handle = button_characteristic.getHandle()
print("button_handle 0x%X" % button_handle)

button_handle_cccd = button_characteristic.getHandle() + 1
print("button_handle_cccd 0x%X" % button_handle_cccd)
print(led_characteristic)
per.writeCharacteristic(button_handle_cccd, bytes([0x01, 0x00]))

per.withDelegate(MyDelegate(button_handle))

print("button_state %d" % int.from_bytes(button_characteristic.read(), byteorder='little'))

sec = 0
led = 0
while True:
    if per.waitForNotifications(0.5):
        #print("Notification received")
        continue

    #print("Waiting for notifications...")
    if led == 0:
        print("Tx LED ON")
        led_characteristic.write(LED_ON,  withResponse=False)
        #Poll button state
        #print("button_state %d" % int.from_bytes(button_characteristic.read(), byteorder='little'))
    else:
        print("Tx LED OFF")
        led_characteristic.write(LED_OFF, withResponse=False)
        #Poll button state
        #print("button_state %d" % int.from_bytes(button_characteristic.read(), byteorder='little'))
    led ^= 1
    sec += 1
    if sec >= 5:
        break

per.disconnect()
print("disconnected")
MY201314MY commented 5 years ago

It's really a great help for me. Now I can receive the notification sent by ESP32(Another project written by Arduino), but once I write the following code, some errors occurred when any notification arrives. p.withDelegate(MyDelegate(handle)) The object 'p' equals to your 'per'. And have you seen this situation? Only in this way it works: if p.waitForNotifications(0.01) == True: print(time.ctime()[11:11+8]) print("notification arrived:{0}".format(p.readCharacteristic(handle).decode())) else: print("no notification") Thank you very much.