Browse Source

samples: Bluetooth: GATT write commands with connection PHY Update

Update the Central and Peripheral GATT write command sample
to measure throughput across all supported connection PHYs.

Cover the sample in BabbleSIM CI testing to validate the 2M
PHY throughput after all supported PHYs have been changed
once.

Signed-off-by: Vinayak Kariappa Chettimada <vich@nordicsemi.no>
pull/82719/head
Vinayak Kariappa Chettimada 6 months ago committed by Benjamin Cabé
parent
commit
21cb301447
  1. 177
      samples/bluetooth/central_gatt_write/src/gatt_write_common.c
  2. 38
      tests/bsim/bluetooth/ll/throughput/prj.conf
  3. 10
      tests/bsim/bluetooth/ll/throughput/src/main.c

177
samples/bluetooth/central_gatt_write/src/gatt_write_common.c

@ -12,10 +12,137 @@ @@ -12,10 +12,137 @@
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/hci.h>
/* Count down number of metrics intervals before performing a PHY update */
#define PHY_UPDATE_COUNTDOWN 3U
static uint32_t phy_update_countdown;
static uint8_t phy_param_idx;
static void phy_update_iterate(struct bt_conn *conn)
{
const struct bt_conn_le_phy_param phy_param[] = {
/* List of 1M Tx with Rx on other PHYs */
{
.options = BT_CONN_LE_PHY_OPT_NONE,
.pref_tx_phy = BT_GAP_LE_PHY_1M,
.pref_rx_phy = BT_GAP_LE_PHY_1M,
}, {
.options = BT_CONN_LE_PHY_OPT_NONE,
.pref_tx_phy = BT_GAP_LE_PHY_1M,
.pref_rx_phy = BT_GAP_LE_PHY_2M,
}, {
.options = BT_CONN_LE_PHY_OPT_NONE,
.pref_tx_phy = BT_GAP_LE_PHY_1M,
.pref_rx_phy = BT_GAP_LE_PHY_CODED,
},
/* List of 2M Tx with Rx on other PHYs */
{
.options = BT_CONN_LE_PHY_OPT_NONE,
.pref_tx_phy = BT_GAP_LE_PHY_2M,
.pref_rx_phy = BT_GAP_LE_PHY_1M,
}, {
.options = BT_CONN_LE_PHY_OPT_NONE,
.pref_tx_phy = BT_GAP_LE_PHY_2M,
.pref_rx_phy = BT_GAP_LE_PHY_2M,
}, {
.options = BT_CONN_LE_PHY_OPT_NONE,
.pref_tx_phy = BT_GAP_LE_PHY_2M,
.pref_rx_phy = BT_GAP_LE_PHY_CODED,
},
/* List of Coded PHY S8 Tx with Rx on other PHYs */
{
.options = BT_CONN_LE_PHY_OPT_CODED_S8,
.pref_tx_phy = BT_GAP_LE_PHY_CODED,
.pref_rx_phy = BT_GAP_LE_PHY_1M,
}, {
.options = BT_CONN_LE_PHY_OPT_CODED_S8,
.pref_tx_phy = BT_GAP_LE_PHY_CODED,
.pref_rx_phy = BT_GAP_LE_PHY_2M,
}, {
.options = BT_CONN_LE_PHY_OPT_CODED_S8,
.pref_tx_phy = BT_GAP_LE_PHY_CODED,
.pref_rx_phy = BT_GAP_LE_PHY_CODED,
},
/* List of Coded PHY S2 Tx with Rx on other PHYs */
{
.options = BT_CONN_LE_PHY_OPT_CODED_S2,
.pref_tx_phy = BT_GAP_LE_PHY_CODED,
.pref_rx_phy = BT_GAP_LE_PHY_1M,
}, {
.options = BT_CONN_LE_PHY_OPT_CODED_S2,
.pref_tx_phy = BT_GAP_LE_PHY_CODED,
.pref_rx_phy = BT_GAP_LE_PHY_2M,
}, {
.options = BT_CONN_LE_PHY_OPT_CODED_S2,
.pref_tx_phy = BT_GAP_LE_PHY_CODED,
.pref_rx_phy = BT_GAP_LE_PHY_CODED,
},
/* Finally stop at 2M Tx with Rx on 2M */
{
.options = BT_CONN_LE_PHY_OPT_NONE,
.pref_tx_phy = BT_GAP_LE_PHY_2M,
.pref_rx_phy = BT_GAP_LE_PHY_2M,
},
};
int err;
if (phy_update_countdown--) {
return;
}
phy_update_countdown = PHY_UPDATE_COUNTDOWN;
phy_param_idx++;
if (phy_param_idx >= ARRAY_SIZE(phy_param)) {
/* No more PHY updates, stay at the last index */
phy_param_idx = ARRAY_SIZE(phy_param);
return;
}
struct bt_conn_info conn_info;
err = bt_conn_get_info(conn, &conn_info);
if (err) {
printk("Failed to get connection info (%d).\n", err);
return;
}
struct bt_conn_le_phy_param conn_phy_param;
if (conn_info.role == BT_CONN_ROLE_CENTRAL) {
conn_phy_param.options = phy_param[phy_param_idx].options;
conn_phy_param.pref_tx_phy = phy_param[phy_param_idx].pref_tx_phy;
conn_phy_param.pref_rx_phy = phy_param[phy_param_idx].pref_rx_phy;
} else {
conn_phy_param.options = phy_param[phy_param_idx].options;
conn_phy_param.pref_tx_phy = phy_param[phy_param_idx].pref_rx_phy;
conn_phy_param.pref_rx_phy = phy_param[phy_param_idx].pref_tx_phy;
}
printk("%s: PHY Update requested %u %u (%u)\n", __func__,
conn_phy_param.pref_tx_phy,
conn_phy_param.pref_rx_phy,
conn_phy_param.options);
err = bt_conn_le_phy_update(conn, &conn_phy_param);
if (err) {
printk("Failed to update PHY (%d).\n", err);
return;
}
}
/* Interval between storing the measured write rate */
#define METRICS_INTERVAL 1U /* seconds */
static struct bt_gatt_exchange_params mtu_exchange_params;
static uint32_t write_count;
static uint32_t write_len;
static uint32_t write_rate;
/* Globals, reused by central_gatt_write and peripheral_gatt_write samples */
struct bt_conn *conn_connected;
uint32_t last_write_rate;
void (*start_scan_func)(void);
@ -36,7 +163,7 @@ static void write_cmd_cb(struct bt_conn *conn, void *user_data) @@ -36,7 +163,7 @@ static void write_cmd_cb(struct bt_conn *conn, void *user_data)
/* if last data rx-ed was greater than 1 second in the past,
* reset the metrics.
*/
if (delta > (1U * NSEC_PER_SEC)) {
if (delta > (METRICS_INTERVAL * NSEC_PER_SEC)) {
printk("%s: count= %u, len= %u, rate= %u bps.\n", __func__,
write_count, write_len, write_rate);
@ -46,6 +173,11 @@ static void write_cmd_cb(struct bt_conn *conn, void *user_data) @@ -46,6 +173,11 @@ static void write_cmd_cb(struct bt_conn *conn, void *user_data)
write_len = 0U;
write_rate = 0U;
cycle_stamp = k_cycle_get_32();
if (IS_ENABLED(CONFIG_BT_USER_PHY_UPDATE)) {
phy_update_iterate(conn);
}
} else {
uint16_t len;
@ -55,7 +187,7 @@ static void write_cmd_cb(struct bt_conn *conn, void *user_data) @@ -55,7 +187,7 @@ static void write_cmd_cb(struct bt_conn *conn, void *user_data)
len = (uint32_t)user_data & 0xFFFF;
write_len += len;
write_rate = ((uint64_t)write_len << 3) * (1U * NSEC_PER_SEC) /
write_rate = ((uint64_t)write_len << 3) * (METRICS_INTERVAL * NSEC_PER_SEC) /
delta;
}
}
@ -119,6 +251,11 @@ static void connected(struct bt_conn *conn, uint8_t conn_err) @@ -119,6 +251,11 @@ static void connected(struct bt_conn *conn, uint8_t conn_err)
}
}
#endif
if (IS_ENABLED(CONFIG_BT_USER_PHY_UPDATE)) {
phy_update_countdown = PHY_UPDATE_COUNTDOWN;
phy_param_idx = 0U;
}
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
@ -171,14 +308,50 @@ static void security_changed(struct bt_conn *conn, bt_security_t level, @@ -171,14 +308,50 @@ static void security_changed(struct bt_conn *conn, bt_security_t level,
}
#endif
#if defined(CONFIG_BT_USER_PHY_UPDATE)
static void le_phy_updated(struct bt_conn *conn,
struct bt_conn_le_phy_info *param)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("LE PHY Updated: %s Tx 0x%x, Rx 0x%x\n", addr, param->tx_phy,
param->rx_phy);
}
#endif /* CONFIG_BT_USER_PHY_UPDATE */
#if defined(CONFIG_BT_USER_DATA_LEN_UPDATE)
static void le_data_len_updated(struct bt_conn *conn,
struct bt_conn_le_data_len_info *info)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Data length updated: %s max tx %u (%u us) max rx %u (%u us)\n",
addr, info->tx_max_len, info->tx_max_time, info->rx_max_len,
info->rx_max_time);
}
#endif /* CONFIG_BT_USER_DATA_LEN_UPDATE */
BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected,
.disconnected = disconnected,
.le_param_req = le_param_req,
.le_param_updated = le_param_updated,
#if defined(CONFIG_BT_SMP)
.security_changed = security_changed,
#endif
#if defined(CONFIG_BT_USER_PHY_UPDATE)
.le_phy_updated = le_phy_updated,
#endif /* CONFIG_BT_USER_PHY_UPDATE */
#if defined(CONFIG_BT_USER_DATA_LEN_UPDATE)
.le_data_len_updated = le_data_len_updated,
#endif /* CONFIG_BT_USER_DATA_LEN_UPDATE */
};
int write_cmd(struct bt_conn *conn)

38
tests/bsim/bluetooth/ll/throughput/prj.conf

@ -4,13 +4,45 @@ CONFIG_BT_PERIPHERAL=y @@ -4,13 +4,45 @@ CONFIG_BT_PERIPHERAL=y
CONFIG_BT_SMP=y
CONFIG_BT_GATT_CLIENT=y
CONFIG_BT_BUF_ACL_RX_SIZE=255
CONFIG_BT_BUF_ACL_TX_SIZE=251
# Maximum L2CAP size for one LL PDU size
CONFIG_BT_L2CAP_TX_MTU=247
# Auto initiated PHY update on connection established
CONFIG_BT_AUTO_PHY_UPDATE=y
# Enable user initiated PHY update support
CONFIG_BT_USER_PHY_UPDATE=y
# Auto initiated Data Length update on connection established
CONFIG_BT_AUTO_DATA_LEN_UPDATE=y
# Enable user initiated Data Length update support
CONFIG_BT_USER_DATA_LEN_UPDATE=y
# BT HCI Command Buffers
# Greater than BT_BUF_ACL_TX_COUNT, to be able to Send Host Number of Completed Packets commands
CONFIG_BT_BUF_CMD_TX_COUNT=4
CONFIG_BT_BUF_CMD_TX_SIZE=255
# BT HCI Event Buffers
# Greater than BT_BUF_ACL_TX_COUNT, to be able to receive Number of Completed Packets events
CONFIG_BT_BUF_EVT_RX_COUNT=4
CONFIG_BT_BUF_EVT_RX_SIZE=255
# BT HCI Discardable Event Buffers
CONFIG_BT_BUF_EVT_DISCARDABLE_COUNT=3
CONFIG_BT_BUF_EVT_DISCARDABLE_SIZE=255
CONFIG_BT_L2CAP_TX_MTU=247
# BT HCI ACL TX Data Buffers
CONFIG_BT_BUF_ACL_TX_COUNT=3
CONFIG_BT_BUF_ACL_TX_SIZE=251
# BT HCI ACL RX Data Buffers
CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=0
CONFIG_BT_BUF_ACL_RX_SIZE=255
# Maximum LL ACL PDU length
CONFIG_BT_CTLR_DATA_LENGTH_MAX=251
# Enable Coded PHY Support
CONFIG_BT_CTLR_PHY_CODED=y
CONFIG_LOG=y

10
tests/bsim/bluetooth/ll/throughput/src/main.c

@ -16,7 +16,15 @@ @@ -16,7 +16,15 @@
#include "time_machine.h"
#include "bstests.h"
#define COUNT 5000 /* Arbitrary GATT Write Cmd iterations used */
/* There are 13 iterations of PHY update every 3 seconds, and based on actual
* simulation 10000 iterations are sufficient to finish these iterations with
* a stable 2M throughput value to be verified. If Central and Peripheral take
* different duration to complete these iterations, the test will fail due to
* the throughput calculated over one second duration will be low due to the
* connection being disconnected before the other device could complete all the
* iterations.
*/
#define COUNT 10000
/* Write Throughput calculation:
* Measure interval = 1 s

Loading…
Cancel
Save