-
Notifications
You must be signed in to change notification settings - Fork 138
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
sd: handle system attibutes on reconnect #86
base: dev
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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?
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? |
Characteristic attributes are standard in BLE spec, shall be possible to get and set them on other systems too. |
Well, yes. I've found my way around that other closed system's limitation I have no power over. If you want gory details, the other system is FrSky PARA wireless trainer system. |
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 :) |
ca3c1e0
to
e3b405e
Compare
571b1bb
to
d0b389c
Compare
54e7cab
to
d28e8fe
Compare
What is the current state of this PR? Were you able to implement this API on another system, like MacOS? |
Yeah, I believe, I've dropped the ball, sorry. Let me try and look at this again. |
So, I did my research and this is what I've found out.
From "Getting Started with Bluetooth Low Energy by Kevin Townsend, Carles Cufí, Akiba, Robert Davidson" Also, from NordicSemi forums:
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. |
// Store system attributes data | ||
dataLen := uint16(0) | ||
C.sd_ble_gatts_sys_attr_get(gapEvent.conn_handle, nil, &dataLen, 0) // get data length | ||
if int(dataLen) <= cap(DefaultAdapter.systemAttributes) { // we can not allocate here, so ensure at least data fits the buffer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aykevl I may need your help to solve this. We don't know in advance how large system attributes blob will be and we can't allocate here.
So far, I've seen sizes around 16 bytes, but I assume size depends on the number of characteristics published.
In this PR, I'm pre-allocating 64 bytes array and just skipping the storing logic if the blob is larger. I'm sure this can be handled better, but I don't know how.
The tricky question though how to know if the peer is bonded or not. Actually, there is also a case for Unknown Peer. In that case |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this implementation is entirely correct. System attributes are meant to be stored across application restarts in persistent storage (flash). See this diagram:
https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.s140.api.v6.0.0%2Fgroup___b_l_e___g_a_t_t_s___s_y_s___a_t_t_r_s___b_o_n_d_e_d___p_e_e_r___m_s_c.html
Therefore, I believe this needs a new API somehow. I'm not entirely sure how that API would look though, especially as these blobs are requested from interrupts.
Without checking, I suspect reading/writing these values might not actually need to happen from within an interrupt:
- I believe
sd_ble_gatts_sys_attr_set
does not need to be called from within theBLE_GATTS_EVT_SYS_ATTR_MISSING
event interrupt. As long as it is called reasonably soon after the event. - The documentation for
sd_ble_gatts_sys_attr_set
explicitly says that system attributes remain valid until the next device connects. But perhaps it's more reliable to read them from within the interrupt, store them in a global array, and then set a flag to call a callback outside the interrupt context (which copies the value to a new heap-allocated area to make sure it won't disappear or get overwritten).
...we don't have a mechanism to queue a callback outside interrupts, though.
One idea for how the API could look like is as a set of callbacks for reading/writing system attributes. If the callback isn't set, it will behave as it does today. This callback will be a no-op on Linux/macOS/Windows, which presumably store the system attributes in the system Bluetooth daemon.
dataLen := uint16(0) | ||
C.sd_ble_gatts_sys_attr_get(gapEvent.conn_handle, nil, &dataLen, 0) // get data length |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are doing a heap allocation here (dataLen
escapes to the heap). My suggestion is to make dataLen
a global.
if int(dataLen) <= cap(DefaultAdapter.systemAttributes) { // we can not allocate here, so ensure at least data fits the buffer | ||
DefaultAdapter.systemAttributes = DefaultAdapter.systemAttributes[:dataLen] | ||
C.sd_ble_gatts_sys_attr_get(gapEvent.conn_handle, &DefaultAdapter.systemAttributes[0], &dataLen, 0) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no need to call this function twice: you can set dataLen
to the length of the buffer and check the NRF_ERROR_DATA_SIZE
return value.
It is if we have information stored about that peer. System attributes are stored per peer, not globally. |
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.