From 3b26d50012e56f8fb12052d1dca5b535272128d0 Mon Sep 17 00:00:00 2001 From: Michal morsisko Date: Sun, 18 Sep 2022 21:36:58 +0200 Subject: [PATCH] samples: bluetooth: Add sample demonstrating direct advertising This sample shows how to run BLE direct advertising towards central with enabled privacy. Signed-off-by: Michal morsisko --- samples/bluetooth/direct_adv/CMakeLists.txt | 9 ++ samples/bluetooth/direct_adv/README.rst | 32 ++++ samples/bluetooth/direct_adv/prj.conf | 22 +++ samples/bluetooth/direct_adv/sample.yaml | 7 + samples/bluetooth/direct_adv/src/main.c | 168 ++++++++++++++++++++ 5 files changed, 238 insertions(+) create mode 100644 samples/bluetooth/direct_adv/CMakeLists.txt create mode 100644 samples/bluetooth/direct_adv/README.rst create mode 100644 samples/bluetooth/direct_adv/prj.conf create mode 100644 samples/bluetooth/direct_adv/sample.yaml create mode 100644 samples/bluetooth/direct_adv/src/main.c diff --git a/samples/bluetooth/direct_adv/CMakeLists.txt b/samples/bluetooth/direct_adv/CMakeLists.txt new file mode 100644 index 00000000000..fef3a2c8800 --- /dev/null +++ b/samples/bluetooth/direct_adv/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(direct_adv) + +target_sources(app PRIVATE + src/main.c +) diff --git a/samples/bluetooth/direct_adv/README.rst b/samples/bluetooth/direct_adv/README.rst new file mode 100644 index 00000000000..ca0375eca60 --- /dev/null +++ b/samples/bluetooth/direct_adv/README.rst @@ -0,0 +1,32 @@ +.. _ble_direct_adv: + +Bluetooth: Direct Advertising +############################# + +Overview +******** + +Application demonstrating the BLE Direct Advertising capability. If no device is bonded +to the peripheral, casual advertising will be performed. Once bonded, on every subsequent +boot direct advertising to the bonded central will be performed. Additionally this sample +provides two BLE characteristics. To perform write, devices need to be bonded, while read +can be done just after connection (no bonding required). + +Please note that direct advertising towards iOS based devices is not allowed. +For more information about designing BLE devices for Apple products refer to +"Accessory Design Guidelines for Apple Devices". + +Requirements +************ + +* A board with BLE support +* Second BLE device acting as a central with enabled privacy. For example another Zephyr board + or any modern smartphone + +Building and Running +******************** + +This sample can be found under :zephyr_file:`samples/bluetooth/direct_adv` in the +Zephyr tree. + +See :ref:`bluetooth samples section ` for details. diff --git a/samples/bluetooth/direct_adv/prj.conf b/samples/bluetooth/direct_adv/prj.conf new file mode 100644 index 00000000000..a3905e5207c --- /dev/null +++ b/samples/bluetooth/direct_adv/prj.conf @@ -0,0 +1,22 @@ +# Increased stack due to settings API usage +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 + +CONFIG_BT=y +CONFIG_BT_DEBUG_LOG=y +CONFIG_BT_SMP=y +CONFIG_BT_SIGNING=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_DIS=y +CONFIG_BT_ATT_PREPARE_COUNT=1 +CONFIG_BT_PRIVACY=y +CONFIG_BT_DEVICE_NAME="Direct Adv" +CONFIG_BT_DEVICE_APPEARANCE=833 + +CONFIG_BT_KEYS_OVERWRITE_OLDEST=y +CONFIG_BT_SETTINGS=y +CONFIG_FLASH=y +CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_FLASH_MAP=y +CONFIG_NVS=y +CONFIG_SETTINGS=y +CONFIG_REBOOT=y diff --git a/samples/bluetooth/direct_adv/sample.yaml b/samples/bluetooth/direct_adv/sample.yaml new file mode 100644 index 00000000000..bd9f9cba4a9 --- /dev/null +++ b/samples/bluetooth/direct_adv/sample.yaml @@ -0,0 +1,7 @@ +sample: + name: Bluetooh Direct Advertising +tests: + sample.bluetooth.direct_adv: + harness: bluetooth + platform_allow: qemu_cortex_m3 qemu_x86 + tags: bluetooth diff --git a/samples/bluetooth/direct_adv/src/main.c b/samples/bluetooth/direct_adv/src/main.c new file mode 100644 index 00000000000..24addc50936 --- /dev/null +++ b/samples/bluetooth/direct_adv/src/main.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2022 Michal Morsisko + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Custom Service Variables */ +#define BT_UUID_CUSTOM_SERVICE_VAL \ + BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef0) + +static struct bt_uuid_128 primary_service_uuid = BT_UUID_INIT_128( + BT_UUID_CUSTOM_SERVICE_VAL); + +static struct bt_uuid_128 read_characteristic_uuid = BT_UUID_INIT_128( + BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef1)); + +static struct bt_uuid_128 write_characteristic_uuid = BT_UUID_INIT_128( + BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef2)); + +static int signed_value; +static struct bt_le_adv_param adv_param; +static bt_addr_le_t bond_addr; + +static ssize_t read_signed(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + int *value = &signed_value; + + return bt_gatt_attr_read(conn, attr, buf, len, offset, value, + sizeof(signed_value)); +} + +static ssize_t write_signed(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, + uint8_t flags) +{ + int *value = &signed_value; + + if (offset + len > sizeof(signed_value)) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } + + memcpy(value + offset, buf, len); + + return len; +} + +/* Vendor Primary Service Declaration */ +BT_GATT_SERVICE_DEFINE(primary_service, + BT_GATT_PRIMARY_SERVICE(&primary_service_uuid), + BT_GATT_CHARACTERISTIC(&read_characteristic_uuid.uuid, + BT_GATT_CHRC_READ, + BT_GATT_PERM_READ, + read_signed, NULL, NULL), + BT_GATT_CHARACTERISTIC(&write_characteristic_uuid.uuid, + BT_GATT_CHRC_WRITE, + BT_GATT_PERM_WRITE_ENCRYPT, + NULL, write_signed, NULL), +); + +static const struct bt_data ad[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_CUSTOM_SERVICE_VAL), +}; + +static void connected(struct bt_conn *conn, uint8_t err) +{ + if (err) { + printk("Connection failed (err 0x%02x)\n", err); + } else { + printk("Connected\n"); + } +} + +static void disconnected(struct bt_conn *conn, uint8_t reason) +{ + printk("Disconnected (reason 0x%02x)\n", reason); +} + +BT_CONN_CB_DEFINE(conn_callbacks) = { + .connected = connected, + .disconnected = disconnected +}; + +static void copy_last_bonded_addr(const struct bt_bond_info *info, void *data) +{ + bt_addr_le_copy(&bond_addr, &info->addr); +} + +static void bt_ready(void) +{ + int err; + char addr[BT_ADDR_LE_STR_LEN]; + + printk("Bluetooth initialized\n"); + + if (IS_ENABLED(CONFIG_SETTINGS)) { + settings_load(); + } + + bt_addr_le_copy(&bond_addr, BT_ADDR_LE_NONE); + bt_foreach_bond(BT_ID_DEFAULT, copy_last_bonded_addr, NULL); + + /* Address is equal to BT_ADDR_LE_NONE if compare returns 0. + * This means there is no bond yet. + */ + if (bt_addr_le_cmp(&bond_addr, BT_ADDR_LE_NONE) != 0) { + bt_addr_le_to_str(&bond_addr, addr, sizeof(addr)); + printk("Direct advertising to %s\n", addr); + + adv_param = *BT_LE_ADV_CONN_DIR_LOW_DUTY(&bond_addr); + adv_param.options |= BT_LE_ADV_OPT_DIR_ADDR_RPA; + err = bt_le_adv_start(&adv_param, NULL, 0, NULL, 0); + } else { + err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0); + } + + if (err) { + printk("Advertising failed to start (err %d)\n", err); + } else { + printk("Advertising successfully started\n"); + } +} + +void pairing_complete(struct bt_conn *conn, bool bonded) +{ + printk("Pairing completed. Rebooting in 5 seconds...\n"); + + k_sleep(K_SECONDS(5)); + sys_reboot(SYS_REBOOT_WARM); +} + +static struct bt_conn_auth_info_cb bt_conn_auth_info = { + .pairing_complete = pairing_complete +}; + +void main(void) +{ + int err; + + err = bt_enable(NULL); + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + return; + } + + bt_ready(); + bt_conn_auth_info_cb_register(&bt_conn_auth_info); + + while (1) { + k_sleep(K_FOREVER); + } +}