This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

How does ble_gatts_evt_t correspond with any GATT model?

I think one way or the other the struct ble_gatts_evt_t must be correlated with the GATT profile, that is, ble_gatts_evt_t should have services and characteristics as its members that match the definition of what a GATT looks like (I used "looks like" here because a profile is customizable) yet, when I try to compare the two, I find they have much less in common than I thought.

Any expert opinion on this? Was I trying to compare two completely different things? Or GATT is just a small subset inside the ble_gatts_evt_t, thus the massive differences?

Parents
  • The ble_gatts_evt_t contains information for a "GATT Server" event. The ble_gatts_evt_t is usually nested within a ble_evt_t. There are many places that may be interested in "events" as a part of managing the connection and/or running a service.

    The BLE_STACK_HANDLER_INIT that configures the BLE stack is passed a pointer to a function that is expecting a ble_evt_t. When an event happens the function is called and can look at the ble_evt_t. Based on the ble_evt_t's header field's evt_id field it can determine the specific type of event that occurred. Often this function does some work with the event itslf and also passes it on to services/features that may also need to be notified about events. If the event is of interest, the relevant sub-structure (like ble_gatts_evt_t) can be evaluated.

    For example, the stack may be initialized with:

        BLE_STACK_HANDLER_INIT(NRF_CLOCK_LFCLKSRC_RC_250_PPM_8000MS_CALIBRATION,
                           BLE_L2CAP_MTU_DEF,
                           ble_evt_dispatch,
                           true);
    

    So when events happen the ble_evt_dispatch function will be called. It may look like:

    static void ble_evt_dispatch(ble_evt_t * p_ble_evt) { 
         // Pass the event to any services that need it. 
         my_service_ble_evt(p_ble_evt);
         ...
    

    And a service may have the called function, like:

    void my_service_ble_evt(ble_evt_t * p_ble_evt, ConfigData *configData) {
         if(p_ble_evt->header.evt_id == BLE_GATTS_EVT_WRITE) {
         // This is a GATTS write.  The p_ble_evt->gatts_evt (the ble_gatts_evt_t structure) can be used
         ...
    

    Summary: Events are communicated to handler functions (callbacks) via a structure. The handler functions need to interrogate and decode that structure to see if the event is of interest. If so, they often also look at other components of the event structure to get pertinent data.

    Update: After re-reading the question I realized the above may not have explained the aspect of importance.

    Based on the event of interest (from the ble_evt_t's header) a sub-struct of the ble_gatts_evt_t can be used. Some of sub-structs contain things like handles that can be used to identify the specific characteristic the event pertains to. So, for example, if the ble_evt_t's header's id indicates a BLE_GATTS_EVT_WRITE, then the ble_evt_t's gatts_evt field's write field can be accessed to get the handle. An individual service can check this handle to see if it needs to do anything special in response to the write.

Reply
  • The ble_gatts_evt_t contains information for a "GATT Server" event. The ble_gatts_evt_t is usually nested within a ble_evt_t. There are many places that may be interested in "events" as a part of managing the connection and/or running a service.

    The BLE_STACK_HANDLER_INIT that configures the BLE stack is passed a pointer to a function that is expecting a ble_evt_t. When an event happens the function is called and can look at the ble_evt_t. Based on the ble_evt_t's header field's evt_id field it can determine the specific type of event that occurred. Often this function does some work with the event itslf and also passes it on to services/features that may also need to be notified about events. If the event is of interest, the relevant sub-structure (like ble_gatts_evt_t) can be evaluated.

    For example, the stack may be initialized with:

        BLE_STACK_HANDLER_INIT(NRF_CLOCK_LFCLKSRC_RC_250_PPM_8000MS_CALIBRATION,
                           BLE_L2CAP_MTU_DEF,
                           ble_evt_dispatch,
                           true);
    

    So when events happen the ble_evt_dispatch function will be called. It may look like:

    static void ble_evt_dispatch(ble_evt_t * p_ble_evt) { 
         // Pass the event to any services that need it. 
         my_service_ble_evt(p_ble_evt);
         ...
    

    And a service may have the called function, like:

    void my_service_ble_evt(ble_evt_t * p_ble_evt, ConfigData *configData) {
         if(p_ble_evt->header.evt_id == BLE_GATTS_EVT_WRITE) {
         // This is a GATTS write.  The p_ble_evt->gatts_evt (the ble_gatts_evt_t structure) can be used
         ...
    

    Summary: Events are communicated to handler functions (callbacks) via a structure. The handler functions need to interrogate and decode that structure to see if the event is of interest. If so, they often also look at other components of the event structure to get pertinent data.

    Update: After re-reading the question I realized the above may not have explained the aspect of importance.

    Based on the event of interest (from the ble_evt_t's header) a sub-struct of the ble_gatts_evt_t can be used. Some of sub-structs contain things like handles that can be used to identify the specific characteristic the event pertains to. So, for example, if the ble_evt_t's header's id indicates a BLE_GATTS_EVT_WRITE, then the ble_evt_t's gatts_evt field's write field can be accessed to get the handle. An individual service can check this handle to see if it needs to do anything special in response to the write.

Children
  • Thank you! That was most helpful, you cleared a lot of things for me with that answer. Clearly on application layer, you don't need to worry everything about the stack model and there is no reason to in the first place.

    Now we are at it, how does ble_gatts_evt_t correspond with the ble_gatts_evt_t model in the lower stack layer? A pointer? A header file? A forced address conversion? Let me explain a bit, what you explained mainly dealt with application layer, and the struct ble_gatts_evt_t has some members in it that has certain handles application can read, but how do these handles and data files like handles get updated by the stack itself? Can you please help answer this one too? Thank you.

  • Correct me if I'm wrong, but I think your question really boils down to a combination of "how do I construct a service/characteristic" and "where do the handles come from". The sd_ble_gatts_service_add() function is used to create a service and it returns (via a pointer argument) the handle for the newly created service. Once you have that handle you can add characteristics to it via sd_ble_gatts_characteristic_add(), which is passed a pointer to a ble_gatts_char_handles_t structure. This ble_gatts_char_handles_t structure is initialized by sd_ble_gatts_characteristic_add() with the handles relevant to the characteristic. As you build services/characteristics the stack assigns them handles. When you receive events you can check the handles in the event to see if the corresponds to ones assigned when building the service.

Related