From 7e23294bc598a3f32c8518b8e959b68d04abbcb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Battrel?= Date: Wed, 7 Dec 2022 16:01:04 +0100 Subject: [PATCH] Bluetooth: Add a sample to demonstrate MTU update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- samples/bluetooth/mtu_update/README.rst | 21 ++ .../mtu_update/central/CMakeLists.txt | 13 + samples/bluetooth/mtu_update/central/prj.conf | 15 + .../bluetooth/mtu_update/central/sample.yaml | 9 + .../central/src/central_mtu_update.c | 263 ++++++++++++++++++ .../bluetooth/mtu_update/central/src/main.c | 18 ++ .../mtu_update/peripheral/CMakeLists.txt | 13 + .../bluetooth/mtu_update/peripheral/prj.conf | 13 + .../mtu_update/peripheral/sample.yaml | 9 + .../mtu_update/peripheral/src/main.c | 22 ++ .../peripheral/src/peripheral_mtu_update.c | 73 +++++ 11 files changed, 469 insertions(+) create mode 100644 samples/bluetooth/mtu_update/README.rst create mode 100644 samples/bluetooth/mtu_update/central/CMakeLists.txt create mode 100644 samples/bluetooth/mtu_update/central/prj.conf create mode 100644 samples/bluetooth/mtu_update/central/sample.yaml create mode 100644 samples/bluetooth/mtu_update/central/src/central_mtu_update.c create mode 100644 samples/bluetooth/mtu_update/central/src/main.c create mode 100644 samples/bluetooth/mtu_update/peripheral/CMakeLists.txt create mode 100644 samples/bluetooth/mtu_update/peripheral/prj.conf create mode 100644 samples/bluetooth/mtu_update/peripheral/sample.yaml create mode 100644 samples/bluetooth/mtu_update/peripheral/src/main.c create mode 100644 samples/bluetooth/mtu_update/peripheral/src/peripheral_mtu_update.c diff --git a/samples/bluetooth/mtu_update/README.rst b/samples/bluetooth/mtu_update/README.rst new file mode 100644 index 00000000000..b2cf3a8f569 --- /dev/null +++ b/samples/bluetooth/mtu_update/README.rst @@ -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 ` for details. diff --git a/samples/bluetooth/mtu_update/central/CMakeLists.txt b/samples/bluetooth/mtu_update/central/CMakeLists.txt new file mode 100644 index 00000000000..b1a6c12d956 --- /dev/null +++ b/samples/bluetooth/mtu_update/central/CMakeLists.txt @@ -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) diff --git a/samples/bluetooth/mtu_update/central/prj.conf b/samples/bluetooth/mtu_update/central/prj.conf new file mode 100644 index 00000000000..ba93104ac90 --- /dev/null +++ b/samples/bluetooth/mtu_update/central/prj.conf @@ -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 diff --git a/samples/bluetooth/mtu_update/central/sample.yaml b/samples/bluetooth/mtu_update/central/sample.yaml new file mode 100644 index 00000000000..6ece9e2cce3 --- /dev/null +++ b/samples/bluetooth/mtu_update/central/sample.yaml @@ -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 diff --git a/samples/bluetooth/mtu_update/central/src/central_mtu_update.c b/samples/bluetooth/mtu_update/central/src/central_mtu_update.c new file mode 100644 index 00000000000..a7289b3c39c --- /dev/null +++ b/samples/bluetooth/mtu_update/central/src/central_mtu_update.c @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#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(); +} diff --git a/samples/bluetooth/mtu_update/central/src/main.c b/samples/bluetooth/mtu_update/central/src/main.c new file mode 100644 index 00000000000..65732828448 --- /dev/null +++ b/samples/bluetooth/mtu_update/central/src/main.c @@ -0,0 +1,18 @@ +/* main.c - Application main entry point */ + +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +extern void run_central_sample(bt_gatt_notify_func_t cb); + +void main(void) +{ + run_central_sample(NULL); +} diff --git a/samples/bluetooth/mtu_update/peripheral/CMakeLists.txt b/samples/bluetooth/mtu_update/peripheral/CMakeLists.txt new file mode 100644 index 00000000000..8f69405476b --- /dev/null +++ b/samples/bluetooth/mtu_update/peripheral/CMakeLists.txt @@ -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) diff --git a/samples/bluetooth/mtu_update/peripheral/prj.conf b/samples/bluetooth/mtu_update/peripheral/prj.conf new file mode 100644 index 00000000000..3d7a2d69baf --- /dev/null +++ b/samples/bluetooth/mtu_update/peripheral/prj.conf @@ -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 diff --git a/samples/bluetooth/mtu_update/peripheral/sample.yaml b/samples/bluetooth/mtu_update/peripheral/sample.yaml new file mode 100644 index 00000000000..2ffbe15be17 --- /dev/null +++ b/samples/bluetooth/mtu_update/peripheral/sample.yaml @@ -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 diff --git a/samples/bluetooth/mtu_update/peripheral/src/main.c b/samples/bluetooth/mtu_update/peripheral/src/main.c new file mode 100644 index 00000000000..f8f56dd8ebb --- /dev/null +++ b/samples/bluetooth/mtu_update/peripheral/src/main.c @@ -0,0 +1,22 @@ +/* main.c - Application main entry point */ + +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +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); +} diff --git a/samples/bluetooth/mtu_update/peripheral/src/peripheral_mtu_update.c b/samples/bluetooth/mtu_update/peripheral/src/peripheral_mtu_update.c new file mode 100644 index 00000000000..f30e50a85ab --- /dev/null +++ b/samples/bluetooth/mtu_update/peripheral/src/peripheral_mtu_update.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#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); + } +}