1 /*
2 * Copyright (c) 2021-2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stdint.h>
8
9 #include <zephyr/bluetooth/gap.h>
10 #include <zephyr/device.h>
11 #include <zephyr/devicetree.h>
12 #include <zephyr/bluetooth/bluetooth.h>
13 #include <zephyr/bluetooth/conn.h>
14 #include <zephyr/bluetooth/hci.h>
15 #include <zephyr/sys/util.h>
16
17 #define NAME_LEN 30
18
19 static bool per_adv_found;
20 static bt_addr_le_t per_addr;
21 static uint8_t per_sid;
22 static struct bt_conn *default_conn;
23 static uint16_t per_adv_sync_timeout;
24
25 static K_SEM_DEFINE(sem_conn, 0, 1);
26 static K_SEM_DEFINE(sem_conn_lost, 0, 1);
27 static K_SEM_DEFINE(sem_per_adv, 0, 1);
28 static K_SEM_DEFINE(sem_per_sync, 0, 1);
29
data_cb(struct bt_data * data,void * user_data)30 static bool data_cb(struct bt_data *data, void *user_data)
31 {
32 char *name = user_data;
33 uint8_t len;
34
35 switch (data->type) {
36 case BT_DATA_NAME_SHORTENED:
37 case BT_DATA_NAME_COMPLETE:
38 len = MIN(data->data_len, NAME_LEN - 1);
39 memcpy(name, data->data, len);
40 name[len] = '\0';
41 return false;
42 default:
43 return true;
44 }
45 }
46
phy2str(uint8_t phy)47 static const char *phy2str(uint8_t phy)
48 {
49 switch (phy) {
50 case 0: return "No packets";
51 case BT_GAP_LE_PHY_1M: return "LE 1M";
52 case BT_GAP_LE_PHY_2M: return "LE 2M";
53 case BT_GAP_LE_PHY_CODED: return "LE Coded";
54 default: return "Unknown";
55 }
56 }
57
scan_recv(const struct bt_le_scan_recv_info * info,struct net_buf_simple * buf)58 static void scan_recv(const struct bt_le_scan_recv_info *info,
59 struct net_buf_simple *buf)
60 {
61 char le_addr[BT_ADDR_LE_STR_LEN];
62 char name[NAME_LEN];
63 int err;
64
65 /* only parse devices in close proximity */
66 if (info->rssi < -70) {
67 return;
68 }
69
70 (void)memset(name, 0, sizeof(name));
71
72 bt_data_parse(buf, data_cb, name);
73
74 bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
75
76 printk("[DEVICE]: %s, AD evt type %u, Tx Pwr: %i, RSSI %i, name: %s "
77 "C:%u S:%u D:%u SR:%u E:%u Prim: %s, Secn: %s, "
78 "Interval: 0x%04x (%u ms), SID: %u\n",
79 le_addr, info->adv_type, info->tx_power, info->rssi, name,
80 (info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) != 0,
81 (info->adv_props & BT_GAP_ADV_PROP_SCANNABLE) != 0,
82 (info->adv_props & BT_GAP_ADV_PROP_DIRECTED) != 0,
83 (info->adv_props & BT_GAP_ADV_PROP_SCAN_RESPONSE) != 0,
84 (info->adv_props & BT_GAP_ADV_PROP_EXT_ADV) != 0,
85 phy2str(info->primary_phy), phy2str(info->secondary_phy),
86 info->interval, info->interval * 5 / 4, info->sid);
87
88 /* If connectable, connect */
89 if (info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) {
90 if (default_conn) {
91 return;
92 }
93
94 printk("Connecting to %s\n", le_addr);
95
96 err = bt_le_scan_stop();
97 if (err != 0) {
98 printk("Stop LE scan failed (err %d)\n", err);
99 return;
100 }
101
102 err = bt_conn_le_create(info->addr, BT_CONN_LE_CREATE_CONN,
103 BT_LE_CONN_PARAM_DEFAULT,
104 &default_conn);
105 if (err != 0) {
106 printk("Failed to connect (err %d)\n", err);
107 return;
108 }
109 } else {
110 /* If info->interval it is a periodic advertiser, mark for sync */
111 if (!per_adv_found && info->interval) {
112 uint32_t interval_us;
113 uint32_t timeout;
114
115 per_adv_found = true;
116
117 /* Add retries and convert to unit in 10's of ms */
118 interval_us = BT_GAP_PER_ADV_INTERVAL_TO_US(info->interval);
119
120 timeout = BT_GAP_US_TO_PER_ADV_SYNC_TIMEOUT(interval_us);
121
122 /* 10 attempts */
123 timeout *= 10;
124
125 /* Enforce restraints */
126 per_adv_sync_timeout = CLAMP(timeout, BT_GAP_PER_ADV_MIN_TIMEOUT,
127 BT_GAP_PER_ADV_MAX_TIMEOUT);
128
129 per_sid = info->sid;
130 bt_addr_le_copy(&per_addr, info->addr);
131
132 k_sem_give(&sem_per_adv);
133 }
134 }
135 }
136
137 static struct bt_le_scan_cb scan_callbacks = {
138 .recv = scan_recv,
139 };
140
connected(struct bt_conn * conn,uint8_t err)141 static void connected(struct bt_conn *conn, uint8_t err)
142 {
143 char addr[BT_ADDR_LE_STR_LEN];
144 int bt_err;
145
146 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
147
148 if (err != 0) {
149 printk("Failed to connect to %s %u %s\n", addr, err, bt_hci_err_to_str(err));
150
151 bt_conn_unref(default_conn);
152 default_conn = NULL;
153
154
155 bt_err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, NULL);
156 if (bt_err) {
157 printk("Failed to start scan (err %d)\n", bt_err);
158 return;
159 }
160
161 return;
162 }
163
164 if (conn != default_conn) {
165 return;
166 }
167
168 printk("Connected: %s\n", addr);
169
170 k_sem_give(&sem_conn);
171 }
172
disconnected(struct bt_conn * conn,uint8_t reason)173 static void disconnected(struct bt_conn *conn, uint8_t reason)
174 {
175 char addr[BT_ADDR_LE_STR_LEN];
176 int err;
177
178 if (conn != default_conn) {
179 return;
180 }
181
182 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
183
184 printk("Disconnected: %s, reason 0x%02x %s\n", addr, reason, bt_hci_err_to_str(reason));
185
186 bt_conn_unref(default_conn);
187 default_conn = NULL;
188
189 k_sem_give(&sem_conn_lost);
190
191 err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, NULL);
192 if (err != 0) {
193 printk("Failed to start scan (err %d)\n", err);
194 return;
195 }
196 }
197
198 static struct bt_conn_cb conn_callbacks = {
199 .connected = connected,
200 .disconnected = disconnected,
201 };
202
sync_cb(struct bt_le_per_adv_sync * sync,struct bt_le_per_adv_sync_synced_info * info)203 static void sync_cb(struct bt_le_per_adv_sync *sync,
204 struct bt_le_per_adv_sync_synced_info *info)
205 {
206 char le_addr[BT_ADDR_LE_STR_LEN];
207
208 bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
209
210 printk("PER_ADV_SYNC[%u]: [DEVICE]: %s synced, "
211 "Interval 0x%04x (%u ms), PHY %s\n",
212 bt_le_per_adv_sync_get_index(sync), le_addr,
213 info->interval, info->interval * 5 / 4, phy2str(info->phy));
214
215 k_sem_give(&sem_per_sync);
216 }
217
term_cb(struct bt_le_per_adv_sync * sync,const struct bt_le_per_adv_sync_term_info * info)218 static void term_cb(struct bt_le_per_adv_sync *sync,
219 const struct bt_le_per_adv_sync_term_info *info)
220 {
221 char le_addr[BT_ADDR_LE_STR_LEN];
222
223 bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
224
225 printk("PER_ADV_SYNC[%u]: [DEVICE]: %s sync terminated\n",
226 bt_le_per_adv_sync_get_index(sync), le_addr);
227 }
228
recv_cb(struct bt_le_per_adv_sync * sync,const struct bt_le_per_adv_sync_recv_info * info,struct net_buf_simple * buf)229 static void recv_cb(struct bt_le_per_adv_sync *sync,
230 const struct bt_le_per_adv_sync_recv_info *info,
231 struct net_buf_simple *buf)
232 {
233 char le_addr[BT_ADDR_LE_STR_LEN];
234 char data_str[129];
235
236 bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
237 bin2hex(buf->data, buf->len, data_str, sizeof(data_str));
238
239 printk("PER_ADV_SYNC[%u]: [DEVICE]: %s, tx_power %i, "
240 "RSSI %i, CTE %u, data length %u, data: %s\n",
241 bt_le_per_adv_sync_get_index(sync), le_addr, info->tx_power,
242 info->rssi, info->cte_type, buf->len, data_str);
243 }
244
245 static struct bt_le_per_adv_sync_cb sync_callbacks = {
246 .synced = sync_cb,
247 .term = term_cb,
248 .recv = recv_cb
249 };
250
main(void)251 int main(void)
252 {
253 struct bt_le_per_adv_sync_param sync_create_param;
254 struct bt_le_per_adv_sync *sync;
255 int err;
256 char le_addr[BT_ADDR_LE_STR_LEN];
257
258 printk("Starting Central Periodic Advertising Synchronization Transfer (PAST) Demo\n");
259
260 /* Initialize the Bluetooth Subsystem */
261 err = bt_enable(NULL);
262 if (err != 0) {
263 printk("failed to enable BT (err %d)\n", err);
264 return 0;
265 }
266
267 printk("Connection callbacks register\n");
268 bt_conn_cb_register(&conn_callbacks);
269
270 printk("Scan callbacks register\n");
271 bt_le_scan_cb_register(&scan_callbacks);
272
273 printk("Periodic Advertising callbacks register\n");
274 bt_le_per_adv_sync_cb_register(&sync_callbacks);
275
276 printk("Start scanning...");
277 err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, NULL);
278 if (err != 0) {
279 printk("failed (err %d)\n", err);
280 return 0;
281 }
282 printk("success.\n");
283
284 do {
285 printk("Waiting for connection...\n");
286 err = k_sem_take(&sem_conn, K_FOREVER);
287 if (err != 0) {
288 printk("Could not take sem_conn (err %d)\n", err);
289 return 0;
290 }
291 printk("Connected.\n");
292
293 printk("Start scanning for PA...\n");
294 per_adv_found = false;
295 err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, NULL);
296 if (err != 0) {
297 printk("failed (err %d)\n", err);
298 return 0;
299 }
300 printk("Scan started.\n");
301
302 printk("Waiting for periodic advertising...\n");
303 err = k_sem_take(&sem_per_adv, K_FOREVER);
304 if (err != 0) {
305 printk("Could not take sem_per_adv (err %d)\n", err);
306 return 0;
307 }
308 printk("Found periodic advertising.\n");
309
310 bt_addr_le_to_str(&per_addr, le_addr, sizeof(le_addr));
311 printk("Creating Periodic Advertising Sync to %s...\n", le_addr);
312 bt_addr_le_copy(&sync_create_param.addr, &per_addr);
313 sync_create_param.options = 0;
314 sync_create_param.sid = per_sid;
315 sync_create_param.skip = 0;
316 sync_create_param.timeout = per_adv_sync_timeout;
317 err = bt_le_per_adv_sync_create(&sync_create_param, &sync);
318 if (err != 0) {
319 printk("failed (err %d)\n", err);
320 return 0;
321 }
322 printk("success.\n");
323
324 printk("Waiting for periodic sync...\n");
325 err = k_sem_take(&sem_per_sync, K_FOREVER);
326 if (err != 0) {
327 printk("failed (err %d)\n", err);
328 return 0;
329 }
330 printk("Periodic sync established.\n");
331
332 printk("Transferring sync\n");
333 err = bt_le_per_adv_sync_transfer(sync, default_conn, 0);
334 if (err != 0) {
335 printk("Could not transfer sync (err %d)\n", err);
336 return 0;
337 }
338
339 printk("Waiting for connection lost...\n");
340 err = k_sem_take(&sem_conn_lost, K_FOREVER);
341 if (err != 0) {
342 printk("Could not take sem_conn_lost (err %d)\n", err);
343 return 0;
344 }
345 printk("Connection lost.\n");
346 } while (true);
347 }
348