1 /* main.c - Application main entry point */
2
3 /*
4 * Copyright (c) 2019 Andrei Stoica
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <zephyr/bluetooth/gap.h>
10 #include <zephyr/types.h>
11 #include <stddef.h>
12 #include <zephyr/sys/printk.h>
13 #include <zephyr/sys/util.h>
14 #include <zephyr/sys/byteorder.h>
15
16 #include <zephyr/bluetooth/bluetooth.h>
17 #include <zephyr/bluetooth/hci.h>
18 #include <zephyr/bluetooth/hci_vs.h>
19
20 #include <zephyr/bluetooth/conn.h>
21 #include <zephyr/bluetooth/uuid.h>
22 #include <zephyr/bluetooth/gatt.h>
23 #include <zephyr/bluetooth/services/hrs.h>
24
25 BUILD_ASSERT(IS_ENABLED(CONFIG_BT_HAS_HCI_VS),
26 "This app requires Zephyr-specific HCI vendor extensions");
27
28 static struct bt_conn *default_conn;
29 static uint16_t default_conn_handle;
30
31 static const struct bt_data ad[] = {
32 BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
33 BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_HRS_VAL)),
34 };
35
36 static const struct bt_data sd[] = {
37 BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
38 };
39
40 #define DEVICE_NAME CONFIG_BT_DEVICE_NAME
41 #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
42 #define DEVICE_BEACON_TXPOWER_NUM 8
43
44 static struct k_thread pwr_thread_data;
45 static K_THREAD_STACK_DEFINE(pwr_thread_stack, 512);
46
47 static const int8_t txpower[DEVICE_BEACON_TXPOWER_NUM] = {4, 0, -3, -8,
48 -15, -18, -23, -30};
49 static const struct bt_le_adv_param *param = BT_LE_ADV_PARAM(
50 BT_LE_ADV_OPT_CONN, BT_GAP_MS_TO_ADV_INTERVAL(20), BT_GAP_MS_TO_ADV_INTERVAL(20), NULL);
51
read_conn_rssi(uint16_t handle,int8_t * rssi)52 static void read_conn_rssi(uint16_t handle, int8_t *rssi)
53 {
54 struct net_buf *buf, *rsp = NULL;
55 struct bt_hci_cp_read_rssi *cp;
56 struct bt_hci_rp_read_rssi *rp;
57
58 int err;
59
60 buf = bt_hci_cmd_create(BT_HCI_OP_READ_RSSI, sizeof(*cp));
61 if (!buf) {
62 printk("Unable to allocate command buffer\n");
63 return;
64 }
65
66 cp = net_buf_add(buf, sizeof(*cp));
67 cp->handle = sys_cpu_to_le16(handle);
68
69 err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_RSSI, buf, &rsp);
70 if (err) {
71 printk("Read RSSI err: %d\n", err);
72 return;
73 }
74
75 rp = (void *)rsp->data;
76 *rssi = rp->rssi;
77
78 net_buf_unref(rsp);
79 }
80
81
set_tx_power(uint8_t handle_type,uint16_t handle,int8_t tx_pwr_lvl)82 static void set_tx_power(uint8_t handle_type, uint16_t handle, int8_t tx_pwr_lvl)
83 {
84 struct bt_hci_cp_vs_write_tx_power_level *cp;
85 struct bt_hci_rp_vs_write_tx_power_level *rp;
86 struct net_buf *buf, *rsp = NULL;
87 int err;
88
89 buf = bt_hci_cmd_create(BT_HCI_OP_VS_WRITE_TX_POWER_LEVEL,
90 sizeof(*cp));
91 if (!buf) {
92 printk("Unable to allocate command buffer\n");
93 return;
94 }
95
96 cp = net_buf_add(buf, sizeof(*cp));
97 cp->handle = sys_cpu_to_le16(handle);
98 cp->handle_type = handle_type;
99 cp->tx_power_level = tx_pwr_lvl;
100
101 err = bt_hci_cmd_send_sync(BT_HCI_OP_VS_WRITE_TX_POWER_LEVEL,
102 buf, &rsp);
103 if (err) {
104 printk("Set Tx power err: %d\n", err);
105 return;
106 }
107
108 rp = (void *)rsp->data;
109 printk("Actual Tx Power: %d\n", rp->selected_tx_power);
110
111 net_buf_unref(rsp);
112 }
113
get_tx_power(uint8_t handle_type,uint16_t handle,int8_t * tx_pwr_lvl)114 static void get_tx_power(uint8_t handle_type, uint16_t handle, int8_t *tx_pwr_lvl)
115 {
116 struct bt_hci_cp_vs_read_tx_power_level *cp;
117 struct bt_hci_rp_vs_read_tx_power_level *rp;
118 struct net_buf *buf, *rsp = NULL;
119 int err;
120
121 *tx_pwr_lvl = 0xFF;
122 buf = bt_hci_cmd_create(BT_HCI_OP_VS_READ_TX_POWER_LEVEL,
123 sizeof(*cp));
124 if (!buf) {
125 printk("Unable to allocate command buffer\n");
126 return;
127 }
128
129 cp = net_buf_add(buf, sizeof(*cp));
130 cp->handle = sys_cpu_to_le16(handle);
131 cp->handle_type = handle_type;
132
133 err = bt_hci_cmd_send_sync(BT_HCI_OP_VS_READ_TX_POWER_LEVEL,
134 buf, &rsp);
135 if (err) {
136 printk("Read Tx power err: %d\n", err);
137 return;
138 }
139
140 rp = (void *)rsp->data;
141 *tx_pwr_lvl = rp->tx_power_level;
142
143 net_buf_unref(rsp);
144 }
145
connected(struct bt_conn * conn,uint8_t err)146 static void connected(struct bt_conn *conn, uint8_t err)
147 {
148 char addr[BT_ADDR_LE_STR_LEN];
149 int8_t txp;
150 int ret;
151
152 if (err) {
153 printk("Connection failed, err 0x%02x %s\n", err, bt_hci_err_to_str(err));
154 } else {
155 default_conn = bt_conn_ref(conn);
156 ret = bt_hci_get_conn_handle(default_conn,
157 &default_conn_handle);
158 if (ret) {
159 printk("No connection handle (err %d)\n", ret);
160 } else {
161 /* Send first at the default selected power */
162 bt_addr_le_to_str(bt_conn_get_dst(conn),
163 addr, sizeof(addr));
164 printk("Connected via connection (%d) at %s\n",
165 default_conn_handle, addr);
166 get_tx_power(BT_HCI_VS_LL_HANDLE_TYPE_CONN,
167 default_conn_handle, &txp);
168 printk("Connection (%d) - Initial Tx Power = %d\n",
169 default_conn_handle, txp);
170
171 set_tx_power(BT_HCI_VS_LL_HANDLE_TYPE_CONN,
172 default_conn_handle,
173 BT_HCI_VS_LL_TX_POWER_LEVEL_NO_PREF);
174 get_tx_power(BT_HCI_VS_LL_HANDLE_TYPE_CONN,
175 default_conn_handle, &txp);
176 printk("Connection (%d) - Tx Power = %d\n",
177 default_conn_handle, txp);
178 }
179 }
180 }
181
disconnected(struct bt_conn * conn,uint8_t reason)182 static void disconnected(struct bt_conn *conn, uint8_t reason)
183 {
184 printk("Disconnected, reason 0x%02x %s\n", reason, bt_hci_err_to_str(reason));
185
186 if (default_conn) {
187 bt_conn_unref(default_conn);
188 default_conn = NULL;
189 }
190 }
191
192 BT_CONN_CB_DEFINE(conn_callbacks) = {
193 .connected = connected,
194 .disconnected = disconnected,
195 };
196
bt_ready(int err)197 static void bt_ready(int err)
198 {
199 if (err) {
200 printk("Bluetooth init failed (err %d)\n", err);
201 return;
202 }
203
204 printk("Bluetooth initialized\n");
205
206 /* Start advertising */
207 err = bt_le_adv_start(param, ad, ARRAY_SIZE(ad),
208 sd, ARRAY_SIZE(sd));
209 if (err) {
210 printk("Advertising failed to start (err %d)\n", err);
211 return;
212 }
213
214 printk("Dynamic Tx power Beacon started\n");
215 }
216
hrs_notify(void)217 static void hrs_notify(void)
218 {
219 static uint8_t heartrate = 90U;
220
221 /* Heartrate measurements simulation */
222 heartrate++;
223 if (heartrate == 160U) {
224 heartrate = 90U;
225 }
226
227 bt_hrs_notify(heartrate);
228 }
229
modulate_tx_power(void * p1,void * p2,void * p3)230 void modulate_tx_power(void *p1, void *p2, void *p3)
231 {
232 int8_t txp_get = 0;
233 uint8_t idx = 0;
234
235 while (1) {
236 if (!default_conn) {
237 printk("Set Tx power level to %d\n", txpower[idx]);
238 set_tx_power(BT_HCI_VS_LL_HANDLE_TYPE_ADV,
239 0, txpower[idx]);
240
241 k_sleep(K_SECONDS(5));
242
243 printk("Get Tx power level -> ");
244 get_tx_power(BT_HCI_VS_LL_HANDLE_TYPE_ADV,
245 0, &txp_get);
246 printk("TXP = %d\n", txp_get);
247
248 idx = (idx+1) % DEVICE_BEACON_TXPOWER_NUM;
249 } else {
250 int8_t rssi = 0xFF;
251 int8_t txp_adaptive;
252
253 idx = 0;
254
255 read_conn_rssi(default_conn_handle, &rssi);
256 printk("Connected (%d) - RSSI = %d\n",
257 default_conn_handle, rssi);
258 if (rssi > -70) {
259 txp_adaptive = -20;
260 } else if (rssi > -90) {
261 txp_adaptive = -12;
262 } else {
263 txp_adaptive = -4;
264 }
265 printk("Adaptive Tx power selected = %d\n",
266 txp_adaptive);
267 set_tx_power(BT_HCI_VS_LL_HANDLE_TYPE_CONN,
268 default_conn_handle, txp_adaptive);
269 get_tx_power(BT_HCI_VS_LL_HANDLE_TYPE_CONN,
270 default_conn_handle, &txp_get);
271 printk("Connection (%d) TXP = %d\n",
272 default_conn_handle, txp_get);
273
274 k_sleep(K_SECONDS(1));
275 }
276 }
277 }
278
main(void)279 int main(void)
280 {
281 int8_t txp_get = 0xFF;
282 int err;
283
284 default_conn = NULL;
285 printk("Starting Dynamic Tx Power Beacon Demo\n");
286
287 /* Initialize the Bluetooth Subsystem */
288 err = bt_enable(bt_ready);
289 if (err) {
290 printk("Bluetooth init failed (err %d)\n", err);
291 }
292
293 printk("Get Tx power level ->");
294 get_tx_power(BT_HCI_VS_LL_HANDLE_TYPE_ADV, 0, &txp_get);
295 printk("-> default TXP = %d\n", txp_get);
296
297 /* Wait for 5 seconds to give a chance users/testers
298 * to check that default Tx power is indeed the one
299 * selected in Kconfig.
300 */
301 k_sleep(K_SECONDS(5));
302
303 k_thread_create(&pwr_thread_data, pwr_thread_stack,
304 K_THREAD_STACK_SIZEOF(pwr_thread_stack),
305 modulate_tx_power, NULL, NULL, NULL,
306 K_PRIO_COOP(10),
307 0, K_NO_WAIT);
308 k_thread_name_set(&pwr_thread_data, "DYN TX");
309
310 while (1) {
311 hrs_notify();
312 k_sleep(K_SECONDS(2));
313 }
314 return 0;
315 }
316