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