Dear Sir/Madam,
we are trying to send large chunks of data (up to 512 bytes) via bluetooth with notifications with the added difficulty of an arbitrary MTU size.
Our approach is to set the characteristic value to the the desired data with sd_ble_gatts_value_set() and then send it with the sd_ble_gatts_hvx() function.
By increasing the offset in the ble_gatts_hvx_params_t by p_len and setting the p_data pointer to NULL each time we update the notification after each notification was sent successfully we hope to send the data in the characteristic value piece by piece (see code).
void gatt_characteristic_send_notification(uint16_t conn_handle, ble_gatts_char_handles_t *char_handles, uint16_t length) { ret_code_t err_code; ble_gatts_hvx_params_t hvx_params; uint16_t bytes_to_write = length; memset(&hvx_params, 0, sizeof(hvx_params)); hvx_params.handle = char_handles->value_handle; hvx_params.type = BLE_GATT_HVX_NOTIFICATION; hvx_params.offset = 0; hvx_params.p_len = &bytes_to_write; hvx_params.p_data = NULL; while(true) { uint16_t payload_size = MIN(max_payload_size, length); bool notification_enabled = gatt_characteristic_check_notify_enable(conn_handle, char_handles->cccd_handle); if(!notification_enabled) return; err_code = sd_ble_gatts_hvx(conn_handle, &hvx_params); if(err_code == NRF_SUCCESS) { hvx_params.offset += *hvx_params.p_len; length -= *hvx_params.p_len; if(length <= 0) break; bytes_to_write = length; hvx_params.p_len = &bytes_to_write; } else if(err_code == NRF_ERROR_RESOURCES) { osSignalWait(HVN_TX_COMPLETE_SIGNAL, 5000); } else { APP_ERROR_CHECK(err_code); return; } } return; }
The problem is, that p_len represents the number of queued bytes which might be larger of the actual written bytes.
Example:
The length of the data we are trying to send is 512 bytes.
p_len is set to this length and stays the same after the call of the sd_ble_gatts_hvx() function, meaning all 512 bytes have been queued for the notification.
With a MTU size of 23 only 20 bytes are actually sent, the rest seems to be discarded.
When we don't use the p_len to calculate how many bytes we have sent but use (MTU-size - 3) instead and increase the offset of ble_gatts_hvx_params_t after each notification, the sd_ble_gatts_hvx() notifies the same 20 bytes over and over, which are the first 20 bytes of the data we want to notify.
void gatt_characteristic_send_notification(uint16_t conn_handle, ble_gatts_char_handles_t *char_handles, uint16_t length)
{
ret_code_t err_code;
ble_gatts_hvx_params_t hvx_params;
uint16_t bytes_to_write = length;
uint16_t max_payload_size = nrf_ble_gatt_eff_mtu_get(&m_gatt, conn_handle) - 3;
uint16_t payload_size = MIN(max_payload_size, length);
memset(&hvx_params, 0, sizeof(hvx_params));
hvx_params.handle = char_handles->value_handle;
hvx_params.type = BLE_GATT_HVX_NOTIFICATION;
hvx_params.offset = 0;
hvx_params.p_len = &payload_size;
hvx_params.p_data = NULL;
while(true) {
uint16_t payload_size = MIN(max_payload_size, length);
bool notification_enabled = gatt_characteristic_check_notify_enable(conn_handle, char_handles->cccd_handle);
if(!notification_enabled) return;
printf("p_len = %d offset = %d\n", *hvx_params.p_len, hvx_params.offset);
err_code = sd_ble_gatts_hvx(conn_handle, &hvx_params);
if(err_code == NRF_SUCCESS) {
hvx_params.offset += payload_size;
length -= payload_size;
if(length <= 0) break;
hvx_params.p_len = &payload_size;
} else if(err_code == NRF_ERROR_RESOURCES) {
osSignalWait(HVN_TX_COMPLETE_SIGNAL, 5000);
} else {
APP_ERROR_CHECK(err_code);
return;
}
}
return;
}
The documentation of the ble_gatts_hvx_params_t struct is as follows:/**@brief GATT HVx parameters. */
typedef struct
{
uint16_t handle; /**< Characteristic Value Handle. */
uint8_t type; /**< Indication or Notification, see @ref BLE_GATT_HVX_TYPES. */
uint16_t offset; /**< Offset within the attribute value. */
uint16_t *p_len; /**< Length in bytes to be written, length in bytes written after return. */
uint8_t const *p_data; /**< Actual data content, use NULL to use the current attribute value. */
} ble_gatts_hvx_params_t;
This states that ble_gatts_hvx_params_t.offset indicates the offset within the attribute value.
Why does changing this value within our instance of the the ble_gatts_hvx_params_t struct before calling sd_ble_gatts_hvx() not work for us? (As stated, the attribute value is sent, but only the first 20 bytes, never with the offset).
Also why can sd_ble_gatts_hvx() return it queued 512 bytes in the characteristic but only the first 20 bytes are actually sent?
We are using a nRF52840 with the softdevice s140 v7.2.0 and the SDK version 17.0.2.
Thank you very much for your help, if you need any other information I will gladly provide it.
Kind regards,
Max