This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

GATT client read request clarification

Expert 1340 points

I'm studying SWRU393 section 5.3.4.2.4 Read/Write Callback functions. It seems that if a client requests a read then the characteristic value is not immediately sent, but rather a callback such as simpleProfile_ReadAttrCB() is first called and the value of pValue and pLen must be set. Upon return of this callback the stack sends the value to the client.

1) Is this a correct interpretation?

2) What is the purpose of requiring us to set the pLen and pValue values, rather than just sending back what the existing characteristic values are?

3) Since I'm not setting the value of pLen and pValue, where was and how much memory was allocated to pValue, and how do I know that I won't exceed it?

  • Hi Tosa,

    You are not responsible for invoking this callback. The gatt server will trigger it when the client requests a read. The length of each attribute and pointer to it are maintained by the GATT server
  • I don't think your answer is correct (or please detail your response since I may not be understanding it correctly). The document says "Profile copies the value of the characteristic into the data pointer and returns this to GATTServApp".This means you need to set pLen and pValue, as shown below and in the simpleBLEPeripheral example.

    Please clarify. Thanks!

  • Correct the profile "copies the value of the characteristic into the data pointer and returns this to GATTServApp". Your interpretation is incorrect however. pValue and pLen are parameters that are passed into the callback when it is invoked by the GATT server.

    To answer #2 because your profile may want to implement a number of commands when the client requests a read
    to answer #3 you must set pValue and pLen in order for the callback mechanism to work properly
  • I guess I don't understand what simpleProfile_ReadAttrCB() is doing. What's the purpose of doing:

    case SIMPLEPROFILE_CHAR4_UUID:
            *pLen = 1;
            pValue[0] = *pAttr->pValue;
            break;

    and then exiting the function without any further processing (there are no further callbacks in the application as far as I can tell)? What data does stack send to the client; is it the value in pValue before or after this callback?

    Thanks!

  • Take a look at a comment above the callback pValue is the data to be sent to the client , pAttr is a pointer to the attribute in the attribute table. This callback allows the profile to add implementation specific (custom to the profile) processing to requests from the GATT server. Data in pValue is sent. As mentioned in the Software Developer's Guide, there could be further application callbacks added, it all depends on the end use case. Keep in mind that this profile is a minimal example of a "simple profile" much more complex use cases exist. Its all up to the application developer.
  • It's still not clear. If the following code was not put in simpleProfile_ReadAttrCB()

    case SIMPLEPROFILE_CHAR4_UUID:
            *pLen = 1;
            pValue[0] = *pAttr->pValue;
            break;

    then what would be sent to the client upon a read request for this characteristic? From my testing on the 2650DK, it seems that I need to set pValue and pLen otherwise the proper characteristic data is not transmitted to the client. This goes back to my original questions: how do I know how big I can make *pLen and how much memory is allocated to pValue?

  • Tosa,

    My apologies for not making it clear. If you look at the top of simpleGATTprofile.c you'll see that the profile is responsible for setting the size of pLen and pValue dependent on what data needs to be encapsulated in the service. Take a look at characteristic4

    // Characteristic 4 Value
    static uint8 simpleProfileChar4 = 0;

    It is a one byte value, which is why pLen is set equal to 1, and pValue is set to its location in the attribute table (more on this later)

    Now lets look at Characteristic 5

    // Characteristic 5 Value
    static uint8 simpleProfileChar5[SIMPLEPROFILE_CHAR5_LEN] = { 0, 0, 0, 0, 0 };

    Char5 is a 5 byte array, thus pLen is set accordingly in the callback, and pValue is set to point to the corresponding location in the attribute table.

    The attribute table is grouping of all the characteristics available in the profile, this is what is registered with the GATT server along with the Profile's callbacks (of which readAttrCB is part of) you can see this happen in SimpleProfile_AddService where the service is registered with the GATT server.

    Now the GATT server knows your attribute table and your profile's callbacks, thus it invokes the callback whenever a read/write is requested. This is where implementation specific code would go. Also notice in the attribute table that read/write/notify properties are defined for each entry in the table, readCBs will only be triggered on entries that have read or notify permissions.

  • The TL;DR version of the answer regarding pValue and pLen is that both are dependent on the data in the attribute table, and must be consistent with that in order for the proper data to be sent to the client.
  • So you're saying pValue is allocated the number of bytes as declared at the top such as

    // Characteristic 5 Value
    static uint8 simpleProfileChar5[SIMPLEPROFILE_CHAR5_LEN] = { 0, 0, 0, 0, 0 };

    So in simpleProfile_ReadAttrCB() I could set *pLen up to 5 and pValue is allocated 5 bytes? We could fill this buffer with other data rather than pAttr->pValue, but ideally we should return pAttr->pValue since the client expects data of 5 bytes? What does the parameter maxLen do? 

    Thanks!

  • You could write other data to pValue, but that is a bad idea. Section 5.3.4.2.4 of the Software Developer's guide explains that its is the job of the profile to copy the related characteristic data into pValue ( which is then passed to the GATT server, which passes this message down the BLE stack and eventually sends data OTA). As the comment above the function suggests, maxLen specifies the maximum amount of data to be read.
  • Okay, but it seems bizzare that we need to set pLen and pValue if the GATT server can already get these values...anyhow I'll mark your answer as accepted. Thanks for your responses :)