/* 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 #include #include #include #include #include #include #include #include #include static struct bt_conn *default_conn; static uint16_t default_conn_handle; #define ACI_HAL_SET_TX_POWER_LEVEL BT_OP(BT_OGF_VS, 0xFC0F) struct aci_set_tx_power { uint8_t cmd; uint8_t value[2]; }; struct aci_set_tx_power *param; struct net_buf *buf, *rsp; struct bt_hci_cp_read_tx_power_level *param_r; 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 int get_tx_power(uint16_t handle) { int err = 0; struct bt_hci_rp_read_tx_power_level *rp; /* HCI_READ_TRANSMIT_POWER_LEVEL */ buf = bt_hci_cmd_create(BT_HCI_OP_READ_TX_POWER_LEVEL, sizeof(*param_r)); if (!buf) { printk("Failed - buf creation failed \n\r"); return -ENOBUFS; } param_r = net_buf_add(buf, sizeof(*param_r)); param_r->handle = sys_cpu_to_le16(handle); param_r->type = BT_TX_POWER_LEVEL_CURRENT; err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_TX_POWER_LEVEL, buf, &rsp); if (err) { printk("Failed - hci_read_transmit_power_level : %d\n\r", err); return err; } rp = (void *) rsp->data; printk ("Current TX power level in dBm : %d\n\r",rp->tx_power_level); net_buf_unref(rsp); return 0; } static void connected(struct bt_conn *conn, uint8_t err) { int ret; if (err) { printk("Connection failed, err 0x%02x %s\n", err, bt_hci_err_to_str(err)); } else { printk("Connected\n"); default_conn = bt_conn_ref(conn); ret = bt_hci_get_conn_handle(default_conn, &default_conn_handle); (void)atomic_set_bit(state, STATE_CONNECTED); if(ret) { printk("No connection handle (err %d)\n", ret); } else { printk("Conn handle : %d\n", default_conn_handle); get_tx_power(default_conn_handle); } } } static void disconnected(struct bt_conn *conn, uint8_t reason) { bt_conn_unref(conn); conn = NULL; 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 #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"); /* Send ACI_HAL_SET_TX_POWER_LEVEL */ buf = bt_hci_cmd_create(ACI_HAL_SET_TX_POWER_LEVEL, 3); if (!buf) { return -ENOBUFS; } param = net_buf_add(buf, sizeof(*param)); /* See STM32WBA table conversion in Annex A */ param->value[0] = 0x0F; /* Deprecated and ignored */ param->value[1] = 0x00; err = bt_hci_cmd_send_sync(ACI_HAL_SET_TX_POWER_LEVEL, buf, &rsp); if (err) { printk("Failed - aci_hal_tx_pwr : %d\n\r", err); return err; } else{ printk("Setting PA output level to : 0x%02x \n\r", param->value[0]); } net_buf_unref(rsp); 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; }