Understanding NUS throughput using bt_gatt_write and bt_gatt_write_without_response

In my application I had been using the NUS service as a simple way of sending and receiving data between devices in a serial-like manner. One bottleneck I had hit was throughput, as I had only been getting a maximum of about 8 KBps (64 kbps), but in my case a command station needs to download up to 128MB of data which, as you could imagine, would take way too long.

I'm now trying to look at what part of this pipeline is failing, and as a first test I wanted to modify the `throughput` example to use NUS to see what I could get between two Nordic boards (nRF52-DK and nRF5340-DK). With the original (non-NUS) configuration, I can hit pretty close to the expected max, around 1300 kbps. Once I swap things out with the NUS clients, the max achievable is around 260 kbps using the smallest possible connection interval. The rate drops proportional to the increase in connection interval, seeming to indicate I can only send one NUS message per connection event. If I replace the `bt_nus_client_send` function with a direct call to `bt_gatt_write_without_response` I regain the 1300 kbps performance without seeing a strong dependence on connection interval (and very low intervals have slightly reduced throughput, as expected).

In trying to research this problem on this board, I've seen a number of conflicting statements on this matter, although many of the posts are pretty old so a lot could have changed, so I would like to check my knowledge on the expected behavior and understand whether or not it's matching the observed behavior (and, if not, why).

  • Is it expected that using `bt_gatt_write` will only allow one message sent per connection interval? I've seen conflicting statements, even at least one post claiming to see 1300 kbps using the NUS calls, but I haven't reproduced this. If this is not correct, then I'm confused where this dependence on the connection interval is coming from.
  • If it is expected, is 260 kbps about what you would expect for a max throughput? I'm confused why some posts claim a higher throughput is possible, I could be doing something wrong still or this might have been a change to the NUS interface (the post I'm referencing is 2 years old now).
  • Am I correct in understanding `bt_gatt_write_without_response` still receives acknowledgment at some level in the stack, ensuring delivery? It seems like that's the most common opinion from what I've read, so then I wonder if there's any disadvantage to using this call instead of `bt_nus_client_send`. Put another way, I wonder why NUS uses `bt_gatt_write` instead.
  • In either case (`write` or `write_without_response`) what happens if there's no acknowledgement? Does it try to resend the packet some set number of times, or just update an error counter somewhere?

I'm tempted to just make the minor modification of writing without response, but before I do I'd like to better understand the repercussions!

Related