bluetooth icon indicating copy to clipboard operation
bluetooth copied to clipboard

sd: handle system attibutes on reconnect

Open ysoldak opened this issue 4 years ago • 10 comments

This PR implements the logic of storing system attributes (think CCCD + checksum, but we treat the data as blob of unknown format) on disconnect and restoring them on BLE_GATTS_EVT_SYS_ATTR_MISSING event on subsequent re-connect.

This implements behaviour mandated by Bluetooth specification. See comment below.

~I use this functionality to automatically restore subscription state (CCCD=1) of a characteristic on subsequent re-connects.~

~Well, to tell the truth, I use this to fake "subscribed" state of a characteristic since I'm dealing with a controller that does implement BLE stack in a bit "special" way and never actually subscribes, nonetheless expecting notifications to start arriving.~

~The proper, expected way to use this functionality would be to call GetAttributes before controlled disconnect to save state and call SetAttributes before or directly after next connect to restore state.~

~Anyway, API is there, in SD headers and after this PR it shall be available to use it in go.~

ysoldak avatar Nov 10 '21 23:11 ysoldak

Well, to tell the truth, I use this to fake "subscribed" state of a characteristic since I'm dealing with a controller that does implement BLE stack in a bit "special" way and never actually subscribes, nonetheless expecting notifications to start arriving.

I'm also not entirely sure about the motivation here. So essentially you're using this API to hack around a firmware bug in another device?

aykevl avatar Nov 11 '21 00:11 aykevl

Hmm, I'm unsure about this. I don't really like to add an API specific to one underlying system. In this case, this looks very specific to the SoftDevice. Is there something similar for Linux (BlueZ) or MacOS to make one unified API?

Characteristic attributes are standard in BLE spec, shall be possible to get and set them on other systems too.

ysoldak avatar Nov 11 '21 00:11 ysoldak

So essentially you're using this API to hack around a firmware bug in another device?

Well, yes. I've found my way around that other closed system's limitation I have no power over. The API is were, not exposed. This PR makes it avaliable. Hopefully others will find more noble uses of it.

If you want gory details, the other system is FrSky PARA wireless trainer system. FrSky RC radios run on OpenTX but the bluetooth stack is closed source and hidden.

ysoldak avatar Nov 11 '21 00:11 ysoldak

Characteristic attributes are standard in BLE spec, shall be possible to get and set them on other systems too.

To make sure this API is indeed portable, can you also implement it on some other system? For example, Linux BlueZ or on macOS. (If you have a Linux/macOS system with Bluetooth, that is. Note that many Raspberry Pis also have BLE).

aykevl avatar Nov 11 '21 00:11 aykevl

Characteristic attributes are standard in BLE spec, shall be possible to get and set them on other systems too.

To make sure this API is indeed portable, can you also implement it on some other system? For example, Linux BlueZ or on macOS. (If you have a Linux/macOS system with Bluetooth, that is. Note that many Raspberry Pis also have BLE).

OK, I'll try and implement this for darwin at least. My MacBook does have bluetooth :)

ysoldak avatar Nov 11 '21 00:11 ysoldak

What is the current state of this PR? Were you able to implement this API on another system, like MacOS?

aykevl avatar Apr 27 '23 01:04 aykevl

Yeah, I believe, I've dropped the ball, sorry. Let me try and look at this again. This is a feature I use in my project and have to keep a branch in my fork just for that, and that's annoying.

ysoldak avatar Apr 27 '23 08:04 ysoldak

So, I did my research and this is what I've found out.

Additionally, CCCDs have two special properties that separate them from other attributes:

Their values are unique per connection In multi-connection scenarios, in which a central is connected to multiple peripherals and also acting as a GATT server, each peripheral will receive its own copy of the CCCD's value when reading it with ATT.

Their values are preserved across connections with bonded devices Attribute Caching discusses attribute caching in more detail, but that concerns only attribute handles. Values are typically not stored per-device and the GATT server can reset them between connections. This is not the case with CCCDs among bonded devices: the last value written by a client to a CCCD on the server is guaranteed to be restored upon reconnection, regardless of the time elapsed between connections.

From "Getting Started with Bluetooth Low Energy by Kevin Townsend, Carles Cufí, Akiba, Robert Davidson"

Also, from NordicSemi forums:

By "system attributes" we primarily mean the Client Characteristic Configuration Descriptors (CCCD), i.e. attributes that are handled by the SoftDevice and have special requirements. It is mandated by Bluetooth specification that CCCD values are altered only by the peer, so you cannot change them from the application. Since CCCD values for a bonded peer should be stored between connections, an API exist for the application to store the values after disconnection, and to restore them on connection.

While you are in a connection, the SoftDevice will handle the CCCD as appropriate. When the peer writes to CCCD, that will be handled by SoftDevice.

The only thing related to the CCCD that you have to do from the application, is to store the data you get from sd_ble_gatts_sys_attr_get() on disconnection, and provide it back to the SoftDevice with sd_ble_gatts_sys_attr_set() on the BLE_GATTS_EVT_SYS_ATTR_MISSING event. You should treat the data as a blob of data of unknown format, i.e. you should store it as is and provide it back as is, without changing it. You should not try to write to the CCCD directly from the application.

https://devzone.nordicsemi.com/f/nordic-q-a/53548/what-is-a-system-attribute/217385


See also GATTS System Attributes Handling: Bonded Peer


This PR implements the logic of storing system attributes (think CCCD + checksum, but we treat the data as blob of unknown format) on disconnect and restoring them on SYS_ATTR_MISSING event.

System attributes is a unique thing for SoftDevice, no such thing exist for other targets. I don't really know how other implementations satisfy CCCD state restore on reconnect, probably it is hidden/abstracted from application layer.

No new API introduced, everything is internal.


In my project, to hack the subscription state, I'm calling respective SoftDevice function directly via CGo. No extra API needed from bluetooth package.

ysoldak avatar May 01 '23 12:05 ysoldak

The tricky question though how to know if the peer is bonded or not. This PR implements the logic for bonded peer (i.e. any peer deemed bonded).

Actually, there is also a case for Unknown Peer. In that case nil shall be sent back.

ysoldak avatar May 01 '23 12:05 ysoldak

The tricky question though how to know if the peer is bonded or not.

It is if we have information stored about that peer. System attributes are stored per peer, not globally.

aykevl avatar May 01 '23 13:05 aykevl