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