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 <morsisko@gmail.com>
This commit is contained in:
parent
9f56e3d8a5
commit
3b26d50012
5 changed files with 238 additions and 0 deletions
9
samples/bluetooth/direct_adv/CMakeLists.txt
Normal file
9
samples/bluetooth/direct_adv/CMakeLists.txt
Normal file
|
@ -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
|
||||||
|
)
|
32
samples/bluetooth/direct_adv/README.rst
Normal file
32
samples/bluetooth/direct_adv/README.rst
Normal file
|
@ -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 <bluetooth-samples>` for details.
|
22
samples/bluetooth/direct_adv/prj.conf
Normal file
22
samples/bluetooth/direct_adv/prj.conf
Normal file
|
@ -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
|
7
samples/bluetooth/direct_adv/sample.yaml
Normal file
7
samples/bluetooth/direct_adv/sample.yaml
Normal file
|
@ -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
|
168
samples/bluetooth/direct_adv/src/main.c
Normal file
168
samples/bluetooth/direct_adv/src/main.c
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Michal Morsisko
|
||||||
|
* Copyright (c) 2015-2016 Intel Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/types.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <zephyr/sys/printk.h>
|
||||||
|
#include <zephyr/sys/byteorder.h>
|
||||||
|
#include <zephyr/sys/reboot.h>
|
||||||
|
#include <zephyr/settings/settings.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>
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue