1 /*
2 * Copyright (c) 2023 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/bluetooth/bluetooth.h>
8 #include <zephyr/bluetooth/conn.h>
9 #include <zephyr/bluetooth/gap.h>
10 #include <zephyr/bluetooth/hci.h>
11
12 #define NUM_RSP_SLOTS 5
13 #define NUM_SUBEVENTS 5
14 #define PACKET_SIZE 5
15 #define SUBEVENT_INTERVAL 0x30
16
17 static const struct bt_le_per_adv_param per_adv_params = {
18 .interval_min = 0xFF,
19 .interval_max = 0xFF,
20 .options = 0,
21 .num_subevents = NUM_SUBEVENTS,
22 .subevent_interval = SUBEVENT_INTERVAL,
23 .response_slot_delay = 0x5,
24 .response_slot_spacing = 0x50,
25 .num_response_slots = NUM_RSP_SLOTS,
26 };
27
28 static struct bt_le_per_adv_subevent_data_params subevent_data_params[NUM_SUBEVENTS];
29 static struct net_buf_simple bufs[NUM_SUBEVENTS];
30 static uint8_t backing_store[NUM_SUBEVENTS][PACKET_SIZE];
31
32 BUILD_ASSERT(ARRAY_SIZE(bufs) == ARRAY_SIZE(subevent_data_params));
33 BUILD_ASSERT(ARRAY_SIZE(backing_store) == ARRAY_SIZE(subevent_data_params));
34
request_cb(struct bt_le_ext_adv * adv,const struct bt_le_per_adv_data_request * request)35 static void request_cb(struct bt_le_ext_adv *adv, const struct bt_le_per_adv_data_request *request)
36 {
37 int err;
38 uint8_t to_send;
39
40 /* Continuously send the same dummy data and listen to all response slots */
41
42 to_send = MIN(request->count, ARRAY_SIZE(subevent_data_params));
43 for (size_t i = 0; i < to_send; i++) {
44 subevent_data_params[i].subevent =
45 (request->start + i) % per_adv_params.num_subevents;
46 subevent_data_params[i].response_slot_start = 0;
47 subevent_data_params[i].response_slot_count = NUM_RSP_SLOTS;
48 subevent_data_params[i].data = &bufs[i];
49 }
50
51 err = bt_le_per_adv_set_subevent_data(adv, to_send, subevent_data_params);
52 if (err) {
53 printk("Failed to set subevent data (err %d)\n", err);
54 }
55 }
56
get_address(struct bt_data * data,void * user_data)57 static bool get_address(struct bt_data *data, void *user_data)
58 {
59 bt_addr_le_t *addr = user_data;
60
61 if (data->type == BT_DATA_LE_BT_DEVICE_ADDRESS) {
62 memcpy(addr->a.val, data->data, sizeof(addr->a.val));
63 addr->type = data->data[sizeof(addr->a)];
64
65 return false;
66 }
67
68 return true;
69 }
70
71 static struct bt_conn *default_conn;
72
response_cb(struct bt_le_ext_adv * adv,struct bt_le_per_adv_response_info * info,struct net_buf_simple * buf)73 static void response_cb(struct bt_le_ext_adv *adv, struct bt_le_per_adv_response_info *info,
74 struct net_buf_simple *buf)
75 {
76 int err;
77 bt_addr_le_t peer;
78 char addr_str[BT_ADDR_LE_STR_LEN];
79 struct bt_conn_le_create_synced_param synced_param;
80 struct bt_le_conn_param conn_param;
81
82 if (!buf) {
83 return;
84 }
85
86 if (default_conn) {
87 /* Do not initiate new connections while already connected */
88 return;
89 }
90
91 bt_addr_le_copy(&peer, &bt_addr_le_none);
92 bt_data_parse(buf, get_address, &peer);
93 if (bt_addr_le_eq(&peer, &bt_addr_le_none)) {
94 /* No address found */
95 return;
96 }
97
98 bt_addr_le_to_str(&peer, addr_str, sizeof(addr_str));
99 printk("Connecting to %s in subevent %d\n", addr_str, info->subevent);
100
101 synced_param.peer = &peer;
102 synced_param.subevent = info->subevent;
103
104 /* Choose same interval as PAwR advertiser to avoid scheduling conflicts */
105 conn_param.interval_min = SUBEVENT_INTERVAL;
106 conn_param.interval_max = SUBEVENT_INTERVAL;
107
108 /* Default values */
109 conn_param.latency = 0;
110 conn_param.timeout = 400;
111
112 err = bt_conn_le_create_synced(adv, &synced_param, &conn_param, &default_conn);
113 if (err) {
114 printk("Failed to initiate connection (err %d)", err);
115 }
116 }
117
118 static const struct bt_le_ext_adv_cb adv_cb = {
119 .pawr_data_request = request_cb,
120 .pawr_response = response_cb,
121 };
122
connected_cb(struct bt_conn * conn,uint8_t err)123 static void connected_cb(struct bt_conn *conn, uint8_t err)
124 {
125 printk("Connected (err 0x%02X)\n", err);
126
127 __ASSERT(conn == default_conn, "Unexpected connected callback");
128
129 if (err) {
130 bt_conn_unref(default_conn);
131 default_conn = NULL;
132 }
133 }
134
disconnected_cb(struct bt_conn * conn,uint8_t reason)135 static void disconnected_cb(struct bt_conn *conn, uint8_t reason)
136 {
137 printk("Disconnected, reason 0x%02X %s\n", reason, bt_hci_err_to_str(reason));
138
139 __ASSERT(conn == default_conn, "Unexpected disconnected callback");
140
141 bt_conn_unref(default_conn);
142 default_conn = NULL;
143 }
144
145 BT_CONN_CB_DEFINE(conn_cb) = {
146 .connected = connected_cb,
147 .disconnected = disconnected_cb,
148 };
149
init_bufs(void)150 static void init_bufs(void)
151 {
152 /* Set up some dummy data to send */
153 for (size_t i = 0; i < ARRAY_SIZE(backing_store); i++) {
154 backing_store[i][0] = ARRAY_SIZE(backing_store[i]) - 1;
155 backing_store[i][1] = BT_DATA_MANUFACTURER_DATA;
156 backing_store[i][2] = 0x59; /* Nordic */
157 backing_store[i][3] = 0x00;
158
159 net_buf_simple_init_with_data(&bufs[i], &backing_store[i],
160 ARRAY_SIZE(backing_store[i]));
161 }
162 }
163
164 static const struct bt_data ad[] = {
165 BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
166 };
167
168
main(void)169 int main(void)
170 {
171 int err;
172 struct bt_le_ext_adv *pawr_adv;
173
174 init_bufs();
175
176 printk("Starting Periodic Advertising Demo\n");
177
178 /* Initialize the Bluetooth Subsystem */
179 err = bt_enable(NULL);
180 if (err) {
181 printk("Bluetooth init failed (err %d)\n", err);
182 return 0;
183 }
184
185 /* Create a non-connectable advertising set */
186 err = bt_le_ext_adv_create(BT_LE_EXT_ADV_NCONN, &adv_cb, &pawr_adv);
187 if (err) {
188 printk("Failed to create advertising set (err %d)\n", err);
189 return 0;
190 }
191
192 /* Set advertising data to have complete local name set */
193 err = bt_le_ext_adv_set_data(pawr_adv, ad, ARRAY_SIZE(ad), NULL, 0);
194 if (err) {
195 printk("Failed to set advertising data (err %d)\n", err);
196 return 0;
197 }
198
199 /* Set periodic advertising parameters */
200 err = bt_le_per_adv_set_param(pawr_adv, &per_adv_params);
201 if (err) {
202 printk("Failed to set periodic advertising parameters (err %d)\n", err);
203 return 0;
204 }
205
206 /* Enable Periodic Advertising */
207 err = bt_le_per_adv_start(pawr_adv);
208 if (err) {
209 printk("Failed to enable periodic advertising (err %d)\n", err);
210 return 0;
211 }
212
213 printk("Start Periodic Advertising\n");
214 err = bt_le_ext_adv_start(pawr_adv, BT_LE_EXT_ADV_START_DEFAULT);
215 if (err) {
216 printk("Failed to start extended advertising (err %d)\n", err);
217 return 0;
218 }
219
220 while (true) {
221 k_sleep(K_SECONDS(1));
222 }
223
224 return 0;
225 }
226