I am trying to implement long writes on the gatt server side. My implementation is stack handled, so I am going for the "GATTS Queued Writes: Stack handled, one or more attributes require authorization" sequence. So far everything has been working fine, whenever I get BLE_EVT_USER_MEM_REQUEST I reply with a large memory block, and long writes from the client side has worked without an issue.
My query is regarding memory optimisation, as I would like to only allocate enough memory for a write to one attribute in the gatt server. In other words, for an attribute of length 20 bytes, I'll need to allocate only 20+X bytes of memory for a queued write to be successful. Our app supports variable length attributes, so the amount of memory given to sd_ble_user_mem_reply() will vary. So my question is, how do I calculate how much memory is needed to perform a long write on one attribute?
We're using softdevice S132 v3.0.0 on nRF52.
As you can see in the sequence chart you quoted, we receive BLE_EVT_USER_MEM_REQUEST before the peer finished with its write request. Meaning we don't know how many bytes will be written when we receive the event.
Our suggestion is to use the maximum size of the characteristic when you declare it.
Note that if you have more than one characteristic, you may need to allocate enough memory for all of them, but it's should be avoildable from the master side (don't write to more than one characteristic at the same time ).
Thanks for your quick response. So how much memory do I need for a characteristic of size 20 bytes? I tried allocating 20 bytes of memory on the gatt server, and when the ATT_MTU is set to 23 on both devices, I tried to send 20 bytes of data from the client in two chuncks of 10. The first one passed successfully, but on the second one the remote gatt client got the response BLE_GATTC_EVT_WRITE_RSP with status equal to 0x109, (Prepare Queue Full). So 20 bytes of memory does not seem to be enough. After some trial and error I found that the minimum memory needed was 34 bytes in order for this to work, but I don't know how much memory will be needed for larger attributes and why?
Sorry I forgot about the overhead. Yes there are header need to be counted. So it would be possible to send only 18 bytes payload when it comes to queued write (2 bytes for HandleID, 2 byte for offset, 2 byte for length). In this page you will find how the user memory should be parsed.
The table is for one single packet, if you have more than one packet for the queued write, the next packet will come right after.
In short if you need to send 20 bytes, the first packet requires 2 (handle) + 2 (offset) + 2 (length) + 18 (payload) = 24 bytes.
The next one require 6 (headers as first packet) + 2 (2 bytes payload left) = 8.
In total it's 32 bytes. But as you already figured out we need 34. This is because the last command "Execute write request" needs 2 bytes (one opcode one value).
So you need to count 2 extra byte in.
In shot, minimum the size of the buffer is 6xnumber of packet + payload size + 2.
There is an exception for the case that only one packet needed, I found the buffer size need to be more than 21 bytes.
Perfect. Thank you for the detailed response.