Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

Getting Wrong Value Handle and CCCD handle, when adding multiple characteristics in a service.

Hi All,

I am using nRF52 DK board & nRF5_SDK_17.1.0 for Development(Emulated Project for nRF52805), My application needs creation of multiple service and multiple characteristics. So, I created a Service of two characteristics as shown in code snippet below. Services and its characteristics  populated on mobile application with its properties(read/write/notify as done at the time of characteristics making). but when Write/Notify button pressed from Mobile application, I am getting Wrong value handle and cccd handle and not able to read data, written from mobile application side. but  I am able to read data written from mobile application, if only one characteristics is created.

below,i am sharing my code snippet, kindly correct me where i am doing wrong.

/**@brief   Macro for defining a Connectivity_Service instance.
 *
 * @param   _name   Name of the instance.
 * @hideinitializer
 */
#define BLE_CONNECTIVITY_SER_DEF(_name)                                                             \
static ble_Connecivity_t _name;                                                                     \
NRF_SDH_BLE_OBSERVER(_name ## _obs,                                                                 \
                     BLE_HRS_BLE_OBSERVER_PRIO,                                                     \
                     ble_connectivity_on_ble_evt, &_name)
typedef enum
{
    /*! \VarExTransmissionofsmartphoneCHANNEL1 */
    BLE_VAREX_TX_OF_SM_CH_1_EVT_NOTIFICATION_ENABLED,                             /* "VarEx Transmission of smartphone CHANNEL 1" notification enabled event. */
    BLE_VAREX_TX_OF_SM_CH_1_EVT_NOTIFICATION_DISABLED,                            /* "VarEx Transmission of smartphone CHANNEL 1" notification disabled event. */

    /*! \VarExTransmissionOfTCUCHANNEL1ACK */
    VAREX_TX_OF_TCU_CH1_ACK_EVT_NOTIFICATION_ENABLED,                             /* "VarEx Transmission of smartphone CHANNEL 1 ACK" notification enabled event. */
    VAREX_TX_OF_TCU_CH1_ACK_EVT_NOTIFICATION_DISABLED,                            /* "VarEx Transmission of smartphone CHANNEL 1 ACK" notification disabled event. */

    BLE_CUS_EVT_DISCONNECTED,
    BLE_CUS_EVT_CONNECTED
} ble_conn_service_type_t;

/**@brief Connectivity Service event. */
typedef struct
{
    ble_conn_service_type_t evt_type;                                  /**< Type of event. */
} ble_conn_service_evt_t;

// Forward declaration of the ble_Connecivity_t type.
typedef struct ble_conn_Service_s ble_Connecivity_t;


/**@brief Connectivity Service event handler type. */
typedef void (*ble_connectivity_evt_handler_t) (ble_Connecivity_t * p_conn, ble_conn_service_evt_t * p_evt);

/**@brief Connectivity Service init structure. This contains all options and data needed for
 *        initialization of the service.*/
typedef struct
{
    ble_connectivity_evt_handler_t                      evt_handler;                        /*! Event handler to be called for handling events in the Connectivity Service. */
    ble_srv_cccd_security_mode_t  			VarExTXofSm_Ch1_char_attr_md;       /*! Initial security level for "VarEx Transmission of smartphone CHANNEL 1" characteristics attribute */
    ble_srv_cccd_security_mode_t  			VarExTXofSm_Ch1_ACK_char_attr_md;   /*! Initial security level for "VarEx Transmission of smartphone CHANNEL 1 \ACK" characteristics attribute */
} ble_Connectivity_Ser_init_t;

/**@brief Connectivity Service structure. This contains various status information for the service. */
struct ble_conn_Service_s
{
    ble_connectivity_evt_handler_t                      evt_handler;                        /*! Event handler to be called for handling events in the Connectivity Service. */
    uint16_t                      			service_handle;                     /*! Handle of Connectivity Service (as provided by the BLE stack). */
    ble_gatts_char_handles_t      			VarExTXofSm_Ch1_char_handles;       /*! Handles related to the "VarEx Transmission of smartphone CHANNEL 1" characteristic. */
    ble_gatts_char_handles_t      			VarExTXofSm_Ch1_ACK_char_handles;   /*! Handles related to the "VarEx Transmission of smartphone CHANNEL 1 \ACK" characteristic. */
    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; 
};
/**@brief Function for handling the Write event.
 *
 * @param[in]   p_con       Connectivity Service structure.
 * @param[in]   p_ble_evt   Event received from the BLE stack.
 */
static void on_write(ble_Connecivity_t * p_con, ble_evt_t const * p_ble_evt)
{
    ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
    /*!         \VarEx Transmission of smartphone CHANNEL 1 Write Event             */  
    // "VarEx Transmission of smartphone CHANNEL 1" Characteristic Written to.
    if (p_evt_write->handle == p_con->VarExTXofSm_Ch1_char_handles.value_handle)
    {
        //Update Received data to Buffter
        memset(&valPtr,0,sizeof(valPtr)/sizeof(valPtr[0]));
        memcpy(&valPtr,&p_evt_write->data,p_evt_write->len);
        if(*p_evt_write->data == 0x01)
        {
            #if ((DEVELOP_IN_NRF52832) && (NRFX_COREDEP_DELAY_US_LOOP_CYCLES==3))
            nrf_gpio_pin_toggle(LED_4); 
            #endif
            NRF_LOG_INFO("Data Wr VarExTXofSm_Ch1 = %d\r\n", *p_evt_write->data);
        }
        else if(*p_evt_write->data == 0x00)
        {
            #if ((DEVELOP_IN_NRF52832) && (NRFX_COREDEP_DELAY_US_LOOP_CYCLES==3))
            nrf_gpio_pin_toggle(LED_4);  
            #endif
            NRF_LOG_INFO("Data Wr VarExTXofSm_Ch1 = %d\r\n", *p_evt_write->data);
        }
        else
        {
            //Do nothing
            NRF_LOG_INFO("Data Wr VarExTXofSm_Ch1=%s\r\n",valPtr);
        }
    }

    /*!         \VarEx Transmission of smartphone CHANNEL 1 ACK Write Event             */  
    // "VarEx Transmission of smartphone CHANNEL 1 ACK" Characteristic Written to.
    if (p_evt_write->handle == p_con->VarExTXofSm_Ch1_ACK_char_handles.value_handle)
    {
        //Update Received data to Buffter
        memset(&valPtr2,0,sizeof(valPtr2)/sizeof(valPtr2[0]));
        memcpy(&valPtr2,&p_evt_write->data,p_evt_write->len);
        if(*p_evt_write->data == 0x01)
        {
            #if ((DEVELOP_IN_NRF52832) && (NRFX_COREDEP_DELAY_US_LOOP_CYCLES==3))
            nrf_gpio_pin_toggle(LED_4); 
            #endif
            NRF_LOG_INFO("Data Wr VarExTXofSm_Ch1_ACK = %d\r\n", *p_evt_write->data);
        }
        else if(*p_evt_write->data == 0x00)
        {
            #if ((DEVELOP_IN_NRF52832) && (NRFX_COREDEP_DELAY_US_LOOP_CYCLES==3))
            nrf_gpio_pin_toggle(LED_4);  
            #endif
            NRF_LOG_INFO("Data Wr VarExTXofSm_Ch1_ACK = %d\r\n", *p_evt_write->data);
        }
        else
        {
            //Do nothing
            NRF_LOG_INFO("Data Wr VarExTXofSm_Ch1_ACK=%s\r\n",valPtr);
        }
    }

    // Check if the "VarEx Transmission of smartphone CHANNEL 1" CCCD is written to and that the value is the appropriate length, i.e 2 bytes.
    if ((p_evt_write->handle == p_con->VarExTXofSm_Ch1_char_handles.cccd_handle) && (p_evt_write->len == 2))
    {
        // CCCD written, call application event handler
        if (p_con->evt_handler != NULL)
        {
            ble_conn_service_evt_t evt;
            NRF_LOG_INFO("CCCD Wr VarExTXofSm_Ch1=%s\r\n",(*p_evt_write->data)?("True"):("False"));
            if (ble_srv_is_notification_enabled(p_evt_write->data))
            {
                evt.evt_type = BLE_VAREX_TX_OF_SM_CH_1_EVT_NOTIFICATION_ENABLED;
            }
            else
            {
                evt.evt_type = BLE_VAREX_TX_OF_SM_CH_1_EVT_NOTIFICATION_DISABLED;
            }
            // Call the application event handler.
            p_con->evt_handler(p_con, &evt);
        }
    }

}
void ble_connectivity_on_ble_evt( ble_evt_t const * p_ble_evt, void * p_context)
{
    ble_Connecivity_t * p_con = (ble_Connecivity_t *) p_context;
    
    NRF_LOG_INFO("BLE event received. Event type = %d\r\n", p_ble_evt->header.evt_id); 
    if (p_con == NULL || p_ble_evt == NULL)
    {
        return;
    }
    
    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
            on_connect(p_con, p_ble_evt);
            break;

        case BLE_GAP_EVT_DISCONNECTED:
            on_disconnect(p_con, p_ble_evt);
            break;

        case BLE_GATTS_EVT_WRITE:
            on_write(p_con, p_ble_evt);
            break;
/* Handling this event is not necessary
        case BLE_GATTS_EVT_EXCHANGE_MTU_REQUEST:
            NRF_LOG_INFO("EXCHANGE_MTU_REQUEST event received.\r\n");
            break;
*/
        default:
            // No implementation needed.
            break;
    }
}
/**@brief Function for adding the Connectivity Value characteristic.
 *
 * @param[in]   p_con        Connectivity Service structure.
 * @param[in]   p_con_init   Information needed to initialize the service.
 *
 * @return      NRF_SUCCESS on success, otherwise an error code.
 */
static uint32_t custom_value_chararacterstic_add(ble_Connecivity_t * p_con, const ble_Connectivity_Ser_init_t * p_con_init,ble_gatts_char_md_t *char_meta_data,ble_gatts_attr_t    *attr_characteristic_value)
{
    uint32_t            err_code;
    ble_gatts_char_md_t char_md;  
    ble_gatts_attr_t    attr_char_value;
    //GATT Characteristic metadata
    memset(&char_md, 0, sizeof(char_md));

    char_md.char_props.read   = char_meta_data->char_props.read;
    char_md.char_props.write  = char_meta_data->char_props.write;
    char_md.char_props.notify = char_meta_data->char_props.notify; 
    char_md.p_char_user_desc  = char_meta_data->p_char_user_desc;
    char_md.p_char_pf         = char_meta_data->p_char_pf;
    char_md.p_user_desc_md    = char_meta_data->p_user_desc_md;
    char_md.p_cccd_md         = char_meta_data->p_cccd_md; 
    char_md.p_sccd_md         = char_meta_data->p_sccd_md;
    memset(&attr_char_value, 0, sizeof(attr_char_value));

    attr_char_value.p_uuid    = attr_characteristic_value->p_uuid;
    attr_char_value.p_attr_md = attr_characteristic_value->p_attr_md;
    attr_char_value.init_len  = attr_characteristic_value->init_len;
    attr_char_value.init_offs = attr_characteristic_value->init_offs;
    attr_char_value.max_len   = attr_characteristic_value->max_len;
    attr_char_value.p_value   = attr_characteristic_value->p_value;

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

    return NRF_SUCCESS;
}

uint32_t ble_connectivity_Service_init(ble_Connecivity_t * p_con, const ble_Connectivity_Ser_init_t * p_con_init)
{
    
    if (p_con == NULL || p_con_init == NULL)
    {
        return NRF_ERROR_NULL;
    }

    uint32_t   err_code;
    ble_uuid_t ble_uuid;
    
    //Recently Added GATT Characterstic MetaData
    ble_gatts_char_md_t char_md;
    //Client Characteristic Configuration Descriptor
    ble_gatts_attr_md_t cccd_md;
    //Recently Added Attribute metadata.
    ble_gatts_attr_md_t attr_md;
    //Recently Added for Attribute Characterstic Value
    ble_gatts_attr_t    attr_char_value;

    // Initialize service structure
    p_con->evt_handler               = p_con_init->evt_handler;
    p_con->conn_handle               = BLE_CONN_HANDLE_INVALID;

    // Add Connectivity Service UUID
    ble_uuid128_t base_uuid = {CONNECTIVITY_SERVICE_UUID_BASE};
    err_code =  sd_ble_uuid_vs_add(&base_uuid, &p_con->uuid_type);
    VERIFY_SUCCESS(err_code);
    
    ble_uuid.type = p_con->uuid_type;
    ble_uuid.uuid = CONNECTIVITY_SERVICE_UUID;

    // Add the Connectivity Service
    err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_con->service_handle);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
    /**********************************************************VAREX_TX_OF_SM_CH1_CHAR_UUID**********************************************************************/
    //Attribute metadata forClient Characteristic Configuration Descriptor(CCCD)
    memset(&cccd_md, 0, sizeof(cccd_md));
    //Read/Write  operation on cccd should be possible without authentication.
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
    
    cccd_md.write_perm = p_con_init->VarExTXofSm_Ch1_char_attr_md.cccd_write_perm;
    cccd_md.vloc       = BLE_GATTS_VLOC_STACK;
    //GATT Characteristic metadata
    memset(&char_md, 0, sizeof(char_md));
    char_md.char_props.read   = 1;
    char_md.char_props.write  = 1;
    char_md.char_props.notify = 1; 
    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         = &cccd_md; 
    char_md.p_sccd_md         = NULL;
    
    ////Add Base UUID For Characterstics
    //ble_uuid128_t base_uuid1 = {VAREX_TX_OF_SM_CH1_CHAR_UUID_BASE};
    //err_code =  sd_ble_uuid_vs_add(&base_uuid1, &p_con->uuid_type);
    //VERIFY_SUCCESS(err_code);
    //Characterstic UUID
    ble_uuid.type = p_con->uuid_type;
    ble_uuid.uuid = VAREX_TX_OF_SM_CH1_CHAR_UUID;

    memset(&attr_md, 0, sizeof(attr_md));
    attr_md.read_perm  = p_con_init->VarExTXofSm_Ch1_char_attr_md.read_perm;
    attr_md.write_perm = p_con_init->VarExTXofSm_Ch1_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;

    //Attribute Characterstic Values
    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(valPtr);//sizeof(uint8_t);
    attr_char_value.p_value   = valPtr;

    // Add "VarEx Transmission of smartphone CHANNEL 1" characteristic
    err_code = custom_value_chararacterstic_add(p_con, p_con_init,&char_md,&attr_char_value);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
    /***************************************************************************************************************************************************************/

    /**********************************************************VAREX_TX_OF_TCU_CH1_ACK_UUID**********************************************************************/
    //Attribute metadata forClient Characteristic Configuration Descriptor(CCCD)
    memset(&cccd_md, 0, sizeof(cccd_md));
    //Read/Write  operation on cccd should be possible without authentication.
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
    
    cccd_md.write_perm = p_con_init->VarExTXofSm_Ch1_ACK_char_attr_md.cccd_write_perm;
    cccd_md.vloc       = BLE_GATTS_VLOC_STACK;
    //GATT Characteristic metadata
    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/* &cccd_md*/; 
    char_md.p_sccd_md         = NULL;

    ////Add Base UUID For Characterstics
    //ble_uuid128_t base_uuid2 = {VAREX_TX_OF_SM_CH1_ACK_UUID_BASE};
    //err_code =  sd_ble_uuid_vs_add(&base_uuid2, &p_con->uuid_type);
    //VERIFY_SUCCESS(err_code);

    //Characterstic UUID
    ble_uuid.type = p_con->uuid_type;
    ble_uuid.uuid = VAREX_TX_OF_TCU_CH1_ACK_UUID;

    memset(&attr_md, 0, sizeof(attr_md));
    attr_md.read_perm  = p_con_init->VarExTXofSm_Ch1_ACK_char_attr_md.read_perm;
    attr_md.write_perm = p_con_init->VarExTXofSm_Ch1_ACK_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;

    //Attribute Characterstic Values
    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(valPtr2);//sizeof(uint8_t);
    attr_char_value.p_value   = valPtr2;

    // Add Connectivity Value characteristic
    err_code = custom_value_chararacterstic_add(p_con, p_con_init,&char_md,&attr_char_value);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
    /***************************************************************************************************************************************************************/
  return NRF_SUCCESS;
}

Service Initialization 

    // Initialize Connectivity Service init structure to zero.
    memset(&Con_Ser_Init, 0, sizeof(Con_Ser_Init));
    Con_Ser_Init.evt_handler                = on_connectivity_evt;
    
    /*! VarEx Transmission of smartphone CHANNEL \1 */
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&Con_Ser_Init.VarExTXofSm_Ch1_char_attr_md.cccd_write_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&Con_Ser_Init.VarExTXofSm_Ch1_char_attr_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&Con_Ser_Init.VarExTXofSm_Ch1_char_attr_md.write_perm);
    /*! VarEx Transmission of smartphone CHANNEL 1 \ACK */
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&Con_Ser_Init.VarExTXofSm_Ch1_ACK_char_attr_md.cccd_write_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&Con_Ser_Init.VarExTXofSm_Ch1_ACK_char_attr_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&Con_Ser_Init.VarExTXofSm_Ch1_ACK_char_attr_md.write_perm);

    err_code = ble_connectivity_Service_init(&m_connectivity, &Con_Ser_Init);
    APP_ERROR_CHECK(err_code);

Related