According to some benchmarks shown here: https://www.novelbits.io/bluetooth-5-speed-maximum-throughput/, I should be able to get a connection speed of around 120,000 bytes / second. However, in my testing, I can only get up to 20,000 bytes / second.
I have a nR52 2832 S132 chip that I connect to from an Android (galaxy s9) phone. The following settings are enabled:
-MTU: 247
-Data length extension: 251
-PHY: 2M
-TX Power: 4
-Connection interval: 7.5 min, 7.5 max
My code is available here in full: https://github.com/wdavies973/MagiCoil-microcode
Gatt init: (set mtu & data length extension)
static void gatt_init(void) { ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, gatt_evt_handler); printf("GATT SUCCESS: %d\n",err_code);
conn_evt_len_ext_set();
printf("nrf_ble_mut_max: %d, mtu_default: %d\n", NRF_SDH_BLE_GATT_MAX_MTU_SIZE, BLE_GATT_ATT_MTU_DEFAULT);
err_code = nrf_ble_gatt_att_mtu_periph_set(&m_gatt, 247); printf("mtu: %d\n",err_code);
err_code = nrf_ble_gatt_data_length_set(&m_gatt, BLE_CONN_HANDLE_INVALID, 251); printf("data_length: %d\n",err_code);}
PHY & MTU update requests:
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context){ uint32_t err_code;
switch (p_ble_evt->header.evt_id) { case BLE_GAP_EVT_CONNECTED: printf("Connected"); err_code = bsp_indication_set(BSP_INDICATE_CONNECTED); APP_ERROR_CHECK(err_code); m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle; err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle); APP_ERROR_CHECK(err_code);
err_code = sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_CONN, m_conn_handle, 4);
break;
case BLE_GAP_EVT_DISCONNECTED: NRF_LOG_INFO("Disconnected"); // LED indication will be changed when advertising starts. m_conn_handle = BLE_CONN_HANDLE_INVALID; advertising_start(true); break; case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: { ble_gap_conn_params_t params; params.max_conn_interval = MAX_CONN_INTERVAL; err_code = sd_ble_gap_conn_param_update(p_ble_evt->evt.gap_evt.conn_handle, ¶ms); } break; case BLE_GAP_EVT_CONN_PARAM_UPDATE: { uint16_t max_con_int = p_ble_evt->evt.gap_evt.params.conn_param_update.conn_params.max_conn_interval; uint16_t min_con_int = p_ble_evt->evt.gap_evt.params.conn_param_update.conn_params.min_conn_interval;
printf("conn updated\n");
//m_ble_params_info.con_interval = max_con_int; //ble_its_ble_params_info_send(&m_its, &m_ble_params_info); //NRF_LOG_INFO("Con params updated: CI %i, %i", (int)min_con_int, (int)max_con_int); } break;
case BLE_GAP_EVT_PHY_UPDATE_REQUEST: { printf("PHY update request."); ble_gap_phys_t const phys = { .rx_phys = BLE_GAP_PHY_2MBPS, .tx_phys = BLE_GAP_PHY_2MBPS, }; err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys); printf("PHY update to 2MBPS: %d\n",err_code); } break;
case BLE_GAP_EVT_PHY_UPDATE: //m_ble_params_info.tx_phy = p_ble_evt->evt.gap_evt.params.phy_update.tx_phy; // m_ble_params_info.rx_phy = p_ble_evt->evt.gap_evt.params.phy_update.rx_phy; // ble_its_ble_params_info_send(&m_its, &m_ble_params_info); printf("Phy update: %i, %i", (int)p_ble_evt->evt.gap_evt.params.phy_update.tx_phy, (int) p_ble_evt->evt.gap_evt.params.phy_update.rx_phy); break;
case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
// Pairing not supported err_code = sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL); APP_ERROR_CHECK(err_code); break;
/*case BLE_GATTS_EVT_SYS_ATTR_MISSING: // No system attributes have been stored. err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0, 0); APP_ERROR_CHECK(err_code); break;*/
case BLE_GATTC_EVT_TIMEOUT: // Disconnect on GATT Client timeout event. err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); APP_ERROR_CHECK(err_code); break;
case BLE_GATTS_EVT_TIMEOUT: // Disconnect on GATT Server timeout event. err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION); APP_ERROR_CHECK(err_code); break;
default: // No implementation needed. //NRF_LOG_INFO("BLE event not handled by app: %i", p_ble_evt->header.evt_id); break; }}
And finally, the send method:
void ble_cus_custom_value_update(ble_cus_t * p_cus){ uint8_t data[244]; uint16_t payload_len = 244;
ble_gatts_hvx_params_t hvx_param;
memset(&hvx_param, 0, sizeof(hvx_param));
hvx_param.handle = p_cus->custom_value_handles.value_handle; hvx_param.type = BLE_GATT_HVX_NOTIFICATION; hvx_param.p_len = &payload_len; hvx_param.p_data = data;
uint32_t err_code = NRF_SUCCESS;
while(err_code == NRF_SUCCESS) { (void)uint32_encode(0, data);
err_code = sd_ble_gatts_hvx(p_cus->conn_handle, &hvx_param);
if (err_code == NRF_ERROR_RESOURCES) { // Wait for BLE_GATTS_EVT_HVN_TX_COMPLETE. busy = true; break; } else if (err_code != NRF_SUCCESS) { printf("sd_ble_gatts_hvx() failed: 0x%x", err_code); } }
}
Android Code:
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {super.onCharacteristicChanged(gatt, characteristic);
// if(status == BluetoothGatt.GATT_SUCCESS) {
// characteristic available for reading
//final byte[] data = characteristic.getValue();
// System.out.println("Receiving characteristic read"+characteristic.getValue().length);
if(receivedBytes > 10_000) {double elapsedSeconds = (System.nanoTime() - startTime) / 1000000000.0;
double tp = 10_000.0 / elapsedSeconds;
System.out.println("TP: "+tp);
receivedBytes = 0;
startTime = System.nanoTime();
} else {receivedBytes+=244;
}// if (data != null && data.length > 0) {
// final StringBuilder stringBuilder = new StringBuilder(data.length);
// for(byte byteChar : data)
// stringBuilder.append(String.format("%02X ", byteChar));
// intent.putExtra(EXTRA_DATA, new String(data) + "\n" +
// stringBuilder.toString());
// }
}