| /* |
| * Copyright (c) 2021 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/device.h> |
| #include <zephyr/devicetree.h> |
| #include <zephyr/drivers/gpio.h> |
| #include <zephyr/bluetooth/bluetooth.h> |
| #include <zephyr/bluetooth/conn.h> |
| #include <zephyr/bluetooth/hci.h> |
| |
| static struct bt_conn *default_conn; |
| |
| static K_SEM_DEFINE(sem_per_sync, 0, 1); |
| static K_SEM_DEFINE(sem_per_sync_lost, 0, 1); |
| |
| static void sync_cb(struct bt_le_per_adv_sync *sync, |
| struct bt_le_per_adv_sync_synced_info *info) |
| { |
| char le_addr[BT_ADDR_LE_STR_LEN]; |
| char past_peer_addr[BT_ADDR_LE_STR_LEN]; |
| |
| if (!info->conn) { |
| printk("Sync not from PAST\n"); |
| return; |
| } |
| |
| bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr)); |
| |
| bt_addr_le_to_str(bt_conn_get_dst(info->conn), past_peer_addr, |
| sizeof(past_peer_addr)); |
| |
| printk("PER_ADV_SYNC[%u]: [DEVICE]: %s synced, Interval 0x%04x (%u ms). PAST peer %s\n", |
| bt_le_per_adv_sync_get_index(sync), le_addr, info->interval, |
| info->interval * 5 / 4, past_peer_addr); |
| |
| k_sem_give(&sem_per_sync); |
| } |
| |
| static void term_cb(struct bt_le_per_adv_sync *sync, |
| const struct bt_le_per_adv_sync_term_info *info) |
| { |
| char le_addr[BT_ADDR_LE_STR_LEN]; |
| |
| bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr)); |
| |
| printk("PER_ADV_SYNC[%u]: [DEVICE]: %s sync terminated\n", |
| bt_le_per_adv_sync_get_index(sync), le_addr); |
| |
| k_sem_give(&sem_per_sync_lost); |
| } |
| |
| static void recv_cb(struct bt_le_per_adv_sync *sync, |
| const struct bt_le_per_adv_sync_recv_info *info, |
| struct net_buf_simple *buf) |
| { |
| char le_addr[BT_ADDR_LE_STR_LEN]; |
| char data_str[129]; |
| |
| bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr)); |
| bin2hex(buf->data, buf->len, data_str, sizeof(data_str)); |
| |
| printk("PER_ADV_SYNC[%u]: [DEVICE]: %s, tx_power %i, RSSI %i, CTE %u, data length %u, " |
| "data: %s\n", bt_le_per_adv_sync_get_index(sync), le_addr, |
| info->tx_power, info->rssi, info->cte_type, buf->len, data_str); |
| } |
| |
| static struct bt_le_per_adv_sync_cb sync_callbacks = { |
| .synced = sync_cb, |
| .term = term_cb, |
| .recv = recv_cb |
| }; |
| |
| 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 (default_conn == NULL) { |
| default_conn = bt_conn_ref(conn); |
| } |
| |
| if (conn != default_conn) { |
| return; |
| } |
| |
| printk("Connected: %s\n", addr); |
| } |
| |
| 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 %s\n", addr, reason, bt_hci_err_to_str(reason)); |
| |
| bt_conn_unref(default_conn); |
| default_conn = NULL; |
| } |
| |
| static struct bt_conn_cb conn_callbacks = { |
| .connected = connected, |
| .disconnected = disconnected, |
| }; |
| |
| static const struct bt_data sd[] = { |
| BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1), |
| }; |
| |
| int main(void) |
| { |
| struct bt_le_per_adv_sync_transfer_param past_param; |
| int err; |
| |
| printk("Starting Peripheral Periodic Advertising Synchronization Transfer (PAST) Demo\n"); |
| |
| /* Initialize the Bluetooth Subsystem */ |
| err = bt_enable(NULL); |
| if (err) { |
| printk("Bluetooth init failed (err %d)\n", err); |
| return 0; |
| } |
| |
| printk("Connection callbacks register..."); |
| bt_conn_cb_register(&conn_callbacks); |
| printk("Success\n"); |
| |
| printk("Periodic Advertising callbacks register..."); |
| bt_le_per_adv_sync_cb_register(&sync_callbacks); |
| printk("Success.\n"); |
| |
| printk("Subscribing to periodic advertising sync transfers\n"); |
| past_param.skip = 1; |
| past_param.timeout = 1000; /* 10 seconds */ |
| err = bt_le_per_adv_sync_transfer_subscribe(NULL /* any peer */, |
| &past_param); |
| if (err) { |
| printk("PAST subscribe failed (err %d)\n", err); |
| return 0; |
| } |
| |
| err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, NULL, 0, sd, ARRAY_SIZE(sd)); |
| if (err) { |
| printk("Advertising failed to start (err %d)\n", err); |
| return 0; |
| } |
| |
| do { |
| printk("Waiting for periodic sync...\n"); |
| err = k_sem_take(&sem_per_sync, K_FOREVER); |
| if (err) { |
| printk("failed (err %d)\n", err); |
| return 0; |
| } |
| printk("Periodic sync established.\n"); |
| |
| printk("Waiting for periodic sync lost...\n"); |
| err = k_sem_take(&sem_per_sync_lost, K_FOREVER); |
| if (err) { |
| printk("failed (err %d)\n", err); |
| return 0; |
| } |
| printk("Periodic sync lost.\n"); |
| } while (true); |
| return 0; |
| } |