petzval / btferret

Python and C Bluetooth Library
MIT License
127 stars 22 forks source link

Unable to get notify to work #43

Open tkrmnz opened 5 months ago

tkrmnz commented 5 months ago

I'm currently using a silabs BLE device emulating the SPP mode. SPP data is transmitted as notified packet with UUID FEC26EC46D7144429F8155BC21D658D6. I can see the data on Android BLE terminal app, RaspberryPi Gatttool and RaspberryPi bluez/btgatt-client example code. However, I cannot get the notify to work via Btferret and the btferret/le_client example code. I think the problem has to do with my understanding of the device.txt configuration of the device. Below is the capture of the BLE services using CySmart.

Reference sensor_1

Any help would be greatly appreciated.

petzval commented 4 months ago

If the LE device's handles are always the same, then this is the simplest implementation. Use the handle of the value (001D) then it is not necessary to run find_ctics() to find the handle. Just set the ADDRESS= of the LE device in the devices.txt info.

/**** devices.txt file - set ADDRESS *****/

DEVICE=My Pi  ..... local device info

DEVICE=BLE device TYPE=LE NODE=2  ADDRESS=11:22:33:44:55:66
  LECHAR=Data HANDLE=001D PERMIT=14 SIZE=3 UUID=FEC26EC46D7144429F8155BC21D658D6  ; index 0

/***** C code file ******/    

int notify_callback(int lenode,int cticn,unsigned char *buf,int nread);

int main()
  {    
  if(init_blue("devices.txt") == 0)
    return(0);

    // connect to node 2
  connect_node(2,CHANNEL_LE,0);

    // enable notifications for characteristic index 0
  notify_ctic(2,0,NOTIFY_ENABLE,notify_callback);

    // read notifications for 1 minute
  read_notify(60000); 

  disconnect_node(2);
  close_all();
  return(0);
  } 

int notify_callback(int lenode,int cticn,unsigned char *buf,int nread)
  { 
  // LE device lenode has sent notification of characteristic index cticn
  // data in buf[0] to buf[nread-1]

  printf("Notify %02X %02X %02X\n",buf[0],buf[1],buf[2]);

  return(0);
  }  
tkrmnz commented 4 months ago

Didn't work. The callback was never called. The Notificaton was never triggered. This is the same with btferret as well.

petzval commented 4 months ago

If notify_ctic is not returning an error, then notifications are being enabled, but the device must decide to send one - the client cannot request it. You can check if the BLE device is sending notifications as follows. The Bluetooth traffic will be saved to a file "monitor.txt". If notifications are being sent, there will be packets listed as:

> CHANNEL 0004 Opcode = 1B

1B is the opcode for a notification.

int main()
  {    
  if(init_blue("devices.txt") == 0)
    return(0);

  set_print_flag(PRINT_VERBOSE);

    // connect to node 2
  connect_node(2,CHANNEL_LE,0);

    // enable notifications for characteristic index 0
  notify_ctic(2,0,NOTIFY_ENABLE,notify_callback);

    // read notifications for 1 minute
  read_notify(60000); 

  output_file("monitor.txt");

  disconnect_node(2);
  close_all();
  return(0);
  } 
tkrmnz commented 4 months ago

Based on your recommendation, I added a CCC section to the devices.txt DEVICE=BLE device TYPE=LE NODE=2 ADDRESS=B4:3A:31:EB:4C:01 LECHAR=Data HANDLE=001D PERMIT=14 SIZE=3 UUID=FEC26EC46D7144429F8155BC21D658D6 ; index 0
LECHAR=CCC HANDLE=001E PERMIT=14 SIZE=2 UUID=2902 ; index 1

Also, for gattool this is what I have to do. sudo gatttool -i hci0 -b B4:3A:31:EB:4C:01 --char-write-req -a 0x001e -n 0100 --listen

However, a W into ctic 1 (0x001e) with 01 or 01 00 did not start the notification from the BLE device. BTW, I'm using btferret as a quick test. Any other recommendations?

petzval commented 4 months ago

That is exactly what notify_ctic does - it writes 01 00 to handle 001E. If this appears in monitor.txt without an error return, then notifications are being enabled. There are maybe two things to look at:

  1. The services list shows another enabled notification characteristic with handle 0021. Does this need to be enabled as well?
  2. Some BLE devices such as HID keyboards only work if paired with security as follows.
connect_node(2,CHANNEL_LE,0);
set_le_wait(5000);
le_pair(2,JUST_WORKS,0);

Otherwise, the solution should be apparent by using btmon to get a listing of the Bluetooth traffic when running the procedures that do generate notifications.

tkrmnz commented 4 months ago

This is the closest that I can get btferret btmon logs to match the working gatttool comman. Note there is a difference in the Attn:Write Request. Note, right after the Notiffy Enable was sent to the BLE device, BTFerret just sits there waiting from the BLE device to send data.

btmon_1b btferret_hcidump.log gatttool_hcidump.log

petzval commented 4 months ago

That's interesting. There are two types of write.

PERMIT   OPCODE
04          52             Write no acknowledge
08          12             Write with ack

Gatttool is using the acknowledge version. To replicate this, use your method of defining the notification as a separate characteristic, but with PERMIT=08. Then write 01 00 to it and do not call notify_ctic. Btferret will then use opcode 12.

LECHAR=CCC HANDLE=001E PERMIT=08 SIZE=2 UUID=2902 ; index 1

buf[0] = 1;
buf[1] = 0;
write_ctic(2,1,buf,2);
tkrmnz commented 4 months ago

Yup, that did the trick. Thank you very much.

Here is the working solution for others with the same issue. The UUID is what Silabs used to emulate serial terminal.

;DEVICE=BLE device TYPE=LE NODE=2 ADDRESS=B4:3A:31:EB:4C:01 ; LECHAR=Data HANDLE=001D PERMIT=14 SIZE=3 UUID=FEC26EC46D7144429F8155BC21D658D6 ; index 0
; LECHAR=CCC1 HANDLE=001E PERMIT=08 SIZE=2 UUID=2902 ; index 1

include "btlib.h"

int notify_callback(int lenode,int cticn,unsigned char *buf,int nread);

int main() {
unsigned char data[8];
data[0] = 0x01; data[1] = 0x00;

if(init_blue("devices.txt") == 0) return(0);

// connect to node 2

connect_node(2,CHANNEL_LE,0);

write_ctic(2,1,data,0);

// enable notifications for characteristic index 0

notify_ctic(2,0,NOTIFY_ENABLE,notify_callback);

// read notifications for 1 minute

read_notify(60000); output_file("monitor.txt");

disconnect_node(2); close_all(); return(0); }

int notify_callback(int lenode,int cticn,unsigned char *buf,int nread) { // LE device lenode has sent notification of characteristic index cticn // data in buf[0] to buf[nread-1]

printf("Notify %02X %02X %02X\n",buf[0],buf[1],buf[2]);

return(0); }

petzval commented 4 months ago

OK, good. Thanks for flagging this up. It's the first time I've seen notification enable work this way. I'll have to consider updates to the code.

petzval commented 4 months ago

I've uploaded a new version 16.1 of btlib.c to fix this problem. Now notify_ctic should work with your LE device.

tkrmnz commented 4 months ago

I can confirm that btlib.c v16.1 works with Silabs SPP demo without the write_ctic hack. Thank you.