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

Issue in Adding 2nd characteristics in Custom Service

Hi,

I am following the below link for creating custom services/characteristics.
https://github.com/bjornspockeli/nRF52-Bluetooth-Course 

Now I am adding the second characteristic in that service but unable to see in the nRF app.

I have added 2nd custom_value_char_add_2 function for 2nd characteristics. 

uint32_t custom_value_char_add_2(ble_cus_t * p_cus, const ble_cus_init_t * p_cus_init)
{
uint32_t err_code;
ble_gatts_char_md_t char_md;
ble_gatts_attr_md_t cccd_md;
ble_gatts_attr_t attr_char_value;
ble_uuid_t ble_uuid;
ble_gatts_attr_md_t attr_md;

memset(&char_md, 0, sizeof(char_md));

char_md.char_props.read = 1;
char_md.char_props.write = 1;
char_md.char_props.notify = 0;
char_md.p_char_user_desc = NULL;
char_md.p_char_pf = NULL;
char_md.p_user_desc_md = NULL;
char_md.p_cccd_md = NULL;
char_md.p_sccd_md = NULL;

memset(&attr_md, 0, sizeof(attr_md));

attr_md.read_perm = p_cus_init->custom_value_char_attr_md.read_perm;
attr_md.write_perm = p_cus_init->custom_value_char_attr_md.write_perm;
attr_md.vloc = BLE_GATTS_VLOC_STACK;
attr_md.rd_auth = 0;
attr_md.wr_auth = 0;
attr_md.vlen = 0;
/* This code belongs in custom_value_char_add() in ble_cus.c*/

ble_uuid.type = p_cus->uuid_type;
ble_uuid.uuid = CUSTOM_VALUE_CHAR_UUID;


/* This code belongs in custom_value_char_add() in ble_cus.c*/

memset(&attr_char_value, 0, sizeof(attr_char_value));

attr_char_value.p_uuid = &ble_uuid;
attr_char_value.p_attr_md = &attr_md;
attr_char_value.init_len = sizeof(uint8_t);
attr_char_value.init_offs = 0;
attr_char_value.max_len = sizeof(uint8_t);


/* This code belongs in custom_value_char_add() in ble_cus.c*/

err_code = sd_ble_gatts_characteristic_add(p_cus->service_handle, &char_md,
&attr_char_value,
&p_cus->custom_value_handles);
if (err_code != NRF_SUCCESS)
{
return err_code;
}

return NRF_SUCCESS;


}

And also call this function in ble_cus_init.

uint32_t ble_cus_init(ble_cus_t * p_cus, const ble_cus_init_t * p_cus_init)
{
if (p_cus == NULL || p_cus_init == NULL)
{
return NRF_ERROR_NULL;
}

uint32_t err_code;
ble_uuid_t ble_uuid;

// Initialize service structure
p_cus->conn_handle = BLE_CONN_HANDLE_INVALID;

// Add Custom Service UUID
ble_uuid128_t base_uuid = {CUSTOM_SERVICE_UUID_BASE};
err_code = sd_ble_uuid_vs_add(&base_uuid, &p_cus->uuid_type);
VERIFY_SUCCESS(err_code);

ble_uuid.type = p_cus->uuid_type;
ble_uuid.uuid = CUSTOM_SERVICE_UUID;

// Add the Custom Service
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_cus->service_handle);
if (err_code != NRF_SUCCESS)
{
return err_code;
}

// Add Custom Value characteristic
return custom_value_char_add(p_cus, p_cus_init);
// Add Custom Value characteristic
return custom_value_char_add_2(p_cus, p_cus_init);
}



I am also attaching  ble_cus.c file.
Any suggestions, please?

/* This code belongs in ble_cus.c*/
#include "sdk_common.h"
#include "ble_srv_common.h"
#include "ble_cus.h"
#include <string.h>
#include "nrf_gpio.h"
#include "boards.h"
#include "nrf_log.h"



/* This code belongs in ble_cus.c*/

/**@brief Function for initializing the Custom Service.
 *
 * @param[out]  p_cus       Custom Service structure. This structure will have to be supplied by
 *                          the application. It will be initialized by this function, and will later
 *                          be used to identify this particular service instance.
 * @param[in]   p_cus_init  Information needed to initialize the service.
 *
 * @return      NRF_SUCCESS on successful initialization of service, otherwise an error code.
 */
uint32_t ble_cus_init(ble_cus_t * p_cus, const ble_cus_init_t * p_cus_init);
uint32_t custom_value_char_add(ble_cus_t * p_cus, const ble_cus_init_t * p_cus_init);
uint32_t custom_value_char_add_2(ble_cus_t * p_cus, const ble_cus_init_t * p_cus_init);

void ble_cus_on_ble_evt( ble_evt_t const * p_ble_evt, void * p_context);
/* This code belongs in ble_cus.c*/

uint32_t ble_cus_init(ble_cus_t * p_cus, const ble_cus_init_t * p_cus_init)
{
    if (p_cus == NULL || p_cus_init == NULL)
    {
        return NRF_ERROR_NULL;
    }

    uint32_t   err_code;
    ble_uuid_t ble_uuid;

    // Initialize service structure
    p_cus->conn_handle               = BLE_CONN_HANDLE_INVALID;

    // Add Custom Service UUID
    ble_uuid128_t base_uuid = {CUSTOM_SERVICE_UUID_BASE};
    err_code =  sd_ble_uuid_vs_add(&base_uuid, &p_cus->uuid_type);
    VERIFY_SUCCESS(err_code);
    
    ble_uuid.type = p_cus->uuid_type;
    ble_uuid.uuid = CUSTOM_SERVICE_UUID;

    // Add the Custom Service
    err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_cus->service_handle);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    // Add Custom Value characteristic
    return custom_value_char_add(p_cus, p_cus_init);
    // Add Custom Value characteristic
    return custom_value_char_add_2(p_cus, p_cus_init);
}

/* This code belongs in ble_cus.c*/

/**@brief Function for adding the Custom Value characteristic.
 *
 * @param[in]   p_cus        Custom Service structure.
 * @param[in]   p_cus_init   Information needed to initialize the service.
 *
 * @return      NRF_SUCCESS on success, otherwise an error code.
 */
uint32_t custom_value_char_add(ble_cus_t * p_cus, const ble_cus_init_t * p_cus_init)
{
    uint32_t            err_code;
    ble_gatts_char_md_t char_md;
    ble_gatts_attr_md_t cccd_md;
    ble_gatts_attr_t    attr_char_value;
    ble_uuid_t          ble_uuid;
    ble_gatts_attr_md_t attr_md;

    memset(&char_md, 0, sizeof(char_md));

    char_md.char_props.read   = 1;
    char_md.char_props.write  = 1;
    char_md.char_props.notify = 0; 
    char_md.p_char_user_desc  = NULL;
    char_md.p_char_pf         = NULL;
    char_md.p_user_desc_md    = NULL;
    char_md.p_cccd_md         = NULL; 
    char_md.p_sccd_md         = NULL;

memset(&attr_md, 0, sizeof(attr_md));

    attr_md.read_perm  = p_cus_init->custom_value_char_attr_md.read_perm;
    attr_md.write_perm = p_cus_init->custom_value_char_attr_md.write_perm;
    attr_md.vloc       = BLE_GATTS_VLOC_STACK;
    attr_md.rd_auth    = 0;
    attr_md.wr_auth    = 0;
    attr_md.vlen       = 0;
 /* This code belongs in custom_value_char_add() in ble_cus.c*/

    ble_uuid.type = p_cus->uuid_type;
    ble_uuid.uuid = CUSTOM_VALUE_CHAR_UUID;


    /* This code belongs in custom_value_char_add() in ble_cus.c*/

    memset(&attr_char_value, 0, sizeof(attr_char_value));

    attr_char_value.p_uuid    = &ble_uuid;
    attr_char_value.p_attr_md = &attr_md;
    attr_char_value.init_len  = sizeof(uint8_t);
    attr_char_value.init_offs = 0;
    attr_char_value.max_len   = sizeof(uint8_t);


    /* This code belongs in custom_value_char_add() in ble_cus.c*/

err_code = sd_ble_gatts_characteristic_add(p_cus->service_handle, &char_md,
                                               &attr_char_value,
                                               &p_cus->custom_value_handles);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    return NRF_SUCCESS;




}

uint32_t custom_value_char_add_2(ble_cus_t * p_cus, const ble_cus_init_t * p_cus_init)
{
    uint32_t            err_code;
    ble_gatts_char_md_t char_md;
    ble_gatts_attr_md_t cccd_md;
    ble_gatts_attr_t    attr_char_value;
    ble_uuid_t          ble_uuid;
    ble_gatts_attr_md_t attr_md;

    memset(&char_md, 0, sizeof(char_md));

    char_md.char_props.read   = 1;
    char_md.char_props.write  = 1;
    char_md.char_props.notify = 0; 
    char_md.p_char_user_desc  = NULL;
    char_md.p_char_pf         = NULL;
    char_md.p_user_desc_md    = NULL;
    char_md.p_cccd_md         = NULL; 
    char_md.p_sccd_md         = NULL;

memset(&attr_md, 0, sizeof(attr_md));

    attr_md.read_perm  = p_cus_init->custom_value_char_attr_md.read_perm;
    attr_md.write_perm = p_cus_init->custom_value_char_attr_md.write_perm;
    attr_md.vloc       = BLE_GATTS_VLOC_STACK;
    attr_md.rd_auth    = 0;
    attr_md.wr_auth    = 0;
    attr_md.vlen       = 0;
 /* This code belongs in custom_value_char_add() in ble_cus.c*/

    ble_uuid.type = p_cus->uuid_type;
    ble_uuid.uuid = CUSTOM_VALUE_CHAR_UUID;


    /* This code belongs in custom_value_char_add() in ble_cus.c*/

    memset(&attr_char_value, 0, sizeof(attr_char_value));

    attr_char_value.p_uuid    = &ble_uuid;
    attr_char_value.p_attr_md = &attr_md;
    attr_char_value.init_len  = sizeof(uint8_t);
    attr_char_value.init_offs = 0;
    attr_char_value.max_len   = sizeof(uint8_t);


    /* This code belongs in custom_value_char_add() in ble_cus.c*/

err_code = sd_ble_gatts_characteristic_add(p_cus->service_handle, &char_md,
                                               &attr_char_value,
                                               &p_cus->custom_value_handles);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    return NRF_SUCCESS;




}

/* This code belongs in ble_cus.c*/



/**@brief Function for handling the Connect event.
 *
 * @param[in]   p_cus       Custom Service structure.
 * @param[in]   p_ble_evt   Event received from the BLE stack.
 */
void on_connect(ble_cus_t * p_cus, ble_evt_t const * p_ble_evt)
{
    p_cus->conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
}

/**@brief Function for handling the Disconnect event.
 *
 * @param[in]   p_cus       Custom Service structure.
 * @param[in]   p_ble_evt   Event received from the BLE stack.
 */
void on_disconnect(ble_cus_t * p_cus, ble_evt_t const * p_ble_evt)
{
    UNUSED_PARAMETER(p_ble_evt);
    p_cus->conn_handle = BLE_CONN_HANDLE_INVALID;
}
/**@brief Function for handling the Write event.
 *
 * @param[in]   p_cus       Custom Service structure.
 * @param[in]   p_ble_evt   Event received from the BLE stack.
 */
void on_write(ble_cus_t * p_cus, ble_evt_t const * p_ble_evt)
{
    ble_gatts_evt_write_t *p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
    
    // Check if the handle passed with the event matches the Custom Value Characteristic handle.
    if (p_evt_write->handle == p_cus->custom_value_handles.value_handle)
    {
        NRF_LOG_INFO("Data Write");
        nrf_gpio_pin_toggle(LED_4); 
    }

}

void ble_cus_on_ble_evt( ble_evt_t const * p_ble_evt, void * p_context)
{
    ble_cus_t * p_cus = (ble_cus_t *) p_context;
    
    if (p_cus == NULL || p_ble_evt == NULL)
    {
        return;
    }
   switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
            on_connect(p_cus, p_ble_evt);
            break;

        case BLE_GAP_EVT_DISCONNECTED:
            on_disconnect(p_cus, p_ble_evt);
            break;
       case BLE_GATTS_EVT_WRITE:
           on_write(p_cus, p_ble_evt);
           break;
        default:
            // No implementation needed.
            break;
    }
}

Parents
  • Hello,

     

    Now I am adding the second characteristic in that service but unable to see in the nRF app.

    I have added 2nd custom_value_char_add_2 function for 2nd characteristics. 

     Do you see the device at all? Are you able to connect to the device?

    It is quite common that phones cache BLE services. Try to disconnect from the device from your phone. If you have the option "forget device" try that. Then turn Bluetooth completely off for a minute, and turn it back on. Does the new service appear?

    I wrote this before I looked at your attached file. But it may still be the case after you fix the bug in the file. So that is why I keep it.

    The issue with your code is in ble_cus_init(). 

    You return the value from custom_value_char_add(). When you return from a function, the part after this return will not be executed, so your line:

    return custom_value_char_add_2() will not be executed. Try to change to this:

    uint32_t ble_cus_init(ble_cus_t * p_cus, const ble_cus_init_t * p_cus_init)
    {
        ... //all the stuff that you already have
        
        // Add Custom value characteristic
        err_code = custom_value_char_add(p_cus, p_cus_init);
        APP_ERROR_CHECK(err_code);
        err_code = custom_value_char_add2(p_cus, p_cus_init);
        APP_ERROR_CHECK(err_code);  // This line is not strictly needed, but if you plan to do more, don't return this value
        
        return err_code;
    }

    Best regards,

    Edvin

  • Thanks, 
    I have changed the above code and now able to see 2nd characteristic. Slight smile


    1-Now the issue both characteristic is showing the same id. (As shown in the attached image)



    2-How can I handle data of both characteristics separately?
    3-How can I write back data from the device side on that characteristic? (For response)


    /**@brief Function for handling the Write event.
     *
     * @param[in]   p_cus       Custom Service structure.
     * @param[in]   p_ble_evt   Event received from the BLE stack.
     */
    void on_write(ble_cus_t * p_cus, ble_evt_t const * p_ble_evt)
    {
        ble_gatts_evt_write_t *p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
        
        // Check if the handle passed with the event matches the Custom Value Characteristic handle.
        if (p_evt_write->handle == p_cus->custom_value_handles.value_handle)
        {
            NRF_LOG_INFO("Data Write: %x", p_evt_write->data[0]);
     
            if(nrf_gpio_pin_read(Input_PIN)){
              NRF_LOG_INFO("Button is Off");
            }else{
              NRF_LOG_INFO("Button is On");
            }
    
            if(p_evt_write->data[0]==0x01)
            {
                NRF_LOG_INFO("Locked");
                nrf_gpio_pin_set(Lock_PIN);
                nrf_delay_ms(1000);
                nrf_gpio_pin_clear(Lock_PIN);
    
            }
            else if(p_evt_write->data[0]==0x02)
            {
                NRF_LOG_INFO("UnLocked");
                nrf_gpio_pin_set(Lock_PIN);
                nrf_delay_ms(1000);
                nrf_gpio_pin_clear(Lock_PIN);
            }
        }
    
    }
    


    Thanks again 

  • Muqarrab said:
    1-Now the issue both characteristic is showing the same id. (As shown in the attached image)

     That is because you are using the same UUID in custom_value_char_add() and custom_value_char_add_2().

     

    Muqarrab said:
    2-How can I handle data of both characteristics separately?

     You need to implement and handle the handles correctly. Play around with it and read Bjørn's tutorial (the first link in your ticket).

     

    Muqarrab said:
    3-How can I write back data from the device side on that characteristic? (For response)

     You need to send something called a notification. Please see section 8 in the guide.

Reply
  • Muqarrab said:
    1-Now the issue both characteristic is showing the same id. (As shown in the attached image)

     That is because you are using the same UUID in custom_value_char_add() and custom_value_char_add_2().

     

    Muqarrab said:
    2-How can I handle data of both characteristics separately?

     You need to implement and handle the handles correctly. Play around with it and read Bjørn's tutorial (the first link in your ticket).

     

    Muqarrab said:
    3-How can I write back data from the device side on that characteristic? (For response)

     You need to send something called a notification. Please see section 8 in the guide.

Children
  • Thanks @Edvin

     That is because you are using the same UUID in custom_value_char_add() and custom_value_char_add_2().

    I have solved the issue by using this.

    #define CUSTOM_VALUE_CHAR_UUID       0x1401
    #define CUSTOM_VALUE_CHAR_UUID_2     0x1402
    
    ble_uuid.uuid = CUSTOM_VALUE_CHAR_UUID_2;//In custom_value_char_add_2
    




     You need to implement and handle the handles correctly. Play around with it and read Bjørn's tutorial (the first link in your ticket).

    Also solved by p_evt_write->uuid.uuid by this.


    You need to send something called a notification. Please see section 8 in the guide.

    I have seen the tutorial. But there is not mentioned how can I send data on specific characteristics?
    Can you please explain how can I write back on specific UUID characteristics?

    Thanks!

  • For the solution, please see how it is done in the example:

    SDK\examples\ble_peripheral\ble_app_uart

    Look at how the function ble_nus_data_send() is implemented. it sends a notification using a specific handle:

    hvx_params.handle = p_nus->tx_handles.value_handle;

    This characteristic specific handle is set in characteristic_add(..., p_nus->tx_handles).

    There is actually a lot of useful information in this example.

    As far as I can tell, you haven't implemented anything that sends any data yet. I suggest you try to start by following the tutorial, which only has one characteristic, to get the basic understanding first, and then you can look at the ble_app_uart example, to see how to use two different characteristics. It is difficult to point to something in your code, because I don't see your ble_cus.h file. But see how this has two handles in the ble_app_uart example. One for the TX characteristic and one for the RX characteristic:

    struct ble_nus_s
    {
        uint8_t                         uuid_type;          /**< UUID type for Nordic UART Service Base UUID. */
        uint16_t                        service_handle;     /**< Handle of Nordic UART Service (as provided by the SoftDevice). */
        ble_gatts_char_handles_t        tx_handles;         /**< Handles related to the TX characteristic (as provided by the SoftDevice). */
        ble_gatts_char_handles_t        rx_handles;         /**< Handles related to the RX characteristic (as provided by the SoftDevice). */
        blcm_link_ctx_storage_t * const p_link_ctx_storage; /**< Pointer to link context storage with handles of all current connections and its context. */
        ble_nus_data_handler_t          data_handler;       /**< Event handler to be called for handling received data. */
    };

    BR,

    Edvin

  • Hi @Edvin,

    I have checked the mentioned example which has Tx Rx characteristics.

    The above-mentioned example making me confuse as it has only 1 characteristic to write back data.

    I have also successfully written data in our code on one characteristic but unable to write the second characteristic.(Unable to handle p_cus->custom_value_handles.value_handle).

    This making me confuse as both characteristics are added by the same p_cus so how can I differentiate  
    p_cus->custom_value_handles.value_handle for 2 characteristics.

    Any help for this point?

        // Add Custom value characteristic
        err_code = custom_value_char_add(p_cus, p_cus_init);
        APP_ERROR_CHECK(err_code);
        
        err_code = custom_value_char_add_2(p_cus, p_cus_init);
        APP_ERROR_CHECK(err_code);  // This line is not strictly needed, but if you plan to do more, don't return this value
    



    Is there any other example that has 2+ characteristics in 1 service and sending back data?

    Thanks!

  • Muqarrab said:
    This making me confuse as both characteristics are added by the same p_cus so how can I differentiate  
    p_cus->custom_value_handles.value_handle for 2 characteristics.

     Just like in ble_app_uart, you need to declare two different handles in p_cus.

    p_cus->custom_value_handles_characteristic_1

    and 

    p_cus->custom_value_handles_characteristic_2

    So somewhere in ble_cus.h, you should have something like this:

    struct ble_cus_s
    {
        uint8_t                         uuid_type;                                      /**< UUID type for Nordic UART Service Base UUID. */
        uint16_t                        service_handle;                                 /**< Handle of Nordic UART Service (as provided by the SoftDevice). */
        ble_gatts_char_handles_t        custom_value_handles_characteristic_1;          /**< Handles related to the TX characteristic (as provided by the SoftDevice). */
        ble_gatts_char_handles_t        custom_value_handles_characteristic_2;          /**< Handles related to the RX characteristic (as provided by the SoftDevice). */
        ble_cus_data_handler_t          data_handler;                                   /**< Event handler to be called for handling received data. */
    };

     

    Muqarrab said:
    Is there any other example that has 2+ characteristics in 1 service and sending back data?

     Yes. ble_app_uart, but only one is for sending. The other characteristic is only for receiving. But what I am trying to tell you is that this example points to the characteristic handle that belongs to that specific characteristic. 

  • Thanks I have solved the problem by adding this.

    /**@brief Custom Service structure. This contains various status information for the service. */
    struct ble_cus_s
    {
        uint16_t                      service_handle;                 /**< Handle of Custom Service (as provided by the BLE stack). */
        uint16_t                      conn_handle;                    /**< Handle of the current connection (as provided by the BLE stack, is BLE_CONN_HANDLE_INVALID if not in a connection). */
        uint8_t                       uuid_type; 
        ble_gatts_char_handles_t      custom_value_handles;           /**< Handles related to the Custom Value characteristic. */
        ble_gatts_char_handles_t      custom_value_handles_2;           /**< Handles related to the Custom Value characteristic. */
    
    };    
    

Related