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!

  • 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!

  • Okay, so I did some digging and I found a function that I think is right for what I need. It is called "sd_ble_gattc_write". I modified my new "writeble" routine like this:

    static uint32_t writeble()
    {
        uint32_t error_code;
    	uint8_t enc_data[13];
    	ble_gattc_write_params_t write_params;
    	
    	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;
    	
    	write_params.write_op = BLE_GATT_OP_WRITE_CMD;
    	write_params.flags = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_WRITE;
    	write_params.handle = m_hrm_char_handle2;
    	write_params.offset = 0;
    	write_params.len = 0xD;
    	write_params.p_value = enc_data;
    	
    	error_code = sd_ble_gattc_write(m_adapter, m_connection_handle, &write_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;
    }

    Now, I don't get any error when I run this code, and the event handler enters the case for BLE_GATTC_EVT_WRITE_CMD_TX_COMPLETE, which should mean that the Write Without Response command completed successfully.

    However, I am not seeing the proper response from my peripheral. For reference, I am monitoring a parallel bus on my peripheral, and when this characteristic is written to (using the specified 13-byte packet layout), I will see a response. If I use nRF Connect, I can type these 13 bytes into the characteristic, click "write", and I will see the response on the bus I'm monitoring. However, when using this write command in the program, nothing appears on the bus.

    I know the peripheral is behaving properly, since it works in nRF Connect, and it seems that the write command is executing correctly, so there must just be something configured differently in how the program is setting up the command vs how the nRF Connect is setting up the command. I have already tried reversing the byte order in the enc_data array, to no avail. As with the previous code I posted, the handle used here is the one that was assigned to my "write" characteristic (the UUID is matched correctly). I am only discovering this one characteristic for right now, so as to simplify the process. This characteristic does not have any descriptors defined, so I am not calling the "descr_discovery_start" function for now.

  • A Shot in the dark:

    You set enc_data[0] twice, and you do not set enc_data[10]. Just in case you are not checking for the event, but only a specific packet. Since you mention that you tried to reverse the packet here as well. The content of the payload shouldn't really matter for the BLE stack. 

    Do you check whether or not the callback event is triggered on the peripheral?

Related