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
698 stars 144 forks source link

How to force a attribute refresh with Service Changed Characteristic #279

Closed ln-12 closed 3 months ago

ln-12 commented 3 years ago

I am using your library to develop a bluetooth device which should connect to iOS and Android smartphones. When using iOS, I came across the topic attribute caching for which the following is suggested:

If the GAP name changes and you want this to be reflected in the .name property, you should 
employ the Service Changed Characteristic, so iOS will read the GAP name (among other things) 
again and update the .name property.

The name of my device can change, so I need to force any client to update the cached values before a connection is established (the device to connect to is determined by the device name). How would I do this with your library? Would it be enough to call void NimBLEServer::serviceChanged() if it was public? Thanks in advance!

h2zero commented 3 years ago

Interesting question, I haven't thought about this use case. You want to change the actual device name or just the advertised name? To change the device name what you propose is half way there, you would also need to call ble_svc_gap_device_name_set() first. Perhaps this could be an added member function in NimBLEServer.

ln-12 commented 3 years ago

I also never thought of it, until I just learned that attribute caching is a thing in BLE.

Well, I think the best way would be to keep the device advertisement name and the GAP device name in sync and I don't see why they should differ. For me it would also be enough if I could set a new name when NimBLE is intialized.

chegewara commented 3 years ago

I dont know how your code is designed, but there is 2 ways to set device name:

Service change is only required in first case.

Another thing about caching is the problem during development. It is highly advised to clean cache on smartphone every time you change code on device. If you dont do it you can have many issues related to secure connection, not refreshed services, characteristics UUIDs etc.

ln-12 commented 3 years ago

@chegewara For now my code is really simple as I simply use NimBLEDevice::init(). When I reboot the microcontroller with a different name set, it stays the same on the phone as mentioned. How would your suggestions look in example code?

I share your opinion on cleaning the cache, but as stated for example here, the only way to do so on iOS is to completely reset the device (= erase ALL data), which is not practical.

chegewara commented 3 years ago

There is no need to reset iOS, why would you have to do it.

I dont have iPhone in my hands right now, but there is option is BLE settings to remove ble device. Also you should be able to use nrf connect or similar app (maybe lightblue) to refresh services. Ive been working a bit with iOS, but i never had to do some strange things, even didnt have to restart iPhone. I dont really like iPhones but i dont think it is designed so lame to force user to erase data for such lame reason. Even on windows device name is stored, but it is enough to delete ble device, then search and add it again to see new name.

ln-12 commented 3 years ago

Of course, I can also use LightBlue to force a refresh. But out of the box there is no option in the operating system to do so. That's why a hard reset is the only way other than using external apps.

My concern is not how I would do it. I would rather like to have an approach where my bluetooth device forces an update of the cached data on the smartphone (btw, Android does the same thing as it is defined so in the bluetooth spec). The problem is that iOS won't give you the real mac of a bluetooth device but a random UUID. I would need the mac to identify the correct device to connect to. So my first thought was to use the name as unique identifier. I can't tell my customers "Hey, download this app and do that if you want to make it work again." if the name of that device ever changes. For now I use the manufacturer data field as workaround.

chegewara commented 3 years ago

Like i said, if name is set in advertising then i dont see reason why the name would not be refreshed. You dont have to use any app to see new name, just start new scan.

ln-12 commented 3 years ago

No, that's not the case. Even LightBlue shows me the old name until I manually connect to the device.

sivar2311 commented 3 years ago

Maybe it's the bonding? I suspect that the behaviour of already paired devices is different from unpaired devices.

chegewara commented 3 years ago

@ln-12 then your case is the one with name in 0x1800 service. I believe you should be able to force ble stack on iOS to refresh data, just lightblue does it, even without sending service changed characteristic. I am not going to tell you what should you do, because your question is correct and i asked espressif to add it in bluedroid, just that service is used for something different. It is usually used, which is rare, when during connection you add/delete characteristics or services.

h2zero commented 3 years ago

No, that's not the case. Even LightBlue shows me the old name until I manually connect to the device.

This is normal behavior, the device name shown on the phone will not change until you connect to the device because it needs to read it from the characteristic. For some reason phones seem to ignore the advertised name once the device has been connected to and instead caches the device name at this time and uses that for future display. Also in this situation the displayed name will still not change until reconnected as it will need to read the characteristic as well as to receive the service changed notification to force the cache clearing.

ln-12 commented 3 years ago

@h2zero Ok, that's exactly what I needed to know. Mh... I see why this was done but it's also not the best solution in my opinion. Well, so it seems like I have to stick with my workaround to use the manufacturer field to propagate a unique ID as this seems to be not cached.

Thanks to all for the help and clarification! I think we can close this issue then.

h2zero commented 3 years ago

It's still a good topic in the way that the device name should be changeable in the library. I will look to add a function for this in the future.

amplatzer commented 9 months ago

@h2zero Ok, that's exactly what I needed to know. Mh... I see why this was done but it's also not the best solution in my opinion. Well, so it seems like I have to stick with my workaround to use the manufacturer field to propagate a unique ID as this seems to be not cached.

Thanks to all for the help and clarification! I think we can close this issue then.

Hi, have you solved this issue after 2 years?

h2zero commented 3 months ago

The device name is changeable now.