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

S132: Is it possible to send an empty notification

My previous product uses an empty notification to indicate that a bulk transfer is finished. I attempted to send an empty notification but got error code 0x10 (NRF_ERROR_INVALID_FLAGS).

Is there a way to send an empty notification?

ble_gatts_hvx_params_t hvx_param =
{
    .type   = BLE_GATT_HVX_NOTIFICATION,
    .handle = p_ctx->bulkdata_handle,
    .p_data = data,
    .p_len  = &len,
};
hvx_param.p_len = 0;
err_code = sd_ble_gatts_hvx(p_ctx->conn_handle, &hvx_param);

[Edit]

No empty packet sent when p_data = NULL and p_len = 0.

image description

Example of an empty notification packet being sent by TI CC2540.

image description

[Edit 2]

Alright. Added print on TX_COMPLETE.

Heres the log at the end of the transfer. Its hard to tell if one of these completes correlates to the empty notification.

BULKDATA:INFO:on_tx_complete: 503
BULKDATA:INFO:on_tx_complete: 504
APP:INFO:Sent 10 KBytes
BULKDATA:INFO:on_tx_complete: 505
BULKDATA:INFO:Sent empty packet, error code: 0x0
APP:INFO:Finished.
BULKDATA:INFO:on_tx_complete: 506
BULKDATA:INFO:on_tx_complete: 507
BULKDATA:INFO:on_tx_complete: 508
BULKDATA:INFO:on_tx_complete: 509
BULKDATA:INFO:on_tx_complete: 510
BULKDATA:INFO:on_tx_complete: 511
BULKDATA:INFO:on_tx_complete: 512
BULKDATA:INFO:on_tx_complete: 513

I rebuilt without sending the empty packet and there does appear to be one less "on_tx_complete" after the Finished printout.

BULKDATA:INFO:on_tx_complete: 503
BULKDATA:INFO:on_tx_complete: 504
APP:INFO:Sent 10 KBytes
BULKDATA:INFO:on_tx_complete: 505
APP:INFO:Finished.
BULKDATA:INFO:on_tx_complete: 506
BULKDATA:INFO:on_tx_complete: 507
BULKDATA:INFO:on_tx_complete: 508
BULKDATA:INFO:on_tx_complete: 509
BULKDATA:INFO:on_tx_complete: 510
BULKDATA:INFO:on_tx_complete: 511
BULKDATA:INFO:on_tx_complete: 512

Next I modified the code to send only empty notifications. This is where things get weird. It appears that each notification is 9 bytes of 0xFF. 9 bytes happens to be the initial length of the characteristic however the characteristic is initialized to all zeros.

Char init code, generated by BDS:

ble_app_app_generic_data_rcv_t app_generic_data_rcv_initial_value = p_app_init->ble_app_app_generic_data_rcv_initial_value; 

ble_add_char_params_t add_app_generic_data_rcv_params;
memset(&add_app_generic_data_rcv_params, 0, sizeof(add_app_generic_data_rcv_params));

add_app_generic_data_rcv_params.uuid                = 0xFF25;
add_app_generic_data_rcv_params.uuid_type           = ble_uuid.type; 
add_app_generic_data_rcv_params.max_len             = NRF_BLE_GATT_MAX_MTU_SIZE;
add_app_generic_data_rcv_params.init_len            = app_generic_data_rcv_encode(&app_generic_data_rcv_initial_value, app_generic_data_rcv_encoded_value);
add_app_generic_data_rcv_params.p_init_value        = app_generic_data_rcv_encoded_value; 
add_app_generic_data_rcv_params.is_value_user       = 1; 
add_app_generic_data_rcv_params.char_props.notify   = 1; 
add_app_generic_data_rcv_params.char_props.write    = 1; 
add_app_generic_data_rcv_params.write_access        = SEC_OPEN; 
add_app_generic_data_rcv_params.cccd_write_access   = SEC_OPEN;
// 1 for variable length and 0 for fixed length.
add_app_generic_data_rcv_params.is_var_len          = 1;

err_code = characteristic_add(p_app->service_handle, &add_app_generic_data_rcv_params, &(p_app->app_generic_data_rcv_handles));

How did you setup your characteristic?

Parents
  • So it would appear that when I try to send an empty notification, it instead sends whatever is currently contained in the characteristic. So when I send only empty notifications, I receive 9 bytes of 0xFF because thats what I wrote to the start the transfer.

    When I send the count pattern, the last notification is the count that would have been sent next. So it appears that sd_ble_gatts_hvx() updates the characteristcs value even though a BLE_ERROR_NO_TX_PACKETS is returned.

    This leads me to the following code which does send empty notifications. Thanks to @endnote for all the help.

    uint32_t err_code = NRF_SUCCESS;
    uint16_t               hvx_len = 0;
    ble_gatts_hvx_params_t hvx_param_empty =
    {
        .type   = BLE_GATT_HVX_NOTIFICATION,
        .handle = p_ctx->bulkdata_handle,
        .p_data = NULL,
        .p_len  = &hvx_len,
    };
    
     // Set characteristic value to len zero so we can send an empty notification
     ble_gatts_value_t gatts_value;
     memset(&gatts_value, 0, sizeof(gatts_value));
     gatts_value.len = 0;
     err_code = sd_ble_gatts_value_set(p_ctx->conn_handle, p_ctx->bulkdata_handle, &gatts_value);
     APP_ERROR_CHECK(err_code);
    
     err_code = sd_ble_gatts_hvx(p_ctx->conn_handle, &hvx_param_empty);
     if (err_code != NRF_SUCCESS)
     {
         NRF_LOG_ERROR("sd_ble_gatts_hvx() failed: 0x%x\r\n", err_code);
     }
    
Reply
  • So it would appear that when I try to send an empty notification, it instead sends whatever is currently contained in the characteristic. So when I send only empty notifications, I receive 9 bytes of 0xFF because thats what I wrote to the start the transfer.

    When I send the count pattern, the last notification is the count that would have been sent next. So it appears that sd_ble_gatts_hvx() updates the characteristcs value even though a BLE_ERROR_NO_TX_PACKETS is returned.

    This leads me to the following code which does send empty notifications. Thanks to @endnote for all the help.

    uint32_t err_code = NRF_SUCCESS;
    uint16_t               hvx_len = 0;
    ble_gatts_hvx_params_t hvx_param_empty =
    {
        .type   = BLE_GATT_HVX_NOTIFICATION,
        .handle = p_ctx->bulkdata_handle,
        .p_data = NULL,
        .p_len  = &hvx_len,
    };
    
     // Set characteristic value to len zero so we can send an empty notification
     ble_gatts_value_t gatts_value;
     memset(&gatts_value, 0, sizeof(gatts_value));
     gatts_value.len = 0;
     err_code = sd_ble_gatts_value_set(p_ctx->conn_handle, p_ctx->bulkdata_handle, &gatts_value);
     APP_ERROR_CHECK(err_code);
    
     err_code = sd_ble_gatts_hvx(p_ctx->conn_handle, &hvx_param_empty);
     if (err_code != NRF_SUCCESS)
     {
         NRF_LOG_ERROR("sd_ble_gatts_hvx() failed: 0x%x\r\n", err_code);
     }
    
Children
No Data
Related