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