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

Not receiving Notifications/Indications in the central events handler [nRF52832 - s132]

Hello,

Let me introduce you to the issue:

I am working on a custom firmware for a custom hardware, based on the nRF52832 and SD S132, as a central which need to connect to an already implemented peripheral device and write, read and receive indications/notifications from it.

I am developing on Keil 5.0 and I have the PCA10040 for development/testing purposes.

So the main issue here is that in the central part, after I connect successfully, discover services and characteristics with VS (vendor specific) UUIDs, I enable the notifications/indications for a characteristic, and when I write an encrypted value to that characteristic, I never receive the notifications/indication the peripheral sends as response.

Different points to consider:

  • I mention both notifications and indications as the peripheral sends the same information on either of them when enabled. 
  • The peripheral works properly, as I developed an Android APP for testing purposes and the notifications/indications are received correctly.
  • The encryption method on the central is working properly. Results are compared with the Android APP. Also, when writing the encrypted value to the peripheral, this processes the right "physical" task, but the notification (as response) from it it is never received.

Once I am connected to the peripheral and all the services and characteristics I need are discovered, I enable the notifications on CCCD handle using the code:

err_code = ble_peripheral_char_notif_enable(p_custom_srv_c);
APP_ERROR_CHECK(err_code);

And the ''ble_peripheral_char_notif_enable()" function is:

uint32_t ble_peripheral_char_notif_enable(ble_custom_srv_c_t * evt)
{
	VERIFY_PARAM_NOT_NULL(evt);

    if ( (evt->conn_handle == BLE_CONN_HANDLE_INVALID)
       ||(evt->handles[BLE_CUSTOM_SRV_C_CHAR_CCCD] == BLE_GATT_HANDLE_INVALID)
       )
    {
        return NRF_ERROR_INVALID_STATE;
    }
    return cccd_configure(evt->conn_handle, evt->handles[BLE_CUSTOM_SRV_C_CHAR_CCCD], true);
}

So above I check if my handles for CCCD and connection are OK. This works properly. (*the handles value is a structure which keeps all the handles for services and characteristics).

Then the cccd_configure function looks like:

static uint32_t cccd_configure(uint16_t conn_handle, uint16_t cccd_handle, bool enable)
{

	uint8_t buf[BLE_CCCD_VALUE_LEN];

    buf[0] = enable ? BLE_GATT_HVX_INDICATION : 0;
    buf[1] = 0;

    ble_gattc_write_params_t const write_params =
    {
        .write_op = BLE_GATT_OP_WRITE_REQ,
        .flags    = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_WRITE,
        .handle   = cccd_handle,
        .offset   = 0,
        .len      = BLE_CCCD_VALUE_LEN,
        .p_value  = (uint8_t *)buf
    };

    return sd_ble_gattc_write(conn_handle, &write_params);
}

After calling the above function, I successfully receive the event BLE_GATTC_EVT_WRITE_RSP for my char_type -> BLE_CUSTOM_SRV_C_CHAR_CCCD.

No error until here so I suppose my events handler works well.

Now, I write a characteristic with an encrypted value, and the Peripheral receives it properly (as it moves a servo motor as it should) but I never receive anything on the BLE_GATTC_EVT_HVX as I should as a response from it.

This is how my events handler looks like:

static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
    uint16_t conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
    uint16_t role        = ble_conn_state_role(conn_handle);
	
    if (role == BLE_GAP_ROLE_PERIPH || ble_evt_is_advertising_timeout(p_ble_evt))
    {
        on_ble_peripheral_evt(p_ble_evt);
    }
    else if ((role == BLE_GAP_ROLE_CENTRAL))
    {

        ble_custom_srv_on_ble_evt(p_ble_evt, &m_custom_srv_c);
        ble_dis_c_on_ble_evt(p_ble_evt, &m_dis_c);
        on_ble_central_evt(p_ble_evt);
    }
}

which forwards the events to my custom handler:

void ble_custom_srv_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
    ble_custom_srv_c_t * p_ble_custom_srv_c = (ble_custom_srv_c_t *) p_context;

    if ((p_ble_custom_srv_c == NULL) || (p_ble_evt == NULL))
    {
        return;
    }

    if (p_ble_custom_srv_c->conn_handle == BLE_CONN_HANDLE_INVALID)
    {
        return;
    }

    switch (p_ble_evt->header.evt_id)
    {
		case BLE_GATTC_EVT_HVX:
            on_hvx(p_ble_custom_srv_c, p_ble_evt);
            break;
				
        case BLE_GATTC_EVT_READ_RSP:
            on_read_rsp(p_ble_custom_srv_c, p_ble_evt);
            break;

        case BLE_GAP_EVT_DISCONNECTED:
            on_disconnected(p_ble_custom_srv_c, p_ble_evt);
            break;
            
		case BLE_GATTC_EVT_WRITE_RSP:
			on_write_rsp(p_ble_custom_srv_c, p_ble_evt);
			break;
			
        default:
            // No implementation needed.
            break;
    }
}

All the above cases are triggered properly when needed, except the HVX one.

I have also tried to read the CCCD value from the central side after enabling notifications/indications as seen on this post, but without success. The values are never, 0x0000, 0x0001, or 0x0002, but something like 0x20007723.

Furthermore, I have tried to compile and run different central examples from the SDK on my PCA10040 but without success, it never goes past "ble_stack_init()" from the main func.

Any idea on what am I doing wrong? Any advice on what I should test?

Thanks in advance for your help and sorry for the long question.

BR,

Parents
  • HI Gabriel, 

    would you be able to provide a sniffer trace of the on-air data between the nRF52 central and the peripheral? This way we would be able to check whether the nRF52 is correctly enabling notifcations by writting to the CCCD of the characteristic on the peripheral side. 

    Best regards

    Bjørn

  • Hi Björn, thank you for your reply and sorry for my late answer. I did not really knew I could see the connection packets, but only the advertising ones (noobie me...) with the sniffer.

    Now, and after comparing with the Android connection, I saw different issues I am trying to fix. This is one of them:

    As you can see, the mentioned service is the default "Device Information 0x180a" and the characteristic is my custom one. This is obviously not right as my characteristic is in a custom service with UUID: 00035b0358e607dd021a08123a000300. This causes a "Malformed Packet".

    The problem comes from the discovery event handler, where at some point I forward the events at both my custom event handler and the default event handler for default characteristics and services, and somewhere over there the handles for each service and characteristics are overlapped.

    I will isolate the connection to handle only my custom service, and if this doesn't fix the issue I will reply again here.

    Thanks again for the hint.

    BR,

  • Happy to help. :) Just post a comment here if you're still having issues. 

    Bjørn

  • I am not able to understand where the 0x180a UUID is assigned. I isolated my code and commented everything related to generic services and characteristics, and I still got it. Also, where does that service come from when enabling the CCCD? The only parameters there are conn_handle and write_params:

    static uint32_t cccd_configure(uint16_t conn_handle, uint16_t cccd_handle, bool enable)
    {
    
    	uint8_t buf[BLE_CCCD_VALUE_LEN];
    
        buf[0] = enable ? BLE_GATT_HVX_INDICATION : 0;
        buf[1] = 0;
    
        ble_gattc_write_params_t const write_params =
        {
            .write_op = BLE_GATT_OP_WRITE_REQ,
            .flags    = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_WRITE,
            .handle   = cccd_handle,
            .offset   = 0,
            .len      = BLE_CCCD_VALUE_LEN,
            .p_value  = (uint8_t *)buf
        };
    
        return sd_ble_gattc_write(conn_handle, &write_params);
    }

    Where

    conn_handle: 0x0000
    cccd_handle: 0x0019

    Are the right ones.

    The handle on the sniffer should look like this (when enabled with nRF Connect APP):

    BR,

  • Hi Gabriel, 

    Gabriel C. said:
    I am not able to understand where the 0x180a UUID is assigned. I isolated my code and commented everything related to generic services and characteristics, and I still got it.

    Is this on the peripheral device or the central device? Or is the central device also a peripheral device at the same time? If its the latter, can you post the services_init() function as well as the code where you define your custom service and its characteristics, i.e. where you call sd_ble_gatts_service_add and characteristic_add(which in turn calls sd_ble_gatts_characteristic_add) ?

    Gabriel C. said:
    Also, where does that service come from when enabling the CCCD?

     No quite sure what you mean here. The cccd_configure is a function that writes to the CCCD of characteristic on the peripheral that the central want to receive notifications/indications from. 

    Best regards

    Bjørn

Reply
  • Hi Gabriel, 

    Gabriel C. said:
    I am not able to understand where the 0x180a UUID is assigned. I isolated my code and commented everything related to generic services and characteristics, and I still got it.

    Is this on the peripheral device or the central device? Or is the central device also a peripheral device at the same time? If its the latter, can you post the services_init() function as well as the code where you define your custom service and its characteristics, i.e. where you call sd_ble_gatts_service_add and characteristic_add(which in turn calls sd_ble_gatts_characteristic_add) ?

    Gabriel C. said:
    Also, where does that service come from when enabling the CCCD?

     No quite sure what you mean here. The cccd_configure is a function that writes to the CCCD of characteristic on the peripheral that the central want to receive notifications/indications from. 

    Best regards

    Bjørn

Children
  • Hi Bjørn,

    Yes, my device is both peripheral and central at the same time. I did not developed the peripheral side so the issue could also com from here. I commented the advertising start functions and services_init() and the 0x180a changed to 0x1801 (Generic Attribute Profile). Let me show you some code:

    This is the services init:

    static void services_init(void)
    {
    	device_information_srv_init();
    	custom_service_init();
    	ibeacon_service_init();
    }

    Here is how I declare my "vendor specific" UUIDs for the central services discovery:

    ret_code_t ble_custom_srv_c_init(ble_custom_srv_c_t * p_ble_custom_srv_c, ble_custom_srv_c_init_t * p_ble_custom_srv_c_init)
    {
    	uint32_t err_code;
    
        VERIFY_PARAM_NOT_NULL(p_ble_custom_srv_c);
        VERIFY_PARAM_NOT_NULL(p_ble_custom_srv_c_init);
    	
    	//UUID 128bits for wKnob Service
    	ble_uuid_t        service_uuid;
    	ble_uuid128_t     base_uuid = BLE_UUID_WKNOB_C_SERVICE;
    	service_uuid.uuid = BLE_WKNOB_BASE_UUID;
    	err_code = sd_ble_uuid_vs_add(&base_uuid, &service_uuid.type);
    	APP_ERROR_CHECK(err_code);
    	
    	err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &service_uuid, &p_ble_custom_srv_c_init->service_handle);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    	
    	//UUID 128bits for wKnob Characteristic
    	ble_uuid_t          char_uuid;
    	ble_uuid128_t       char_base_uuid = BLE_UUID_WKNOB_C_CHAR;
    	char_uuid.uuid      = BLE_WKNOB_BASE_UUID;
    	err_code = sd_ble_uuid_vs_add(&char_base_uuid, &char_uuid.type);
    	APP_ERROR_CHECK(err_code);
    	
        p_ble_custom_srv_c->conn_handle   = BLE_CONN_HANDLE_INVALID;
        p_ble_custom_srv_c->evt_handler   = p_ble_custom_srv_c_init->evt_handler;
        p_ble_custom_srv_c->error_handler = p_ble_custom_srv_c_init->error_handler;
        memset(p_ble_custom_srv_c->handles, BLE_GATT_HANDLE_INVALID, sizeof(p_ble_custom_srv_c->handles));
    
        return ble_db_discovery_evt_register(&service_uuid);
    }

    After more testing it looks to me like it could have something to do with the order in which everything is declared and called. This is how all is called in main.c:

    int main(void)
    {
        // Initialize.
        log_init();
        timer_init();
        scheduler_init();
    
        power_management_init();
        ble_stack_init();
        sd_power_dcdc_mode_set(NRF_POWER_DCDC_ENABLE);
    		
    	db_discovery_init();
    	
    	//Here is where the service for central discovery is called
    	custom_srv_c_init();
    
        gatt_init();
        conn_params_init();
        custom_sys_init();
    
        //Here is where the peripheral services and advertising starts
        services_init();
        gap_params_init();
        advertising_init();
    	advertising_start(APP_BLE_CONN_CFG_TAG);
    
    
        //Other declarations
        motor_ctrl_init();
        peripherals_ctrl_init();
    	nrf_drv_gpiote_init();
    	mic_ctrl_init();
    	ext_flash_init();
    	ext_flash_deep_sleep();
        gprs_ctrl_init();
        custom_init();
    
        peripherals_ctrl_led_switch_turn_on(3);  
    
        gprs_ctrl_set_msg_type(ACTION_REQUEST_MSG);
        
        //Here is where I start the scan and connection to the remote peripheral
    	scan_init();
    	
    	
    	uint8_t mac[6] = {0x00, 0x1E, 0xC0, 0x5F, 0x00, 0xE3};
    	scan_custom_dev_by_name(WKNOB_DEV_TYPE, mac);
    		
    		
        // Enter main loop.
        for (;;)
        {
    			
            custom_periodic_task();
    			
            gprs_ctrl_task();
    				
    		idle_state_handle();
        }
    }

    Again, If I comment the peripheral services init and advertising start, or if I move the custom_srv_c_init() after it, when calling the CCCD function everything changes again (both the service UUID and char UUID).

    Seems like the SD is stuck on peripheral mode, so when I try to enable the CCCD as central, it tries to do so for the services declared in the peripheral side. Could that be possible?

    Thank you a lot for the help you are providing.

    BR,

  • Hi Gabriel, 

    The following snippet in ble_custom_srv_c_init() does not belong there. 

    	err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &service_uuid, &p_ble_custom_srv_c_init->service_handle);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }

    This should be placed in the custom_service_init(). What does custom_service_init() look like?

    Best regards

    Bjørn

  • Hi Bjørn,

    Thanks for the hint. I removed that snippet. It looked to me that when I programmed it it was not adding and detecting properly the VS UUID. But it does. 

    Here is the custom_service_init():

    void custom_service_init(void)
    {
        ret_code_t                  err_code;
    	custom_srv_init_t           custom_srv;
    
        // Initialize evt handlers and the service
        memset(&custom_srv, 0, sizeof(custom_srv));
        custom_srv.write_evt_handler = custom_srv_gatts_write_evt_handler;
        custom_srv.read_evt_handler  = custom_srv_gatts_read_evt_handler;
    	
        err_code = custom_srv_init(&m_ble_custom, &custom_srv);
        APP_ERROR_CHECK(err_code);    
    }

    Where "m_ble_custom" is a "custom_srv_t" configuration struct.

    And the custom_srv_init() looks like:

    ret_code_t custom_srv_init(custom_srv_t * p_custom, const custom_srv_init_t * p_custom_init)
    {
        uint32_t      err_code;
        ble_uuid_t    ble_uuid;
    
        VERIFY_PARAM_NOT_NULL(p_custom);
        VERIFY_PARAM_NOT_NULL(p_custom_init);
    	
        // Initialize the service structure.
        p_custom->conn_handle       = BLE_CONN_HANDLE_INVALID;
        p_custom->write_evt_handler = p_custom_init->write_evt_handler;
        p_custom->read_evt_handler  = p_custom_init->read_evt_handler;
    	
    	BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_CUSTOM_SERVICE);
    
        // Add the service.
        err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
                                            &ble_uuid,
                                            &p_custom->service_handle);
        VERIFY_SUCCESS(err_code);
        
        err_code = custom_status_char_add(p_custom, p_custom_init);
        VERIFY_SUCCESS(err_code);
        
        err_code = custom_relays_action_char_add(p_custom, p_custom_init);
        VERIFY_SUCCESS(err_code);
        
        err_code = custom_publickeys_char_add(p_custom, p_custom_init);
        VERIFY_SUCCESS(err_code);
        
        err_code = custom_hash_char_add(p_custom, p_custom_init);
        VERIFY_SUCCESS(err_code);
        
        err_code = custom_lockstate_char_add(p_custom, p_custom_init);
        VERIFY_SUCCESS(err_code);
        
        err_code = custom_fwupdate_char_add(p_custom, p_custom_init);
        VERIFY_SUCCESS(err_code);
        
        err_code = custom_iccid_char_add(p_custom, p_custom_init);
        VERIFY_SUCCESS(err_code);
        
        err_code = custom_rssi_char_add(p_custom, p_custom_init);
        VERIFY_SUCCESS(err_code);   
        
        return NRF_SUCCESS;    
    }

    Do you need to see any of the characteristics declaration functions?

    This is how the cccd write sniffed packet looks like after removing the code snippet you said:

    The information in there does not make any sense anymore.

    Is it useful If I show you a full connection sniffed log?

    BR and thanks a lot.

  • Hi Gabriel

    Yes, if you could attach the entire sniffer trace, then that would be great.  I would like to see the service discovery procedure as the handle for the CCCD write doesnt make sense if you are initializing the Device Information Service first in services_init(). 

    If you could also post the snippet where you're calling cccd_configure() then that would be great as well. 

    Best regards

    Bjørn

  • Hi Bjørn,

    For some reason yesterday it started working... sometimes. After removing the snipped you mentioned, and moving the custom_srv_c_init() down right before the scan_init() (in main.c), I started reeving the correct notifications for some connections. It works something around once every 5 connections. 

    However, the handles are still wrong when checking in Wireshark. Is it possible that for some reason Wireshark is not reading properly the connection data, maybe because interferences (I have around me many dozens of BLE devices).

    The received notifications (2), even if for the first one it says [Malformed Packet], contains the expected values.

    In the following link you can find the two sniffer trace files. One with a successful connection, cccd activated successfully and notifications received, and one with the connection successful but no notifications/indications:

    drive.google.com/.../1pLI9kpgB1jAxd8ZesN2l3A06QMzXFkx_

    1. In the good one, the connection starts at line nr. 364 and the notifications are received starting at line nr. 448.

    2. In the one without notifications, the connection starts at line nr. 209.

    About the CCCD, it is called from my custom discovery event handler:

    static void custom_srv_c_evt_handler(ble_custom_srv_c_t * p_custom_srv_c, ble_custom_srv_c_evt_t * p_custom_srv_c_evt){
    	switch (p_custom_srv_c_evt->evt_type)
    	{
    		case BLE_CUSTOM_SRV_C_EVT_DISCOVERY_COMPLETE:{
    				if (m_conn_handle_custom_srv_c == BLE_CONN_HANDLE_INVALID)
    				{
    						ret_code_t err_code;
    
    						m_conn_handle_custom_srv_c = p_custom_srv_c_evt->conn_handle;
    
    						err_code = ble_custom_srv_c_handles_assign(p_custom_srv_c, m_conn_handle_custom_srv_c, &p_custom_srv_c_evt->params.disc_complete.handles[0]); 
    						APP_ERROR_CHECK(err_code);
    
    
                            //Enable notifications for Custom CCCD
    						err_code = ble_custom_char_notif_enable(p_custom_srv_c_evt);
    						APP_ERROR_CHECK(err_code);
    				}
    		} 
    		break;
    		
    		......
    	}
    }

    Where the ble_custom_char_notif_enable() is a check for the handles:

    uint32_t ble_custom_char_notif_enable(ble_custom_srv_c_evt_t * evt)
    {
    	VERIFY_PARAM_NOT_NULL(evt);
    
        if ((evt->conn_handle == BLE_CONN_HANDLE_INVALID) ||(evt->params.disc_complete.handles[BLE_CUSTOM_SRV_C_CHAR_CCCD] == BLE_GATT_HANDLE_INVALID))
        {
            return NRF_ERROR_INVALID_STATE;
        }
        return cccd_configure(evt->conn_handle, evt->params.disc_complete.handles[BLE_CUSTOM_SRV_C_CHAR_CCCD], true);
    }

    And the ble_custom_srv_c_handles_assign() looks like:

    ret_code_t ble_custom_srv_c_handles_assign(ble_custom_srv_c_t                   * p_ble_custom_srv_c,
    										   uint16_t                             conn_handle,
    										   ble_custom_srv_c_handle_t const      * p_peer_handles)
    {
        VERIFY_PARAM_NOT_NULL(p_ble_watchman_srv_c);
    
        p_ble_custom_srv_c->conn_handle = conn_handle;
        p_ble_custom_srv_c_queue_reset();
        if (p_peer_handles != NULL)
        {
            memcpy(p_ble_custom_srv_c->handles, p_peer_handles, sizeof(p_ble_custom_srv_c->handles));
        }
        else
        {
            memset(p_ble_custom_srv_c->handles, BLE_GATT_HANDLE_INVALID, sizeof(p_ble_custom_srv_c->handles));
        }
        return NRF_SUCCESS;
    }

    Again, thanks for your help.

    BR,

    Gabriel.

Related