8

I am writing a program using bluepy that listen for a characteristic sent by a bluetooth device. I can also use any library or language, the only constraint is to run on Linux and not in mobile environment (it seems is widely used only in mobile devices, no one use BLE with desktop). Using bluepy I register the delegate and after trying to register for notification calling write('\x01\x00') as described in the bluetooth rfc. But it doesn't work, any notification for the characteristic is received. Maybe I am wrong in writing the message for subscribing. Is there an error in the small snippet I wrote? Thank you so much.

class MyDelegate(btle.DefaultDelegate):

    def __init__(self, hndl):
        btle.DefaultDelegate.__init__(self)
   self.hndl=hndl;

   def handleNotification(self, cHandle, data):
   if (cHandle==self.hndl):
            val = binascii.b2a_hex(data)
            val = binascii.unhexlify(val)
            val = struct.unpack('f', val)[0]
            print str(val) + " deg C"


p = btle.Peripheral("xx:xx:xx:xx", "random")

try:
   srvs = (p.getServices());
   chs=srvs[2].getCharacteristics();
   ch=chs[1];
   print(str(ch)+str(ch.propertiesToString()));
   p.setDelegate(MyDelegate(ch.getHandle()));
   # Setup to turn notifications on, e.g.
   ch.write("\x01\x00");

   # Main loop --------
   while True:
      if p.waitForNotifications(1.0):
      continue

      print "Waiting..."
finally:
    p.disconnect();
Alexander.It
  • 187
  • 1
  • 1
  • 16

4 Answers4

6

I was struggling with this myself, and jgrant's comment really helped. I'd like to share my solution, if it could help anyone.

Note that I needed indication, hence the x02 rather than x01.

If it were possible to read the descriptors using bluepy, I would do that, but it doesn't seem to work (bluepy v 1.0.5). The method in the service class appears to be missing, and the method in the peripheral class gets stuck when I try to use it.

from bluepy import btle

class MyDelegate(btle.DefaultDelegate):
    def __init__(self):
        btle.DefaultDelegate.__init__(self)

    def handleNotification(self, cHandle, data):
        print("A notification was received: %s" %data)


p = btle.Peripheral(<MAC ADDRESS>, btle.ADDR_TYPE_RANDOM)
p.setDelegate( MyDelegate() )

# Setup to turn notifications on, e.g.
svc = p.getServiceByUUID( <UUID> )
ch = svc.getCharacteristics()[0]
print(ch.valHandle)

p.writeCharacteristic(ch.valHandle+1, "\x02\x00")

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

    print("Waiting...")
    # Perhaps do something else here
Uffe
  • 161
  • 1
  • 2
3

It looks like the problem is that you're trying to write \x01\x00 to the characteristic itself. You need to write it to the Client Characteristic Configuration descriptor that proceeds it (0x2902). The handle is likely 1 greater than the characteristic (but you may want to confirm by reading the descriptors).

ch=chs[1]
cccd = ch.valHandle + 1
cccd.write("\x01\x00")
jgrant
  • 1,273
  • 1
  • 14
  • 22
0

What was confusing for me was that in https://ianharvey.github.io/bluepy-doc/notifications.html the part that enabled the notifications was in comments, so it didn't look obligatory to me.

the bare minimum (given you know the MAC-adress already an you included everything and declared the Delegateclass) for me is

p1 = Peripheral(<MAC>)
ch1 = p1.getCharacteristics()[3]
p1.setDelegate(MyDelegate())
p1.writeCharacteristic(ch1.valHandle + 1, b"\x01\x00")

Note that I already knew I wanted to get notifications from characteristic#3. Also, without the 'b'-bytesprefix infront of "\x0\x00", it wouldn't work for me.

jkbs1337
  • 53
  • 6
0

bluepy classes docs and samples are crazy, and not complete. To get more details, just checkout bluepy source (it is not big and easy to read)

But, as starting point you can use this notifications code sample, working with from Heart Rate Service (tested on bluepy 1.3.0)

Don't forget to replace device MAC to your own in Peripheral!

from bluepy import btle
from bluepy.btle import AssignedNumbers

import binascii

class MyDelegate(btle.DefaultDelegate):
    def __init__(self, handle):
        btle.DefaultDelegate.__init__(self)
        self.handle = handle
        print "Created delegate for handle", self.handle
        # ... more initialise here

    def handleNotification(self, cHandle, data):
        if(cHandle == self.handle):
            print "handleNotification for handle: ", cHandle, "; Raw data: ", binascii.b2a_hex(data)
            #Found somewhere. Not tested is this working, but leave here as decode example
            #val = binascii.b2a_hex(data)
            #val = binascii.unhexlify(val)
            #val = struct.unpack('f', val)[0]
            #print str(val) + " deg C"

print "Connecting..."
dev = btle.Peripheral("c8:2b:96:a3:d4:76")

try:
    print "Device services list:"
    for svc in dev.services:
        print str(svc)


    HRService = dev.getServiceByUUID(AssignedNumbers.heartRate)
    print "HRService", HRService

    print "HRService characteristics list: "
    for char in HRService.getCharacteristics():
        print "HRService char[", char.getHandle(), "]: ", char

    HRMeasurementChar = HRService.getCharacteristics(AssignedNumbers.heart_rate_measurement)[0] #Notice! Check is characteristic found before usage in production code!
    print "HRMeasurementChar", HRMeasurementChar, HRMeasurementChar.propertiesToString();

    # Assign delegate to target characteristic
    dev.setDelegate(MyDelegate(HRMeasurementChar.getHandle()));

    # We need to write into org.bluetooth.descriptor.gatt.client_characteristic_configuration descriptor to enabe notifications
    # to do so, we must get this descriptor from characteristic first
    # more details you can find in bluepy source (def getDescriptors(self, forUUID=None, hndEnd=0xFFFF))
    desc = HRMeasurementChar.getDescriptors(AssignedNumbers.client_characteristic_configuration);
    print "desc", desc

    print "Writing \"notification\" flag to descriptor with handle: ", desc[0].handle
    dev.writeCharacteristic(desc[0].handle, b"\x01\x00")# Notice! Do not use [0] in production. Check is descriptor found first!

    print "Waiting for notifications..."

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

finally:
    dev.disconnect();
SyCoDeR
  • 23
  • 4