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

Read added custom characteristic value from peripheral

Hi, 

I am developing a central application that will connect to my custom made peripheral device and continuously read a characteristic available on the peripheral device . I have currently managed to find and connect to my peripheral device, and also request to read the device name characteristic without any problems. However, when I start reading the custom service that is added on the peripheral device, with the sd_ble_gattc_char_value_by_uuid_read function, it returns err_code 7, which I assume is invalid params. 

On the peripheral I can see that there are two characteristics that I can read through the nrf connect application.

I had my code in the following way when requesting to read the device name characteristic:

static void repeated_timer_handler(void * p_context)
{
    ret_code_t err_code;
    
    ble_uuid_t p_uuid = {.uuid = BLE_UUID_GAP_CHARACTERISTIC_DEVICE_NAME, .type = BLE_UUID_TYPE_BLE};
    ble_gattc_handle_range_t p_handle_range = {.start_handle = 0x0001, .end_handle = 0xFFFF};

    err_code = sd_ble_gattc_char_value_by_uuid_read(connection_handle, &p_uuid, &p_handle_range);
    if(err_code != NRF_SUCCESS)
    {
        NRF_LOG_INFO("Failed to send read request to peer error = %d", err_code);
    }
}

And when I now want to read my service characteristic I instead now changed the uuid and type to following:

static void repeated_timer_handler(void * p_context)
{
    ret_code_t err_code;
    
    ble_uuid_t p_uuid = {.uuid = 0xF011, .type = BLE_UUID_TYPE_VENDOR_BEGIN};
    ble_gattc_handle_range_t p_handle_range = {.start_handle = 0x0001, .end_handle = 0xFFFF};
    err_code = sd_ble_gattc_char_value_by_uuid_read(connection_handle, &p_uuid, &p_handle_range);
    if(err_code != NRF_SUCCESS)
    {
        NRF_LOG_INFO("Failed to send read request to peer error = %d", err_code);
    }
}

Am I calling the wrong function or do I have to perform some other function call in order to be able of requesting to read the following service?

I assume that I don't have to use the base uuid to call the specific service, or do I have to? I have seen that some examples perform a service discovery when initiating the connection, but if I know the characteristic uuid, do I still have to call this function? I tried adding this sd_ble_gattc_primary_services_discover function but didn't solve the problem.

Update: I have seen that some have used the sd_ble_gattc_read(connection_handle, 0xF011, 0) function. When I use this instead, I get no error but the value I get in the event handler has a length of 0.

 

Thanks in advance!

/Hadi

Parents
  • Hello,

    I recommend you look into the service discovery. I can also recommend that you read through this guide:

    https://devzone.nordicsemi.com/nordic/short-range-guides/b/bluetooth-low-energy/posts/ble-central-tutorial

    Although it is written for an older version of the SDK, the theory is still correct, and you should be able to figure out how to do it in the SDK you are using by looking at the ble_nus_c example. 

    Is there a reason why you are using sd_ble_gattc_ read instead of using notifications? I am mostly curious, but it may also be easier to set up, since most of our examples are using it. It is also a more efficient way of sending data from a peripheral to a central. 

    Best regards,

    Edvin

  • Hi,

    First of all thank you for the recommendations and guidance, I'll take a look on the guide.

    Secondly regarding the services, I was a bit unsure whether the central would send continuous requests since I need to read a characteristic on the peripheral until the central and peripheral are disconnected. If I use notifications is it possible to adjust how often I can request a value, I need to read a characteristic in an interval of 500ms?

    Another thing that I forgot to mention is that the peripheral device does not have nus implemented, is it necessary to implement nus on the peripheral too? I have only added the custom services on the peripheral and they work as expected but cant get the values.

    Best regards,

    Hadi

Reply
  • Hi,

    First of all thank you for the recommendations and guidance, I'll take a look on the guide.

    Secondly regarding the services, I was a bit unsure whether the central would send continuous requests since I need to read a characteristic on the peripheral until the central and peripheral are disconnected. If I use notifications is it possible to adjust how often I can request a value, I need to read a characteristic in an interval of 500ms?

    Another thing that I forgot to mention is that the peripheral device does not have nus implemented, is it necessary to implement nus on the peripheral too? I have only added the custom services on the peripheral and they work as expected but cant get the values.

    Best regards,

    Hadi

Children
  • If the central doesn't use the nus service, then you don't need that on the peripheral either. But the peripheral and central needs to use the same type of service. NUS is just an example of that.

     

    Hadi Deknache said:
    Secondly regarding the services, I was a bit unsure whether the central would send continuous requests since I need to read a characteristic on the peripheral until the central and peripheral are disconnected. If I use notifications is it possible to adjust how often I can request a value, I need to read a characteristic in an interval of 500ms?

     Notifications work similar, but except for the central actively reading the peripheral's value, the periheral will push the value to the central, which will trigger an event. This way, you will have less delay from the value is updated until the central receives it.

    So if you want to update the value every 500ms, then you can send this notification every 500ms from the peripheral, and the central will receive this as an event. 

    If you use write and read, it may happen that the central reads the value, and right after, the peripheral updates this value, which will not be known by the central until the next time it decides to read it. 

  • Ok,  then I understand. Thanks for the clarifications.

    I have one last question. When I request to read the information, e.g., the device name characteristic, the  BLE_GATTC_EVT_CHAR_VAL_BY_UUID_READ_RSP event is triggered.

    How do I read out the information that is received from the peripheral? I assume that the data and length should be available in: p_ble_evt->evt.gattc_evt.params.char_val_by_uuid_read_rsp, because I can see the length which is correct if I change the lentgh of the device name, but the data in p_ble_evt->evt.gattc_evt.params.char_val_by_uuid_read_rsp.handle_value I haven't managed to read out and log it with NRF_LOG_INFO.

    /Hadi
  • Hello Hadi,

    Check the (short) description on line 90 in ble_gattc.h. (At least it is found here in SDK15.3.0).

    BLE_GATTC_EVT_CHAR_VAL_BY_UUID_READ_RSP,                /**< Read By UUID Response event.                       \n See @ref ble_gattc_evt_char_val_by_uuid_read_rsp_t.   */

    You can also look at the struct that belongs to the BLE_GATTC_EVT_CHAR_VAL_BY_UUID_READ_RSP on line 257 -> 265 in ble_gattc.h:

    /**@brief Event structure for @ref BLE_GATTC_EVT_CHAR_VAL_BY_UUID_READ_RSP. */
    typedef struct
    {
      uint16_t                  count;            /**< Handle-Value Pair Count. */
      uint16_t                  value_len;        /**< Length of the value in Handle-Value(s) list. */
      uint8_t                   handle_value[1];  /**< Handle-Value(s) list. To iterate through the list use @ref sd_ble_gattc_evt_char_val_by_uuid_read_rsp_iter.
                                                       @note This is a variable length array. The size of 1 indicated is only a placeholder for compilation.
                                                       See @ref sd_ble_evt_get for more information on how to use event structures with variable length array members. */
    } ble_gattc_evt_char_val_by_uuid_read_rsp_t;

    So you need to use sd_ble_gattc_evt_char_val_by_uuid_read_rsp_iter() to read the actual value (snippet from ble_gattc.h line 669):

     * \code
     * ble_gattc_handle_value_t iter;
     * memset(&iter, 0, sizeof(ble_gattc_handle_value_t));
     * while (sd_ble_gattc_evt_char_val_by_uuid_read_rsp_iter(&ble_evt.evt.gattc_evt, &iter) == NRF_SUCCESS)
     * {
     *   app_handle = iter.handle;
     *   memcpy(app_value, iter.p_value, ble_evt.evt.gattc_evt.params.char_val_by_uuid_read_rsp.value_len);
     * }
     * \endcode
     *
     * @retval ::NRF_SUCCESS Successfully retrieved the next Handle-Value pair.
     * @retval ::NRF_ERROR_NOT_FOUND No more Handle-Value pairs available in the list.
     */

    After the memcpy(), you should have the data in app_value. It is copied from iter.p_value, and has a length of o_ble_evt.evt.gattc_evt.params.char_val_by_uuid_read_rsp.value_len.

    I haven't seen this been used much. Notifications are a bit easier to work with, in my opinion. 

  • Hi Edvin,

    That was what I was looking for, thank you for the help!

    I'll also consider looking into the notifications and how to use them, I really appreciate your help and thank you once again.

    /Best regards

    Hadi

Related