1 /*
2  * Copyright (c) 2019 Tobias Svehagen
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/sys/printk.h>
8 #include <zephyr/settings/settings.h>
9 #include <zephyr/bluetooth/bluetooth.h>
10 #include <zephyr/bluetooth/mesh.h>
11 #include <zephyr/drivers/gpio.h>
12 
13 #define SW0_NODE	DT_ALIAS(sw0)
14 
15 static const uint16_t net_idx;
16 static const uint16_t app_idx;
17 static uint16_t self_addr = 1, node_addr;
18 static const uint8_t dev_uuid[16] = { 0xdd, 0xdd };
19 static uint8_t node_uuid[16];
20 
21 K_SEM_DEFINE(sem_unprov_beacon, 0, 1);
22 K_SEM_DEFINE(sem_node_added, 0, 1);
23 #ifdef CONFIG_MESH_PROVISIONER_USE_SW0
24 K_SEM_DEFINE(sem_button_pressed, 0, 1);
25 #endif
26 
27 static struct bt_mesh_cfg_cli cfg_cli = {
28 };
29 
health_current_status(struct bt_mesh_health_cli * cli,uint16_t addr,uint8_t test_id,uint16_t cid,uint8_t * faults,size_t fault_count)30 static void health_current_status(struct bt_mesh_health_cli *cli, uint16_t addr,
31 				  uint8_t test_id, uint16_t cid, uint8_t *faults,
32 				  size_t fault_count)
33 {
34 	size_t i;
35 
36 	printk("Health Current Status from 0x%04x\n", addr);
37 
38 	if (!fault_count) {
39 		printk("Health Test ID 0x%02x Company ID 0x%04x: no faults\n",
40 		       test_id, cid);
41 		return;
42 	}
43 
44 	printk("Health Test ID 0x%02x Company ID 0x%04x Fault Count %zu:\n",
45 	       test_id, cid, fault_count);
46 
47 	for (i = 0; i < fault_count; i++) {
48 		printk("\t0x%02x\n", faults[i]);
49 	}
50 }
51 
52 static struct bt_mesh_health_cli health_cli = {
53 	.current_status = health_current_status,
54 };
55 
56 static const struct bt_mesh_model root_models[] = {
57 	BT_MESH_MODEL_CFG_SRV,
58 	BT_MESH_MODEL_CFG_CLI(&cfg_cli),
59 	BT_MESH_MODEL_HEALTH_CLI(&health_cli),
60 };
61 
62 static const struct bt_mesh_elem elements[] = {
63 	BT_MESH_ELEM(0, root_models, BT_MESH_MODEL_NONE),
64 };
65 
66 static const struct bt_mesh_comp mesh_comp = {
67 	.cid = BT_COMP_ID_LF,
68 	.elem = elements,
69 	.elem_count = ARRAY_SIZE(elements),
70 };
71 
setup_cdb(void)72 static void setup_cdb(void)
73 {
74 	struct bt_mesh_cdb_app_key *key;
75 	uint8_t app_key[16];
76 	int err;
77 
78 	key = bt_mesh_cdb_app_key_alloc(net_idx, app_idx);
79 	if (key == NULL) {
80 		printk("Failed to allocate app-key 0x%04x\n", app_idx);
81 		return;
82 	}
83 
84 	bt_rand(app_key, 16);
85 
86 	err = bt_mesh_cdb_app_key_import(key, 0, app_key);
87 	if (err) {
88 		printk("Failed to import appkey into cdb. Err:%d\n", err);
89 		return;
90 	}
91 
92 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
93 		bt_mesh_cdb_app_key_store(key);
94 	}
95 }
96 
configure_self(struct bt_mesh_cdb_node * self)97 static void configure_self(struct bt_mesh_cdb_node *self)
98 {
99 	struct bt_mesh_cdb_app_key *key;
100 	uint8_t app_key[16];
101 	uint8_t status = 0;
102 	int err;
103 
104 	printk("Configuring self...\n");
105 
106 	key = bt_mesh_cdb_app_key_get(app_idx);
107 	if (key == NULL) {
108 		printk("No app-key 0x%04x\n", app_idx);
109 		return;
110 	}
111 
112 	err = bt_mesh_cdb_app_key_export(key, 0, app_key);
113 	if (err) {
114 		printk("Failed to export appkey from cdb. Err:%d\n", err);
115 		return;
116 	}
117 
118 	/* Add Application Key */
119 	err = bt_mesh_cfg_cli_app_key_add(self->net_idx, self->addr, self->net_idx, app_idx,
120 					  app_key, &status);
121 	if (err || status) {
122 		printk("Failed to add app-key (err %d, status %d)\n", err,
123 		       status);
124 		return;
125 	}
126 
127 	err = bt_mesh_cfg_cli_mod_app_bind(self->net_idx, self->addr, self->addr, app_idx,
128 					   BT_MESH_MODEL_ID_HEALTH_CLI, &status);
129 	if (err || status) {
130 		printk("Failed to bind app-key (err %d, status %d)\n", err,
131 		       status);
132 		return;
133 	}
134 
135 	atomic_set_bit(self->flags, BT_MESH_CDB_NODE_CONFIGURED);
136 
137 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
138 		bt_mesh_cdb_node_store(self);
139 	}
140 
141 	printk("Configuration complete\n");
142 }
143 
configure_node(struct bt_mesh_cdb_node * node)144 static void configure_node(struct bt_mesh_cdb_node *node)
145 {
146 	NET_BUF_SIMPLE_DEFINE(buf, BT_MESH_RX_SDU_MAX);
147 	struct bt_mesh_comp_p0_elem elem;
148 	struct bt_mesh_cdb_app_key *key;
149 	uint8_t app_key[16];
150 	struct bt_mesh_comp_p0 comp;
151 	uint8_t status;
152 	int err, elem_addr;
153 
154 	printk("Configuring node 0x%04x...\n", node->addr);
155 
156 	key = bt_mesh_cdb_app_key_get(app_idx);
157 	if (key == NULL) {
158 		printk("No app-key 0x%04x\n", app_idx);
159 		return;
160 	}
161 
162 	err = bt_mesh_cdb_app_key_export(key, 0, app_key);
163 	if (err) {
164 		printk("Failed to export appkey from cdb. Err:%d\n", err);
165 		return;
166 	}
167 
168 	/* Add Application Key */
169 	err = bt_mesh_cfg_cli_app_key_add(net_idx, node->addr, net_idx, app_idx, app_key, &status);
170 	if (err || status) {
171 		printk("Failed to add app-key (err %d status %d)\n", err, status);
172 		return;
173 	}
174 
175 	/* Get the node's composition data and bind all models to the appkey */
176 	err = bt_mesh_cfg_cli_comp_data_get(net_idx, node->addr, 0, &status, &buf);
177 	if (err || status) {
178 		printk("Failed to get Composition data (err %d, status: %d)\n",
179 		       err, status);
180 		return;
181 	}
182 
183 	err = bt_mesh_comp_p0_get(&comp, &buf);
184 	if (err) {
185 		printk("Unable to parse composition data (err: %d)\n", err);
186 		return;
187 	}
188 
189 	elem_addr = node->addr;
190 	while (bt_mesh_comp_p0_elem_pull(&comp, &elem)) {
191 		printk("Element @ 0x%04x: %u + %u models\n", elem_addr,
192 		       elem.nsig, elem.nvnd);
193 		for (int i = 0; i < elem.nsig; i++) {
194 			uint16_t id = bt_mesh_comp_p0_elem_mod(&elem, i);
195 
196 			if (id == BT_MESH_MODEL_ID_CFG_CLI ||
197 			    id == BT_MESH_MODEL_ID_CFG_SRV) {
198 				continue;
199 			}
200 			printk("Binding AppKey to model 0x%03x:%04x\n",
201 			       elem_addr, id);
202 
203 			err = bt_mesh_cfg_cli_mod_app_bind(net_idx, node->addr, elem_addr, app_idx,
204 							   id, &status);
205 			if (err || status) {
206 				printk("Failed (err: %d, status: %d)\n", err,
207 				       status);
208 			}
209 		}
210 
211 		for (int i = 0; i < elem.nvnd; i++) {
212 			struct bt_mesh_mod_id_vnd id =
213 				bt_mesh_comp_p0_elem_mod_vnd(&elem, i);
214 
215 			printk("Binding AppKey to model 0x%03x:%04x:%04x\n",
216 			       elem_addr, id.company, id.id);
217 
218 			err = bt_mesh_cfg_cli_mod_app_bind_vnd(net_idx, node->addr, elem_addr,
219 							       app_idx, id.id, id.company, &status);
220 			if (err || status) {
221 				printk("Failed (err: %d, status: %d)\n", err,
222 				       status);
223 			}
224 		}
225 
226 		elem_addr++;
227 	}
228 
229 	atomic_set_bit(node->flags, BT_MESH_CDB_NODE_CONFIGURED);
230 
231 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
232 		bt_mesh_cdb_node_store(node);
233 	}
234 
235 	printk("Configuration complete\n");
236 }
237 
unprovisioned_beacon(uint8_t uuid[16],bt_mesh_prov_oob_info_t oob_info,uint32_t * uri_hash)238 static void unprovisioned_beacon(uint8_t uuid[16],
239 				 bt_mesh_prov_oob_info_t oob_info,
240 				 uint32_t *uri_hash)
241 {
242 	memcpy(node_uuid, uuid, 16);
243 	k_sem_give(&sem_unprov_beacon);
244 }
245 
node_added(uint16_t idx,uint8_t uuid[16],uint16_t addr,uint8_t num_elem)246 static void node_added(uint16_t idx, uint8_t uuid[16], uint16_t addr, uint8_t num_elem)
247 {
248 	node_addr = addr;
249 	k_sem_give(&sem_node_added);
250 }
251 
252 static const struct bt_mesh_prov prov = {
253 	.uuid = dev_uuid,
254 	.unprovisioned_beacon = unprovisioned_beacon,
255 	.node_added = node_added,
256 };
257 
bt_ready(void)258 static int bt_ready(void)
259 {
260 	uint8_t net_key[16], dev_key[16];
261 	int err;
262 
263 	err = bt_mesh_init(&prov, &mesh_comp);
264 	if (err) {
265 		printk("Initializing mesh failed (err %d)\n", err);
266 		return err;
267 	}
268 
269 	printk("Mesh initialized\n");
270 
271 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
272 		printk("Loading stored settings\n");
273 		settings_load();
274 	}
275 
276 	bt_rand(net_key, 16);
277 
278 	err = bt_mesh_cdb_create(net_key);
279 	if (err == -EALREADY) {
280 		printk("Using stored CDB\n");
281 	} else if (err) {
282 		printk("Failed to create CDB (err %d)\n", err);
283 		return err;
284 	} else {
285 		printk("Created CDB\n");
286 		setup_cdb();
287 	}
288 
289 	bt_rand(dev_key, 16);
290 
291 	err = bt_mesh_provision(net_key, BT_MESH_NET_PRIMARY, 0, 0, self_addr,
292 				dev_key);
293 	if (err == -EALREADY) {
294 		printk("Using stored settings\n");
295 	} else if (err) {
296 		printk("Provisioning failed (err %d)\n", err);
297 		return err;
298 	} else {
299 		printk("Provisioning completed\n");
300 	}
301 
302 	return 0;
303 }
304 
check_unconfigured(struct bt_mesh_cdb_node * node,void * data)305 static uint8_t check_unconfigured(struct bt_mesh_cdb_node *node, void *data)
306 {
307 	if (!atomic_test_bit(node->flags, BT_MESH_CDB_NODE_CONFIGURED)) {
308 		if (node->addr == self_addr) {
309 			configure_self(node);
310 		} else {
311 			configure_node(node);
312 		}
313 	}
314 
315 	return BT_MESH_CDB_ITER_CONTINUE;
316 }
317 
318 #ifdef CONFIG_MESH_PROVISIONER_USE_SW0
319 static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios, {0});
320 static struct gpio_callback button_cb_data;
321 
button_pressed(const struct device * dev,struct gpio_callback * cb,uint32_t pins)322 static void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
323 {
324 	k_sem_give(&sem_button_pressed);
325 }
326 
button_init(void)327 static void button_init(void)
328 {
329 	int ret;
330 
331 	if (!gpio_is_ready_dt(&button)) {
332 		printk("Error: button device %s is not ready\n", button.port->name);
333 		return;
334 	}
335 	ret = gpio_pin_configure_dt(&button, GPIO_INPUT);
336 	if (ret != 0) {
337 		printk("Error %d: failed to configure %s pin %d\n", ret, button.port->name,
338 		       button.pin);
339 		return;
340 	}
341 	ret = gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_TO_ACTIVE);
342 	if (ret != 0) {
343 		printk("Error %d: failed to configure interrupt on %s pin %d\n", ret,
344 		       button.port->name, button.pin);
345 		return;
346 	}
347 	gpio_init_callback(&button_cb_data, button_pressed, BIT(button.pin));
348 	gpio_add_callback(button.port, &button_cb_data);
349 }
350 #endif
351 
main(void)352 int main(void)
353 {
354 	char uuid_hex_str[32 + 1];
355 	int err;
356 
357 	printk("Initializing...\n");
358 
359 	/* Initialize the Bluetooth Subsystem */
360 	err = bt_enable(NULL);
361 	if (err) {
362 		printk("Bluetooth init failed (err %d)\n", err);
363 		return 0;
364 	}
365 
366 	printk("Bluetooth initialized\n");
367 	bt_ready();
368 
369 #ifdef CONFIG_MESH_PROVISIONER_USE_SW0
370 	button_init();
371 #endif
372 
373 	while (1) {
374 		k_sem_reset(&sem_unprov_beacon);
375 		k_sem_reset(&sem_node_added);
376 		bt_mesh_cdb_node_foreach(check_unconfigured, NULL);
377 
378 		printk("Waiting for unprovisioned beacon...\n");
379 		err = k_sem_take(&sem_unprov_beacon, K_SECONDS(10));
380 		if (err == -EAGAIN) {
381 			continue;
382 		}
383 
384 		bin2hex(node_uuid, 16, uuid_hex_str, sizeof(uuid_hex_str));
385 
386 #ifdef CONFIG_MESH_PROVISIONER_USE_SW0
387 		k_sem_reset(&sem_button_pressed);
388 		printk("Device %s detected, press button 1 to provision.\n", uuid_hex_str);
389 		err = k_sem_take(&sem_button_pressed, K_SECONDS(30));
390 		if (err == -EAGAIN) {
391 			printk("Timed out, button 1 wasn't pressed in time.\n");
392 			continue;
393 		}
394 #endif
395 
396 		printk("Provisioning %s\n", uuid_hex_str);
397 		err = bt_mesh_provision_adv(node_uuid, net_idx, 0, 0);
398 		if (err < 0) {
399 			printk("Provisioning failed (err %d)\n", err);
400 			continue;
401 		}
402 
403 		printk("Waiting for node to be added...\n");
404 		err = k_sem_take(&sem_node_added, K_SECONDS(10));
405 		if (err == -EAGAIN) {
406 			printk("Timeout waiting for node to be added\n");
407 			continue;
408 		}
409 
410 		printk("Added node 0x%04x\n", node_addr);
411 	}
412 	return 0;
413 }
414