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