1 /* main.c - Application main entry point */
2
3 /*
4 * Copyright (c) 2024 Nordic Semiconductor ASA
5 * Copyright (c) 2015-2016 Intel Corporation
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 */
9
10 #include <zephyr/device.h>
11 #include <zephyr/devicetree.h>
12
13 #include <zephyr/bluetooth/bluetooth.h>
14 #include <zephyr/bluetooth/hci.h>
15 #include <zephyr/bluetooth/conn.h>
16 #include <zephyr/bluetooth/uuid.h>
17 #include <zephyr/bluetooth/gatt.h>
18 #include <zephyr/bluetooth/services/bas.h>
19 #include <zephyr/bluetooth/services/hrs.h>
20
21 static bool hrf_ntf_enabled;
22
23 static const struct bt_data ad[] = {
24 BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
25 BT_DATA_BYTES(BT_DATA_UUID16_ALL,
26 BT_UUID_16_ENCODE(BT_UUID_HRS_VAL),
27 BT_UUID_16_ENCODE(BT_UUID_BAS_VAL),
28 BT_UUID_16_ENCODE(BT_UUID_DIS_VAL)),
29 #if defined(CONFIG_BT_EXT_ADV)
30 BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
31 #endif /* CONFIG_BT_EXT_ADV */
32 };
33
34 #if !defined(CONFIG_BT_EXT_ADV)
35 static const struct bt_data sd[] = {
36 BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
37 };
38 #endif /* !CONFIG_BT_EXT_ADV */
39
40 enum {
41 STATE_CONNECTED,
42 STATE_DISCONNECTED,
43
44 STATE_BITS,
45 };
46
47 static ATOMIC_DEFINE(state, STATE_BITS);
48
connected(struct bt_conn * conn,uint8_t err)49 static void connected(struct bt_conn *conn, uint8_t err)
50 {
51 if (err) {
52 printk("Connection failed, err 0x%02x %s\n", err, bt_hci_err_to_str(err));
53 } else {
54 printk("Connected\n");
55
56 (void)atomic_set_bit(state, STATE_CONNECTED);
57 }
58 }
59
disconnected(struct bt_conn * conn,uint8_t reason)60 static void disconnected(struct bt_conn *conn, uint8_t reason)
61 {
62 printk("Disconnected, reason 0x%02x %s\n", reason, bt_hci_err_to_str(reason));
63
64 (void)atomic_set_bit(state, STATE_DISCONNECTED);
65 }
66
67 BT_CONN_CB_DEFINE(conn_callbacks) = {
68 .connected = connected,
69 .disconnected = disconnected,
70 };
71
hrs_ntf_changed(bool enabled)72 static void hrs_ntf_changed(bool enabled)
73 {
74 hrf_ntf_enabled = enabled;
75
76 printk("HRS notification status changed: %s\n",
77 enabled ? "enabled" : "disabled");
78 }
79
80 static struct bt_hrs_cb hrs_cb = {
81 .ntf_changed = hrs_ntf_changed,
82 };
83
auth_cancel(struct bt_conn * conn)84 static void auth_cancel(struct bt_conn *conn)
85 {
86 char addr[BT_ADDR_LE_STR_LEN];
87
88 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
89
90 printk("Pairing cancelled: %s\n", addr);
91 }
92
93 static struct bt_conn_auth_cb auth_cb_display = {
94 .cancel = auth_cancel,
95 };
96
bas_notify(void)97 static void bas_notify(void)
98 {
99 uint8_t battery_level = bt_bas_get_battery_level();
100
101 battery_level--;
102
103 if (!battery_level) {
104 battery_level = 100U;
105 }
106
107 bt_bas_set_battery_level(battery_level);
108 }
109
hrs_notify(void)110 static void hrs_notify(void)
111 {
112 static uint8_t heartrate = 90U;
113
114 /* Heartrate measurements simulation */
115 heartrate++;
116 if (heartrate == 160U) {
117 heartrate = 90U;
118 }
119
120 if (hrf_ntf_enabled) {
121 bt_hrs_notify(heartrate);
122 }
123 }
124
125 #if defined(CONFIG_GPIO)
126 /* The devicetree node identifier for the "led0" alias. */
127 #define LED0_NODE DT_ALIAS(led0)
128
129 #if DT_NODE_HAS_STATUS_OKAY(LED0_NODE)
130 #include <zephyr/drivers/gpio.h>
131 #define HAS_LED 1
132 static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
133 #define BLINK_ONOFF K_MSEC(500)
134
135 static struct k_work_delayable blink_work;
136 static bool led_is_on;
137
blink_timeout(struct k_work * work)138 static void blink_timeout(struct k_work *work)
139 {
140 led_is_on = !led_is_on;
141 gpio_pin_set(led.port, led.pin, (int)led_is_on);
142
143 k_work_schedule(&blink_work, BLINK_ONOFF);
144 }
145
blink_setup(void)146 static int blink_setup(void)
147 {
148 int err;
149
150 printk("Checking LED device...");
151 if (!gpio_is_ready_dt(&led)) {
152 printk("failed.\n");
153 return -EIO;
154 }
155 printk("done.\n");
156
157 printk("Configuring GPIO pin...");
158 err = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
159 if (err) {
160 printk("failed.\n");
161 return -EIO;
162 }
163 printk("done.\n");
164
165 k_work_init_delayable(&blink_work, blink_timeout);
166
167 return 0;
168 }
169
blink_start(void)170 static void blink_start(void)
171 {
172 printk("Start blinking LED...\n");
173 led_is_on = false;
174 gpio_pin_set(led.port, led.pin, (int)led_is_on);
175 k_work_schedule(&blink_work, BLINK_ONOFF);
176 }
177
blink_stop(void)178 static void blink_stop(void)
179 {
180 struct k_work_sync work_sync;
181
182 printk("Stop blinking LED.\n");
183 k_work_cancel_delayable_sync(&blink_work, &work_sync);
184
185 /* Keep LED on */
186 led_is_on = true;
187 gpio_pin_set(led.port, led.pin, (int)led_is_on);
188 }
189 #endif /* LED0_NODE */
190 #endif /* CONFIG_GPIO */
191
main(void)192 int main(void)
193 {
194 int err;
195
196 err = bt_enable(NULL);
197 if (err) {
198 printk("Bluetooth init failed (err %d)\n", err);
199 return 0;
200 }
201
202 printk("Bluetooth initialized\n");
203
204 bt_conn_auth_cb_register(&auth_cb_display);
205
206 bt_hrs_cb_register(&hrs_cb);
207
208 #if !defined(CONFIG_BT_EXT_ADV)
209 printk("Starting Legacy Advertising (connectable and scannable)\n");
210 err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
211 if (err) {
212 printk("Advertising failed to start (err %d)\n", err);
213 return 0;
214 }
215
216 #else /* CONFIG_BT_EXT_ADV */
217 struct bt_le_adv_param adv_param = {
218 .id = BT_ID_DEFAULT,
219 .sid = 0U,
220 .secondary_max_skip = 0U,
221 .options = (BT_LE_ADV_OPT_EXT_ADV | BT_LE_ADV_OPT_CONN | BT_LE_ADV_OPT_CODED),
222 .interval_min = BT_GAP_ADV_FAST_INT_MIN_2,
223 .interval_max = BT_GAP_ADV_FAST_INT_MAX_2,
224 .peer = NULL,
225 };
226 struct bt_le_ext_adv *adv;
227
228 printk("Creating a Coded PHY connectable non-scannable advertising set\n");
229 err = bt_le_ext_adv_create(&adv_param, NULL, &adv);
230 if (err) {
231 printk("Failed to create Coded PHY extended advertising set (err %d)\n", err);
232
233 printk("Creating a non-Coded PHY connectable non-scannable advertising set\n");
234 adv_param.options &= ~BT_LE_ADV_OPT_CODED;
235 err = bt_le_ext_adv_create(&adv_param, NULL, &adv);
236 if (err) {
237 printk("Failed to create extended advertising set (err %d)\n", err);
238 return 0;
239 }
240 }
241
242 printk("Setting extended advertising data\n");
243 err = bt_le_ext_adv_set_data(adv, ad, ARRAY_SIZE(ad), NULL, 0);
244 if (err) {
245 printk("Failed to set extended advertising data (err %d)\n", err);
246 return 0;
247 }
248
249 printk("Starting Extended Advertising (connectable non-scannable)\n");
250 err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
251 if (err) {
252 printk("Failed to start extended advertising set (err %d)\n", err);
253 return 0;
254 }
255 #endif /* CONFIG_BT_EXT_ADV */
256
257 printk("Advertising successfully started\n");
258
259 #if defined(HAS_LED)
260 err = blink_setup();
261 if (err) {
262 return 0;
263 }
264
265 blink_start();
266 #endif /* HAS_LED */
267
268 /* Implement notification. */
269 while (1) {
270 k_sleep(K_SECONDS(1));
271
272 /* Heartrate measurements simulation */
273 hrs_notify();
274
275 /* Battery level simulation */
276 bas_notify();
277
278 if (atomic_test_and_clear_bit(state, STATE_CONNECTED)) {
279 /* Connected callback executed */
280
281 #if defined(HAS_LED)
282 blink_stop();
283 #endif /* HAS_LED */
284 } else if (atomic_test_and_clear_bit(state, STATE_DISCONNECTED)) {
285 #if !defined(CONFIG_BT_EXT_ADV)
286 printk("Starting Legacy Advertising (connectable and scannable)\n");
287 err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd,
288 ARRAY_SIZE(sd));
289 if (err) {
290 printk("Advertising failed to start (err %d)\n", err);
291 return 0;
292 }
293
294 #else /* CONFIG_BT_EXT_ADV */
295 printk("Starting Extended Advertising (connectable and non-scannable)\n");
296 err = bt_le_ext_adv_start(adv, BT_LE_EXT_ADV_START_DEFAULT);
297 if (err) {
298 printk("Failed to start extended advertising set (err %d)\n", err);
299 return 0;
300 }
301 #endif /* CONFIG_BT_EXT_ADV */
302
303 #if defined(HAS_LED)
304 blink_start();
305 #endif /* HAS_LED */
306 }
307 }
308
309 return 0;
310 }
311