h2zero / NimBLE-Arduino

A fork of the NimBLE library structured for compilation with Arduino, for use with ESP32, nRF5x.
https://h2zero.github.io/NimBLE-Arduino/
Apache License 2.0
703 stars 147 forks source link

can´t get Notification activated #340

Open ArcadeD3vil opened 2 years ago

ArcadeD3vil commented 2 years ago

Hi mates,

how to get the Notification activated?

doing this ... if(pChr->canNotify()) { if(!pChr->subscribe(true, notifyCB)) { ...

but nothing happened. when i use the nordic app and send "1" to the 2902 of the correspondant Char, the notification is activated succesfully. when i use neil kolbans ble part to write for the descriptor it also works. but i can´t get it handled by nimble.... isn´t it possble anymore? how can i get it solved?

thx & best !!

working on ESP32 Dev Mdule

h2zero commented 2 years ago

Sorry for your trouble. I think you may need to to enable write with response for this situation. Try adding true as the third parameter to the subscribe call.

Joshy-2010 commented 2 years ago

I sadly seem to have a similar problem. I am using the default "Nimble_client" example, only changing the UUID's to match my service. I am trying to connect to one of those small, generic selfie buttons and get a notification whenever the button is pressed. This is the service: NimBLEUUID HIDserviceUUID("00001812-0000-1000-8000-00805f9b34fb") and this the characteristic: NimBLEUUID HIDcharUUID ("00002A4D-0000-1000-8000-00805F9B34FB") With the standart ble library it works out of the box. Strangely, with nimble, it doesn't, even after adding true as the third parameter as suggest.

What does work, however, is just calling

pChr = pSvc->getCharacteristic(HIDcharUUID); .... if(pChr->canNotify()) { if(!pChr->subscribe(true, notifyCB)) {

2 times in a row. It will work the second time. Very strange indeed.

BTW: I just started rewriting my code for nimble today. My second device I want to connect to (nrf52) works without problems, even the notifications. It is amazing how much less heap memory is used! Amazing job!

h2zero commented 2 years ago

Could you enable debug logs and post them here when this happens?

Joshy-2010 commented 2 years ago

Well I didn't even know there is a debug log, but I will try. I only need to change a setting in the nimconfig.h file, right?

h2zero commented 2 years ago

In Arduino IDE you can set it in the tools menu, core debug level. In platformio you can set build_flags = -DCORE_DEBUG_LEVEL=5.

Joshy-2010 commented 2 years ago

Ok, got it now I think.

19:56:53.851 -> I NimBLEDevice: NimBle host synced. 19:56:53.851 -> D NimBLEDevice: Setting bonding: 0, mitm: 0, sc: 1 19:56:53.851 -> D NimBLEDevice: >> setPower: 7 (type: 11) 19:56:53.851 -> D NimBLEDevice: << setPower 19:56:53.851 -> D NimBLEScan: >> start: duration=0 19:56:53.851 -> D NimBLEScan: << start() 19:56:55.296 -> I NimBLEScan: New advertiser: eb:01:8a:81:f4:49 19:56:58.295 -> I NimBLEScan: Updated advertiser: eb:01:8a:81:f4:49 19:57:01.162 -> I NimBLEScan: New advertiser: ad:43:aa:e6:dd:4a 19:57:01.162 -> I NimBLEScan: Updated advertiser: ad:43:aa:e6:dd:4a 19:57:01.251 -> D NimBLEScan: >> stop() 19:57:01.251 -> Scan Ended 19:57:01.251 -> D NimBLEScan: << stop() 19:57:01.251 -> New client created 19:57:01.251 -> D NimBLEClient: >> connect(ad:43:aa:e6:dd:4a) 19:57:01.695 -> D NimBLEClient: Got Client event 19:57:01.695 -> I NimBLEClient: Connected event 19:57:01.772 -> D NimBLEClient: Got Client event 19:57:01.772 -> I NimBLEClient: mtu update event; conn_handle=0 mtu=248 19:57:01.772 -> I NimBLEClient: Connection established 19:57:01.772 -> D NimBLEClient: >> deleteServices 19:57:01.772 -> D NimBLEClient: << deleteServices 19:57:01.772 -> Connected 19:57:01.772 -> D NimBLEClient: << connect() 19:57:01.860 -> Connected to: ad:43:aa:e6:dd:4a 19:57:01.860 -> RSSI: D NimBLEClient: >> getRssi() 19:57:01.860 -> -34 19:57:01.927 -> D NimBLEClient: Got Client event 19:57:01.927 -> I NimBLEClient: Connection parameters updated. 19:57:01.948 -> D NimBLEClient: >> getService: uuid: 00001812-0000-1000-8000-00805f9b34fb 19:57:01.948 -> D NimBLEClient: >> retrieveServices 19:57:02.197 -> D NimBLEClient: Service Discovered >> status: 14 handle: -1 19:57:02.197 -> D NimBLEClient: << Service Discovered 19:57:02.197 -> D NimBLEClient: << retrieveServices 19:57:02.197 -> D NimBLEClient: >> retrieveServices 19:57:02.499 -> D NimBLEClient: Service Discovered >> status: 0 handle: 35 19:57:02.499 -> D NimBLERemoteService: >> NimBLERemoteService() 19:57:02.499 -> D NimBLERemoteService: << NimBLERemoteService(): 0x1812 19:57:02.768 -> D NimBLEClient: Service Discovered >> status: 14 handle: -1 19:57:02.801 -> D NimBLEClient: << Service Discovered 19:57:02.801 -> D NimBLEClient: << retrieveServices 19:57:02.801 -> Attempt #0 19:57:02.801 -> D NimBLERemoteService: >> getCharacteristic: uuid: 00002a4d-0000-1000-8000-00805f9b34fb 19:57:02.801 -> D NimBLERemoteService: >> retrieveCharacteristics() for service: 0x1812 19:57:03.370 -> D NimBLERemoteService: Characteristic Discovered >> status: 14 handle: -1 19:57:03.405 -> D NimBLERemoteService: << Characteristic Discovered 19:57:03.405 -> D NimBLERemoteService: << retrieveCharacteristics() 19:57:03.405 -> D NimBLERemoteService: >> retrieveCharacteristics() for service: 0x1812 19:57:03.673 -> D NimBLERemoteService: Characteristic Discovered >> status: 0 handle: 39 19:57:03.706 -> D NimBLERemoteCharacteristic: >> NimBLERemoteCharacteristic() 19:57:03.706 -> D NimBLERemoteCharacteristic: << NimBLERemoteCharacteristic(): 0x2a4d 19:57:03.706 -> D NimBLERemoteService: Characteristic Discovered >> status: 0 handle: 43 19:57:03.706 -> D NimBLERemoteCharacteristic: >> NimBLERemoteCharacteristic() 19:57:03.706 -> D NimBLERemoteCharacteristic: << NimBLERemoteCharacteristic(): 0x2a4d 19:57:03.706 -> D NimBLERemoteService: Characteristic Discovered >> status: 0 handle: 47 19:57:03.782 -> D NimBLERemoteCharacteristic: >> NimBLERemoteCharacteristic() 19:57:03.782 -> D NimBLERemoteCharacteristic: << NimBLERemoteCharacteristic(): 0x2a4d 19:57:03.782 -> D NimBLERemoteService: Characteristic Discovered >> status: 0 handle: 51 19:57:03.782 -> D NimBLERemoteCharacteristic: >> NimBLERemoteCharacteristic() 19:57:03.782 -> D NimBLERemoteCharacteristic: << NimBLERemoteCharacteristic(): 0x2a4d 19:57:03.972 -> D NimBLERemoteService: Characteristic Discovered >> status: 14 handle: -1 19:57:03.972 -> D NimBLERemoteService: << Characteristic Discovered 19:57:04.042 -> D NimBLERemoteService: << retrieveCharacteristics() 19:57:04.042 -> Attempt #1 19:57:04.042 -> D NimBLERemoteService: >> getCharacteristic: uuid: 00002a4d-0000-1000-8000-00805f9b34fb 19:57:04.042 -> D NimBLERemoteService: << getCharacteristic: found the characteristic with uuid: 00002a4d-0000-1000-8000-00805f9b34fb 19:57:04.042 -> Can notify 19:57:04.042 -> D NimBLERemoteCharacteristic: >> setNotify(): Characteristic: uuid: 0x2a4d, handle: 39 0x0027, props: 0x1a, 01 19:57:04.042 -> D NimBLERemoteCharacteristic: >> getDescriptor: uuid: 0x2902 19:57:04.042 -> D NimBLERemoteCharacteristic: >> retrieveDescriptors() for characteristic: 0x2a4d 19:57:04.279 -> D NimBLERemoteCharacteristic: Next Characteristic >> status: 0 handle: 43 19:57:04.580 -> D NimBLERemoteCharacteristic: Descriptor Discovered >> status: 0 handle: 40 19:57:04.580 -> D NimBLERemoteDescriptor: >> NimBLERemoteDescriptor() 19:57:04.580 -> D NimBLERemoteDescriptor: << NimBLERemoteDescriptor(): 0x2902 19:57:04.638 -> D NimBLERemoteCharacteristic: << Descriptor Discovered. status: 0 19:57:04.638 -> D NimBLERemoteCharacteristic: << retrieveDescriptors(): Found 1 descriptors. 19:57:04.638 -> D NimBLERemoteCharacteristic: << setNotify() 19:57:04.638 -> D NimBLERemoteDescriptor: >> Descriptor writeValue: Descriptor: uuid: 0x2902, handle: 40 19:57:04.880 -> I NimBLERemoteDescriptor: Write complete; status=0 conn_handle=0 19:57:04.880 -> D NimBLERemoteDescriptor: << Descriptor writeValue, rc: 0 19:57:04.940 -> SubscriDb CNimBLEClient: Got Client event 19:57:04.940 -> D NimBLEClient: Peer requesting to update connection parameters 19:57:04.940 -> D NimBLEClient: MinInterval: 6, MaxInterval: 9, Latency: 100, Timeout: 600 19:57:04.940 -> D NimBLEScan: >> start: duration=0 19:57:04.940 -> D NimBLEClient: Rejected peer params 19:57:04.940 -> D NimBLEScan: << start() 19:57:05.179 -> D NimBLEClient: Got Client event 19:57:05.179 -> D NimBLEClient: Peer requesting to update connection parameters 19:57:05.179 -> D NimBLEClient: MinInterval: 12, MaxInterval: 12, Latency: 30, Timeout: 400 19:57:05.179 -> D NimBLEClient: Rejected peer params 19:57:09.393 -> D NimBLEClient: Got Client event 19:57:09.393 -> D NimBLEClient: Notify Recieved for handle: 39 19:57:09.393 -> D NimBLEClient: checking service 0x1812 for handle: 39 19:57:09.393 -> D NimBLEClient: Got Notification for characteristic Characteristic: uuid: 0x2a4d, handle: 39 0x0027, props: 0x1a 19:57:09.393 -> Descriptor: uuid: 0x2902, handle: 40 19:57:09.393 -> D NimBLEClient: Invoking callback for notification on characteristic Characteristic: uuid: 0x2a4d, handle: 39 0x0027, props: 0x1a 19:57:09.490 -> Descriptor: uuid: 0x2902, handle: 40 19:57:09.490 -> Notification from ad:43:aa:e6:dd:4a: Service = 0x1812, Characteristic = 0x2a4d, Value =  19:57:09.490 -> D NimBLEClient: Got Client event 19:57:09.490 -> D NimBLEClient: Notify Recieved for handle: 39 19:57:09.490 -> D NimBLEClient: checking service 0x1812 for handle: 39 19:57:09.490 -> D NimBLEClient: Got Notification for characteristic Characteristic: uuid: 0x2a4d, handle: 39 0x0027, props: 0x1a 19:57:09.490 -> Descriptor: uuid: 0x2902, handle: 40 19:57:09.490 -> D NimBLEClient: Invoking callback for notification on characteristic Characteristic: uuid: 0x2a4d, handle: 39 0x0027, props: 0x1a 19:57:09.490 -> Descriptor: uuid: 0x2902, handle: 40 19:57:09.490 -> Notification from ad:43:aa:e6:dd:4a: Service = 0x1812, Characteristic = 0x2a4d, Value = 19:57:09.834 -> D NimBLEClient: Got Client event 19:57:09.834 -> D NimBLEClient: Notify Recieved for handle: 39 19:57:09.834 -> D NimBLEClient: checking service 0x1812 for handle: 39 19:57:09.834 -> D NimBLEClient: Got Notification for characteristic Characteristic: uuid: 0x2a4d, handle: 39 0x0027, props: 0x1a 19:57:09.929 -> Descriptor: uuid: 0x2902, handle: 40 19:57:09.929 -> D NimBLEClient: Invoking callback for notification on characteristic Characteristic: uuid: 0x2a4d, handle: 39 0x0027, props: 0x1a 19:57:09.929 -> Descriptor: uuid: 0x2902, handle: 40 19:57:09.929 -> Notification from ad:43:aa:e6:dd:4a: Service = 0x1812, Characteristic = 0x2a4d, Value =  19:57:09.929 -> D NimBLEClient: Got Client event 19:57:09.929 -> D NimBLEClient: Notify Recieved for handle: 39 19:57:09.929 -> D NimBLEClient: checking service 0x1812 for handle: 39 19:57:09.929 -> D NimBLEClient: Got Notification for characteristic Characteristic: uuid: 0x2a4d, handle: 39 0x0027, props: 0x1a 19:57:09.929 -> Descriptor: uuid: 0x2902, handle: 40 19:57:09.929 -> D NimBLEClient: Invoking callback for notification on characteristic Characteristic: uuid: 0x2a4d, handle: 39 0x0027, props: 0x1a 19:57:09.929 -> Descriptor: uuid: 0x2902, handle: 40 19:57:09.929 -> Notification from ad:43:aa:e6:dd:4a: Service = 0x1812, Characteristic = 0x2a4d, Value = 19:57:10.305 -> I NimBLEScan: New advertiser: eb:01:8a:81:f4:49 19:57:10.305 -> I NimBLEScan: Updated advertiser: eb:01:8a:81:f4:49 19:57:12.077 -> D NimBLEClient: Got Client event 19:57:12.077 -> I NimBLEClient: disconnect; reason=520, 19:57:12.077 -> ad:43:aa:e6:dd:4a Disconnected - Starting scan

h2zero commented 2 years ago

Thanks, the log is quite revealing, I'll have a look into this.

h2zero commented 2 years ago

@Joshy-2010 I think I can see the problem now. Since this device has multiple characteristics with the same UUID in the same service the first call to getCharacteristic is returning the last instance of that uuid (handle 51) which does not seem to have a CCCD. Then the second call to getCharacteristic returns the first instance of the uuid (handle 39), which does have a CCCD and you're able to subscribe to notifications.

I think to handle this situation and indexing method for the getCharacteristic function will need to be implemented.

Joshy-2010 commented 2 years ago

Yeah, that seems likely. Because when I used the nRf Connect App it also showed me this characteristic mutliple times. I didnt think much of it thought it was a bug with the app but this makes sense. For now, I am perfectly fine with calling the method twice, but I am glad that I could help you out. But why would a device have the same chracateristic twice or more? This surely is against the ble specification, isn't it?

h2zero commented 2 years ago

It's an acceptable but confusing practice. But if you consider that, say, an environmental service could have multiple temperature characteristics then it would make sense that that all use the temperature UUID, so they have to be distinguished by their handles instead.

kostuch commented 2 years ago

Hi, I have similar problem. But in my case, even trying to run many times code if(pChr->canNotify()) { if(!pChr->subscribe(true, notifyCB)) {

do not work.

My BLE device is like this: https://www.joom.com/en/products/1506935141901327344-132-1-26193-339693970 and it connect to phone or computer as a hid keyboard. Unfortunately it does not notify client built on ESP32C3 or ESP32. According to log, everything is fine(?), but I receive no notifications from device. I tried also to relax testing connection parameters, as there are messages "Rejected peer params". Even if I commented every condition in onConnParamsUpdateRequest procedure and got message "Accepted peer params", it didn't help. In log below I repeat 7 times notify, as device reports 7 handles of characteristic 0x2a4d

Debug log:

I NimBLEScan: New advertiser: be:0f:48:64:bb:de I NimBLEScan: Updated advertiser: be:0f:48:64:bb:de Advertised Device found: Name: SmartRemote, Address: be:0f:48:64:bb:de, appearance: 961, serviceUUID: 0x1812 Found Our Service D NimBLEScan: >> stop() Scan Ended D NimBLEScan: << stop() New client created D NimBLEClient: >> connect(be:0f:48:64:bb:de) D NimBLEClient: Got Client event BLE_GAP_EVENT_CONNECT I NimBLEClient: Connected event D NimBLEClient: Got Client event BLE_GAP_EVENT_MTU I NimBLEClient: mtu update event; conn_handle=1 mtu=64 I NimBLEClient: Connection established D NimBLEClient: >> deleteServices D NimBLEClient: << deleteServices Connected D NimBLEClient: << connect() Connected to: be:0f:48:64:bb:de RSSI: D NimBLEClient: >> getRssi() -57 D NimBLEClient: >> getService: uuid: 0x1812 D NimBLEClient: >> retrieveServices D NimBLEClient: Service Discovered >> status: 0 handle: 35 D NimBLERemoteService: >> NimBLERemoteService() D NimBLERemoteService: << NimBLERemoteService(): 0x1812 D NimBLEClient: Service Discovered >> status: 14 handle: -1 D NimBLEClient: << Service Discovered D NimBLEClient: << retrieveServices D NimBLERemoteService: >> getCharacteristic: uuid: 0x2a4d D NimBLERemoteService: >> retrieveCharacteristics() for service: 0x1812 D NimBLERemoteService: Characteristic Discovered >> status: 0 handle: 39 D NimBLERemoteCharacteristic: >> NimBLERemoteCharacteristic() D NimBLERemoteCharacteristic: << NimBLERemoteCharacteristic(): 0x2a4d D NimBLERemoteService: Characteristic Discovered >> status: 0 handle: 43 D NimBLERemoteCharacteristic: >> NimBLERemoteCharacteristic() D NimBLERemoteCharacteristic: << NimBLERemoteCharacteristic(): 0x2a4d D NimBLERemoteService: Characteristic Discovered >> status: 0 handle: 47 D NimBLERemoteCharacteristic: >> NimBLERemoteCharacteristic() D NimBLERemoteCharacteristic: << NimBLERemoteCharacteristic(): 0x2a4d D NimBLERemoteService: Characteristic Discovered >> status: 0 handle: 50 D NimBLERemoteCharacteristic: >> NimBLERemoteCharacteristic() D NimBLERemoteCharacteristic: << NimBLERemoteCharacteristic(): 0x2a4d D NimBLERemoteService: Characteristic Discovered >> status: 0 handle: 54 D NimBLERemoteCharacteristic: >> NimBLERemoteCharacteristic() D NimBLERemoteCharacteristic: << NimBLERemoteCharacteristic(): 0x2a4d D NimBLERemoteService: Characteristic Discovered >> status: 0 handle: 58 D NimBLERemoteCharacteristic: >> NimBLERemoteCharacteristic() D NimBLERemoteCharacteristic: << NimBLERemoteCharacteristic(): 0x2a4d D NimBLERemoteService: Characteristic Discovered >> status: 0 handle: 62 D NimBLERemoteCharacteristic: >> NimBLERemoteCharacteristic() D NimBLERemoteCharacteristic: << NimBLERemoteCharacteristic(): 0x2a4d D NimBLEClient: Got Client event BLE_GAP_EVENT_CONN_UPDATE I NimBLEClient: Connection parameters updated. D NimBLERemoteService: Characteristic Discovered >> status: 14 handle: -1 D NimBLERemoteService: << Characteristic Discovered D NimBLERemoteService: << retrieveCharacteristics() D NimBLERemoteCharacteristic: >> setNotify(): Characteristic: uuid: 0x2a4d, handle: 62 0x003e, props: 0x1a, 01 D NimBLERemoteCharacteristic: >> getDescriptor: uuid: 0x2902 D NimBLERemoteCharacteristic: >> retrieveDescriptors() for characteristic: 0x2a4d D NimBLERemoteCharacteristic: Next Characteristic >> status: 0 handle: 66 D NimBLERemoteCharacteristic: Descriptor Discovered >> status: 0 handle: 63 D NimBLERemoteDescriptor: >> NimBLERemoteDescriptor() D NimBLERemoteDescriptor: << NimBLERemoteDescriptor(): 0x2902 D NimBLERemoteCharacteristic: << Descriptor Discovered. status: 0 D NimBLERemoteCharacteristic: << retrieveDescriptors(): Found 1 descriptors. D NimBLERemoteCharacteristic: << setNotify() D NimBLERemoteDescriptor: >> Descriptor writeValue: Descriptor: uuid: 0x2902, handle: 63 I NimBLERemoteDescriptor: Write complete; status=0 conn_handle=1 D NimBLERemoteDescriptor: << Descriptor writeValue, rc: 0 D NimBLEClient: Got Client event BLE_GAP_EVENT_L2CAP_UPDATE_REQ D NimBLEClient: Peer requesting to update connection parameters D NimBLEClient: MinInterval: 44, MaxInterval: 132, Latency: 30, Timeout: 2000 Check parameters D NimBLEClient: Rejected peer params Notification registered D NimBLERemoteService: >> getCharacteristic: uuid: 0x2a4d D NimBLERemoteService: << getCharacteristic: found the characteristic with uuid: 0x2a4d D NimBLERemoteCharacteristic: >> setNotify(): Characteristic: uuid: 0x2a4d, handle: 39 0x0027, props: 0x1a, 01 D NimBLERemoteCharacteristic: >> getDescriptor: uuid: 0x2902 D NimBLERemoteCharacteristic: >> retrieveDescriptors() for characteristic: 0x2a4d D NimBLEClient: Got Client event BLE_GAP_EVENT_L2CAP_UPDATE_REQ D NimBLEClient: Peer requesting to update connection parameters D NimBLEClient: MinInterval: 12, MaxInterval: 44, Latency: 20, Timeout: 2000 Check parameters D NimBLEClient: Rejected peer params D NimBLERemoteCharacteristic: Next Characteristic >> status: 0 handle: 43 D NimBLEClient: Got Client event BLE_GAP_EVENT_L2CAP_UPDATE_REQ D NimBLEClient: Peer requesting to update connection parameters D NimBLEClient: MinInterval: 8, MaxInterval: 44, Latency: 10, Timeout: 2000 Check parameters D NimBLEClient: Rejected peer params D NimBLERemoteCharacteristic: Descriptor Discovered >> status: 0 handle: 40 D NimBLERemoteDescriptor: >> NimBLERemoteDescriptor() D NimBLERemoteDescriptor: << NimBLERemoteDescriptor(): 0x2902 D NimBLERemoteCharacteristic: << Descriptor Discovered. status: 0 D NimBLERemoteCharacteristic: << retrieveDescriptors(): Found 1 descriptors. D NimBLERemoteCharacteristic: << setNotify() D NimBLERemoteDescriptor: >> Descriptor writeValue: Descriptor: uuid: 0x2902, handle: 40 D NimBLEClient: Got Client event BLE_GAP_EVENT_L2CAP_UPDATE_REQ D NimBLEClient: Peer requesting to update connection parameters D NimBLEClient: MinInterval: 36, MaxInterval: 44, Latency: 0, Timeout: 2000 Check parameters D NimBLEClient: Rejected peer params I NimBLERemoteDescriptor: Write complete; status=0 conn_handle=1 D NimBLEClient: Got Client event BLE_GAP_EVENT_L2CAP_UPDATE_REQ D NimBLEClient: Peer requesting to update connection parameters D NimBLEClient: MinInterval: 36, MaxInterval: 44, Latency: 0, Timeout: 2000 ChD NimBLERemoteDescriptor: << Descriptor writeValue, rc: 0 eck parameters D NimBLEClient: Rejected peer params Notification registered D NimBLERemoteService: >> getCharacteristic: uuid: 0x2a4d D NimBLERemoteService: << getCharacteristic: found the characteristic with uuid: 0x2a4d D NimBLERemoteCharacteristic: >> setNotify(): Characteristic: uuid: 0x2a4d, handle: 39 0x0027, props: 0x1a Descriptor: uuid: 0x2902, handle: 40, 01 D NimBLERemoteCharacteristic: >> getDescriptor: uuid: 0x2902 D NimBLERemoteCharacteristic: << getDescriptor: found the descriptor with uuid: 0x2902 D NimBLERemoteCharacteristic: << setNotify() D NimBLERemoteDescriptor: >> Descriptor writeValue: Descriptor: uuid: 0x2902, handle: 40 D NimBLEClient: Got Client event BLE_GAP_EVENT_L2CAP_UPDATE_REQ D NimBLEClient: Peer requesting to update connection parameters D NimBLEClient: MinInterval: 12, MaxInterval: 12, Latency: 30, Timeout: 400 Check parameters D NimBLEClient: Rejected peer params I NimBLERemoteDescriptor: Write complete; status=0 conn_handle=1 D NimBLEClient: Got Client event BLE_GAP_EVENT_L2CAP_UPDATE_REQ D NimBLEClient: Peer requesting to update connection parameters D NimBLEClient: MinInterval: 44, MaxInterval: 132, Latency: 30, Timeout: 2000 ChD NimBLERemoteDescriptor: << Descriptor writeValue, rc: 0 eck parameters D NimBLEClient: Rejected peer params Notification registered D NimBLERemoteService: >> getCharacteristic: uuid: 0x2a4d D NimBLERemoteService: << getCharacteristic: found the characteristic with uuid: 0x2a4d D NimBLERemoteCharacteristic: >> setNotify(): Characteristic: uuid: 0x2a4d, handle: 39 0x0027, props: 0x1a Descriptor: uuid: 0x2902, handle: 40, 01 D NimBLERemoteCharacteristic: >> getDescriptor: uuid: 0x2902 D NimBLERemoteCharacteristic: << getDescriptor: found the descriptor with uuid: 0x2902 D NimBLERemoteCharacteristic: << setNotify() D NimBLERemoteDescriptor: >> Descriptor writeValue: Descriptor: uuid: 0x2902, handle: 40 D NimBLEClient: Got Client event BLE_GAP_EVENT_L2CAP_UPDATE_REQ D NimBLEClient: Peer requesting to update connection parameters D NimBLEClient: MinInterval: 12, MaxInterval: 44, Latency: 20, Timeout: 2000 Check parameters D NimBLEClient: Rejected peer params D NimBLEClient: Got Client event BLE_GAP_EVENT_L2CAP_UPDATE_REQ D NimBLEClient: Peer requesting to update connection parameters D NimBLEClient: MinInterval: 8, MaxInterval: 44, Latency: 10, Timeout: 2000 Check parameters D NimBLEClient: Rejected peer params I NimBLERemoteDescriptor: Write complete; status=0 conn_handle=1 D NimBLEClient: Got Client event BLE_GAP_EVENT_L2CAP_UPDATE_REQ D NimBLEClient: Peer requesting to update connection parameters D NimBLEClient: MinInterval: 8, MaxInterval: 44, Latency: 10, Timeout: 2000 ChD NimBLERemoteDescriptor: << Descriptor writeValue, rc: 0 eck parameters D NimBLEClient: Rejected peer params Notification registered D NimBLERemoteService: >> getCharacteristic: uuid: 0x2a4d D NimBLERemoteService: << getCharacteristic: found the characteristic with uuid: 0x2a4d D NimBLERemoteCharacteristic: >> setNotify(): Characteristic: uuid: 0x2a4d, handle: 39 0x0027, props: 0x1a Descriptor: uuid: 0x2902, handle: 40, 01 D NimBLERemoteCharacteristic: >> getDescriptor: uuid: 0x2902 D NimBLERemoteCharacteristic: << getDescriptor: found the descriptor with uuid: 0x2902 D NimBLERemoteCharacteristic: << setNotify() D NimBLERemoteDescriptor: >> Descriptor writeValue: Descriptor: uuid: 0x2902, handle: 40 D NimBLEClient: Got Client event BLE_GAP_EVENT_L2CAP_UPDATE_REQ D NimBLEClient: Peer requesting to update connection parameters D NimBLEClient: MinInterval: 36, MaxInterval: 44, Latency: 0, Timeout: 2000 Check parameters D NimBLEClient: Rejected peer params D NimBLEClient: Got Client event BLE_GAP_EVENT_L2CAP_UPDATE_REQ D NimBLEClient: Peer requesting to update connection parameters D NimBLEClient: MinInterval: 12, MaxInterval: 12, Latency: 30, Timeout: 400 Check parameters D NimBLEClient: Rejected peer params I NimBLERemoteDescriptor: Write complete; status=0 conn_handle=1 D NimBLEClient: Got Client event BLE_GAP_EVENT_L2CAP_UPDATE_REQ D NimBLEClient: Peer requesting to update connection parameters D NimBLEClient: MinInterval: 44, MaxInterval: 132, Latency: 30, Timeout: 2000 ChD NimBLERemoteDescriptor: << Descriptor writeValue, rc: 0 eck parameters D NimBLEClient: Rejected peer params Notification registered D NimBLERemoteService: >> getCharacteristic: uuid: 0x2a4d D NimBLERemoteService: << getCharacteristic: found the characteristic with uuid: 0x2a4d D NimBLERemoteCharacteristic: >> setNotify(): Characteristic: uuid: 0x2a4d, handle: 39 0x0027, props: 0x1a Descriptor: uuid: 0x2902, handle: 40, 01 D NimBLERemoteCharacteristic: >> getDescriptor: uuid: 0x2902 D NimBLERemoteCharacteristic: << getDescriptor: found the descriptor with uuid: 0x2902 D NimBLERemoteCharacteristic: << setNotify() D NimBLERemoteDescriptor: >> Descriptor writeValue: Descriptor: uuid: 0x2902, handle: 40 D NimBLEClient: Got Client event BLE_GAP_EVENT_L2CAP_UPDATE_REQ D NimBLEClient: Peer requesting to update connection parameters D NimBLEClient: MinInterval: 12, MaxInterval: 44, Latency: 20, Timeout: 2000 Check parameters D NimBLEClient: Rejected peer params D NimBLEClient: Got Client event BLE_GAP_EVENT_L2CAP_UPDATE_REQ D NimBLEClient: Peer requesting to update connection parameters D NimBLEClient: MinInterval: 8, MaxInterval: 44, Latency: 10, Timeout: 2000 Check parameters D NimBLEClient: Rejected peer params D NimBLEClient: Got Client event BLE_GAP_EVENT_L2CAP_UPDATE_REQ D NimBLEClient: Peer requesting to update connection parameters D NimBLEClient: MinInterval: 36, MaxInterval: 44, Latency: 0, Timeout: 2000 Check parameters D NimBLEClient: Rejected peer params I NimBLERemoteDescriptor: Write complete; status=0 conn_handle=1 D NimBLEClient: Got Client event BLE_GAP_EVENT_L2CAP_UPDATE_REQ D NimBLEClient: Peer requesting to update connection parameters D NimBLEClient: MinInterval: 36, MaxInterval: 44, Latency: 0, Timeout: 2000 ChD NimBLERemoteDescriptor: << Descriptor writeValue, rc: 0 eck parameters D NimBLEClient: Rejected peer params Notification registered D NimBLERemoteService: >> getCharacteristic: uuid: 0x2a4d D NimBLERemoteService: << getCharacteristic: found the characteristic with uuid: 0x2a4d D NimBLERemoteCharacteristic: >> setNotify(): Characteristic: uuid: 0x2a4d, handle: 39 0x0027, props: 0x1a Descriptor: uuid: 0x2902, handle: 40, 01 D NimBLERemoteCharacteristic: >> getDescriptor: uuid: 0x2902 D NimBLERemoteCharacteristic: << getDescriptor: found the descriptor with uuid: 0x2902 D NimBLERemoteCharacteristic: << setNotify() D NimBLERemoteDescriptor: >> Descriptor writeValue: Descriptor: uuid: 0x2902, handle: 40 D NimBLEClient: Got Client event BLE_GAP_EVENT_L2CAP_UPDATE_REQ D NimBLEClient: Peer requesting to update connection parameters D NimBLEClient: MinInterval: 12, MaxInterval: 12, Latency: 30, Timeout: 400 Check parameters D NimBLEClient: Rejected peer params I NimBLERemoteDescriptor: Write complete; status=0 conn_handle=1 D NimBLERemoteDescriptor: << Descriptor writeValue, rc: 0 Notification registered Done with this device! Success! we should now be getting notifications, scanning for more!

h2zero commented 2 years ago

If you connect with your phone using nRFConnect and do the same process does it work? Can you share a screenshot of this?

kostuch commented 2 years ago

Thank you for the quick response. nRFConnect shows similar information, but I can not manually force notification (in new android it just generate log "Exception occured. BLUETOOTH_PRIVILEGED permission required"). See the screenshoot. I have also wireshark log where I pair device with pc, press two buttons and disconnect. Attached at the end. Screenshot_20220912-093819_nRF Connect smartremote_pair_press.zip

h2zero commented 2 years ago

I pair device with pc

I suspect this is the issue, you need to pair (bond) with the device first.

kostuch commented 2 years ago

I pair device with pc

I suspect this is the issue, you need to pair (bond) with the device first.

It is about lacking android permission? No - it's currently by android design. It doesn't matter if device is bonded in advance or not, user has no privileges to raw access to BLE.

kostuch commented 2 years ago

Anyway, I narrowed down the problem (using gatttool on linux). NimBLE retrieve all characteristics 0x2a4d and then use last found descriptor 0x2902 to write for notification. In this case it is handle 63 (decimal)


D NimBLERemoteService: >> getCharacteristic: uuid: 0x2a4d
D NimBLERemoteService: >> retrieveCharacteristics() for service: 0x1812
D NimBLERemoteService: Characteristic Discovered >> status: 0 handle: 39
D NimBLERemoteCharacteristic: >> NimBLERemoteCharacteristic()
D NimBLERemoteCharacteristic: << NimBLERemoteCharacteristic(): 0x2a4d
D NimBLERemoteService: Characteristic Discovered >> status: 0 handle: 43
D NimBLERemoteCharacteristic: >> NimBLERemoteCharacteristic()
D NimBLERemoteCharacteristic: << NimBLERemoteCharacteristic(): 0x2a4d
D NimBLERemoteService: Characteristic Discovered >> status: 0 handle: 47
D NimBLERemoteCharacteristic: >> NimBLERemoteCharacteristic()
D NimBLERemoteCharacteristic: << NimBLERemoteCharacteristic(): 0x2a4d
D NimBLERemoteService: Characteristic Discovered >> status: 0 handle: 50
D NimBLERemoteCharacteristic: >> NimBLERemoteCharacteristic()
D NimBLERemoteCharacteristic: << NimBLERemoteCharacteristic(): 0x2a4d
D NimBLERemoteService: Characteristic Discovered >> status: 0 handle: 54
D NimBLERemoteCharacteristic: >> NimBLERemoteCharacteristic()
D NimBLERemoteCharacteristic: << NimBLERemoteCharacteristic(): 0x2a4d
D NimBLERemoteService: Characteristic Discovered >> status: 0 handle: 58
D NimBLERemoteCharacteristic: >> NimBLERemoteCharacteristic()
D NimBLERemoteCharacteristic: << NimBLERemoteCharacteristic(): 0x2a4d
D NimBLERemoteService: Characteristic Discovered >> status: 0 handle: 62
D NimBLERemoteCharacteristic: >> NimBLERemoteCharacteristic()
D NimBLERemoteCharacteristic: << NimBLERemoteCharacteristic(): 0x2a4d
D NimBLEClient: Got Client event BLE_GAP_EVENT_CONN_UPDATE
I NimBLEClient: Connection parameters updated.
D NimBLERemoteService: Characteristic Discovered >> status: 14 handle: -1
D NimBLERemoteService: << Characteristic Discovered
D NimBLERemoteService: << retrieveCharacteristics()
D NimBLERemoteCharacteristic: >> setNotify(): Characteristic: uuid: 0x2a4d, handle: 62 0x003e, props:  0x1a, 01
D NimBLERemoteCharacteristic: >> getDescriptor: uuid: 0x2902
D NimBLERemoteCharacteristic: >> retrieveDescriptors() for characteristic: 0x2a4d
D NimBLERemoteCharacteristic: Next Characteristic >> status: 0 handle: 66
D NimBLERemoteCharacteristic: Descriptor Discovered >> status: 0 handle: 63
D NimBLERemoteDescriptor: >> NimBLERemoteDescriptor()
D NimBLERemoteDescriptor: << NimBLERemoteDescriptor(): 0x2902
D NimBLERemoteCharacteristic: << Descriptor Discovered. status: 0
D NimBLERemoteCharacteristic: << retrieveDescriptors(): Found 1 descriptors.
D NimBLERemoteCharacteristic: << setNotify()
D NimBLERemoteDescriptor: >> Descriptor writeValue: Descriptor: uuid: 0x2902, handle: 63
I NimBLERemoteDescriptor: Write complete; status=0 conn_handle=1
D NimBLERemoteDescriptor: << Descriptor writeValue, rc: 0

Testing with gatttool:


gatttool -i hci0 -b BE:0F:48:64:BB:DE -I
[BE:0F:48:64:BB:DE][LE]> connect
Attempting to connect to BE:0F:48:64:BB:DE
Connection successful
[BE:0F:48:64:BB:DE][LE]> primary 1812
Starting handle: 0x0023 Ending handle: 0x0049
[BE:0F:48:64:BB:DE][LE]> characteristics 23 49
handle: 0x0024, char properties: 0x06, char value handle: 0x0025, uuid: 00002a4e-0000-1000-8000-00805f9b34fb
handle: 0x0026, char properties: 0x1a, char value handle: 0x0027, uuid: 00002a4d-0000-1000-8000-00805f9b34fb
handle: 0x002a, char properties: 0x1a, char value handle: 0x002b, uuid: 00002a4d-0000-1000-8000-00805f9b34fb
handle: 0x002e, char properties: 0x0e, char value handle: 0x002f, uuid: 00002a4d-0000-1000-8000-00805f9b34fb
handle: 0x0031, char properties: 0x1a, char value handle: 0x0032, uuid: 00002a4d-0000-1000-8000-00805f9b34fb
handle: 0x0035, char properties: 0x1a, char value handle: 0x0036, uuid: 00002a4d-0000-1000-8000-00805f9b34fb
handle: 0x0039, char properties: 0x1a, char value handle: 0x003a, uuid: 00002a4d-0000-1000-8000-00805f9b34fb
handle: 0x003d, char properties: 0x1a, char value handle: 0x003e, uuid: 00002a4d-0000-1000-8000-00805f9b34fb
handle: 0x0041, char properties: 0x02, char value handle: 0x0042, uuid: 00002a4b-0000-1000-8000-00805f9b34fb
handle: 0x0043, char properties: 0x1a, char value handle: 0x0044, uuid: 00002a33-0000-1000-8000-00805f9b34fb
handle: 0x0046, char properties: 0x02, char value handle: 0x0047, uuid: 00002a4a-0000-1000-8000-00805f9b34fb
handle: 0x0048, char properties: 0x04, char value handle: 0x0049, uuid: 00002a4c-0000-1000-8000-00805f9b34fb

Last 0x2a4d handle is 0x3e (62 decimal)


[BE:0F:48:64:BB:DE][LE]> char-read-uuid 2902
handle: 0x000b   value: 00 00 
handle: 0x0022   value: 00 00 
handle: 0x0028   value: 00 00 
handle: 0x002c   value: 00 00 
handle: 0x0033   value: 00 00 
[BE:0F:48:64:BB:DE][LE]> char-write-req 33 0100 <-- Other handles have no effect
Characteristic value was written successfully
Notification handle = 0x0032 value: 01 <-- button press
Notification handle = 0x0032 value: 00 <-- button release
Notification handle = 0x0032 value: 10 <-- other button
Notification handle = 0x0032 value: 00 
Notification handle = 0x0032 value: 02 
Notification handle = 0x0032 value: 00 
Notification handle = 0x0032 value: 04 
Notification handle = 0x0032 value: 00 
Notification handle = 0x0032 value: 08 
Notification handle = 0x0032 value: 00

In my test, only writing 0x2902 handle 0x33 (51 decimal) cause device to notify client. Writing to other handles has no effect. NimBLE uses just last 0x2902 handle 0x3e (63 decimal) to set notification. So, the question is: How to force NimBLE to set notification on specific handle?

h2zero commented 2 years ago

Thanks for the info. A modification to the code will be required to support this situation by adding an index to the get functions.

I have planned to add this in the future, just haven't had a chance to yet.

kostuch commented 2 years ago

Great! I suspect, this is issue with each hid device with multiple 0x2a4d characteristic (and multiple 0x2902 descriptors). Maybe it's better to write during iteration to write to each found 0x2902 ? Writing for notification/indication to "inactive" descriptor has no effect, so it's harmless. And the user usually don't know which handle should be used, to make notification active. At least it require linux commandline or lot try&error attempts. Waiting for the patch. Great lib. Thank you.

kostuch commented 1 year ago

Hi, In the meantime I managed to add some code to fulfill my needs (register notification on specific handle). As your lib seems to register notification/indication on the last found handle, I decided to just point the right handle as the last one where search should stop. Maybe this is not the most elegant way, but do not break existing code. Feel free to use it, if it's acceptable solution.

In NimBLERemoteService.h I added three declarations:

    NimBLERemoteCharacteristic*               getCharacteristic(const char* uuid, uint16_t ch_handle);
    NimBLERemoteCharacteristic*               getCharacteristic(const NimBLEUUID &uuid, uint16_t ch_handle);
    bool                retrieveCharacteristics(uint16_t ch_handle, const NimBLEUUID *uuid_filter = nullptr);

In NimBLERemoteService.cpp I added three functions which overload existing ones:


/**
 * @brief Get the remote characteristic object for the characteristic UUID.
 * @param [in] uuid Remote characteristic uuid.
 * @param [in] ch_handle Remote characteristic handle.
 * @return A pointer to the remote characteristic object.
 */
NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const char* uuid, uint16_t ch_handle) {
    return getCharacteristic(NimBLEUUID(uuid), ch_handle);
} // getCharacteristic

/**
 * @brief Get the characteristic object for the UUID.
 * @param [in] uuid Characteristic uuid.
 * @param [in] ch_handle Remote characteristic handle.
 * @return A pointer to the characteristic object, or nullptr if not found.
 */
NimBLERemoteCharacteristic* NimBLERemoteService::getCharacteristic(const NimBLEUUID &uuid, uint16_t ch_handle) {
    NIMBLE_LOGD(LOG_TAG, ">> getCharacteristic: uuid: %s handle: %d", uuid.toString().c_str(), ch_handle);

    for(auto &it: m_characteristicVector) {
        NIMBLE_LOGD(LOG_TAG, "!!! %d", it->getHandle());
                if((it->getUUID() == uuid) && (it->getHandle() == ch_handle)) {
            NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: found the characteristic with uuid: %s handle: %d", uuid.toString().c_str(), ch_handle);
            return it;
        }
    }

    size_t prev_size = m_characteristicVector.size();
    if(retrieveCharacteristics(ch_handle, &uuid)) {
        if(m_characteristicVector.size() > prev_size) {
            return m_characteristicVector.back();
        }

        // If the request was successful but 16/32 bit uuid not found
        // try again with the 128 bit uuid.
        if(uuid.bitSize() == BLE_UUID_TYPE_16 ||
           uuid.bitSize() == BLE_UUID_TYPE_32)
        {
            NimBLEUUID uuid128(uuid);
            uuid128.to128();
            if (retrieveCharacteristics(ch_handle, &uuid128)) {
                if(m_characteristicVector.size() > prev_size) {
                    return m_characteristicVector.back();
                }
            }
        } else {
            // If the request was successful but the 128 bit uuid not found
            // try again with the 16 bit uuid.
            NimBLEUUID uuid16(uuid);
            uuid16.to16();
            // if the uuid was 128 bit but not of the BLE base type this check will fail
            if (uuid16.bitSize() == BLE_UUID_TYPE_16) {
                if(retrieveCharacteristics(ch_handle, &uuid16)) {
                    if(m_characteristicVector.size() > prev_size) {
                        return m_characteristicVector.back();
                    }
                }
            }
        }
    }

    NIMBLE_LOGD(LOG_TAG, "<< getCharacteristic: not found");
    return nullptr;
} // getCharacteristic

bool NimBLERemoteService::retrieveCharacteristics(uint16_t ch_handle, const NimBLEUUID *uuid_filter) {
    NIMBLE_LOGD(LOG_TAG, ">> retrieveCharacteristics() for service: %s handle: %d", getUUID().toString().c_str(), ch_handle);

    int rc = 0;
    TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
    ble_task_data_t taskData = {this, cur_task, 0, nullptr};

    if(uuid_filter == nullptr) {
        rc = ble_gattc_disc_all_chrs(m_pClient->getConnId(),
                             m_startHandle,
                             ch_handle,
                             NimBLERemoteService::characteristicDiscCB,
                             &taskData);
    } else {
        rc = ble_gattc_disc_chrs_by_uuid(m_pClient->getConnId(),
                             m_startHandle,
                             ch_handle,
                             &uuid_filter->getNative()->u,
                             NimBLERemoteService::characteristicDiscCB,
                             &taskData);
    }

    if (rc != 0) {
        NIMBLE_LOGE(LOG_TAG, "ble_gattc_disc_all_chrs: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
        return false;
    }

#ifdef ulTaskNotifyValueClear
    // Clear the task notification value to ensure we block
    ulTaskNotifyValueClear(cur_task, ULONG_MAX);
#endif
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

    if(taskData.rc == 0){
        if (uuid_filter == nullptr) {
            if (m_characteristicVector.size() > 1) {
                for (auto it = m_characteristicVector.begin(); it != m_characteristicVector.end(); ++it ) {
                    auto nx = std::next(it, 1);
                    if (nx == m_characteristicVector.end()) {
                        break;
                    }
                    (*it)->m_endHandle = (*nx)->m_defHandle - 1;
                }
            }

            if (m_characteristicVector.size() > 0) {
                m_characteristicVector.back()->m_endHandle = getEndHandle();
            }
        }

        NIMBLE_LOGD(LOG_TAG, "<< retrieveCharacteristics()");
        return true;
    }

    NIMBLE_LOGE(LOG_TAG, "Could not retrieve characteristics");
    return false;

} // retrieveCharacteristics

So, in the application code I just use something like this (50 is a dec handle for characteristic to register notification):

pSvc = pClient->getService("1812");
    if (pSvc)
    {    
        pChr = pSvc->getCharacteristic("2a4d", 50);