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

sd_ble_gatts_characteristic_add invalid param when adding CCCD for notifications

Hello,

I implemented a function to add characteristics to a service, but I'm having a problem when I add notifiable characteristics. I pass a set of flags to my function, and when I set the NOTIFY bit, sd_ble_gatts_characteristic_add returns NRF_ERROR_INVALID_PARAM. Without that bit, all characteristics work just fine.

I looked at all the questions regarding this error, but most of them are because of the permissions. Every piece of code that I looked at, sets the CCCD exactly the same way as I do. I attached a part of my code below.

// OUR_JOB: Step 2.F Add read/write properties to our characteristic
    ble_gatts_char_md_t char_md;
    memset(&char_md, 0, sizeof(char_md));
    char_md.p_char_user_desc	= NULL;
	char_md.p_char_pf			= NULL;
	char_md.p_user_desc_md		= NULL;
	char_md.p_sccd_md			= NULL;
	char_md.p_cccd_md			= NULL;
    char_md.char_props.read  = (flags & READ)  ? 1 : 0;
    char_md.char_props.write = (flags & WRITE) ? 1 : 0;

    //WHEN THIS GETS EXECUTED, I GET AN ERROR AT RUNTIME
    if (flags & NOTIFY) {
		// OUR_JOB: Step 3.A, Configuring Client Characteristic Configuration Descriptor metadata and add to char_md structure
		ble_gatts_attr_md_t cccd_md;
		memset(&cccd_md, 0, sizeof(cccd_md));
		BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
		BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
		cccd_md.vloc                = BLE_GATTS_VLOC_STACK;		// CCCD should always be on the stack
		char_md.p_cccd_md           = &cccd_md;
		char_md.char_props.notify   = 1;
    }

    // OUR_JOB: Step 2.B, Configure the attribute metadata
    ble_gatts_attr_md_t attr_md;
    memset(&attr_md, 0, sizeof(attr_md));
    attr_md.vloc        = BLE_GATTS_VLOC_STACK;
    attr_md.rd_auth		= ((flags & READ)  && ((flags & CONST) == 0)) ? 1 : 0;
    attr_md.wr_auth		= ((flags & WRITE) && ((flags & CONST) == 0)) ? 1 : 0;
    attr_md.vlen 		= (flags & VARIABLE_LEN) ? 1 : 0;

    // OUR_JOB: Step 2.G, Set read/write security levels to our characteristic
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);

    // OUR_JOB: Step 2.C, Configure the characteristic value attribute
    ble_gatts_attr_t    attr_char_value;
    memset(&attr_char_value, 0, sizeof(attr_char_value));
    attr_char_value.p_uuid      = &char_uuid;
    attr_char_value.p_attr_md   = &attr_md;

    // OUR_JOB: Step 2.H, Set characteristic length in number of bytes
    attr_char_value.max_len     = max_len;
	attr_char_value.init_len    = init_len;
    if (init_len>0 && value != (uint8_t *)0) {
		attr_char_value.p_value     = value;
    }

    // OUR_JOB: Step 2.E, Add our new characteristic to the service
    err_code = sd_ble_gatts_characteristic_add(p_custom_service->service_handle,
                                               &char_md,
                                               &attr_char_value,
                                               char_handle);
  • I just added a small change to the code and it got rid of the error. I basically changed the scope of the if statement like this:

    // OUR_JOB: Step 3.A, Configuring Client Characteristic Configuration Descriptor metadata and add to char_md structure
    	ble_gatts_attr_md_t cccd_md;
    	memset(&cccd_md, 0, sizeof(cccd_md));
    	BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
    	BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
    	cccd_md.vloc                = BLE_GATTS_VLOC_STACK;		// CCCD should always be on the stack
    	char_md.p_cccd_md           = &cccd_md;
    
    if (flags & NOTIFY) {
    	char_md.char_props.notify   = 1;
    }
    

    I didn't do that because I thought I didn't need a CCCD if I didn't need notifications for a particular characteristic, so I just avoided it by putting it inside the if. What's very curious to me is that even now, only the characteristics created with the NOTIFY flag have a CCCD handle. All others don't have it. Does the softdevice only create handles for characteristics with char_md.char_props.notify = 1? I'm aware that this is a question in an answer, but I believe it has more context if I leave it here, and it can be answered in a single comment :)

Related