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?

  • 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

Related