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

S130 experimental example - discovery problem

I am trying to modify s130 experimental app example so that my board could connect to any peripheral and discover all of its services. As peripheral I use "ble_app_multilink" because it has custom UUID service.

Devices can connect, but problem occurs during service discovery. After discovery of standard ble services (1800, 1801) central should find custom service. Despite of it it finds "nothing" - UUID is 0, and change "start_handle" to 1. Then it discover all services again, this time finds custom service but also change start_handle to 1.

This is screen from s130 app console: image description

And this is "discovery" code (the part with HEART_RATE is useless, also I will change variable "service_found"), I took part of that code from here :

   /* Service discovery */
handle_range.start_handle = 0x0001;
handle_range.end_handle = BLE_GATTC_HANDLE_END;
do
{
    if ((error_code = sd_ble_gattc_primary_services_discover(gs_peripheral[peripheral_id].conn_handle, handle_range.start_handle, NULL)) != NRF_SUCCESS)
    {
        LOG_DEBUG("(Peripheral %i) Service discovery: error code 0x%4x", peripheral_id, error_code);
        return error_code;
    }
    if ((error_code = event_handle(BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP, 2000, gs_evt_buf, sizeof(gs_evt_buf))) != NRF_SUCCESS)
    {
        LOG_DEBUG("(Peripheral %i) service discovery response: error code 0x%4x", peripheral_id, error_code);
        return error_code;
    }
    if (gsp_ble_evt->evt.gattc_evt.gatt_status != BLE_GATT_STATUS_SUCCESS)
    {
        LOG_DEBUG("(Peripheral %i) Service discovery response: status code 0x%4x", peripheral_id, error_code);
        return NRF_ERROR_NOT_FOUND;
    }
			
			uint8_t services_number = gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.count;
			LOG_DEBUG("Number of services: %d", gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.count);
    for (i = 0; i < services_number; i++)
    {
				
				LOG_DEBUG("UUID type: 0x%x", gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[i].uuid.type);
				LOG_DEBUG("UUID value: 0x%16x", gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[i].uuid.uuid);
				LOG_DEBUG("i= %d", i);
				if (gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[i].uuid.type !=1) {
// Unknown 128-bit UUID => read handle again to get full answer.
					LOG_DEBUG("New unknown UUID found");
error_code = sd_ble_gattc_read(gs_peripheral[peripheral_id].conn_handle, gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[i].handle_range.start_handle, 0);
if (error_code != NRF_SUCCESS) {
		LOG_DEBUG("Error code: 0x%4x", error_code);
    return error_code;
}
error_code = event_handle(BLE_GATTC_EVT_READ_RSP,2000, gs_evt_buf, sizeof(gs_evt_buf));
if (error_code != NRF_SUCCESS) {
    return error_code;
}

// Response should contain full 128-bit UUID.
uint8_t * rsp_data    = (uint8_t *)gsp_ble_evt->evt.gattc_evt.params.read_rsp.data;
uint8_t rsp_data_len  = gsp_ble_evt->evt.gattc_evt.params.read_rsp.len;
if (rsp_data_len == 16) {
    // Mask 16-bit UUID part to zeros.
    rsp_data[12] = 0x00;
    rsp_data[13] = 0x00;
		
		ble_uuid128_t base_uuid;
		uint8_t uuid_type = gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[i].uuid.type;
		
		

    // Copy gathered 128bit UUID as future base.
    memcpy(base_uuid.uuid128, rsp_data, 16);
    error_code = sd_ble_uuid_vs_add((const ble_uuid128_t *)&base_uuid, &uuid_type);
    APP_ERROR_CHECK(error_code);
		
}

}

        if ((gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[i].uuid.uuid == HEART_RATE_SERVICE )
            && (gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[i].uuid.type == BLE_UUID_TYPE_BLE))
        {
            LOG_DEBUG("Service found UUID 0x%04X", gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[i].uuid.uuid);
            handle_range.start_handle = gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[i].handle_range.start_handle;
            if (gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.count > i + 1)
            {
                handle_range.end_handle = gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[i + 1].handle_range.start_handle - 1;
            }
            service_found = 1;
            break;
        }
    }
    if (service_found == 0)
    {		LOG_DEBUG("Start handle = 0x%4x", handle_range.start_handle);
					LOG_DEBUG("End handle = 0x%4x", gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.count-1 ].handle_range.end_handle);
        handle_range.start_handle = gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.count-1 ].handle_range.end_handle+0x1;
					LOG_DEBUG("Start handle = 0x%4x", handle_range.start_handle);
			}
} while ((service_found == 0) && (gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.count > 0));

if (service_found == 0)
{
    LOG_DEBUG("ISSUE (Peripheral %i) Service not found", peripheral_id);
    return NRF_ERROR_NOT_FOUND;
}

Does anyone know what is wrong here? Thank you for answers.

  • Hi Charlie329,

    Hard to spot particular mistake in your code but here is my service_dicovery function which works. Note that it has strict assumptions:

    • Only one proprietary 128-bit UUID base is ever provisioned through function to SD (obviously if you know it in advance then just hardcode it to some init function and big part of the code below is not needed).
    • You are looking for one particular Service and Characteristic with the same proprietary 128-bit UUID base (otherwise you need to modify the second stage of the function).
    • You are looking for Client Characteristic Configuration Descriptor (CCCD) attached to that proprietary Characteristic (otherwise delete or modify third stage of the discovery).
    • Service Discovery procedure is focused on these three items and as soon as these are found it exits. If you are looking for function which lists all GATT handles then you again need to customize outer loop.

    Modify the defines at the top to match your UUID values. Cheers Jan

    #define MY_SERVICE_UUID                         0x1234
    #define MY_CHARACTERISTIC_UUID                  0x5679
    #define CCCD_UUID                               BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG
    #define MY_SERVICE_UUID128_BASE_PREFIX          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    #define MY_SERVICE_UUID128_BASE_SUFIX           0x00, 0x00
    #define MY_SERVICE_UUID128                      MY_SERVICE_UUID128_BASE_PREFIX, (MY_SERVICE_UUID & 0x00FF), ((MY_SERVICE_UUID >> 8) & 0x00FF), MY_SERVICE_UUID128_BASE_SUFIX
    #define MY_SERVICE_UUID128_BASE                 MY_SERVICE_UUID128_BASE_PREFIX, 0x00, 0x00, MY_SERVICE_UUID128_BASE_SUFIX
    #define RTC1_DEFAULT_TIMEOUT                    2000
    
    static ble_uuid128_t m_uuid128base              = {{MY_SERVICE_UUID128_BASE}};
    static ble_uuid128_t m_my_service_uuid128       = {{MY_SERVICE_UUID128}};
    static uint32_t m_time_out                      = RTC1_DEFAULT_TIMEOUT;
    static uint16_t m_central_conn_handle           = BLE_CONN_HANDLE_INVALID;
    static uint16_t m_central_cccd_handle           = BLE_CONN_HANDLE_INVALID;
    static uint16_t m_central_char_handle           = BLE_CONN_HANDLE_INVALID;
    
    /**
     * @brief  Function discovers service, characteristic and descriptor at given connected peripheral.
     * @param  None.
     * @retval TBC.
     */
    static uint32_t service_discovery(void) {
        // Local variables.
        uint32_t                 err_code             = NRF_ERROR_NOT_FOUND;
        ble_gattc_handle_range_t handle_range         = {0};
        bool                     service_found        = false;
        bool                     characteristic_found = false;
        uint16_t                 index;
    
        LOG_INFO("INFO: Full Service Discovery (enumeration of stack on Peripheral/Server side over BLE GATT commands).");
    
        // Erase CCCD handle.
        m_central_cccd_handle = INVALID_DESCRIPTOR_HANDLE;
    
        // Initiate discovery procedure ("tree-like" graph).
        handle_range.start_handle = 0x0001;
        handle_range.end_handle = BLE_GATTC_HANDLE_END;
    
        // Service discovery.
        do {
            // Discover next primary Service.
            err_code = sd_ble_gattc_primary_services_discover(m_central_conn_handle, handle_range.start_handle, NULL);
            if (err_code != NRF_SUCCESS) {
                LOG_DEBUG("sd_ble_gattc_primary_services_discover returned error code 0x%04X", err_code);
                return err_code;
            }
            err_code = event_handle(BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP, m_time_out, gs_evt_buf, sizeof(gs_evt_buf));
            if (err_code != NRF_SUCCESS) {
                LOG_DEBUG("BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP not returned after sd_ble_gattc_primary_services_discover. Error code 0x%04X", err_code);
                return err_code;
            }
            LOG_DEBUG("Services found = %i", gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.count);
            if (gsp_ble_evt->evt.gattc_evt.gatt_status != BLE_GATT_STATUS_SUCCESS) {
                LOG_DEBUG("sd_ble_gattc_primary_services_discover returned gatt_status 0x%04X", gsp_ble_evt->evt.gattc_evt.gatt_status);
                return NRF_ERROR_NOT_FOUND;
            }
            // Go through all Services.
            for (index = 0; index < gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.count; index++) {
                // Log handle ranges for future use (in case this is our Service and we need to discover subsequent Characteristics).
                handle_range.start_handle = gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[index].handle_range.start_handle;
                handle_range.end_handle = gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[index].handle_range.end_handle;
                // We are interested only in one specific Service with 128-bit proprietary UUID
                // (assuming that we have only one such 128-bit UUID base registered in SoftDevice through "sd_ble_uuid_vs_add" call!).
                if ((gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[index].uuid.uuid == MY_SERVICE_UUID) &&
                    (gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[index].uuid.type == BLE_UUID_TYPE_VENDOR_BEGIN)) {
                    // My Service found.
                    LOG_DEBUG("My Service found, index = %d, UUID = 0x%04X, handle = 0x%04X", index,
                                                                                              gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[index].uuid.uuid,
                                                                                              handle_range.start_handle);
                    service_found = true;
                    break;
                }
                else if (gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[index].uuid.type == BLE_UUID_TYPE_UNKNOWN) {
                    // Unknown 128-bit UUID => read handle again to get full answer.
                    err_code = sd_ble_gattc_read(m_central_conn_handle, gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[index].handle_range.start_handle, 0);
                    if (err_code != NRF_SUCCESS) {
                        LOG_DEBUG("sd_ble_gattc_read returned error code 0x%04X", err_code);
                        return err_code;
                    }
                    err_code = event_handle(BLE_GATTC_EVT_READ_RSP, m_time_out, gs_evt_buf, sizeof(gs_evt_buf));
                    if (err_code != NRF_SUCCESS) {
                        LOG_DEBUG("BLE_GATTC_EVT_READ_RSP not returned after sd_ble_gattc_read. Error code 0x%04X", err_code);
                        return err_code;
                    }
                    // Response should contain full 128-bit UUID.
                    uint8_t * rsp_data    = (uint8_t *)gsp_ble_evt->evt.gattc_evt.params.read_rsp.data;
                    uint8_t rsp_data_len  = gsp_ble_evt->evt.gattc_evt.params.read_rsp.len;
                    if (rsp_data_len == 16) {
                        LOG_DEBUG("Custom Primary Service with 128-bit UUID found.");
                        LOG_DEBUG("128-bit UUID value = 0x%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X.", rsp_data[0],
                                                                                                                                             rsp_data[1],
                                                                                                                                             rsp_data[2],
                                                                                                                                             rsp_data[3],
                                                                                                                                             rsp_data[4],
                                                                                                                                             rsp_data[5],
                                                                                                                                             rsp_data[6],
                                                                                                                                             rsp_data[7],
                                                                                                                                             rsp_data[8],
                                                                                                                                             rsp_data[9],
                                                                                                                                             rsp_data[10],
                                                                                                                                             rsp_data[11],
                                                                                                                                             rsp_data[12],
                                                                                                                                             rsp_data[13],
                                                                                                                                             rsp_data[14],
                                                                                                                                             rsp_data[15]);
                        // Check if it is My proprietary 128-bit UUID.
                        if (memcmp(m_my_service_uuid128.uuid128, rsp_data, 16) == 0) {
                            // My Service found.
                            LOG_DEBUG("My Service found, index = %d, UUID = 0x%04X, handle = 0x%04X", index,
                                                                                                     (uint16_t)(rsp_data[12] + 256 * rsp_data[13]),
                                                                                                     handle_range.start_handle);
                            service_found = true;
    
                            // Mask 16-bit UUID part to zeros.
                            rsp_data[12] = 0x00;
                            rsp_data[13] = 0x00;
                            // Provision proprietary 16-byte UUID base.
                            LOG_DEBUG("Set My proprietary Service UUID base (full 128 bits).");
                            uint8_t uuid128base_type = BLE_UUID_TYPE_VENDOR_BEGIN;
                            memcpy(m_uuid128base.uuid128, rsp_data, 16);
                            err_code = sd_ble_uuid_vs_add((const ble_uuid128_t *)&m_uuid128base, &uuid128base_type);
                            APP_ERROR_CHECK(err_code);
                            LOG_DEBUG("sd_ble_uuid_vs_add passed.");
    
                            break;
                        }
                    }
                    else {
                        LOG_DEBUG("Ignored Service index = %d, BLE_GATTC_EVT_READ_RSP len = %d", index, rsp_data_len);
                    }
                }
                else {
                    LOG_DEBUG("Ignored Service index = %d, UUID = 0x%04X, UUID type = %d", index,
                                                                                           gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[index].uuid.uuid,
                                                                                           gsp_ble_evt->evt.gattc_evt.params.prim_srvc_disc_rsp.services[index].uuid.type);
                }
            }
            // If My Service not found => jump to next handle range.
            if (!service_found) {
                handle_range.start_handle = handle_range.end_handle;
                LOG_DEBUG("Scanned handle range ends at 0x%04X.", handle_range.start_handle);
                if (handle_range.start_handle != BLE_GATTC_HANDLE_END) {
                    // Go to next handle.
                    handle_range.start_handle++;
                    LOG_DEBUG("Scan continues.");
                }
            }
        }
        while ((!service_found) &&
               (handle_range.start_handle < BLE_GATTC_HANDLE_END));
    
        // Check if My Service was found.
        if (!service_found) {
            return NRF_ERROR_NOT_FOUND;
        }
    
        // Characteristics discovery.
        do {
            // Discover next Characteristic.
            err_code = sd_ble_gattc_characteristics_discover(m_central_conn_handle, &handle_range);
            if (err_code != NRF_SUCCESS) {
                LOG_DEBUG("sd_ble_gattc_characteristics_discover returned error code 0x%04X", err_code);
                return err_code;
            }
            err_code = event_handle(BLE_GATTC_EVT_CHAR_DISC_RSP, m_time_out, gs_evt_buf, sizeof(gs_evt_buf));
            if (err_code != NRF_SUCCESS) {
                LOG_DEBUG("BLE_GATTC_EVT_CHAR_DISC_RSP not returned after sd_ble_gattc_characteristics_discover. Error code 0x%04X", err_code);
                return err_code;
            }
            if (gsp_ble_evt->evt.gattc_evt.gatt_status != BLE_GATT_STATUS_SUCCESS) {
                LOG_DEBUG("sd_ble_gattc_characteristics_discover returned gatt_status 0x%04X", gsp_ble_evt->evt.gattc_evt.gatt_status);
                return NRF_ERROR_NOT_FOUND;
            }
            // Go through all Characteristics.
            for (index = 0; index < gsp_ble_evt->evt.gattc_evt.params.char_disc_rsp.count; index++) {
                // Log handle ranges for future use (in case this is My Characteristic and we need to discover subsequent Descriptors).
                m_central_char_handle = gsp_ble_evt->evt.gattc_evt.params.char_disc_rsp.chars[index].handle_value;
                handle_range.start_handle = m_central_char_handle + 1;
                // We are interested only into My proprietary Characteristic.
                if ((gsp_ble_evt->evt.gattc_evt.params.char_disc_rsp.chars[index].uuid.uuid == MY_CHARACTERISTIC_UUID) &&
                    (gsp_ble_evt->evt.gattc_evt.params.char_disc_rsp.chars[index].uuid.type == BLE_UUID_TYPE_VENDOR_BEGIN)) {
                    // Characteristic found.
                    LOG_DEBUG("M Characteristic found, index = %d, UUID = 0x%04X, handle = 0x%04X", index,
                                                                                                    gsp_ble_evt->evt.gattc_evt.params.char_disc_rsp.chars[index].uuid.uuid,
                                                                                                    m_central_char_handle);
                    characteristic_found = true;
                    break;
                }
                else {
                    LOG_DEBUG("Ignored Characteristic index = %d, UUID = 0x%04X, UUID type = %d", index,
                                                                                                  gsp_ble_evt->evt.gattc_evt.params.char_disc_rsp.chars[index].uuid.uuid,
                                                                                                  gsp_ble_evt->evt.gattc_evt.params.char_disc_rsp.chars[index].uuid.type);
                }
            }
            // If My Characteristic not found => jump to next.
            if (!characteristic_found) {
                handle_range.start_handle = gsp_ble_evt->evt.gattc_evt.params.char_disc_rsp.chars[index].handle_decl + 1;
                LOG_DEBUG("Characteristic discovery continues from handle 0x%04X", handle_range.start_handle);
            }
        }
        while ((!characteristic_found) &&
               (gsp_ble_evt->evt.gattc_evt.params.char_disc_rsp.count > 0));
    
        // Check if My Characteristic was found.
        if (!characteristic_found) {
            return NRF_ERROR_NOT_FOUND;
        }
    
        // Descriptor discovery.
        do {
            // Discover next Descriptor.
            err_code = sd_ble_gattc_descriptors_discover(m_central_conn_handle, &handle_range);
            if (err_code != NRF_SUCCESS) {
                LOG_DEBUG("sd_ble_gattc_descriptors_discover returned error code 0x%04X", err_code);
                return err_code;
            }
            err_code = event_handle(BLE_GATTC_EVT_DESC_DISC_RSP, m_time_out, gs_evt_buf, sizeof(gs_evt_buf));
            if (err_code != NRF_SUCCESS) {
                LOG_DEBUG("BLE_GATTC_EVT_DESC_DISC_RSP not returned after sd_ble_gattc_descriptors_discover. Error code 0x%04X", err_code);
                return err_code;
            }
            if (gsp_ble_evt->evt.gattc_evt.gatt_status != BLE_GATT_STATUS_SUCCESS) {
                LOG_DEBUG("sd_ble_gattc_descriptors_discover returned gatt_status 0x%04X", gsp_ble_evt->evt.gattc_evt.gatt_status);
                return NRF_ERROR_NOT_FOUND;
            }
            // Go through all Descriptors.
            for (index = 0; index < gsp_ble_evt->evt.gattc_evt.params.desc_disc_rsp.count; index++) {
                if ((gsp_ble_evt->evt.gattc_evt.params.desc_disc_rsp.descs[index].uuid.uuid == CCCD_UUID) &&
                    (gsp_ble_evt->evt.gattc_evt.params.desc_disc_rsp.descs[index].uuid.type == BLE_UUID_TYPE_BLE)) {
                    // CCCD handle found.
                    m_central_cccd_handle = gsp_ble_evt->evt.gattc_evt.params.desc_disc_rsp.descs[index].handle;
                    LOG_DEBUG("CCC Descriptor found, index = %d, UUID = 0x%04X, handle = 0x%04X", index,
                                                                                                  gsp_ble_evt->evt.gattc_evt.params.desc_disc_rsp.descs[index].uuid.uuid,
                                                                                                  m_central_cccd_handle);
                    break;
                }
                else {
                    LOG_DEBUG("Ignored Descriptor index = %d, UUID = 0x%04X, UUID type = %d", index,
                                                                                              gsp_ble_evt->evt.gattc_evt.params.desc_disc_rsp.descs[index].uuid.uuid,
                                                                                              gsp_ble_evt->evt.gattc_evt.params.desc_disc_rsp.descs[index].uuid.type);
                }
            }
            if (m_central_cccd_handle == BLE_CONN_HANDLE_INVALID) {
                handle_range.start_handle = gsp_ble_evt->evt.gattc_evt.params.desc_disc_rsp.descs[index].handle + 1;
            }
        }
        while ((m_central_cccd_handle == INVALID_DESCRIPTOR_HANDLE) &&
               (gsp_ble_evt->evt.gattc_evt.params.desc_disc_rsp.count > 0));
    
        if (m_central_cccd_handle == INVALID_DESCRIPTOR_HANDLE) {
            return NRF_ERROR_NOT_FOUND;
        }
    
        return err_code;
    }
    
  • Hi Charlies,

    The S130 experimental demo code is pretty old and not recommended.

    We provide several better example in ble_central folder in the SDK. Also we provide the ble_db_discovery.c library which can handle the service discovery task.

Related