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

Service Discovery Failed using pc-ble-driver

I am using an nRF52840 Dongle to try and connect to a product over Bluetooth and communicate with it. I have been able to open this dongle using nRF Connect Bluetooth Low Energy, and can successfully find my product, connect to it, and write to its characteristics. I am now needing to use a C program to do this same thing.

I have downloaded the pc-ble-driver, created a project in my C IDE, and copied the code from the heart rate collector example to be the project's source file. I got the project to build without issue, and following the instructions in the examples github page, have flashed the dongle with the correct firmware to allow the c program to connect to it. I am able to successfully connect to the dongle's COM port and scan for devices.

I did have to make some modifications to the source file. My product I want to connect to doesn't advertise its name, so I was unable to connect to it even when finding it. This product does, however, advertise the Manufacturer Specific Data field, and after some trial and error, I was able to modify the find_adv_name routine to instead check for Manufacturer Specific Data, and I was able to connect to the device this way.

After this, I get output strings that read: "Connection Established", "Discovering Primary Services", and "Received Service Discovery Response". However, I then get an error message saying "Service Discovery Failed. Error Code 0x10A". Stepping through the code, this error is getting set after the "on_connected" routine is finished, but before starting the on_service_discovery_response routine.

I have changed the value of BLE_UUID_HEART_RATE_SERVICE to match the UUID of my product's service, but there was no effect. I had also been told in the past to try changing the srvc_uuid.type from BLE_UUID_TYPE_BLE to BLE_UUID_TYPE_VENDOR_BEGIN, but when doing this, I get a failure returned from the function "sd_ble_gattc_primary_services_discover", with the error code 0x8005, which seemingly corresponds to NRF_ERROR_SD_RPC_NO_RESPONSE. 

Could anyone give me any pointers for where to look for what all to change in the source code in order to connect to my product rather than whatever heart rate monitor it is expecting? Thanks!

Parents
  • Ok, so I think I may have been getting the 0x8005 error due to stopping the program with a breakpoint, and the BLE device losing connectivity when stepping through slowly. I moved the breakpoint to right at the printing of the error message, and now my error code is consistently 0x7, which (according to nrf_error.h) should be an invalid parameter error.

    Considering this does not result in an error when I have srvc_uuid.type set to BLE_UUID_TYPE_BLE, I am assuming the invalid parameter is my srvc_uuid struct. I have been trying to figure out how exactly to set the "uuid" parameter of this struct properly. According to the struct definition, it should be either the 16bit uuid, or "octets 12-13" of the 128bit uuid. Considering I'm using BLE_UUID_TYPE_VENDOR_BEGIN, I'm guessing it will need to be the 2nd option.

    How do I determine what "octets 12-13" are though? Is this a little endian value? Big endian? Does it begin at octet 0, or 1? My full 128bit uuid is 17C30001EB0747848BF33212453B0D32. Thanks!

  • mmercier said:
    Is this a little endian value? Big endian?

    It is probably reversed, but I see it listed a bit different in different places. Let us say that the long UUID is "17C30001-EB07-4784-8BF3-3212453B0D32", then the UUID would be something like:

    "0100C317-07EB-8447-F38B-320D3B451232" in the source code.

    What you should do is to look at the examples. If you have an 128-bit UUID, you should look at the examples that use a 128-bit UUID, such as the ble_app_uart example. Try to flash it, and compare the UUID you get in nRF Connect to the one that is listed in ble_nus.c on line 64 in the SDK16.0.0:

    #define NUS_BASE_UUID                  {{0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, 0x93, 0xF3, 0xA3, 0xB5, 0x00, 0x00, 0x40, 0x6E}} /**< Used vendor specific UUID. */

    When I connect to it in nRF Connect, it says:

    (you have to hover the mouse over the service to see the UUID when it is one of the standards in nRF Connect)

    So it translates to:

    6E400001B5A3F393E0A9E50E24DCCA9E

    While the raw UUID from ble_nus.c:

    9ECADC240EE5A9E093F3A3B50000406E

    Or:

    6E400001-B5A3-F393-E0A9-E50E24DCCA9E

    9ECADC24-0EE5-A9E0-93F3-A3B50000406E

    So it is basically reversed, but notice that one of the 0000 are replaced with 0001, which is the service UUID. You will note that the characteristic UUIDs are identical, but the 0001 are switched to 0002 and 0003 for each of the characteristics.

    BR,

    Edvin

  • What does your on_connected() look like, then?

     

    mmercier said:
    If it helps, the only code I've modified is the "on_adv_report" routine

     That is probably the issue, because this example will by default look for the HRS UUID in the service discovery. This is why I keep pointing to the SDK example ble_app_uart_c, because it shows you how to look for a long (128-bit) UUID, which you then need to modify to your own.

    If you haven't changed it, your on_connected() function looks like this, right?

    static void on_connected(const ble_gap_evt_t * const p_ble_gap_evt)
    {
        printf("Connection established\n");
        fflush(stdout);
    
        m_connected_devices++;
        m_connection_handle         = p_ble_gap_evt->conn_handle;
        m_connection_is_in_progress = false;
    
        service_discovery_start();
    }

    And your service_discover_start() looks like this:

    static uint32_t service_discovery_start()
    {
        uint32_t   err_code;
        uint16_t   start_handle = 0x01;
        ble_uuid_t srvc_uuid;
    
        printf("Discovering primary services\n");
        fflush(stdout);
    
        srvc_uuid.type = BLE_UUID_TYPE_BLE;
        srvc_uuid.uuid = BLE_UUID_HEART_RATE_SERVICE;
    
        // Initiate procedure to find the primary BLE_UUID_HEART_RATE_SERVICE.
        err_code = sd_ble_gattc_primary_services_discover(m_adapter,
                                                          m_connection_handle, start_handle,
                                                          &srvc_uuid);
        if (err_code != NRF_SUCCESS)
        {
            printf("Failed to initiate or continue a GATT Primary Service Discovery procedure\n");
            fflush(stdout);
        }
    
        return err_code;
    }

    What does service_discover_start()? And did you modify it somehow? If not, I suggest you try to change BLE_UUID_TYPE_BLE and BLE_UUID_HEART_RATE_SERVICE according to the device that you are trying to connect to.

    Don't use BLE_UUID_TYPE_BLE, because these are the 16-bit UUIDs defined by Bluetooth. You want to use BLE_UUID_TYPE_VENDOR_BEGIN, which are the 128-bit UUIDs. Then you need to change the BLE_UUID_HEART_RATE_SERVICE to a variable containing the UUID that you are expecting to find.

    Look at how this is done in SDK16.0.0\examples\ble_central\ble_app_uart_c.

    If you look in ble_nus_c_init(). the base_uuid is added as NUS_BASE_UUID, and the discovery is registered as uart_uuid.

    Now, I have not tried this with pc-ble-driver, which works a bit different, but you need to change the srvc_uuid that you pass on in service_discovery_start. Try to do this, and see if you can manage to make it discover the custom uuid.

  • I have been describing my modifications to the "service_discovery_start" routine in the above posts, as this is the routine that I'd been originally seeing errors in. For reference, here is the code for this routine:

    static uint32_t service_discovery_start()
    {
        uint32_t   err_code;
        uint16_t   start_handle = 0x01;
        ble_uuid_t srvc_uuid;
    	ble_uuid128_t srvc_uuid128;
    
        printf("Discovering primary services\n");
        fflush(stdout);
    
        //srvc_uuid.type = BLE_UUID_TYPE_BLE;
        srvc_uuid.type = BLE_UUID_TYPE_VENDOR_BEGIN;
        //srvc_uuid.type = BLE_UUID_TYPE_UNKNOWN;
        srvc_uuid.uuid = BLE_UUID_HEART_RATE_SERVICE;
    	
    	//for(int i=0;i<16;i++)
    		//srvc_uuid128.uuid128[i] = BLE_UUID_LONG[i];
        	//printf("%02X ", BLE_UUID_LONG[i]);
    	srvc_uuid128.uuid128[0] = 0x32;
    	srvc_uuid128.uuid128[1] = 0x0D;
    	srvc_uuid128.uuid128[2] = 0x3B;
    	srvc_uuid128.uuid128[3] = 0x45;
    	srvc_uuid128.uuid128[4] = 0x12;
    	srvc_uuid128.uuid128[5] = 0x32;
    	srvc_uuid128.uuid128[6] = 0xF3;
    	srvc_uuid128.uuid128[7] = 0x8B;
    	srvc_uuid128.uuid128[8] = 0x84;
    	srvc_uuid128.uuid128[9] = 0x47;
    	srvc_uuid128.uuid128[10] = 0x07;
    	srvc_uuid128.uuid128[11] = 0xEB;
    	srvc_uuid128.uuid128[12] = 0x00;
    	srvc_uuid128.uuid128[13] = 0x00;
    	srvc_uuid128.uuid128[14] = 0xC3;
    	srvc_uuid128.uuid128[15] = 0x17;
    	
    	err_code = sd_ble_uuid_vs_add(m_adapter, &srvc_uuid128, &srvc_uuid.type);
    	
    	if (err_code != NRF_SUCCESS)
        {
            printf("Failed to add custom 128-bit UUID\n");
            fflush(stdout);
        }
    
        // Initiate procedure to find the primary BLE_UUID_HEART_RATE_SERVICE.
        err_code = sd_ble_gattc_primary_services_discover(m_adapter,
                                                          m_connection_handle, start_handle,
                                                          &srvc_uuid);
                                                          //NULL);
        if (err_code != NRF_SUCCESS)
        {
            printf("Failed to initiate or continue a GATT Primary Service Discovery procedure\n");
            fflush(stdout);
        }
    
        return err_code;
    }

    The "on_connected" routine is identical to what you've posted. As you can see, I've changed the type parameter of the srvc_uuid struct to BLE_UUID_TYPE_VENDOR_BEGIN, and I've defined a new struct (srvc_uuid128) which I've populated with the values of my device's 128-bit UUID. I've added a call to "sd_ble_uuid_vs_add" to add this 128-bit UUID to the list of available UUIDs, and this does not return an error code.

    I've also changed the value of BLE_UUID_HEART_RATE_SERVICE to 0x0100, to match with octets 12-13 of my service UUID. For reference, the full UUID of the service is 17C30001EB0747848BF33212453B0D32.

    I have been using this code as my base because it is the only example in the pc-ble-driver. I've been told multiple times, by multiple different Nordic reps, that this is what I need to use. So that is why I am using it. This UART example is not in the pc-ble-driver. I need to be able to make a C program that can connect to my device's service, and read/write to its characteristics. From all I've been told, I need the pc-ble-driver to be able to do that. If there is other code that can do this, that can be built into a C project, please let me know. Thanks.

  • As I'd mentioned, after adding the call to "sd_ble_uuid_vs_add", I do not get error messages in the "service_discovery_start" or "on_connected" functions. I do not get any errors until after the event handler finishes the pass in which it called this function. When the event handler enters its next pass, with the p_ble_evt->header.evt_id set to BLE_GATTC_EVT_PRIM_SRVC_DISC_RSP, this is when the value of p_ble_evt->evt.gattc_evt.gatt_status becomes 0x10A. This is the error I am trying to solve. Thanks.

  • I have been looking at that example code you mention, and I don't think it will work in this project. It seems to be using an entirely different set of library functions than what I have. I am currently using the libraries/headers/dlls from the pc-ble-driver.

    Is there some other set of libraries/headers/dlls that I could use in order to build a project using these other example files you sent? I want to know the simplest way to build a C program which will allow me to connect to my peripheral, and read/write to its characteristics through my dongle, which will be attached to the PC running the C program.

    I keep getting conflicting information about how to go about making this program, and I want to ensure I am going down the right path for what I'm trying to accomplish.

  • Okay, so I've gotten into contact with a Nordic rep to try and figure out what kind of software I should be using for the project I need. He has informed me that, in order to do what I need, I will need to use the pc-ble-driver, and modify the HRC example to search for my peripheral rather than the HRM it's set up to look for. So that UART example will not work. This is what I'd thought at first, but I wanted to make sure I wasn't just making things more difficult for myself.

    So now that I know I'm on the right path, I need to figure out how to modify this code in order to search for my specific peripheral rather than the HRM it's normally expecting. So far, I have made 2 major modifications to the original HRC code. The first is in the "find_adv_name" routine, called in the "on_adv_report" routine. Here is the original version of this routine:

    static bool find_adv_name(const ble_gap_evt_adv_report_t *p_adv_report, const char * name_to_find)
    {
        uint32_t err_code;
        data_t   adv_data;
        data_t   dev_name;
    
        // Initialize advertisement report for parsing
    #if NRF_SD_BLE_API >= 6
        adv_data.p_data     = (uint8_t *)p_adv_report->data.p_data;
        adv_data.data_len   = p_adv_report->data.len;
    #else
        adv_data.p_data     = (uint8_t *)p_adv_report->data;
        adv_data.data_len   = p_adv_report->dlen;
    #endif
    
        //search for advertising names
        err_code = adv_report_parse(BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME,
                                    &adv_data,
                                    &dev_name);
        if (err_code == NRF_SUCCESS)
        {
            if (memcmp(name_to_find, dev_name.p_data, dev_name.data_len )== 0)
            {
                return true;
            }
        }
        else
        {
            // Look for the short local name if it was not found as complete
            err_code = adv_report_parse(BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME,
                                        &adv_data,
                                        &dev_name);
            if (err_code != NRF_SUCCESS)
            {
                return false;
            }
            if (memcmp(name_to_find, dev_name.p_data, dev_name.data_len )== 0)
            {
                return true;
            }
        }
        return false;
    }

    And here is my modified code:

    static bool find_adv_name(const ble_gap_evt_adv_report_t *p_adv_report, uint8_t name_to_find)
    {
        uint32_t err_code;
        data_t   adv_data;
        data_t   dev_name;
    
        // Initialize advertisement report for parsing
    //#if NRF_SD_BLE_API >= 6
        adv_data.p_data     = (uint8_t *)p_adv_report->data.p_data;
        adv_data.data_len   = p_adv_report->data.len;
    //#else
        //adv_data.p_data     = (uint8_t *)p_adv_report->data;
        //adv_data.data_len   = p_adv_report->dlen;
    //#endif
    	
        //search for advertising names
        /*err_code = adv_report_parse(BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME,
                                    &adv_data,
                                    &dev_name);*/
        err_code = adv_report_parse(BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA,
                                    &adv_data,
                                    &dev_name);
    	
        if (err_code == NRF_SUCCESS)
        {
            //if (memcmp(name_to_find, dev_name.p_data, dev_name.data_len )== 0)
            if (memcmp(&name_to_find, dev_name.p_data, 1 )== 0)
            {
                return true;
            }
        }
        else
        {
            // Look for the short local name if it was not found as complete
            err_code = adv_report_parse(BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME,
                                        &adv_data,
                                        &dev_name);
            if (err_code != NRF_SUCCESS)
            {
                return false;
            }
            if (memcmp(name_to_find, dev_name.p_data, dev_name.data_len )== 0)
            {
                return true;
            }
        }
        return false;
    }

    As you can see, I've changed the final argument of this function to be a uint8_t rather than a const char*. This is because my peripheral does not advertise its Name data (which is a const char*), but it does advertise its Manufacturer Specific data, which is a uint8_t. The "on_adv_report" function calls "find_adv_name", and passes TARGET_DEV_NAME as its final argument. I've changed this #define statement from a string to the uint8_t value of the Manufacturer Specific Data I intend to find.

    I am fairly confident that this change works, as the program seems to find and connect to my peripheral properly. The remainder of the "on_adv_report" function carries on without any error flags returned.

    The other change I've made is what I'd described above, in the "service_discovery_start" routine, called by "on_connected". The full text is pasted above, but basically, I've changed the uuid type value from BLE_UUID_TYPE_BLE to BLE_UUID_TYPE_VENDOR_BEGIN, changed the value of the 16-bit UUID field to 0x0100 (octets 12-13 of my peripheral's service UUID), and defined a struct for a 128-bit UUID, populated it with the bytes of my peripheral's UUID, and passed it with a call to "sd_ble_uuid_vs_add".

    Notably, before I had added this call to "sd_ble_uuid_vs_add", I was getting an error returned by the "sd_ble_gattc_primary_services_discover" call. After adding this 128-bit UUID, I no longer get an error here, and I am able to move on to the next event. This leads me to believe that the UUID is being passed properly.

    Now, my issue is with the next pass through the event handler, which goes to the routine "on_service_discovery_response". The very first thing done in this routine is to check the value of the "gatt_service" parameter of the struct passed by the event handler (called p_ble_evt->evt.gattc_evt). This is resulting in an error, as the value is not 0x0, but 0x10A.

    By stepping through the code following the completion of the "on_connected" routine, I discovered that this "gatt_service" value stays 0 until the end of that pass through the event handler. Then, on the next pass (when the "on_service_discovery_response" will be called), the value of "gatt_service" changes to 0x10A. This seems to correspond to the error message for BLE_GATT_STATUS_ATTERR_ATTRIBUTE_NOT_FOUND.

    I need to know how to continue on from here. I am not getting any error messages in any of the fields that I've modified, so I do not know where this error could be coming from.

Reply
  • Okay, so I've gotten into contact with a Nordic rep to try and figure out what kind of software I should be using for the project I need. He has informed me that, in order to do what I need, I will need to use the pc-ble-driver, and modify the HRC example to search for my peripheral rather than the HRM it's set up to look for. So that UART example will not work. This is what I'd thought at first, but I wanted to make sure I wasn't just making things more difficult for myself.

    So now that I know I'm on the right path, I need to figure out how to modify this code in order to search for my specific peripheral rather than the HRM it's normally expecting. So far, I have made 2 major modifications to the original HRC code. The first is in the "find_adv_name" routine, called in the "on_adv_report" routine. Here is the original version of this routine:

    static bool find_adv_name(const ble_gap_evt_adv_report_t *p_adv_report, const char * name_to_find)
    {
        uint32_t err_code;
        data_t   adv_data;
        data_t   dev_name;
    
        // Initialize advertisement report for parsing
    #if NRF_SD_BLE_API >= 6
        adv_data.p_data     = (uint8_t *)p_adv_report->data.p_data;
        adv_data.data_len   = p_adv_report->data.len;
    #else
        adv_data.p_data     = (uint8_t *)p_adv_report->data;
        adv_data.data_len   = p_adv_report->dlen;
    #endif
    
        //search for advertising names
        err_code = adv_report_parse(BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME,
                                    &adv_data,
                                    &dev_name);
        if (err_code == NRF_SUCCESS)
        {
            if (memcmp(name_to_find, dev_name.p_data, dev_name.data_len )== 0)
            {
                return true;
            }
        }
        else
        {
            // Look for the short local name if it was not found as complete
            err_code = adv_report_parse(BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME,
                                        &adv_data,
                                        &dev_name);
            if (err_code != NRF_SUCCESS)
            {
                return false;
            }
            if (memcmp(name_to_find, dev_name.p_data, dev_name.data_len )== 0)
            {
                return true;
            }
        }
        return false;
    }

    And here is my modified code:

    static bool find_adv_name(const ble_gap_evt_adv_report_t *p_adv_report, uint8_t name_to_find)
    {
        uint32_t err_code;
        data_t   adv_data;
        data_t   dev_name;
    
        // Initialize advertisement report for parsing
    //#if NRF_SD_BLE_API >= 6
        adv_data.p_data     = (uint8_t *)p_adv_report->data.p_data;
        adv_data.data_len   = p_adv_report->data.len;
    //#else
        //adv_data.p_data     = (uint8_t *)p_adv_report->data;
        //adv_data.data_len   = p_adv_report->dlen;
    //#endif
    	
        //search for advertising names
        /*err_code = adv_report_parse(BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME,
                                    &adv_data,
                                    &dev_name);*/
        err_code = adv_report_parse(BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA,
                                    &adv_data,
                                    &dev_name);
    	
        if (err_code == NRF_SUCCESS)
        {
            //if (memcmp(name_to_find, dev_name.p_data, dev_name.data_len )== 0)
            if (memcmp(&name_to_find, dev_name.p_data, 1 )== 0)
            {
                return true;
            }
        }
        else
        {
            // Look for the short local name if it was not found as complete
            err_code = adv_report_parse(BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME,
                                        &adv_data,
                                        &dev_name);
            if (err_code != NRF_SUCCESS)
            {
                return false;
            }
            if (memcmp(name_to_find, dev_name.p_data, dev_name.data_len )== 0)
            {
                return true;
            }
        }
        return false;
    }

    As you can see, I've changed the final argument of this function to be a uint8_t rather than a const char*. This is because my peripheral does not advertise its Name data (which is a const char*), but it does advertise its Manufacturer Specific data, which is a uint8_t. The "on_adv_report" function calls "find_adv_name", and passes TARGET_DEV_NAME as its final argument. I've changed this #define statement from a string to the uint8_t value of the Manufacturer Specific Data I intend to find.

    I am fairly confident that this change works, as the program seems to find and connect to my peripheral properly. The remainder of the "on_adv_report" function carries on without any error flags returned.

    The other change I've made is what I'd described above, in the "service_discovery_start" routine, called by "on_connected". The full text is pasted above, but basically, I've changed the uuid type value from BLE_UUID_TYPE_BLE to BLE_UUID_TYPE_VENDOR_BEGIN, changed the value of the 16-bit UUID field to 0x0100 (octets 12-13 of my peripheral's service UUID), and defined a struct for a 128-bit UUID, populated it with the bytes of my peripheral's UUID, and passed it with a call to "sd_ble_uuid_vs_add".

    Notably, before I had added this call to "sd_ble_uuid_vs_add", I was getting an error returned by the "sd_ble_gattc_primary_services_discover" call. After adding this 128-bit UUID, I no longer get an error here, and I am able to move on to the next event. This leads me to believe that the UUID is being passed properly.

    Now, my issue is with the next pass through the event handler, which goes to the routine "on_service_discovery_response". The very first thing done in this routine is to check the value of the "gatt_service" parameter of the struct passed by the event handler (called p_ble_evt->evt.gattc_evt). This is resulting in an error, as the value is not 0x0, but 0x10A.

    By stepping through the code following the completion of the "on_connected" routine, I discovered that this "gatt_service" value stays 0 until the end of that pass through the event handler. Then, on the next pass (when the "on_service_discovery_response" will be called), the value of "gatt_service" changes to 0x10A. This seems to correspond to the error message for BLE_GATT_STATUS_ATTERR_ATTRIBUTE_NOT_FOUND.

    I need to know how to continue on from here. I am not getting any error messages in any of the fields that I've modified, so I do not know where this error could be coming from.

Children
  • I do not mean that you should use the SDK's example in the end, but that this example shows you how to try to discover a 128bit UUID. I guess if you ask 4 different people how to develop your application, you will get 4 different answers. What I wanted you to check in the central ble_app_uart_c example was what octets that are being used in the service discovery. This has nothing to do with your scanning or find_adv_name() function. Do you know what service discovery is, in the first place?

    The reason I started listing the UUID of the ble_app_uart peripheral is because it is a 128bit UUID. 

     

    mmercier said:
    I've also changed the value of BLE_UUID_HEART_RATE_SERVICE to 0x0100, to match with octets 12-13 of my service UUID.

     Can you try to set it to 0x0001 as well? This is why I tried to point you to the ble_app_uart example, to show a bit of how the UUID is represented in different applications. I am sorry if it was confusing.

    If that doesn't work, what happens if you set the uuid.type to 0x00 in start_discovery()? Do you get a on_service_discovery_response() callback in that case? If so, what does the log say from this part?

  • Changing the 16-bit UUID to 0x0001 worked! I can now move past the service discovery response. So it seems that the 128-bit UUID needs to be defined in little endian format, but the 16-bit service UUID section must be big endian.

    I am now able to move past into the characteristic discovery. This has given me a lot more to experiment with. I just have a few questions that I am curious about.

    1: I am able to discover a characteristic from my service and read back its UUID. The program also responds with the handle value of the characteristic (it is 0xF, one up from the service handle of 0xE). However, I noticed that, when connecting to the peripheral in nRF Connect, the handle assigned to the characteristic is not 0xF, but 0x10 (service handle is still 0xE, I couldn't find where 0xF was). I just wanted to ask if these handles need to match, or if they are just assigned sequentially and it doesn't particularly matter the exact value.

    2: My peripheral's service has 3 characteristics, but the call to "sd_ble_gattc_characteristics_discover" is only returning a value of 1 for the count of characteristics found (only the first, with the 0xF handle, is being found). I experimented by changing the value of handle_range.start_handle to be equal to the service handle + 2 (so that it would skip the first characteristic), and it still returned a count value of 1, but this time, it was the service's second characteristic that had been found (since I made it skip the first). So it seems like something is causing the function to only consider the first characteristic it finds. This seems to make sense, due to the expected HRM only having the one characteristic, but I couldn't find anywhere that specifically told the function to only look for up to 1 characteristic. Is there some setting I can modify to have this function discover all three?

    Thanks for the suggestions!

  • One more thing I noticed about the handles, when I discover the characteristic with handle 0xF, and the event handler moves into the descriptor discovery, the returned value of the CCCD handle is also 0xF. On the other hand, in nRF Connect, the characteristic's handle is 0x10, and the CCCD handle is 0x11. It seems strange to me that the program would read the same handle for the characteristic and its CCCD. Is this normal? Thanks.

  • mmercier said:
    Changing the 16-bit UUID to 0x0001 worked!

     Great! That is good to hear. This is why I wanted you to check the ble_nus_c example, because I wanted you to compare the UUID with the part of the UUID that the central looked for in the service discovery.

     

    mmercier said:
    I just wanted to ask if these handles need to match, or if they are just assigned sequentially and it doesn't particularly matter the exact value.

     They do not need to match exactly, no. They are just used internally in your application, and are assigned during runtime. 

     

    mmercier said:
    My peripheral's service has 3 characteristics, but the call to "sd_ble_gattc_characteristics_discover" is only returning a value of 1 for the count of characteristics found

     Can you try to set the uuid.type to 0x00 in start_discovery()? What does things look like in the on_service_discovery_response() then?

  • Changing the uuid.type to 0x00 had no change to the behavior with it defined as BLU_UUID_TYPE_VENDOR_BEGIN. So, I am still discovering the service properly, and I'm able to get a characteristic and its descriptor.

    By looking through other cases here on the help website, I found suggestions that users simply call the "sd_ble_gattc_characteristics_discover" routine multiple times in order to discover more than one characteristic, just increasing the lower boundary of the handle range each time. I thought this was strange, since the params.char_disc_rsp object has a count parameter, it seems like it should be able to return multiple characteristics. Either way, by using multiple calls, I've been able to assign all 3 characteristics to different handles. For now, though, since I am still trying to figure out the read/write commands, I'm only assigning one characteristic at a time.

    So, my next concern is with the write command. Currently, I have my code begin the "sd_ble_gattc_characteristics_discover" routine using an increased lower handle, so that the characteristic it returns has a UUID that matches my peripheral's "write" characteristic. I am wanting to know what code to use to write from this characteristic, mimicking the behavior of the nRF Connect program, where I can type data into the characteristic's string field and click the "write" button.

    I've started by looking at the HRM code in the pc-ble-driver example. I've added a function based on this example, here's what it looks like so far:

    static uint32_t writeble()
    {
        uint32_t error_code;
    	uint8_t enc_data[13];
    	uint16_t data_len;
    	ble_gatts_hvx_params_t hvx_params;
    	
    	data_len = (uint16_t)13;
    	
    	enc_data[0] = 0x01;
    	enc_data[1] = 0x00;
    	enc_data[2] = 0xFF;
    	enc_data[3] = 0x20;
    	enc_data[4] = 0x08;
    	enc_data[5] = 0x01;
    	enc_data[6] = 0x02;
    	enc_data[7] = 0x03;
    	enc_data[8] = 0x04;
    	enc_data[9] = 0x05;
    	enc_data[0] = 0x06;
    	enc_data[11] = 0x07;
    	enc_data[12] = 0x08;
    	
    	hvx_params.handle = m_hrm_char_handle2;
    	hvx_params.type = BLE_GATT_HVX_NOTIFICATION;
    	hvx_params.offset = 0;
    	hvx_params.p_len = &data_len;
    	hvx_params.p_data = enc_data;
    	
    	error_code = sd_ble_gatts_hvx(m_adapter, m_connection_handle, &hvx_params);
    	
    	if(error_code != NRF_SUCCESS){
    		printf("Failed to send. Error code 0x%02X\n", error_code);
    		fflush(stdout);
    		return error_code;
    	}
    	return NRF_SUCCESS;
    }

    This is based on the HRM example. The data is a 13-byte packet, it is the same data that I type into the characteristic's field in the nRF Connect program. The value "m_hrm_char_handle_2" is the handle that this characteristic is being assigned to by "sd_ble_gattc_characteristics_discover".

    I've modified the main loop of the example, which waits for a character input into the terminal, then calls the HRC notification toggle. I've commented out this function call, and replaced it with a call to this new function. So after the peripheral is connected, and the service, characteristic, and descriptor are all discovered, pressing a key will call this function.

    As it is now, doing so gives me an error code of 0x05, which seems to correspond to NRF_ERROR_NOT_FOUND. So something is not being set up correctly. Is there any other code I need to add to ensure that I can carry out this write command? Or am I going about this the wrong way? For reference, this characteristic only has the "WriteWithoutResponse" property enabled. So if this is doing something other than a write (without response) type of command, maybe that's where the error is from. But since this is the only "write" type command I could find in the examples, I'm not sure where to go from here.

    Please let me know if you've got any pointers for me, thanks!

Related