Skip to content
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

Enable bt_gatt_notify() to overwrite notified value before sending rather than queue values #28809

Closed
justinleavesley opened this issue Sep 30, 2020 · 6 comments
Assignees
Labels
area: Bluetooth Host area: Bluetooth Enhancement Changes/Updates/Additions to existing features

Comments

@justinleavesley
Copy link

Is your enhancement proposal related to a problem? Please describe.
It is difficult to use the current BLE notify mechanism in Zephyr to deliver to the client the most current possible data due to the queue behavior of the buffer for bt_gatt_notify() and the server not knowing exactly when a connection event is going to occur.

An example use case: The client subscribes to an attribute on the server with a connection interval of 50ms. The server has a high-speed sensor generating data every 1ms when active. The client does not know when the sensor will be active.
When the sensor is active, the client does not need data more often than every 50ms. However, when the connection event does occur, the client does require the received data to be less than 4ms old in terms of real-world sample time. Since the sensor runs at 1khz and the connection event should be short, this should be possible since the server data is only 1ms old.

Describe the solution you'd like
A bt_gatt_notify() mode that updates the value in the buffer, if one currently exists, instead of adding to the buffer or blocking. This will ensure the value sent in a notification is the latest data available to the server.

Describe alternatives you've considered

  1. client polls with read requests. This significantly increases the complexity of listening for data changes across large numbers of sensor devices for which notify is very good.
  2. Use the notify callback to ensure data only added to the notify queue when it is empty. This prevents buffered latency but still adds 1 connection internal of latency to the data (e.g. 50ms in the above example).
  3. Use the notify callback to try and estimate the likely next connection event time and sneak in the bt_gatt_notify() just before the connection event is expected. This is obviously not very robust.
  4. Get a reference to the underlying buffer and directly update it. Not obvious at all how to do this and might require a fork of the zephyr code which is not acceptable.

Additional context
I have asked if there is a way to do this using the discussion forum and had no response. I don't know if that means there isn't a way to do this or no one has seen the question.

@justinleavesley justinleavesley added the Enhancement Changes/Updates/Additions to existing features label Sep 30, 2020
@carlescufi
Copy link
Member

@pdunaj would you have any input for @justinleavesley, given the constraints of the software you've developed for Zephyr?

@pdunaj
Copy link
Collaborator

pdunaj commented Oct 2, 2020

@carlescufi The problem is true but we did not care so much as HID is polled every 1ms by USB or LLPM.

@justinleavesley is right and there is no way of updating the data once sent. I don't think what is requested is doable as data is copied to the lower layers so controller can use it as soon as connection interval occurs. (at least this is what I recall is happening - @joerchan can tell more).
If there is some data ready to be sent and you push more it lies in queue waiting for tx thread to pass it down the pipe once previous data is out. But this would not be a case when you have no data - I think data goes down the pipe straight away in such case. This would be needed so BLE has data ready to be sent at connection event.

It could be that reference to buffer is passed to stack instead of buffer with data to copy (I guess it would have to have space for header and footer for g/att stuff). But that would be huge change.

The solution would be to have a callback informing about starting of the connection event. Assuming there would be enough time to pass the data from application, down the pipe to LL for time for it to be sent. Note that LL has to know when it occurs anyway so it could ping application about it (but LL timing around this is critical so I expect it would be hard to do). @cvinayak

@justinleavesley
Copy link
Author

@carlescufi and @pdunaj thanks for looking into this.

I can understand that the notify data has been packaged up and even split across different packets at lower layers making this non-trivial.
Getting a callback when the connection event actually starts would be ideal if the timing could be made to work.

Is there currently a means to listen for the start of a connection event at the application layer or would this require an enhancement?

Alternatively, if it were possible to flush the notifications out of the buffer, then the application could flush before each notify. (could this be done right now by de-registering and re-register the service/characteristic?)

For the moment I will probably try and front-run the expected connection event using tx callbacks to estimate this at the application layer. That might actually be better than using the agreed connection interval because central may have many connections and be skipping connection intervals regularly in practice.

Also, I wonder if isochronous channels might be a better way to go for very low latency? I noticed initial support is in the feature set for the next release?

@joerchan
Copy link
Contributor

joerchan commented Oct 5, 2020

Alternatively, if it were possible to flush the notifications out of the buffer, then the application could flush before each notify. (could this be done right now by de-registering and re-register the service/characteristic?)

There is no means to flush, so I think the best is to simply keep one notification queued, and not send a new one before you receive the sent callback. De-registering wouldn't flush anything.

@carlescufi
Copy link
Member

@justinleavesley the BLE spec doesn't allow packets that have been sent from the Host to the controller to be flushed. So the only opportunity here would be to flush while they are still queued in the Host, but that would probably not solve your problem given that the Host is likely to have sent them to the controller already.

@joerchan
Copy link
Contributor

joerchan commented Feb 5, 2021

Also, I wonder if isochronous channels might be a better way to go for very low latency? I noticed initial support is in the feature set for the next release?

That is a possibility, but note that this is still work in progress.

Alternatives could include using the SoftDevice controller (in nRF Connect SDK), using radio notifications or low-latency packet mode (or both).

I will close this issue as there is consensus that we shouldn't add this feature since it would be against the bluetooth behavior in the protocol layers and wouldn't be possible without invasive changes in the protocols.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: Bluetooth Host area: Bluetooth Enhancement Changes/Updates/Additions to existing features
Projects
None yet
Development

No branches or pull requests

4 participants