This article will consider ATT_MTU equal to 23. See Part 1 for more details.
With respect to the limited size of ATT packets, there are a few problems that need to be taken in consideration when working with long attributes. A broad definition of a long attribute is an attribute whose value cannot be transmitted in a single ATT packet.
I said “broad definition” because it also depends on the type of ATT operation that involves the respective attribute. For example, in case a Client wants to read an attribute’s value, it will issue an ATT_READ_REQUEST containing the attribute’s handle, at which the Server will respond with an ATT_READ_RESPONSE packet. Such a packet will first contain the ATT packet type (1 byte, the code for the operation), then the remaining 22 will contain the actual attribute value. So here the maximum value size for an attribute in order to be able to send it in one packet is 22.
For writing an attribute, the Client would issue an ATT_WRITE_REQUEST to the Server. That packet also contains 1 byte for the operation code, but in addition to that is must specify the handle of the attribute to be written, which takes another 2 bytes. That in effect leaves only maximum 20 bytes for the actual attribute value.
Reading long attributes
When the client issues an ATT_READ_REQUEST, if the attribute is longer than 22 bytes long, the Server will only send the first 22 bytes in the response and act like nothing bad happened. That is, it does nothing more to inform the Client that this attribute is long.
At the other end, the Client has the responsibility to notice that it has received exactly 22 bytes of data, which is the maximum possible given the ATT_MTU. Upon noticing this, the Client has to consider the possibility that this attribute may be long (please note that it may not; it is quite normal for an attribute to have a value of exactly 22 bytes) and it should continue issuing ATT_READ_BLOB_REQUEST packets. This is a special type of a read request that specifies not only the attribute handle, but also the offset to read from.
For example, after an ATT_READ_RESPONSE with 22 bytes of data, the Client will issue an ATT_READ_BLOB_REQUEST with the same attribute handle, but with an offset of 22. The Server will then check the attribute’s value in the database and start sending bytes beginning with byte number 22 (first byte is numbered 0). The ATT_READ_BLOB_RESPONSE may also contain at most 22 bytes of attribute value (1 byte is the code), so in case the Client receives a full packet again, it should continue with another ATT_READ_BLOB_REQUEST with the offset 44. And so on, until it receives an ATT_READ_BLOB_RESPONSE with less than 22 bytes of data (which could be empty if the attribute’s value size is multiple of 22).
See Bluetooth Core 4.1 specification, vol. 3, section 3.4.4 for more information on this subject.
Continue reading: Part 3