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

Why don't I get a BLE_GATTS_EVT_SYS_ATTR_MISSING event?

This discussion got mixed up with a discussion about what Nordic means by a 'system attribute' (which is still not clear). In any case, in that discussion I was trying to save pairing/bonding information so I could restore it on a reconnect. The pairing keys worked. I grabbed the keys on the pairing complete event, saved them to a file, read them from that file, and gave them to the peer when asked on a reconnect in the BLE_GAP_EVT_SEC_INFO_REQUEST event.

But the CCCD information doesn't work that way. I can't restore the CCCD state using the same approach, because I cannot initialize the CCCD myself from the application (even prior to a connection). Instead I was told I need to get the 'system attributes' by calling this method sd_ble_gatts_sys_attr_get(), save them to a file, and restore them by calling this method sd_ble_gatts_sys_attr_set() when I get a BLE_GATTS_EVT_SYS_ATTR_MISSING event.

Fantastic! Sounds easy enough. However, I never get a BLE_GATTS_EVT_SYS_ATTR_MISSING event. I get this event BLE_GAP_EVT_SEC_INFO_REQUEST to restore the keys, so that works. What is missing?

Parents
  • Hi,

    You will get the BLE_GATTS_EVT_SYS_ATTR_MISSING event if the peer requests a read on any of the system attributes.

    In the case where you send indications or notifications before the peer requests such a read, for instance you want to perform a sd_ble_gatts_service_changed(), you need to do the sd_ble_gatts_sys_attr_set() first.

    In our SDK, this is done for instance inside service_changed_send_in_evt() in gatt_cache_manager.c (part of Peer Manager), where the function local_db_apply_in_evt() calls gscm_local_db_cache_apply() in gatts_cache_manager.c, which eventually calls sd_ble_gatts_sys_attr_set().

    Regards,
    Terje

  • There are two independent issues happening here. First, I am using the pc-ble-driver. I make direct sd_* calls. I acutally find that easier in most cases than trying to figure out what the SDK is doing and porting it to the pc-ble-driver.

    In any case

    • issue #1 has to do with restoring the state of the CCCDs that were set by the collector in a previous connection. I was told to use the sd_ble_gatts_sys_attr_set() function in the BLE_GATTS_EVT_SYS_ATTR_MISSING. I saved the state from a sd_ble_gatts_sys_attr_get() function in the previous connection, and then on a reconnection waited for the BLE_GATTS_EVT_SYS_ATTR_MISSING event to restore it. It never happened. So what I have done instead is to ignore those instructions and just restore it in the connection event.

    • issue #2 is trying to do a service changed indication on a reconnect. No matter what I do for prep, the sd_ble_gatts_service_changed() fails, and it always fails in the same way. From start handles 0-13 I get invalid handle, and for every handle value above that I get a BLE_ERROR_SYS_ATTRS_MISSING error. This happens regardless of the number of get and set calls I do prior to making the service changed attempt. I see from the table returned from the 'get' call that there is only one entry and that handle is 13.
  • I need to add that this web site implementation has serious issues. I just posted something and I cannot edit it because the buttons at the bottom do not show up. This happens frequently. Sometimes a page refresh works or clicking on a reply button to another message where the buttons ARE visible, or existing and resigning in.

    Adding this extra reply made the buttons at the bottom of the 'real' message re-appear.

  • Hi,

    I just realized, in your code, in BleHeartRateMonitor.c, in ble_evt_dispatch(), line 507, it is currently:

    • else if (err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)

    and not, what it should be:

    • else if (err_code == BLE_ERROR_GATTS_SYS_ATTR_MISSING)

    Note the "!=" instead of "==".

    What this means is, you never got the BLE_ERROR_GATTS_SYS_ATTR_MISSING there whenever the log printout told you you did. Are you still, when checking from the peer device, not sending out the service changed indication?

    A sniffer trace could be a valuable tool for assessing what happens over the air at this moment, if still something is not working.

    Regards,
    Terje

  • Hi,

    I registered a ticket for this in our internal systems. Clearly a bug. If you observe other misbehavior from the system then please let me know and I will register that as well. Your feedback is highly appreciated.

    Regards,
    Terje

  • You are correct about my stupid if statement. I am reporting the wrong error. When I correct that I get (instead) NRF_ERROR_INVALID_STATE; operation not permitted in this state. That is also a problem because I want to invoke the service changed operation as soon as possible to be sure that the bonded client does not try and perform an operation that makes no sense. Anything the client tries to do (read, write, etc) except for service discovery will be done with (potentially) incorrect handle values.

    I will add that the client IS writing the CCCD of the service changed characteristic. I report it in the log here:

    Client Enabling CCCD of characteristic handle 13 with value 2 at time 2016

    Since I have no guaranteed way to get the handle value of the service changed characteristic I cannot print out a good log statement. But looking at the saved table values I see that 13 is the service changed handle.

  • Hi,

    For locating the CCCD of the Service Changed Characteristic, you can do as is done in the function service_changed_cccd() in gatt_cache_manager.c of SDK 16:

    /**@brief Function for getting the value of the CCCD for the service changed characteristic.
     *
     * @details This function will search all system handles consecutively.
     *
     * @param[in]  conn_handle  The connection to check.
     * @param[out] p_cccd       The CCCD value of the service changed characteristic for this link.
     *
     * @return Any error from @ref sd_ble_gatts_value_get or @ref sd_ble_gatts_attr_get.
     */
    static ret_code_t service_changed_cccd(uint16_t conn_handle, uint16_t * p_cccd)
    {
        bool       sc_found = false;
        uint16_t   end_handle;
    
        ret_code_t err_code = sd_ble_gatts_initial_user_handle_get(&end_handle);
        ASSERT(err_code == NRF_SUCCESS);
    
        for (uint16_t handle = 1; handle < end_handle; handle++)
        {
            ble_uuid_t uuid;
            ble_gatts_value_t value = {.p_value = (uint8_t *)&uuid.uuid, .len = 2, .offset = 0};
    
            err_code = sd_ble_gatts_attr_get(handle, &uuid, NULL);
            if (err_code != NRF_SUCCESS)
            {
                return err_code;
            }
            else if (!sc_found && (uuid.uuid == BLE_UUID_GATT_CHARACTERISTIC_SERVICE_CHANGED))
            {
                sc_found = true;
            }
            else if (sc_found && (uuid.uuid == BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG))
            {
                value.p_value = (uint8_t *)p_cccd;
                return sd_ble_gatts_value_get(conn_handle, handle, &value);
            }
        }
        return NRF_ERROR_NOT_FOUND;
    }

    When you get NRF_ERROR_INVALID_STATE, it may be because of a concurrent ATT_MTU exchange, or because the CCCD is not enabled.

    Regards,
    Terje

Reply
  • Hi,

    For locating the CCCD of the Service Changed Characteristic, you can do as is done in the function service_changed_cccd() in gatt_cache_manager.c of SDK 16:

    /**@brief Function for getting the value of the CCCD for the service changed characteristic.
     *
     * @details This function will search all system handles consecutively.
     *
     * @param[in]  conn_handle  The connection to check.
     * @param[out] p_cccd       The CCCD value of the service changed characteristic for this link.
     *
     * @return Any error from @ref sd_ble_gatts_value_get or @ref sd_ble_gatts_attr_get.
     */
    static ret_code_t service_changed_cccd(uint16_t conn_handle, uint16_t * p_cccd)
    {
        bool       sc_found = false;
        uint16_t   end_handle;
    
        ret_code_t err_code = sd_ble_gatts_initial_user_handle_get(&end_handle);
        ASSERT(err_code == NRF_SUCCESS);
    
        for (uint16_t handle = 1; handle < end_handle; handle++)
        {
            ble_uuid_t uuid;
            ble_gatts_value_t value = {.p_value = (uint8_t *)&uuid.uuid, .len = 2, .offset = 0};
    
            err_code = sd_ble_gatts_attr_get(handle, &uuid, NULL);
            if (err_code != NRF_SUCCESS)
            {
                return err_code;
            }
            else if (!sc_found && (uuid.uuid == BLE_UUID_GATT_CHARACTERISTIC_SERVICE_CHANGED))
            {
                sc_found = true;
            }
            else if (sc_found && (uuid.uuid == BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG))
            {
                value.p_value = (uint8_t *)p_cccd;
                return sd_ble_gatts_value_get(conn_handle, handle, &value);
            }
        }
        return NRF_ERROR_NOT_FOUND;
    }

    When you get NRF_ERROR_INVALID_STATE, it may be because of a concurrent ATT_MTU exchange, or because the CCCD is not enabled.

    Regards,
    Terje

Children
No Data
Related