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/gatt.h>
10 #include <zephyr/bluetooth/uuid.h>
11 #include <zephyr/bluetooth/hci.h>
12 #include <zephyr/sys/util.h>
13
14 #define NAME_LEN 30
15
16 static K_SEM_DEFINE(sem_per_adv, 0, 1);
17 static K_SEM_DEFINE(sem_per_sync, 0, 1);
18 static K_SEM_DEFINE(sem_per_sync_lost, 0, 1);
19
20 static struct bt_conn *default_conn;
21 static struct bt_le_per_adv_sync *default_sync;
22 static struct __packed {
23 uint8_t subevent;
24 uint8_t response_slot;
25
26 } pawr_timing;
27
sync_cb(struct bt_le_per_adv_sync * sync,struct bt_le_per_adv_sync_synced_info * info)28 static void sync_cb(struct bt_le_per_adv_sync *sync, struct bt_le_per_adv_sync_synced_info *info)
29 {
30 struct bt_le_per_adv_sync_subevent_params params;
31 uint8_t subevents[1];
32 char le_addr[BT_ADDR_LE_STR_LEN];
33 int err;
34
35 bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
36 printk("Synced to %s with %d subevents\n", le_addr, info->num_subevents);
37
38 default_sync = sync;
39
40 params.properties = 0;
41 params.num_subevents = 1;
42 params.subevents = subevents;
43 subevents[0] = pawr_timing.subevent;
44
45 err = bt_le_per_adv_sync_subevent(sync, ¶ms);
46 if (err) {
47 printk("Failed to set subevents to sync to (err %d)\n", err);
48 } else {
49 printk("Changed sync to subevent %d\n", subevents[0]);
50 }
51
52 k_sem_give(&sem_per_sync);
53 }
54
term_cb(struct bt_le_per_adv_sync * sync,const struct bt_le_per_adv_sync_term_info * info)55 static void term_cb(struct bt_le_per_adv_sync *sync,
56 const struct bt_le_per_adv_sync_term_info *info)
57 {
58 char le_addr[BT_ADDR_LE_STR_LEN];
59
60 bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
61
62 printk("Sync terminated (reason %d)\n", info->reason);
63
64 default_sync = NULL;
65
66 k_sem_give(&sem_per_sync_lost);
67 }
68
print_ad_field(struct bt_data * data,void * user_data)69 static bool print_ad_field(struct bt_data *data, void *user_data)
70 {
71 ARG_UNUSED(user_data);
72
73 printk(" 0x%02X: ", data->type);
74 for (size_t i = 0; i < data->data_len; i++) {
75 printk("%02X", data->data[i]);
76 }
77
78 printk("\n");
79
80 return true;
81 }
82
83 int bt_le_per_adv_set_response_data(struct bt_le_per_adv_sync *per_adv_sync,
84 const struct bt_le_per_adv_response_params *params,
85 const struct net_buf_simple *data);
86
87 static struct bt_le_per_adv_response_params rsp_params;
88
89 NET_BUF_SIMPLE_DEFINE_STATIC(rsp_buf, 247);
90
recv_cb(struct bt_le_per_adv_sync * sync,const struct bt_le_per_adv_sync_recv_info * info,struct net_buf_simple * buf)91 static void recv_cb(struct bt_le_per_adv_sync *sync,
92 const struct bt_le_per_adv_sync_recv_info *info, struct net_buf_simple *buf)
93 {
94 int err;
95
96 if (buf && buf->len) {
97 /* Echo the data back to the advertiser */
98 net_buf_simple_reset(&rsp_buf);
99 net_buf_simple_add_mem(&rsp_buf, buf->data, buf->len);
100
101 rsp_params.request_event = info->periodic_event_counter;
102 rsp_params.request_subevent = info->subevent;
103 /* Respond in current subevent and assigned response slot */
104 rsp_params.response_subevent = info->subevent;
105 rsp_params.response_slot = pawr_timing.response_slot;
106
107 printk("Indication: subevent %d, responding in slot %d\n", info->subevent,
108 pawr_timing.response_slot);
109 bt_data_parse(buf, print_ad_field, NULL);
110
111 err = bt_le_per_adv_set_response_data(sync, &rsp_params, &rsp_buf);
112 if (err) {
113 printk("Failed to send response (err %d)\n", err);
114 }
115 } else if (buf) {
116 printk("Received empty indication: subevent %d\n", info->subevent);
117 } else {
118 printk("Failed to receive indication: subevent %d\n", info->subevent);
119 }
120 }
121
122 static struct bt_le_per_adv_sync_cb sync_callbacks = {
123 .synced = sync_cb,
124 .term = term_cb,
125 .recv = recv_cb,
126 };
127
128 static const struct bt_uuid_128 pawr_svc_uuid =
129 BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef0));
130 static const struct bt_uuid_128 pawr_char_uuid =
131 BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef1));
132
write_timing(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)133 static ssize_t write_timing(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf,
134 uint16_t len, uint16_t offset, uint8_t flags)
135 {
136 if (offset) {
137 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
138 }
139
140 if (len != sizeof(pawr_timing)) {
141 return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
142 }
143
144 memcpy(&pawr_timing, buf, len);
145
146 printk("New timing: subevent %d, response slot %d\n", pawr_timing.subevent,
147 pawr_timing.response_slot);
148
149 struct bt_le_per_adv_sync_subevent_params params;
150 uint8_t subevents[1];
151 int err;
152
153 params.properties = 0;
154 params.num_subevents = 1;
155 params.subevents = subevents;
156 subevents[0] = pawr_timing.subevent;
157
158 if (default_sync) {
159 err = bt_le_per_adv_sync_subevent(default_sync, ¶ms);
160 if (err) {
161 printk("Failed to set subevents to sync to (err %d)\n", err);
162 } else {
163 printk("Changed sync to subevent %d\n", subevents[0]);
164 }
165 } else {
166 printk("Not synced yet\n");
167 }
168
169 return len;
170 }
171
172 BT_GATT_SERVICE_DEFINE(pawr_svc, BT_GATT_PRIMARY_SERVICE(&pawr_svc_uuid.uuid),
173 BT_GATT_CHARACTERISTIC(&pawr_char_uuid.uuid, BT_GATT_CHRC_WRITE,
174 BT_GATT_PERM_WRITE, NULL, write_timing,
175 &pawr_timing));
176
connected(struct bt_conn * conn,uint8_t err)177 void connected(struct bt_conn *conn, uint8_t err)
178 {
179 printk("Connected, err 0x%02X %s\n", err, bt_hci_err_to_str(err));
180
181 if (err) {
182 default_conn = NULL;
183
184 return;
185 }
186
187 default_conn = bt_conn_ref(conn);
188 }
189
disconnected(struct bt_conn * conn,uint8_t reason)190 void disconnected(struct bt_conn *conn, uint8_t reason)
191 {
192 bt_conn_unref(default_conn);
193 default_conn = NULL;
194
195 printk("Disconnected, reason 0x%02X %s\n", reason, bt_hci_err_to_str(reason));
196 }
197
198 BT_CONN_CB_DEFINE(conn_cb) = {
199 .connected = connected,
200 .disconnected = disconnected,
201 };
202
203 static const struct bt_data ad[] = {
204 BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
205 };
206
main(void)207 int main(void)
208 {
209 struct bt_le_per_adv_sync_transfer_param past_param;
210 int err;
211
212 printk("Starting Periodic Advertising with Responses Synchronization Demo\n");
213
214 err = bt_enable(NULL);
215 if (err) {
216 printk("Bluetooth init failed (err %d)\n", err);
217
218 return 0;
219 }
220
221 bt_le_per_adv_sync_cb_register(&sync_callbacks);
222
223 past_param.skip = 1;
224 past_param.timeout = 1000; /* 10 seconds */
225 past_param.options = BT_LE_PER_ADV_SYNC_TRANSFER_OPT_NONE;
226 err = bt_le_per_adv_sync_transfer_subscribe(NULL, &past_param);
227 if (err) {
228 printk("PAST subscribe failed (err %d)\n", err);
229
230 return 0;
231 }
232
233 do {
234 err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), NULL, 0);
235 if (err && err != -EALREADY) {
236 printk("Advertising failed to start (err %d)\n", err);
237
238 return 0;
239 }
240
241 printk("Waiting for periodic sync...\n");
242 err = k_sem_take(&sem_per_sync, K_SECONDS(10));
243 if (err) {
244 printk("Timed out while synchronizing\n");
245
246 continue;
247 }
248
249 printk("Periodic sync established.\n");
250
251 err = k_sem_take(&sem_per_sync_lost, K_FOREVER);
252 if (err) {
253 printk("failed (err %d)\n", err);
254
255 return 0;
256 }
257
258 printk("Periodic sync lost.\n");
259 } while (true);
260
261 return 0;
262 }
263