1 /* main.c - Application main entry point */
2
3 /*
4 * Copyright (c) 2015-2016 Intel Corporation
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <zephyr/types.h>
10 #include <stddef.h>
11 #include <string.h>
12 #include <errno.h>
13 #include <zephyr/sys/printk.h>
14 #include <zephyr/sys/byteorder.h>
15 #include <zephyr/kernel.h>
16
17 #include <zephyr/settings/settings.h>
18
19 #include <zephyr/bluetooth/bluetooth.h>
20 #include <zephyr/bluetooth/hci.h>
21 #include <zephyr/bluetooth/conn.h>
22 #include <zephyr/bluetooth/uuid.h>
23 #include <zephyr/bluetooth/gatt.h>
24 #include <zephyr/bluetooth/services/bas.h>
25 #include <zephyr/bluetooth/services/cts.h>
26 #include <zephyr/bluetooth/services/hrs.h>
27 #include <zephyr/bluetooth/services/ias.h>
28
29 /* Custom Service Variables */
30 #define BT_UUID_CUSTOM_SERVICE_VAL \
31 BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef0)
32
33 static const struct bt_uuid_128 vnd_uuid = BT_UUID_INIT_128(
34 BT_UUID_CUSTOM_SERVICE_VAL);
35
36 static const struct bt_uuid_128 vnd_enc_uuid = BT_UUID_INIT_128(
37 BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef1));
38
39 static const struct bt_uuid_128 vnd_auth_uuid = BT_UUID_INIT_128(
40 BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef2));
41
42 #define VND_MAX_LEN 20
43 #define BT_HR_HEARTRATE_DEFAULT_MIN 90U
44 #define BT_HR_HEARTRATE_DEFAULT_MAX 160U
45
46 static uint8_t vnd_value[VND_MAX_LEN + 1] = { 'V', 'e', 'n', 'd', 'o', 'r'};
47 static uint8_t vnd_auth_value[VND_MAX_LEN + 1] = { 'V', 'e', 'n', 'd', 'o', 'r'};
48 static uint8_t vnd_wwr_value[VND_MAX_LEN + 1] = { 'V', 'e', 'n', 'd', 'o', 'r' };
49
read_vnd(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)50 static ssize_t read_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr,
51 void *buf, uint16_t len, uint16_t offset)
52 {
53 const char *value = attr->user_data;
54
55 return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
56 strlen(value));
57 }
58
write_vnd(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)59 static ssize_t write_vnd(struct bt_conn *conn, const struct bt_gatt_attr *attr,
60 const void *buf, uint16_t len, uint16_t offset,
61 uint8_t flags)
62 {
63 uint8_t *value = attr->user_data;
64
65 if (offset + len > VND_MAX_LEN) {
66 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
67 }
68
69 memcpy(value + offset, buf, len);
70 value[offset + len] = 0;
71
72 return len;
73 }
74
75 static uint8_t simulate_vnd;
76 static uint8_t indicating;
77 static struct bt_gatt_indicate_params ind_params;
78
vnd_ccc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)79 static void vnd_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
80 {
81 simulate_vnd = (value == BT_GATT_CCC_INDICATE) ? 1 : 0;
82 }
83
indicate_cb(struct bt_conn * conn,struct bt_gatt_indicate_params * params,uint8_t err)84 static void indicate_cb(struct bt_conn *conn,
85 struct bt_gatt_indicate_params *params, uint8_t err)
86 {
87 printk("Indication %s\n", err != 0U ? "fail" : "success");
88 }
89
indicate_destroy(struct bt_gatt_indicate_params * params)90 static void indicate_destroy(struct bt_gatt_indicate_params *params)
91 {
92 printk("Indication complete\n");
93 indicating = 0U;
94 }
95
96 #define VND_LONG_MAX_LEN 74
97 static uint8_t vnd_long_value[VND_LONG_MAX_LEN + 1] = {
98 'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '1',
99 'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '2',
100 'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '3',
101 'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '4',
102 'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '5',
103 'V', 'e', 'n', 'd', 'o', 'r', ' ', 'd', 'a', 't', 'a', '6',
104 '.', ' ' };
105
write_long_vnd(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)106 static ssize_t write_long_vnd(struct bt_conn *conn,
107 const struct bt_gatt_attr *attr, const void *buf,
108 uint16_t len, uint16_t offset, uint8_t flags)
109 {
110 uint8_t *value = attr->user_data;
111
112 if (flags & BT_GATT_WRITE_FLAG_PREPARE) {
113 return 0;
114 }
115
116 if (offset + len > VND_LONG_MAX_LEN) {
117 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
118 }
119
120 memcpy(value + offset, buf, len);
121 value[offset + len] = 0;
122
123 return len;
124 }
125
126 static const struct bt_uuid_128 vnd_long_uuid = BT_UUID_INIT_128(
127 BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef3));
128
129 static struct bt_gatt_cep vnd_long_cep = {
130 .properties = BT_GATT_CEP_RELIABLE_WRITE,
131 };
132
133 static int signed_value;
134
read_signed(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)135 static ssize_t read_signed(struct bt_conn *conn, const struct bt_gatt_attr *attr,
136 void *buf, uint16_t len, uint16_t offset)
137 {
138 const char *value = attr->user_data;
139
140 return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
141 sizeof(signed_value));
142 }
143
write_signed(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)144 static ssize_t write_signed(struct bt_conn *conn, const struct bt_gatt_attr *attr,
145 const void *buf, uint16_t len, uint16_t offset,
146 uint8_t flags)
147 {
148 uint8_t *value = attr->user_data;
149
150 if (offset + len > sizeof(signed_value)) {
151 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
152 }
153
154 memcpy(value + offset, buf, len);
155
156 return len;
157 }
158
159 static const struct bt_uuid_128 vnd_signed_uuid = BT_UUID_INIT_128(
160 BT_UUID_128_ENCODE(0x13345678, 0x1234, 0x5678, 0x1334, 0x56789abcdef3));
161
162 static const struct bt_uuid_128 vnd_write_cmd_uuid = BT_UUID_INIT_128(
163 BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef4));
164
write_without_rsp_vnd(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)165 static ssize_t write_without_rsp_vnd(struct bt_conn *conn,
166 const struct bt_gatt_attr *attr,
167 const void *buf, uint16_t len, uint16_t offset,
168 uint8_t flags)
169 {
170 uint8_t *value = attr->user_data;
171
172 if (!(flags & BT_GATT_WRITE_FLAG_CMD)) {
173 /* Write Request received. Reject it since this Characteristic
174 * only accepts Write Without Response.
175 */
176 return BT_GATT_ERR(BT_ATT_ERR_WRITE_REQ_REJECTED);
177 }
178
179 if (offset + len > VND_MAX_LEN) {
180 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
181 }
182
183 memcpy(value + offset, buf, len);
184 value[offset + len] = 0;
185
186 return len;
187 }
188
189 /* Vendor Primary Service Declaration */
190 BT_GATT_SERVICE_DEFINE(vnd_svc,
191 BT_GATT_PRIMARY_SERVICE(&vnd_uuid),
192 BT_GATT_CHARACTERISTIC(&vnd_enc_uuid.uuid,
193 BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE |
194 BT_GATT_CHRC_INDICATE,
195 BT_GATT_PERM_READ_ENCRYPT |
196 BT_GATT_PERM_WRITE_ENCRYPT,
197 read_vnd, write_vnd, vnd_value),
198 BT_GATT_CCC(vnd_ccc_cfg_changed,
199 BT_GATT_PERM_READ | BT_GATT_PERM_WRITE_ENCRYPT),
200 BT_GATT_CHARACTERISTIC(&vnd_auth_uuid.uuid,
201 BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
202 BT_GATT_PERM_READ_AUTHEN |
203 BT_GATT_PERM_WRITE_AUTHEN,
204 read_vnd, write_vnd, vnd_auth_value),
205 BT_GATT_CHARACTERISTIC(&vnd_long_uuid.uuid, BT_GATT_CHRC_READ |
206 BT_GATT_CHRC_WRITE | BT_GATT_CHRC_EXT_PROP,
207 BT_GATT_PERM_READ | BT_GATT_PERM_WRITE |
208 BT_GATT_PERM_PREPARE_WRITE,
209 read_vnd, write_long_vnd, &vnd_long_value),
210 BT_GATT_CEP(&vnd_long_cep),
211 BT_GATT_CHARACTERISTIC(&vnd_signed_uuid.uuid, BT_GATT_CHRC_READ |
212 BT_GATT_CHRC_WRITE | BT_GATT_CHRC_AUTH,
213 BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
214 read_signed, write_signed, &signed_value),
215 BT_GATT_CHARACTERISTIC(&vnd_write_cmd_uuid.uuid,
216 BT_GATT_CHRC_WRITE_WITHOUT_RESP,
217 BT_GATT_PERM_WRITE, NULL,
218 write_without_rsp_vnd, &vnd_wwr_value),
219 );
220
221 static const struct bt_data ad[] = {
222 BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
223 BT_DATA_BYTES(BT_DATA_UUID16_ALL,
224 BT_UUID_16_ENCODE(BT_UUID_HRS_VAL),
225 BT_UUID_16_ENCODE(BT_UUID_BAS_VAL),
226 BT_UUID_16_ENCODE(BT_UUID_CTS_VAL)),
227 BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_CUSTOM_SERVICE_VAL),
228 };
229
230 static const struct bt_data sd[] = {
231 BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
232 };
233
mtu_updated(struct bt_conn * conn,uint16_t tx,uint16_t rx)234 void mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx)
235 {
236 printk("Updated MTU: TX: %d RX: %d bytes\n", tx, rx);
237 }
238
239 static struct bt_gatt_cb gatt_callbacks = {
240 .att_mtu_updated = mtu_updated
241 };
242
connected(struct bt_conn * conn,uint8_t err)243 static void connected(struct bt_conn *conn, uint8_t err)
244 {
245 if (err) {
246 printk("Connection failed, err 0x%02x %s\n", err, bt_hci_err_to_str(err));
247 } else {
248 printk("Connected\n");
249 }
250 }
251
disconnected(struct bt_conn * conn,uint8_t reason)252 static void disconnected(struct bt_conn *conn, uint8_t reason)
253 {
254 printk("Disconnected, reason 0x%02x %s\n", reason, bt_hci_err_to_str(reason));
255 }
256
alert_stop(void)257 static void alert_stop(void)
258 {
259 printk("Alert stopped\n");
260 }
261
alert_start(void)262 static void alert_start(void)
263 {
264 printk("Mild alert started\n");
265 }
266
alert_high_start(void)267 static void alert_high_start(void)
268 {
269 printk("High alert started\n");
270 }
271
272 BT_CONN_CB_DEFINE(conn_callbacks) = {
273 .connected = connected,
274 .disconnected = disconnected,
275 };
276
277 BT_IAS_CB_DEFINE(ias_callbacks) = {
278 .no_alert = alert_stop,
279 .mild_alert = alert_start,
280 .high_alert = alert_high_start,
281 };
282
bt_ready(void)283 static void bt_ready(void)
284 {
285 int err;
286
287 printk("Bluetooth initialized\n");
288
289 if (IS_ENABLED(CONFIG_SETTINGS)) {
290 settings_load();
291 }
292
293 err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
294 if (err) {
295 printk("Advertising failed to start (err %d)\n", err);
296 return;
297 }
298
299 printk("Advertising successfully started\n");
300 }
301
auth_passkey_display(struct bt_conn * conn,unsigned int passkey)302 static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey)
303 {
304 char addr[BT_ADDR_LE_STR_LEN];
305
306 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
307
308 printk("Passkey for %s: %06u\n", addr, passkey);
309 }
310
auth_cancel(struct bt_conn * conn)311 static void auth_cancel(struct bt_conn *conn)
312 {
313 char addr[BT_ADDR_LE_STR_LEN];
314
315 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
316
317 printk("Pairing cancelled: %s\n", addr);
318 }
319
320 static struct bt_conn_auth_cb auth_cb_display = {
321 .passkey_display = auth_passkey_display,
322 .passkey_entry = NULL,
323 .cancel = auth_cancel,
324 };
325
bas_notify(void)326 static void bas_notify(void)
327 {
328 uint8_t battery_level = bt_bas_get_battery_level();
329
330 battery_level--;
331
332 if (!battery_level) {
333 battery_level = 100U;
334 }
335
336 bt_bas_set_battery_level(battery_level);
337 }
338
339 static uint8_t bt_heartrate = BT_HR_HEARTRATE_DEFAULT_MIN;
340
hrs_notify(void)341 static void hrs_notify(void)
342 {
343 /* Heartrate measurements simulation */
344 bt_heartrate++;
345 if (bt_heartrate == BT_HR_HEARTRATE_DEFAULT_MAX) {
346 bt_heartrate = BT_HR_HEARTRATE_DEFAULT_MIN;
347 }
348
349 bt_hrs_notify(bt_heartrate);
350 }
351
352 /**
353 * variable to hold reference milliseconds to epoch when device booted
354 * this is only for demo purpose, for more precise synchronization please
355 * review clock_settime API implementation.
356 */
357 static int64_t unix_ms_ref;
358 static bool cts_notification_enabled;
359
bt_cts_notification_changed(bool enabled)360 void bt_cts_notification_changed(bool enabled)
361 {
362 cts_notification_enabled = enabled;
363 }
364
bt_cts_cts_time_write(struct bt_cts_time_format * cts_time)365 int bt_cts_cts_time_write(struct bt_cts_time_format *cts_time)
366 {
367 int err;
368 int64_t unix_ms;
369
370 if (IS_ENABLED(CONFIG_BT_CTS_HELPER_API)) {
371 err = bt_cts_time_to_unix_ms(cts_time, &unix_ms);
372 if (err) {
373 return err;
374 }
375 } else {
376 return -ENOTSUP;
377 }
378
379 /* recalculate reference value */
380 unix_ms_ref = unix_ms - k_uptime_get();
381 return 0;
382 }
383
bt_cts_fill_current_cts_time(struct bt_cts_time_format * cts_time)384 int bt_cts_fill_current_cts_time(struct bt_cts_time_format *cts_time)
385 {
386 int64_t unix_ms = unix_ms_ref + k_uptime_get();
387
388 if (IS_ENABLED(CONFIG_BT_CTS_HELPER_API)) {
389 return bt_cts_time_from_unix_ms(cts_time, unix_ms);
390 } else {
391 return -ENOTSUP;
392 }
393 }
394
395 const struct bt_cts_cb cts_cb = {
396 .notification_changed = bt_cts_notification_changed,
397 .cts_time_write = bt_cts_cts_time_write,
398 .fill_current_cts_time = bt_cts_fill_current_cts_time,
399 };
400
bt_hrs_ctrl_point_write(uint8_t request)401 static int bt_hrs_ctrl_point_write(uint8_t request)
402 {
403 printk("HRS Control point request: %d\n", request);
404 if (request != BT_HRS_CONTROL_POINT_RESET_ENERGY_EXPANDED_REQ) {
405 return -ENOTSUP;
406 }
407
408 bt_heartrate = BT_HR_HEARTRATE_DEFAULT_MIN;
409 return 0;
410 }
411
412 static struct bt_hrs_cb hrs_cb = {
413 .ctrl_point_write = bt_hrs_ctrl_point_write,
414 };
415
main(void)416 int main(void)
417 {
418 struct bt_gatt_attr *vnd_ind_attr;
419 char str[BT_UUID_STR_LEN];
420 int err;
421
422 err = bt_enable(NULL);
423 if (err) {
424 printk("Bluetooth init failed (err %d)\n", err);
425 return 0;
426 }
427
428 bt_ready();
429 bt_cts_init(&cts_cb);
430 bt_hrs_cb_register(&hrs_cb);
431
432 bt_gatt_cb_register(&gatt_callbacks);
433 bt_conn_auth_cb_register(&auth_cb_display);
434
435 vnd_ind_attr = bt_gatt_find_by_uuid(vnd_svc.attrs, vnd_svc.attr_count,
436 &vnd_enc_uuid.uuid);
437 bt_uuid_to_str(&vnd_enc_uuid.uuid, str, sizeof(str));
438 printk("Indicate VND attr %p (UUID %s)\n", vnd_ind_attr, str);
439
440 /* Implement notification. At the moment there is no suitable way
441 * of starting delayed work so we do it here
442 */
443 while (1) {
444 k_sleep(K_SECONDS(1));
445
446 /* Current time update notification example
447 * For testing purposes, we send a manual update notification every second.
448 * In production `bt_cts_send_notification` should only be used when time is changed
449 */
450 if (cts_notification_enabled) {
451 bt_cts_send_notification(BT_CTS_UPDATE_REASON_MANUAL);
452 }
453 /* Heartrate measurements simulation */
454 hrs_notify();
455
456 /* Battery level simulation */
457 bas_notify();
458
459 /* Vendor indication simulation */
460 if (simulate_vnd && vnd_ind_attr) {
461 if (indicating) {
462 continue;
463 }
464
465 ind_params.attr = vnd_ind_attr;
466 ind_params.func = indicate_cb;
467 ind_params.destroy = indicate_destroy;
468 ind_params.data = &indicating;
469 ind_params.len = sizeof(indicating);
470
471 if (bt_gatt_indicate(NULL, &ind_params) == 0) {
472 indicating = 1U;
473 }
474 }
475 }
476 return 0;
477 }
478