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