1 /*
2 * Copyright (c) 2020-2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <stdint.h>
7
8 #include <zephyr/bluetooth/gap.h>
9 #include <zephyr/device.h>
10 #include <zephyr/devicetree.h>
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/bluetooth/bluetooth.h>
13 #include <zephyr/sys/util.h>
14
15 #define TIMEOUT_SYNC_CREATE K_SECONDS(10)
16 #define NAME_LEN 30
17
18 static bool per_adv_found;
19 static bt_addr_le_t per_addr;
20 static uint16_t per_adv_sync_timeout;
21 static uint8_t per_sid;
22
23 static K_SEM_DEFINE(sem_per_adv, 0, 1);
24 static K_SEM_DEFINE(sem_per_sync, 0, 1);
25 static K_SEM_DEFINE(sem_per_sync_lost, 0, 1);
26
27 /* The devicetree node identifier for the "led0" alias. */
28 #define LED0_NODE DT_ALIAS(led0)
29
30 #ifdef CONFIG_PER_BLINK_LED0
31 static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
32 #define BLINK_ONOFF K_MSEC(500)
33
34 static struct k_work_delayable blink_work;
35 static bool led_is_on;
36
blink_timeout(struct k_work * work)37 static void blink_timeout(struct k_work *work)
38 {
39 led_is_on = !led_is_on;
40 gpio_pin_set(led.port, led.pin, (int)led_is_on);
41
42 k_work_schedule(&blink_work, BLINK_ONOFF);
43 }
44 #endif
45
data_cb(struct bt_data * data,void * user_data)46 static bool data_cb(struct bt_data *data, void *user_data)
47 {
48 char *name = user_data;
49 uint8_t len;
50
51 switch (data->type) {
52 case BT_DATA_NAME_SHORTENED:
53 case BT_DATA_NAME_COMPLETE:
54 len = MIN(data->data_len, NAME_LEN - 1);
55 memcpy(name, data->data, len);
56 name[len] = '\0';
57 return false;
58 default:
59 return true;
60 }
61 }
62
phy2str(uint8_t phy)63 static const char *phy2str(uint8_t phy)
64 {
65 switch (phy) {
66 case 0: return "No packets";
67 case BT_GAP_LE_PHY_1M: return "LE 1M";
68 case BT_GAP_LE_PHY_2M: return "LE 2M";
69 case BT_GAP_LE_PHY_CODED: return "LE Coded";
70 default: return "Unknown";
71 }
72 }
73
scan_recv(const struct bt_le_scan_recv_info * info,struct net_buf_simple * buf)74 static void scan_recv(const struct bt_le_scan_recv_info *info,
75 struct net_buf_simple *buf)
76 {
77 char le_addr[BT_ADDR_LE_STR_LEN];
78 char name[NAME_LEN];
79
80 (void)memset(name, 0, sizeof(name));
81
82 bt_data_parse(buf, data_cb, name);
83
84 if (strlen(CONFIG_PER_ADV_NAME) > 0 && strcmp(name, CONFIG_PER_ADV_NAME) != 0) {
85 return;
86 }
87
88 bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
89 printk("[DEVICE]: %s, AD evt type %u, Tx Pwr: %i, RSSI %i %s "
90 "C:%u S:%u D:%u SR:%u E:%u Prim: %s, Secn: %s, "
91 "Interval: 0x%04x (%u ms), SID: %u\n",
92 le_addr, info->adv_type, info->tx_power, info->rssi, name,
93 (info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) != 0,
94 (info->adv_props & BT_GAP_ADV_PROP_SCANNABLE) != 0,
95 (info->adv_props & BT_GAP_ADV_PROP_DIRECTED) != 0,
96 (info->adv_props & BT_GAP_ADV_PROP_SCAN_RESPONSE) != 0,
97 (info->adv_props & BT_GAP_ADV_PROP_EXT_ADV) != 0,
98 phy2str(info->primary_phy), phy2str(info->secondary_phy),
99 info->interval, info->interval * 5 / 4, info->sid);
100
101 if (!per_adv_found && info->interval) {
102 uint32_t interval_us;
103 uint32_t timeout;
104
105 per_adv_found = true;
106
107 /* Add retries and convert to unit in 10's of ms */
108 interval_us = BT_GAP_PER_ADV_INTERVAL_TO_US(info->interval);
109
110 timeout = BT_GAP_US_TO_PER_ADV_SYNC_TIMEOUT(interval_us);
111
112 /* 10 attempts */
113 timeout *= 10;
114
115 /* Enforce restraints */
116 per_adv_sync_timeout =
117 CLAMP(timeout, BT_GAP_PER_ADV_MIN_TIMEOUT, BT_GAP_PER_ADV_MAX_TIMEOUT);
118
119 per_sid = info->sid;
120 bt_addr_le_copy(&per_addr, info->addr);
121
122 k_sem_give(&sem_per_adv);
123 }
124 }
125
126 static struct bt_le_scan_cb scan_callbacks = {
127 .recv = scan_recv,
128 };
129
sync_cb(struct bt_le_per_adv_sync * sync,struct bt_le_per_adv_sync_synced_info * info)130 static void sync_cb(struct bt_le_per_adv_sync *sync,
131 struct bt_le_per_adv_sync_synced_info *info)
132 {
133 char le_addr[BT_ADDR_LE_STR_LEN];
134
135 bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
136
137 printk("PER_ADV_SYNC[%u]: [DEVICE]: %s synced, "
138 "Interval 0x%04x (%u ms), PHY %s\n",
139 bt_le_per_adv_sync_get_index(sync), le_addr,
140 info->interval, info->interval * 5 / 4, phy2str(info->phy));
141
142 k_sem_give(&sem_per_sync);
143 }
144
term_cb(struct bt_le_per_adv_sync * sync,const struct bt_le_per_adv_sync_term_info * info)145 static void term_cb(struct bt_le_per_adv_sync *sync,
146 const struct bt_le_per_adv_sync_term_info *info)
147 {
148 char le_addr[BT_ADDR_LE_STR_LEN];
149
150 bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
151
152 printk("PER_ADV_SYNC[%u]: [DEVICE]: %s sync terminated\n",
153 bt_le_per_adv_sync_get_index(sync), le_addr);
154
155 k_sem_give(&sem_per_sync_lost);
156 }
157
recv_cb(struct bt_le_per_adv_sync * sync,const struct bt_le_per_adv_sync_recv_info * info,struct net_buf_simple * buf)158 static void recv_cb(struct bt_le_per_adv_sync *sync,
159 const struct bt_le_per_adv_sync_recv_info *info,
160 struct net_buf_simple *buf)
161 {
162 char le_addr[BT_ADDR_LE_STR_LEN];
163 char data_str[129];
164
165 bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
166 bin2hex(buf->data, buf->len, data_str, sizeof(data_str));
167
168 printk("PER_ADV_SYNC[%u]: [DEVICE]: %s, tx_power %i, "
169 "RSSI %i, CTE %u, data length %u, data: %s\n",
170 bt_le_per_adv_sync_get_index(sync), le_addr, info->tx_power,
171 info->rssi, info->cte_type, buf->len, data_str);
172 }
173
174 static struct bt_le_per_adv_sync_cb sync_callbacks = {
175 .synced = sync_cb,
176 .term = term_cb,
177 .recv = recv_cb
178 };
179
main(void)180 int main(void)
181 {
182 struct bt_le_per_adv_sync_param sync_create_param;
183 struct bt_le_per_adv_sync *sync;
184 int err;
185
186 printk("Starting Periodic Advertising Synchronization Demo\n");
187
188 #ifdef CONFIG_PER_BLINK_LED0
189 printk("Checking LED device...");
190 if (!gpio_is_ready_dt(&led)) {
191 printk("failed.\n");
192 return 0;
193 }
194 printk("done.\n");
195
196 printk("Configuring GPIO pin...");
197 err = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
198 if (err) {
199 printk("failed.\n");
200 return 0;
201 }
202 printk("done.\n");
203
204 k_work_init_delayable(&blink_work, blink_timeout);
205 #endif /* CONFIG_PER_BLINK_LED0 */
206
207 /* Initialize the Bluetooth Subsystem */
208 err = bt_enable(NULL);
209 if (err) {
210 printk("Bluetooth init failed (err %d)\n", err);
211 return 0;
212 }
213
214 printk("Scan callbacks register...");
215 bt_le_scan_cb_register(&scan_callbacks);
216 printk("success.\n");
217
218 printk("Periodic Advertising callbacks register...");
219 bt_le_per_adv_sync_cb_register(&sync_callbacks);
220 printk("Success.\n");
221
222 printk("Start scanning...");
223 err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, NULL);
224 if (err) {
225 printk("failed (err %d)\n", err);
226 return 0;
227 }
228 printk("success.\n");
229
230 do {
231 #ifdef CONFIG_PER_BLINK_LED0
232 struct k_work_sync work_sync;
233
234 printk("Start blinking LED...\n");
235 led_is_on = false;
236 gpio_pin_set(led.port, led.pin, (int)led_is_on);
237 k_work_schedule(&blink_work, BLINK_ONOFF);
238 #endif /* CONFIG_PER_BLINK_LED0 */
239
240 printk("Waiting for periodic advertising...\n");
241 per_adv_found = false;
242 err = k_sem_take(&sem_per_adv, K_FOREVER);
243 if (err) {
244 printk("failed (err %d)\n", err);
245 return 0;
246 }
247 printk("Found periodic advertising.\n");
248
249 printk("Creating Periodic Advertising Sync...");
250 bt_addr_le_copy(&sync_create_param.addr, &per_addr);
251 sync_create_param.options = 0;
252 sync_create_param.sid = per_sid;
253 sync_create_param.skip = 0;
254 sync_create_param.timeout = per_adv_sync_timeout;
255 err = bt_le_per_adv_sync_create(&sync_create_param, &sync);
256 if (err) {
257 printk("failed (err %d)\n", err);
258 return 0;
259 }
260 printk("success.\n");
261
262 printk("Waiting for periodic sync...\n");
263 err = k_sem_take(&sem_per_sync, TIMEOUT_SYNC_CREATE);
264 if (err) {
265 printk("failed (err %d)\n", err);
266
267 printk("Deleting Periodic Advertising Sync...");
268 err = bt_le_per_adv_sync_delete(sync);
269 if (err) {
270 printk("failed (err %d)\n", err);
271 return 0;
272 }
273 continue;
274 }
275 printk("Periodic sync established.\n");
276
277 #ifdef CONFIG_PER_BLINK_LED0
278 printk("Stop blinking LED.\n");
279 k_work_cancel_delayable_sync(&blink_work, &work_sync);
280
281 /* Keep LED on */
282 led_is_on = true;
283 gpio_pin_set(led.port, led.pin, (int)led_is_on);
284 #endif /* CONFIG_PER_BLINK_LED0 */
285
286 printk("Waiting for periodic sync lost...\n");
287 err = k_sem_take(&sem_per_sync_lost, K_FOREVER);
288 if (err) {
289 printk("failed (err %d)\n", err);
290 return 0;
291 }
292 printk("Periodic sync lost.\n");
293 } while (true);
294 }
295