blob: bb534970a4b596a7f888b8485304c3f4cc9a9a94 [file] [log] [blame]
/* main.c - Application main entry point */
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
* Copyright (c) 2015-2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/device.h>
#include <zephyr/devicetree.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>
#include <zephyr/bluetooth/services/bas.h>
#include <zephyr/bluetooth/services/hrs.h>
static bool hrf_ntf_enabled;
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_UUID16_ALL,
BT_UUID_16_ENCODE(BT_UUID_HRS_VAL),
BT_UUID_16_ENCODE(BT_UUID_BAS_VAL),
BT_UUID_16_ENCODE(BT_UUID_DIS_VAL)),
#if defined(CONFIG_BT_EXT_ADV)
BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
#endif /* CONFIG_BT_EXT_ADV */
};
#if !defined(CONFIG_BT_EXT_ADV)
static const struct bt_data sd[] = {
BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
};
#endif /* !CONFIG_BT_EXT_ADV */
/* Use atomic variable, 2 bits for connection and disconnection state */
static ATOMIC_DEFINE(state, 2U);
#define STATE_CONNECTED 1U
#define STATE_DISCONNECTED 2U
static void connected(struct bt_conn *conn, uint8_t err)
{
if (err) {
printk("Connection failed, err 0x%02x %s\n", err, bt_hci_err_to_str(err));
} else {
printk("Connected\n");
(void)atomic_set_bit(state, STATE_CONNECTED);
}
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
printk("Disconnected, reason 0x%02x %s\n", reason, bt_hci_err_to_str(reason));
(void)atomic_set_bit(state, STATE_DISCONNECTED);
}
BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected,
.disconnected = disconnected,
};
static void hrs_ntf_changed(bool enabled)
{
hrf_ntf_enabled = enabled;
printk("HRS notification status changed: %s\n",
enabled ? "enabled" : "disabled");
}
static struct bt_hrs_cb hrs_cb = {
.ntf_changed = hrs_ntf_changed,
};
static void auth_cancel(struct bt_conn *conn)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Pairing cancelled: %s\n", addr);
}
static struct bt_conn_auth_cb auth_cb_display = {
.cancel = auth_cancel,
};
static void bas_notify(void)
{
uint8_t battery_level = bt_bas_get_battery_level();
battery_level--;
if (!battery_level) {
battery_level = 100U;
}
bt_bas_set_battery_level(battery_level);
}
static void hrs_notify(void)
{
static uint8_t heartrate = 90U;
/* Heartrate measurements simulation */
heartrate++;
if (heartrate == 160U) {
heartrate = 90U;
}
if (hrf_ntf_enabled) {
bt_hrs_notify(heartrate);
}
}
#if defined(CONFIG_GPIO)
/* The devicetree node identifier for the "led0" alias. */
#define LED0_NODE DT_ALIAS(led0)
#if DT_NODE_HAS_STATUS_OKAY(LED0_NODE)
#include <zephyr/drivers/gpio.h>
#define HAS_LED 1
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
#define BLINK_ONOFF K_MSEC(500)
static struct k_work_delayable blink_work;
static bool led_is_on;
static void blink_timeout(struct k_work *work)
{
led_is_on = !led_is_on;
gpio_pin_set(led.port, led.pin, (int)led_is_on);
k_work_schedule(&blink_work, BLINK_ONOFF);
}
static int blink_setup(void)
{
int err;
printk("Checking LED device...");
if (!gpio_is_ready_dt(&led)) {
printk("failed.\n");
return -EIO;
}
printk("done.\n");
printk("Configuring GPIO pin...");
err = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
if (err) {
printk("failed.\n");
return -EIO;
}
printk("done.\n");
k_work_init_delayable(&blink_work, blink_timeout);
return 0;
}
static void blink_start(void)
{
printk("Start blinking LED...\n");
led_is_on = false;
gpio_pin_set(led.port, led.pin, (int)led_is_on);
k_work_schedule(&blink_work, BLINK_ONOFF);
}
static void blink_stop(void)
{
struct k_work_sync work_sync;
printk("Stop blinking LED.\n");
k_work_cancel_delayable_sync(&blink_work, &work_sync);
/* Keep LED on */
led_is_on = true;
gpio_pin_set(led.port, led.pin, (int)led_is_on);
}
#endif /* LED0_NODE */
#endif /* CONFIG_GPIO */
int main(void)
{
int err;
err = bt_enable(NULL);
if (err) {
printk("Bluetooth init failed (err %d)\n", err);
return 0;
}
printk("Bluetooth initialized\n");
bt_conn_auth_cb_register(&auth_cb_display);
bt_hrs_cb_register(&hrs_cb);
#if !defined(CONFIG_BT_EXT_ADV)
printk("Starting Legacy Advertising (connectable and scannable)\n");
err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
if (err) {
printk("Advertising failed to start (err %d)\n", err);
return 0;
}
#else /* CONFIG_BT_EXT_ADV */
struct bt_le_adv_param adv_param = {
.id = BT_ID_DEFAULT,
.sid = 0U,
.secondary_max_skip = 0U,
.options = (BT_LE_ADV_OPT_EXT_ADV | BT_LE_ADV_OPT_CONN | BT_LE_ADV_OPT_CODED),
.interval_min = BT_GAP_ADV_FAST_INT_MIN_2,
.interval_max = BT_GAP_ADV_FAST_INT_MAX_2,
.peer = NULL,
};
struct bt_le_ext_adv *adv;
printk("Creating a Coded PHY connectable non-scannable advertising set\n");
err = bt_le_ext_adv_create(&adv_param, NULL, &adv);
if (err) {
printk("Failed to create Coded PHY extended advertising set (err %d)\n", err);
printk("Creating a non-Coded PHY connectable non-scannable advertising set\n");
adv_param.options &= ~BT_LE_ADV_OPT_CODED;
err = bt_le_ext_adv_create(&adv_param, NULL, &adv);
if (err) {
printk("Failed to create extended advertising set (err %d)\n", err);
return 0;
}
}
printk("Setting extended advertising data\n");
err = bt_le_ext_adv_set_data(adv, ad, ARRAY_SIZE(ad), NULL, 0);
if (err) {
printk("Failed to set extended advertising data (err %d)\n", err);
return 0;
}
printk("Starting Extended Advertising (connectable non-scannable)\n");
err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
if (err) {
printk("Failed to start extended advertising set (err %d)\n", err);
return 0;
}
#endif /* CONFIG_BT_EXT_ADV */
printk("Advertising successfully started\n");
#if defined(HAS_LED)
err = blink_setup();
if (err) {
return 0;
}
blink_start();
#endif /* HAS_LED */
/* Implement notification. */
while (1) {
k_sleep(K_SECONDS(1));
/* Heartrate measurements simulation */
hrs_notify();
/* Battery level simulation */
bas_notify();
if (atomic_test_and_clear_bit(state, STATE_CONNECTED)) {
/* Connected callback executed */
#if defined(HAS_LED)
blink_stop();
#endif /* HAS_LED */
} else if (atomic_test_and_clear_bit(state, STATE_DISCONNECTED)) {
#if !defined(CONFIG_BT_EXT_ADV)
printk("Starting Legacy Advertising (connectable and scannable)\n");
err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd,
ARRAY_SIZE(sd));
if (err) {
printk("Advertising failed to start (err %d)\n", err);
return 0;
}
#else /* CONFIG_BT_EXT_ADV */
printk("Starting Extended Advertising (connectable and non-scannable)\n");
err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
if (err) {
printk("Failed to start extended advertising set (err %d)\n", err);
return 0;
}
#endif /* CONFIG_BT_EXT_ADV */
#if defined(HAS_LED)
blink_start();
#endif /* HAS_LED */
}
}
return 0;
}