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;
    }
}

  • 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