Received empty data array after notification

Hi,

I adapted the nus_client from the Nordic Connect SDK v2.7.0. I changed the UUID and some function names, but in general the code stays the same. I can subscribe one of the characteristics and receive data from it. But the problem is, that everytime I receive a notification, I get another receive event with an empty data array. This leads to an unsubscribtion of the characteristic. 

00> D: [NOTIFICATION] data 0x2000d40c length 4
00> D: ble_mode_data_received
00> data: 01000000
00> D: [NOTIFICATION] data 0 length 0
00> D: [UNSUBSCRIBED]

The client only sends notifications with length = 1, so it's unlikely that the event comes from the client. The client is implemented in nrf5 SDK and sends data with the following code:

void ble_fob_on_mode_change(uint16_t conn_handle, ble_fob_t * p_fob, uint8_t mode_state)
{
    ble_gatts_value_t gatts_value;
    
    // Initialize value struct.
    memset(&gatts_value, 0, sizeof(gatts_value));

    gatts_value.len     = sizeof(uint8_t);
    gatts_value.offset  = 0;
    gatts_value.p_value = &mode_state;
	
    sd_ble_gatts_value_set(conn_handle,
                            p_fob->mode_char_handles.value_handle,
                            &gatts_value);
    
    // send notification
    if(conn_handle != BLE_CONN_HANDLE_INVALID)
    {
        ble_gatts_hvx_params_t params;
        uint16_t len = sizeof(mode_state);

        memset(&params, 0, sizeof(params));
        params.type   = BLE_GATT_HVX_NOTIFICATION;
        params.handle = p_fob->mode_char_handles.value_handle;
        params.p_data = &mode_state;
        params.p_len  = &len;

        sd_ble_gatts_hvx(conn_handle, &params);
    }
}

How can I resolve this problem?

Best regards,

Christian

Parents
  • Triple-check the return code of your notification function. A return value of BT_GATT_ITER_STOP will automagically unsubscribe!

  • Hi, 

    the only place where BT_GATT_ITER_STOP is returned is when the [UNSUBSCRIBE] happens. I attached the wireshark file for the connection.

     keyfob_unsubscribe.pcapng

    At the same time these were the logs of the peripheral:

    00> D: Connecting (0)
    00> I: Connected
    00> 
    00> D: Security changed: E7:B8:CB:8B:91:F8 (random) level 2
    00> D: The discovery procedure succeeded
    00> D: Getting handles from service.
    00> D: Found handle for mode characteristic.
    00> D: Found handle for CCC of mode characteristic.
    00> D: Found handle for serial characteristic.
    00> D: [SUBSCRIBED] mode characteristic
    00> D: [NOTIFICATION] data 0x2000e34c length 4
    00> D: ble_mode_data_received
    00> data: 01000000
    00> D: [NOTIFICATION] data 0 length 0
    00> D: [UNSUBSCRIBED]
    00> D: ble_mode_unsubscribed
    00> D: [SUBSCRIBED] mode characteristic
    00> D: [NOTIFICATION] data 0x2000e34c length 4
    00> D: ble_mode_data_received
    00> data: 00000000
    00> D: [NOTIFICATION] data 0 length 0
    00> D: [UNSUBSCRIBED]
    00> D: ble_mode_unsubscribed
    00> D: [SUBSCRIBED] mode characteristic

    At the moment I use a workaround and subscribe everytime when the characteristic gets unsubscribed. This is why there are more than one [SUBSCRIBED] messages.

    I hope this helps.

    Christian

  • Thanks for the reply. I tried it again with your instructions and received this: 

    6874.keyfob_unsubscribe.pcapng

  • Could you post the code that handles the notifications on the central side?

  • Hi,

    here are the corresponding parts of the central code:

     

    static uint8_t on_received(struct bt_conn *conn,
    			struct bt_gatt_subscribe_params *params,
    			const void *data, uint16_t length)
    {
    	struct bt_ili_client *ili;
    
    	/* Retrieve ILI Client module context. */
    	ili = CONTAINER_OF(params, struct bt_ili_client, mode_notif_params);
    
    	LOG_DBG("[NOTIFICATION] data %p length %u", data, length);
    	
    	if (!data) {
    		LOG_DBG("[UNSUBSCRIBED]");
    		params->value_handle = 0;
    		atomic_clear_bit(&ili->state, ILI_C_MODE_NOTIF_ENABLED);
    		if (ili->cb.unsubscribed) {
    			ili->cb.unsubscribed(ili);
    		}
    		return BT_GATT_ITER_STOP;
    	}
    
    	
    	if (ili->cb.received) {
    		return ili->cb.received(ili, data, length);
    	}
    
    	return BT_GATT_ITER_CONTINUE;
    }
    
    int bt_ili_client_init(struct bt_ili_client *ili_c,
    		       const struct bt_ili_client_init_param *ili_c_init)
    {
    	if (!ili_c || !ili_c_init) {
    		return -EINVAL;
    	}
    
    	if (atomic_test_and_set_bit(&ili_c->state, ILI_C_INITIALIZED)) {
    		return -EALREADY;
    	}
    
    	memcpy(&ili_c->cb, &ili_c_init->cb, sizeof(ili_c->cb));
    
    	return 0;
    }
    
    int bt_ili_handles_assign(struct bt_gatt_dm *dm,
    			  struct bt_ili_client *ili_c)
    {
    	const struct bt_gatt_dm_attr *gatt_service_attr =
    			bt_gatt_dm_service_get(dm);
    	const struct bt_gatt_service_val *gatt_service =
    			bt_gatt_dm_attr_service_val(gatt_service_attr);
    	const struct bt_gatt_dm_attr *gatt_chrc;
    	const struct bt_gatt_dm_attr *gatt_desc;
    
    	if (bt_uuid_cmp(gatt_service->uuid, BT_UUID_ILI_C_SERVICE)) {
    		return -ENOTSUP;
    	}
    	LOG_DBG("Getting handles from service.");
    	memset(&ili_c->handles, 0xFF, sizeof(ili_c->handles));
    
    	/* ILI mode Characteristic */
    	gatt_chrc = bt_gatt_dm_char_by_uuid(dm, BT_UUID_ILI_C_MODE);
    	if (!gatt_chrc) {
    		LOG_ERR("Missing mode characteristic.");
    		return -EINVAL;
    	}
    	/* ILI mode Characteristic */
    	gatt_desc = bt_gatt_dm_desc_by_uuid(dm, gatt_chrc, BT_UUID_ILI_C_MODE);
    	if (!gatt_desc) {
    		LOG_ERR("Missing mode value descriptor in characteristic.");
    		return -EINVAL;
    	}
    	LOG_DBG("Found handle for mode characteristic.");
    	ili_c->handles.mode = gatt_desc->handle;
    	/* ILI mode CCC */
    	gatt_desc = bt_gatt_dm_desc_by_uuid(dm, gatt_chrc, BT_UUID_GATT_CCC);
    	if (!gatt_desc) {
    		LOG_ERR("Missing mode CCC in characteristic.");
    		return -EINVAL;
    	}
    	LOG_DBG("Found handle for CCC of mode characteristic.");
    	ili_c->handles.mode_ccc = gatt_desc->handle;
    
    	/* serial Characteristic */
    	gatt_chrc = bt_gatt_dm_char_by_uuid(dm, BT_UUID_ILI_C_SERIAL);
    	if (!gatt_chrc) {
    		LOG_ERR("Missing serial characteristic.");
    		return -EINVAL;
    	}
    	/* ILI serial */
    	gatt_desc = bt_gatt_dm_desc_by_uuid(dm, gatt_chrc, BT_UUID_ILI_C_SERIAL);
    	if (!gatt_desc) {
    		LOG_ERR("Missing serial value descriptor in characteristic.");
    		return -EINVAL;
    	}
    	LOG_DBG("Found handle for serial characteristic.");
    	ili_c->handles.serial = gatt_desc->handle;
    
    	/* Assign connection instance. */
    	ili_c->conn = bt_gatt_dm_conn_get(dm);
    	return 0;
    }
    
    int bt_ili_subscribe_mode(struct bt_ili_client *ili_c)
    {
    	int err;
    
    	if (atomic_test_and_set_bit(&ili_c->state, ILI_C_MODE_NOTIF_ENABLED)) {
    		return -EALREADY;
    	}
    
    	ili_c->mode_notif_params.notify = on_received;
    	ili_c->mode_notif_params.value = BT_GATT_CCC_NOTIFY;
    	ili_c->mode_notif_params.value_handle = ili_c->handles.mode;
    	ili_c->mode_notif_params.ccc_handle = ili_c->handles.mode_ccc;
    	atomic_set_bit(ili_c->mode_notif_params.flags,
    		       BT_GATT_SUBSCRIBE_FLAG_VOLATILE);
    
    	err = bt_gatt_subscribe(ili_c->conn, &ili_c->mode_notif_params);
    	if (err) {
    		LOG_ERR("Subscribe to mode characteristic failed (err %d)", err);
    		atomic_clear_bit(&ili_c->state, ILI_C_MODE_NOTIF_ENABLED);
    	} else {
    		LOG_DBG("[SUBSCRIBED] mode characteristic");
    	}
    
    	return err;
    }
    

    And here is the part where I subscribe the characteristic in my application:

    static void discovery_completed_cb(struct bt_gatt_dm *dm,
    				   void *context)
    {
    	int err;
    
    	LOG_DBG("The discovery procedure succeeded");
    
    	bt_gatt_dm_data_print(dm);
    
    	err = bt_ili_handles_assign(dm, &m_ili_fob);
    	if (err) {
    		LOG_DBG("Could not init BAS client object, error: %d", err);
    	}
    
        err = bt_ili_subscribe_mode(&m_ili_fob);
    	if (err) {
    		LOG_DBG("Cannot subscribe to ILI mode value notification "
    			"(err: %d)", err);
    		/* Continue anyway */
    	}
    	
    	err = bt_gatt_dm_data_release(dm);
    	if (err) {
    		LOG_DBG("Could not release the discovery data, error "
    		       "code: %d", err);
    	}
    }

Reply
  • Hi,

    here are the corresponding parts of the central code:

     

    static uint8_t on_received(struct bt_conn *conn,
    			struct bt_gatt_subscribe_params *params,
    			const void *data, uint16_t length)
    {
    	struct bt_ili_client *ili;
    
    	/* Retrieve ILI Client module context. */
    	ili = CONTAINER_OF(params, struct bt_ili_client, mode_notif_params);
    
    	LOG_DBG("[NOTIFICATION] data %p length %u", data, length);
    	
    	if (!data) {
    		LOG_DBG("[UNSUBSCRIBED]");
    		params->value_handle = 0;
    		atomic_clear_bit(&ili->state, ILI_C_MODE_NOTIF_ENABLED);
    		if (ili->cb.unsubscribed) {
    			ili->cb.unsubscribed(ili);
    		}
    		return BT_GATT_ITER_STOP;
    	}
    
    	
    	if (ili->cb.received) {
    		return ili->cb.received(ili, data, length);
    	}
    
    	return BT_GATT_ITER_CONTINUE;
    }
    
    int bt_ili_client_init(struct bt_ili_client *ili_c,
    		       const struct bt_ili_client_init_param *ili_c_init)
    {
    	if (!ili_c || !ili_c_init) {
    		return -EINVAL;
    	}
    
    	if (atomic_test_and_set_bit(&ili_c->state, ILI_C_INITIALIZED)) {
    		return -EALREADY;
    	}
    
    	memcpy(&ili_c->cb, &ili_c_init->cb, sizeof(ili_c->cb));
    
    	return 0;
    }
    
    int bt_ili_handles_assign(struct bt_gatt_dm *dm,
    			  struct bt_ili_client *ili_c)
    {
    	const struct bt_gatt_dm_attr *gatt_service_attr =
    			bt_gatt_dm_service_get(dm);
    	const struct bt_gatt_service_val *gatt_service =
    			bt_gatt_dm_attr_service_val(gatt_service_attr);
    	const struct bt_gatt_dm_attr *gatt_chrc;
    	const struct bt_gatt_dm_attr *gatt_desc;
    
    	if (bt_uuid_cmp(gatt_service->uuid, BT_UUID_ILI_C_SERVICE)) {
    		return -ENOTSUP;
    	}
    	LOG_DBG("Getting handles from service.");
    	memset(&ili_c->handles, 0xFF, sizeof(ili_c->handles));
    
    	/* ILI mode Characteristic */
    	gatt_chrc = bt_gatt_dm_char_by_uuid(dm, BT_UUID_ILI_C_MODE);
    	if (!gatt_chrc) {
    		LOG_ERR("Missing mode characteristic.");
    		return -EINVAL;
    	}
    	/* ILI mode Characteristic */
    	gatt_desc = bt_gatt_dm_desc_by_uuid(dm, gatt_chrc, BT_UUID_ILI_C_MODE);
    	if (!gatt_desc) {
    		LOG_ERR("Missing mode value descriptor in characteristic.");
    		return -EINVAL;
    	}
    	LOG_DBG("Found handle for mode characteristic.");
    	ili_c->handles.mode = gatt_desc->handle;
    	/* ILI mode CCC */
    	gatt_desc = bt_gatt_dm_desc_by_uuid(dm, gatt_chrc, BT_UUID_GATT_CCC);
    	if (!gatt_desc) {
    		LOG_ERR("Missing mode CCC in characteristic.");
    		return -EINVAL;
    	}
    	LOG_DBG("Found handle for CCC of mode characteristic.");
    	ili_c->handles.mode_ccc = gatt_desc->handle;
    
    	/* serial Characteristic */
    	gatt_chrc = bt_gatt_dm_char_by_uuid(dm, BT_UUID_ILI_C_SERIAL);
    	if (!gatt_chrc) {
    		LOG_ERR("Missing serial characteristic.");
    		return -EINVAL;
    	}
    	/* ILI serial */
    	gatt_desc = bt_gatt_dm_desc_by_uuid(dm, gatt_chrc, BT_UUID_ILI_C_SERIAL);
    	if (!gatt_desc) {
    		LOG_ERR("Missing serial value descriptor in characteristic.");
    		return -EINVAL;
    	}
    	LOG_DBG("Found handle for serial characteristic.");
    	ili_c->handles.serial = gatt_desc->handle;
    
    	/* Assign connection instance. */
    	ili_c->conn = bt_gatt_dm_conn_get(dm);
    	return 0;
    }
    
    int bt_ili_subscribe_mode(struct bt_ili_client *ili_c)
    {
    	int err;
    
    	if (atomic_test_and_set_bit(&ili_c->state, ILI_C_MODE_NOTIF_ENABLED)) {
    		return -EALREADY;
    	}
    
    	ili_c->mode_notif_params.notify = on_received;
    	ili_c->mode_notif_params.value = BT_GATT_CCC_NOTIFY;
    	ili_c->mode_notif_params.value_handle = ili_c->handles.mode;
    	ili_c->mode_notif_params.ccc_handle = ili_c->handles.mode_ccc;
    	atomic_set_bit(ili_c->mode_notif_params.flags,
    		       BT_GATT_SUBSCRIBE_FLAG_VOLATILE);
    
    	err = bt_gatt_subscribe(ili_c->conn, &ili_c->mode_notif_params);
    	if (err) {
    		LOG_ERR("Subscribe to mode characteristic failed (err %d)", err);
    		atomic_clear_bit(&ili_c->state, ILI_C_MODE_NOTIF_ENABLED);
    	} else {
    		LOG_DBG("[SUBSCRIBED] mode characteristic");
    	}
    
    	return err;
    }
    

    And here is the part where I subscribe the characteristic in my application:

    static void discovery_completed_cb(struct bt_gatt_dm *dm,
    				   void *context)
    {
    	int err;
    
    	LOG_DBG("The discovery procedure succeeded");
    
    	bt_gatt_dm_data_print(dm);
    
    	err = bt_ili_handles_assign(dm, &m_ili_fob);
    	if (err) {
    		LOG_DBG("Could not init BAS client object, error: %d", err);
    	}
    
        err = bt_ili_subscribe_mode(&m_ili_fob);
    	if (err) {
    		LOG_DBG("Cannot subscribe to ILI mode value notification "
    			"(err: %d)", err);
    		/* Continue anyway */
    	}
    	
    	err = bt_gatt_dm_data_release(dm);
    	if (err) {
    		LOG_DBG("Could not release the discovery data, error "
    		       "code: %d", err);
    	}
    }

Children
Related