Bluetooth: Add a sample to demonstrate MTU update
Add a new sample that demonstrate how to exchange MTU to allow larger packet transmission. The sample is split in two applications. One is the Central, it will initiate the MTU exchange. The other one is the Peripheral and will try to send a large notification. If the MTU exchange fail or the new size is not big enough, the Peripheral will not be able to send the notification. Signed-off-by: Théo Battrel <theo.battrel@nordicsemi.no>
This commit is contained in:
parent
1dd0ffbe40
commit
7e23294bc5
11 changed files with 469 additions and 0 deletions
21
samples/bluetooth/mtu_update/README.rst
Normal file
21
samples/bluetooth/mtu_update/README.rst
Normal file
|
@ -0,0 +1,21 @@
|
|||
.. _bluetooth_mtu_update_sample:
|
||||
|
||||
Bluetooth: MTU Update
|
||||
#####################
|
||||
|
||||
Overview:
|
||||
*********
|
||||
|
||||
Application demonstrating the exchange of MTU between two devices to allow a
|
||||
large notification to be sent.
|
||||
|
||||
Updating the MTU is useful to send bigger packets and so have a better
|
||||
throughput.
|
||||
|
||||
Building and Running
|
||||
********************
|
||||
|
||||
This sample can be found under :zephyr_file:`samples/bluetooth/mtu_update` in
|
||||
the Zephyr tree.
|
||||
|
||||
See :ref:`bluetooth samples section <bluetooth-samples>` for details.
|
13
samples/bluetooth/mtu_update/central/CMakeLists.txt
Normal file
13
samples/bluetooth/mtu_update/central/CMakeLists.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(mtu_update)
|
||||
|
||||
target_sources(app PRIVATE
|
||||
src/main.c
|
||||
src/central_mtu_update.c
|
||||
)
|
||||
|
||||
zephyr_library_include_directories(${ZEPHYR_BASE}/samples/bluetooth)
|
15
samples/bluetooth/mtu_update/central/prj.conf
Normal file
15
samples/bluetooth/mtu_update/central/prj.conf
Normal file
|
@ -0,0 +1,15 @@
|
|||
CONFIG_BT=y
|
||||
CONFIG_BT_CENTRAL=y
|
||||
CONFIG_BT_DEVICE_NAME="Zephyr Central MTU Update Sample"
|
||||
|
||||
CONFIG_BT_GATT_CLIENT=y
|
||||
|
||||
CONFIG_LOG=y
|
||||
CONFIG_BT_L2CAP_LOG_LEVEL_DBG=y
|
||||
|
||||
# HCI ACL buffers size
|
||||
# BT_L2CAP_RX_MTU = CONFIG_BT_BUF_ACL_RX_SIZE - BT_L2CAP_HDR_SIZE
|
||||
CONFIG_BT_BUF_ACL_RX_SIZE=251
|
||||
|
||||
# L2CAP SDU/PDU TX MTU
|
||||
CONFIG_BT_L2CAP_TX_MTU=247
|
9
samples/bluetooth/mtu_update/central/sample.yaml
Normal file
9
samples/bluetooth/mtu_update/central/sample.yaml
Normal file
|
@ -0,0 +1,9 @@
|
|||
sample:
|
||||
name: Bluetooth Central MTU Update
|
||||
tests:
|
||||
sample.bluetooth.central_mtu_update:
|
||||
harness: bluetooth
|
||||
platform_allow: qemu_cortex_m3 qemu_x86 nrf52_bsim
|
||||
tags: bluetooth
|
||||
integration_platforms:
|
||||
- qemu_cortex_m3
|
263
samples/bluetooth/mtu_update/central/src/central_mtu_update.c
Normal file
263
samples/bluetooth/mtu_update/central/src/central_mtu_update.c
Normal file
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#include <zephyr/sys/printk.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
|
||||
#include <zephyr/bluetooth/att.h>
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/hci.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
#include <zephyr/bluetooth/uuid.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
|
||||
#define MTU_TEST_SERVICE_TYPE BT_UUID_128_ENCODE(0x2e2b8dc3, 0x06e0, 0x4f93, 0x9bb2, 0x734091c356f0)
|
||||
#define MTU_TEST_SERVICE_NOTIFY_TYPE \
|
||||
BT_UUID_128_ENCODE(0x2e2b8dc3, 0x06e0, 0x4f93, 0x9bb2, 0x734091c356f3)
|
||||
|
||||
#define BT_UUID_MTU_TEST BT_UUID_DECLARE_128(MTU_TEST_SERVICE_TYPE)
|
||||
#define BT_UUID_MTU_TEST_NOTIFY BT_UUID_DECLARE_128(MTU_TEST_SERVICE_NOTIFY_TYPE)
|
||||
|
||||
static void start_scan(void);
|
||||
|
||||
static struct bt_conn *default_conn;
|
||||
|
||||
static struct bt_uuid_128 uuid = BT_UUID_INIT_128(0);
|
||||
static struct bt_gatt_discover_params discover_params;
|
||||
static struct bt_gatt_subscribe_params subscribe_params;
|
||||
|
||||
bt_gatt_notify_func_t notify_cb;
|
||||
|
||||
static uint8_t notify_func(struct bt_conn *conn,
|
||||
struct bt_gatt_subscribe_params *params,
|
||||
const void *data, uint16_t length)
|
||||
{
|
||||
if (!data) {
|
||||
printk("[UNSUBSCRIBED]\n");
|
||||
params->value_handle = 0U;
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
printk("[NOTIFICATION] data %p length %u\n", data, length);
|
||||
|
||||
if (notify_cb != NULL) {
|
||||
notify_cb(conn, params, data, length);
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
static uint8_t discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
||||
struct bt_gatt_discover_params *params)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!attr) {
|
||||
printk("Discover complete\n");
|
||||
(void)memset(params, 0, sizeof(*params));
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
printk("[ATTRIBUTE] handle %u\n", attr->handle);
|
||||
|
||||
if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_MTU_TEST)) {
|
||||
memcpy(&uuid, BT_UUID_MTU_TEST_NOTIFY, sizeof(uuid));
|
||||
discover_params.uuid = &uuid.uuid;
|
||||
discover_params.start_handle = attr->handle + 1;
|
||||
discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
||||
|
||||
err = bt_gatt_discover(conn, &discover_params);
|
||||
if (err) {
|
||||
printk("Discover failed (err %d)\n", err);
|
||||
}
|
||||
} else if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_MTU_TEST_NOTIFY)) {
|
||||
memcpy(&uuid, BT_UUID_GATT_CCC, sizeof(uuid));
|
||||
discover_params.uuid = &uuid.uuid;
|
||||
discover_params.start_handle = attr->handle + 2;
|
||||
discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR;
|
||||
subscribe_params.value_handle = bt_gatt_attr_value_handle(attr);
|
||||
|
||||
err = bt_gatt_discover(conn, &discover_params);
|
||||
if (err) {
|
||||
printk("Discover failed (err %d)\n", err);
|
||||
}
|
||||
} else {
|
||||
subscribe_params.notify = notify_func;
|
||||
subscribe_params.value = BT_GATT_CCC_NOTIFY;
|
||||
subscribe_params.ccc_handle = attr->handle;
|
||||
|
||||
err = bt_gatt_subscribe(conn, &subscribe_params);
|
||||
if (err && err != -EALREADY) {
|
||||
printk("Subscribe failed (err %d)\n", err);
|
||||
} else {
|
||||
printk("[SUBSCRIBED]\n");
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
|
||||
struct net_buf_simple *ad)
|
||||
{
|
||||
char addr_str[BT_ADDR_LE_STR_LEN];
|
||||
int err;
|
||||
|
||||
if (default_conn) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* We're only interested in connectable events */
|
||||
if (type != BT_GAP_ADV_TYPE_ADV_IND &&
|
||||
type != BT_GAP_ADV_TYPE_ADV_DIRECT_IND) {
|
||||
return;
|
||||
}
|
||||
|
||||
bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
|
||||
printk("Device found: %s (RSSI %d)\n", addr_str, rssi);
|
||||
|
||||
/* connect only to devices in close proximity */
|
||||
if (rssi < -40) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bt_le_scan_stop()) {
|
||||
return;
|
||||
}
|
||||
|
||||
err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
|
||||
BT_LE_CONN_PARAM_DEFAULT, &default_conn);
|
||||
if (err) {
|
||||
printk("Create conn to %s failed (%u)\n", addr_str, err);
|
||||
start_scan();
|
||||
}
|
||||
}
|
||||
|
||||
static void start_scan(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
|
||||
if (err) {
|
||||
printk("Scanning failed to start (err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Scanning successfully started\n");
|
||||
}
|
||||
|
||||
static void mtu_exchange_cb(struct bt_conn *conn, uint8_t err,
|
||||
struct bt_gatt_exchange_params *params)
|
||||
{
|
||||
printk("%s: MTU exchange %s (%u)\n", __func__,
|
||||
err == 0U ? "successful" : "failed",
|
||||
bt_gatt_get_mtu(conn));
|
||||
}
|
||||
|
||||
static struct bt_gatt_exchange_params mtu_exchange_params = {
|
||||
.func = mtu_exchange_cb
|
||||
};
|
||||
|
||||
static int mtu_exchange(struct bt_conn *conn)
|
||||
{
|
||||
int err;
|
||||
|
||||
printk("%s: Current MTU = %u\n", __func__, bt_gatt_get_mtu(conn));
|
||||
|
||||
printk("%s: Exchange MTU...\n", __func__);
|
||||
err = bt_gatt_exchange_mtu(conn, &mtu_exchange_params);
|
||||
if (err) {
|
||||
printk("%s: MTU exchange failed (err %d)", __func__, err);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void connected(struct bt_conn *conn, uint8_t err)
|
||||
{
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
if (err) {
|
||||
printk("Failed to connect to %s (%u)\n", addr, err);
|
||||
|
||||
bt_conn_unref(default_conn);
|
||||
default_conn = NULL;
|
||||
|
||||
start_scan();
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Connected: %s\n", addr);
|
||||
|
||||
(void)mtu_exchange(conn);
|
||||
|
||||
if (conn == default_conn) {
|
||||
memcpy(&uuid, BT_UUID_MTU_TEST, sizeof(uuid));
|
||||
discover_params.uuid = &uuid.uuid;
|
||||
discover_params.func = discover_func;
|
||||
discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
|
||||
discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
|
||||
discover_params.type = BT_GATT_DISCOVER_PRIMARY;
|
||||
|
||||
err = bt_gatt_discover(default_conn, &discover_params);
|
||||
if (err) {
|
||||
printk("Discover failed(err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||
{
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
if (conn != default_conn) {
|
||||
return;
|
||||
}
|
||||
|
||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
printk("Disconnected: %s (reason 0x%02x)\n", addr, reason);
|
||||
|
||||
bt_conn_unref(default_conn);
|
||||
default_conn = NULL;
|
||||
|
||||
start_scan();
|
||||
}
|
||||
|
||||
BT_CONN_CB_DEFINE(conn_callbacks) = {
|
||||
.connected = connected,
|
||||
.disconnected = disconnected,
|
||||
};
|
||||
|
||||
void run_central_sample(bt_gatt_notify_func_t cb)
|
||||
{
|
||||
int err;
|
||||
|
||||
notify_cb = cb;
|
||||
|
||||
err = bt_enable(NULL);
|
||||
if (err) {
|
||||
printk("Bluetooth init failed (err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Bluetooth initialized\n");
|
||||
|
||||
start_scan();
|
||||
}
|
18
samples/bluetooth/mtu_update/central/src/main.c
Normal file
18
samples/bluetooth/mtu_update/central/src/main.c
Normal file
|
@ -0,0 +1,18 @@
|
|||
/* main.c - Application main entry point */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern void run_central_sample(bt_gatt_notify_func_t cb);
|
||||
|
||||
void main(void)
|
||||
{
|
||||
run_central_sample(NULL);
|
||||
}
|
13
samples/bluetooth/mtu_update/peripheral/CMakeLists.txt
Normal file
13
samples/bluetooth/mtu_update/peripheral/CMakeLists.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(mtu_update)
|
||||
|
||||
target_sources(app PRIVATE
|
||||
src/main.c
|
||||
src/peripheral_mtu_update.c
|
||||
)
|
||||
|
||||
zephyr_library_include_directories(${ZEPHYR_BASE}/samples/bluetooth)
|
13
samples/bluetooth/mtu_update/peripheral/prj.conf
Normal file
13
samples/bluetooth/mtu_update/peripheral/prj.conf
Normal file
|
@ -0,0 +1,13 @@
|
|||
CONFIG_BT=y
|
||||
CONFIG_BT_PERIPHERAL=y
|
||||
CONFIG_BT_DEVICE_NAME="Zephyr Peripheral MTU Update Sample"
|
||||
|
||||
CONFIG_LOG=y
|
||||
CONFIG_BT_L2CAP_LOG_LEVEL_DBG=y
|
||||
|
||||
# HCI ACL buffers size
|
||||
# BT_L2CAP_RX_MTU = CONFIG_BT_BUF_ACL_RX_SIZE - BT_L2CAP_HDR_SIZE
|
||||
CONFIG_BT_BUF_ACL_RX_SIZE=251
|
||||
|
||||
# L2CAP SDU/PDU TX MTU
|
||||
CONFIG_BT_L2CAP_TX_MTU=247
|
9
samples/bluetooth/mtu_update/peripheral/sample.yaml
Normal file
9
samples/bluetooth/mtu_update/peripheral/sample.yaml
Normal file
|
@ -0,0 +1,9 @@
|
|||
sample:
|
||||
name: Bluetooth Peripheral MTU Update
|
||||
tests:
|
||||
sample.bluetooth.peripheral_mtu_update:
|
||||
harness: bluetooth
|
||||
platform_allow: qemu_cortex_m3 qemu_x86 nrf52_bsim
|
||||
tags: bluetooth
|
||||
integration_platforms:
|
||||
- qemu_cortex_m3
|
22
samples/bluetooth/mtu_update/peripheral/src/main.c
Normal file
22
samples/bluetooth/mtu_update/peripheral/src/main.c
Normal file
|
@ -0,0 +1,22 @@
|
|||
/* main.c - Application main entry point */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern void run_peripheral_sample(uint8_t *notify_data, size_t notify_data_size, uint16_t seconds);
|
||||
|
||||
void main(void)
|
||||
{
|
||||
uint8_t notify_data[100] = {};
|
||||
|
||||
notify_data[13] = 0x7f;
|
||||
notify_data[99] = 0x55;
|
||||
|
||||
run_peripheral_sample(notify_data, sizeof(notify_data), 0);
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <zephyr/sys/printk.h>
|
||||
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/uuid.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
|
||||
#define MTU_TEST_SERVICE_TYPE BT_UUID_128_ENCODE(0x2e2b8dc3, 0x06e0, 0x4f93, 0x9bb2, 0x734091c356f0)
|
||||
|
||||
static struct bt_uuid_128 mtu_test_service = BT_UUID_INIT_128(MTU_TEST_SERVICE_TYPE);
|
||||
|
||||
static struct bt_uuid_128 notify_characteristic_uuid =
|
||||
BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x2e2b8dc3, 0x06e0, 0x4f93, 0x9bb2, 0x734091c356f3));
|
||||
|
||||
static const struct bt_data adv_ad_data[] = {
|
||||
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
|
||||
BT_DATA_BYTES(BT_DATA_UUID128_ALL, MTU_TEST_SERVICE_TYPE)};
|
||||
|
||||
static void ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
|
||||
{
|
||||
ARG_UNUSED(attr);
|
||||
|
||||
bool notif_enabled = (value == BT_GATT_CCC_NOTIFY);
|
||||
|
||||
printk("MTU Test Update: notifications %s\n", notif_enabled ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
BT_GATT_SERVICE_DEFINE(mtu_test, BT_GATT_PRIMARY_SERVICE(&mtu_test_service),
|
||||
BT_GATT_CHARACTERISTIC(¬ify_characteristic_uuid.uuid, BT_GATT_CHRC_NOTIFY,
|
||||
BT_GATT_PERM_NONE, NULL, NULL, NULL),
|
||||
BT_GATT_CCC(ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE));
|
||||
|
||||
void mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx)
|
||||
{
|
||||
printk("Updated MTU: TX: %d RX: %d bytes\n", tx, rx);
|
||||
}
|
||||
|
||||
static struct bt_gatt_cb gatt_callbacks = {.att_mtu_updated = mtu_updated};
|
||||
|
||||
void run_peripheral_sample(uint8_t *notify_data, size_t notify_data_size, uint16_t seconds)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_enable(NULL);
|
||||
if (err) {
|
||||
printk("Bluetooth init failed (err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
bt_gatt_cb_register(&gatt_callbacks);
|
||||
|
||||
struct bt_gatt_attr *notify_crch =
|
||||
bt_gatt_find_by_uuid(mtu_test.attrs, 0xffff, ¬ify_characteristic_uuid.uuid);
|
||||
|
||||
/* Advertise. Auto include name in adv data. Connectable. */
|
||||
bt_le_adv_start(BT_LE_ADV_CONN_NAME, adv_ad_data, ARRAY_SIZE(adv_ad_data), NULL, 0);
|
||||
|
||||
bool infinite = seconds == 0;
|
||||
|
||||
for (int i = 0; (i < seconds) || infinite; i++) {
|
||||
k_sleep(K_SECONDS(1));
|
||||
bt_gatt_notify(NULL, notify_crch, notify_data, notify_data_size);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue