1 /* main.c - Application main entry point */
2
3 /*
4 * Copyright (c) 2017 Intel Corporation
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 /*
10 * This application is specific to the Nordic nRF52840-PDK board.
11 *
12 * It supports the 4 buttons and 4 LEDs as mesh clients and servers.
13 *
14 * Prior to provisioning, a button inverts the state of the
15 * corresponding LED.
16 *
17 * The unprovisioned beacon uses the device address set by Nordic
18 * in the FICR as its UUID and is presumed unique.
19 *
20 * Button and LED 1 are in the root node.
21 * The 3 remaining button/LED pairs are in element 1 through 3.
22 * Assuming the provisioner assigns 0x100 to the root node,
23 * the secondary elements will appear at 0x101, 0x102 and 0x103.
24 *
25 * It's anticipated that after provisioning, the button clients would
26 * be configured to publish and the LED servers to subscribe.
27 *
28 * If a LED server is provided with a publish address, it will
29 * also publish its status on a state change.
30 *
31 * Messages from a button to its corresponding LED are ignored as
32 * the LED's state has already been changed locally by the button client.
33 *
34 * The buttons are debounced at a nominal 250ms. That value can be
35 * changed as needed.
36 *
37 */
38
39 #include <zephyr/sys/printk.h>
40 #include <zephyr/settings/settings.h>
41 #include <zephyr/sys/byteorder.h>
42 #include <zephyr/device.h>
43 #include <zephyr/drivers/gpio.h>
44 #include <zephyr/bluetooth/bluetooth.h>
45 #include <zephyr/bluetooth/conn.h>
46 #include <zephyr/bluetooth/l2cap.h>
47 #include <zephyr/bluetooth/hci.h>
48 #include <zephyr/bluetooth/mesh.h>
49 #include <stdio.h>
50
51 /* Model Operation Codes */
52 #define BT_MESH_MODEL_OP_GEN_ONOFF_GET BT_MESH_MODEL_OP_2(0x82, 0x01)
53 #define BT_MESH_MODEL_OP_GEN_ONOFF_SET BT_MESH_MODEL_OP_2(0x82, 0x02)
54 #define BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x03)
55 #define BT_MESH_MODEL_OP_GEN_ONOFF_STATUS BT_MESH_MODEL_OP_2(0x82, 0x04)
56
57 static int gen_onoff_set(const struct bt_mesh_model *model,
58 struct bt_mesh_msg_ctx *ctx,
59 struct net_buf_simple *buf);
60
61 static int gen_onoff_set_unack(const struct bt_mesh_model *model,
62 struct bt_mesh_msg_ctx *ctx,
63 struct net_buf_simple *buf);
64
65 static int gen_onoff_get(const struct bt_mesh_model *model,
66 struct bt_mesh_msg_ctx *ctx,
67 struct net_buf_simple *buf);
68
69 static int gen_onoff_status(const struct bt_mesh_model *model,
70 struct bt_mesh_msg_ctx *ctx,
71 struct net_buf_simple *buf);
72
73 /*
74 * Client Configuration Declaration
75 */
76
77 static struct bt_mesh_cfg_cli cfg_cli = {
78 };
79
80 /*
81 * Health Server Declaration
82 */
83
84 static struct bt_mesh_health_srv health_srv = {
85 };
86
87 /*
88 * Publication Declarations
89 *
90 * The publication messages are initialized to
91 * the size of the opcode + content
92 *
93 * For publication, the message must be in static or global as
94 * it is re-transmitted several times. This occurs
95 * after the function that called bt_mesh_model_publish() has
96 * exited and the stack is no longer valid.
97 *
98 * Note that the additional 4 bytes for the AppMIC is not needed
99 * because it is added to a stack variable at the time a
100 * transmission occurs.
101 *
102 */
103
104 BT_MESH_HEALTH_PUB_DEFINE(health_pub, 0);
105
106 BT_MESH_MODEL_PUB_DEFINE(gen_onoff_pub_srv, NULL, 2 + 2);
107 BT_MESH_MODEL_PUB_DEFINE(gen_onoff_pub_cli, NULL, 2 + 2);
108 BT_MESH_MODEL_PUB_DEFINE(gen_onoff_pub_srv_s_0, NULL, 2 + 2);
109 BT_MESH_MODEL_PUB_DEFINE(gen_onoff_pub_cli_s_0, NULL, 2 + 2);
110 BT_MESH_MODEL_PUB_DEFINE(gen_onoff_pub_srv_s_1, NULL, 2 + 2);
111 BT_MESH_MODEL_PUB_DEFINE(gen_onoff_pub_cli_s_1, NULL, 2 + 2);
112 BT_MESH_MODEL_PUB_DEFINE(gen_onoff_pub_srv_s_2, NULL, 2 + 2);
113 BT_MESH_MODEL_PUB_DEFINE(gen_onoff_pub_cli_s_2, NULL, 2 + 2);
114
115 /*
116 * Models in an element must have unique op codes.
117 *
118 * The mesh stack dispatches a message to the first model in an element
119 * that is also bound to an app key and supports the op code in the
120 * received message.
121 *
122 */
123
124 /*
125 * OnOff Model Server Op Dispatch Table
126 *
127 */
128
129 static const struct bt_mesh_model_op gen_onoff_srv_op[] = {
130 { BT_MESH_MODEL_OP_GEN_ONOFF_GET, BT_MESH_LEN_EXACT(0), gen_onoff_get },
131 { BT_MESH_MODEL_OP_GEN_ONOFF_SET, BT_MESH_LEN_EXACT(2), gen_onoff_set },
132 { BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK, BT_MESH_LEN_EXACT(2), gen_onoff_set_unack },
133 BT_MESH_MODEL_OP_END,
134 };
135
136 /*
137 * OnOff Model Client Op Dispatch Table
138 */
139
140 static const struct bt_mesh_model_op gen_onoff_cli_op[] = {
141 { BT_MESH_MODEL_OP_GEN_ONOFF_STATUS, BT_MESH_LEN_EXACT(1), gen_onoff_status },
142 BT_MESH_MODEL_OP_END,
143 };
144
145 struct led_onoff_state {
146 const struct gpio_dt_spec led_device;
147 uint8_t current;
148 uint8_t previous;
149 };
150
151 /*
152 * Declare and Initialize Element Contexts
153 * Change to select different GPIO output pins
154 */
155
156 static struct led_onoff_state led_onoff_states[] = {
157 { .led_device = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios), },
158 { .led_device = GPIO_DT_SPEC_GET(DT_ALIAS(led1), gpios), },
159 { .led_device = GPIO_DT_SPEC_GET(DT_ALIAS(led2), gpios), },
160 { .led_device = GPIO_DT_SPEC_GET(DT_ALIAS(led3), gpios), },
161 };
162
163 /*
164 *
165 * Element Model Declarations
166 *
167 * Element 0 Root Models
168 */
169
170 static const struct bt_mesh_model root_models[] = {
171 BT_MESH_MODEL_CFG_SRV,
172 BT_MESH_MODEL_CFG_CLI(&cfg_cli),
173 BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
174 BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op,
175 &gen_onoff_pub_srv, &led_onoff_states[0]),
176 BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op,
177 &gen_onoff_pub_cli, &led_onoff_states[0]),
178 };
179
180 /*
181 * Element 1 Models
182 */
183
184 static const struct bt_mesh_model secondary_0_models[] = {
185 BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op,
186 &gen_onoff_pub_srv_s_0, &led_onoff_states[1]),
187 BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op,
188 &gen_onoff_pub_cli_s_0, &led_onoff_states[1]),
189 };
190
191 /*
192 * Element 2 Models
193 */
194
195 static const struct bt_mesh_model secondary_1_models[] = {
196 BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op,
197 &gen_onoff_pub_srv_s_1, &led_onoff_states[2]),
198 BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op,
199 &gen_onoff_pub_cli_s_1, &led_onoff_states[2]),
200 };
201
202 /*
203 * Element 3 Models
204 */
205
206 static const struct bt_mesh_model secondary_2_models[] = {
207 BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op,
208 &gen_onoff_pub_srv_s_2, &led_onoff_states[3]),
209 BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op,
210 &gen_onoff_pub_cli_s_2, &led_onoff_states[3]),
211 };
212
213 /*
214 * Button to Client Model Assignments
215 */
216
217 const struct bt_mesh_model *mod_cli_sw[] = {
218 &root_models[4],
219 &secondary_0_models[1],
220 &secondary_1_models[1],
221 &secondary_2_models[1],
222 };
223
224 /*
225 * LED to Server Model Assignments
226 */
227
228 const struct bt_mesh_model *mod_srv_sw[] = {
229 &root_models[3],
230 &secondary_0_models[0],
231 &secondary_1_models[0],
232 &secondary_2_models[0],
233 };
234
235 /*
236 * Root and Secondary Element Declarations
237 */
238
239 static const struct bt_mesh_elem elements[] = {
240 BT_MESH_ELEM(0, root_models, BT_MESH_MODEL_NONE),
241 BT_MESH_ELEM(0, secondary_0_models, BT_MESH_MODEL_NONE),
242 BT_MESH_ELEM(0, secondary_1_models, BT_MESH_MODEL_NONE),
243 BT_MESH_ELEM(0, secondary_2_models, BT_MESH_MODEL_NONE),
244 };
245
246 static const struct bt_mesh_comp comp = {
247 .cid = BT_COMP_ID_LF,
248 .elem = elements,
249 .elem_count = ARRAY_SIZE(elements),
250 };
251
252 static const struct gpio_dt_spec sw_device[4] = {
253 GPIO_DT_SPEC_GET(DT_ALIAS(sw0), gpios),
254 GPIO_DT_SPEC_GET(DT_ALIAS(sw1), gpios),
255 GPIO_DT_SPEC_GET(DT_ALIAS(sw2), gpios),
256 GPIO_DT_SPEC_GET(DT_ALIAS(sw3), gpios),
257 };
258
259 struct switch_data {
260 uint8_t sw_num;
261 uint8_t onoff_state;
262 struct k_work button_work;
263 struct k_timer button_timer;
264 };
265
266
267 static uint8_t button_press_cnt;
268 static struct switch_data sw;
269
270 static struct gpio_callback button_cb;
271
272 static uint8_t trans_id;
273 static uint32_t time, last_time;
274 static uint16_t primary_addr;
275 static uint16_t primary_net_idx;
276
277 /*
278 * Generic OnOff Model Server Message Handlers
279 *
280 * Mesh Model Specification 3.1.1
281 *
282 */
283
gen_onoff_get(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)284 static int gen_onoff_get(const struct bt_mesh_model *model,
285 struct bt_mesh_msg_ctx *ctx,
286 struct net_buf_simple *buf)
287 {
288 NET_BUF_SIMPLE_DEFINE(msg, 2 + 1 + 4);
289 struct led_onoff_state *onoff_state = model->rt->user_data;
290
291 printk("addr 0x%04x onoff 0x%02x\n",
292 bt_mesh_model_elem(model)->rt->addr, onoff_state->current);
293 bt_mesh_model_msg_init(&msg, BT_MESH_MODEL_OP_GEN_ONOFF_STATUS);
294 net_buf_simple_add_u8(&msg, onoff_state->current);
295
296 if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
297 printk("Unable to send On Off Status response\n");
298 }
299
300 return 0;
301 }
302
gen_onoff_set_unack(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)303 static int gen_onoff_set_unack(const struct bt_mesh_model *model,
304 struct bt_mesh_msg_ctx *ctx,
305 struct net_buf_simple *buf)
306 {
307 struct net_buf_simple *msg = model->pub->msg;
308 struct led_onoff_state *onoff_state = model->rt->user_data;
309 int err;
310
311 onoff_state->current = net_buf_simple_pull_u8(buf);
312 printk("addr 0x%02x state 0x%02x\n",
313 bt_mesh_model_elem(model)->rt->addr, onoff_state->current);
314
315 gpio_pin_set_dt(&onoff_state->led_device, onoff_state->current);
316
317 /*
318 * If a server has a publish address, it is required to
319 * publish status on a state change
320 *
321 * See Mesh Profile Specification 3.7.6.1.2
322 *
323 * Only publish if there is an assigned address
324 */
325
326 if (onoff_state->previous != onoff_state->current &&
327 model->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
328 printk("publish last 0x%02x cur 0x%02x\n",
329 onoff_state->previous, onoff_state->current);
330 onoff_state->previous = onoff_state->current;
331 bt_mesh_model_msg_init(msg,
332 BT_MESH_MODEL_OP_GEN_ONOFF_STATUS);
333 net_buf_simple_add_u8(msg, onoff_state->current);
334 err = bt_mesh_model_publish(model);
335 if (err) {
336 printk("bt_mesh_model_publish err %d\n", err);
337 }
338 }
339
340 return 0;
341 }
342
gen_onoff_set(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)343 static int gen_onoff_set(const struct bt_mesh_model *model,
344 struct bt_mesh_msg_ctx *ctx,
345 struct net_buf_simple *buf)
346 {
347 printk("gen_onoff_set\n");
348
349 (void)gen_onoff_set_unack(model, ctx, buf);
350 (void)gen_onoff_get(model, ctx, buf);
351
352 return 0;
353 }
354
gen_onoff_status(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)355 static int gen_onoff_status(const struct bt_mesh_model *model,
356 struct bt_mesh_msg_ctx *ctx,
357 struct net_buf_simple *buf)
358 {
359 uint8_t state;
360
361 state = net_buf_simple_pull_u8(buf);
362
363 printk("Node 0x%04x OnOff status from 0x%04x with state 0x%02x\n",
364 bt_mesh_model_elem(model)->rt->addr, ctx->addr, state);
365
366 return 0;
367 }
368
output_number(bt_mesh_output_action_t action,uint32_t number)369 static int output_number(bt_mesh_output_action_t action, uint32_t number)
370 {
371 printk("OOB Number %06u\n", number);
372 return 0;
373 }
374
output_string(const char * str)375 static int output_string(const char *str)
376 {
377 printk("OOB String %s\n", str);
378 return 0;
379 }
380
prov_complete(uint16_t net_idx,uint16_t addr)381 static void prov_complete(uint16_t net_idx, uint16_t addr)
382 {
383 printk("provisioning complete for net_idx 0x%04x addr 0x%04x\n",
384 net_idx, addr);
385 primary_addr = addr;
386 primary_net_idx = net_idx;
387 }
388
prov_reset(void)389 static void prov_reset(void)
390 {
391 bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
392 }
393
394 static uint8_t dev_uuid[16] = { 0xdd, 0xdd };
395
396 #define BUTTON_DEBOUNCE_DELAY_MS 250
397
398 /*
399 * Map GPIO pins to button number
400 * Change to select different GPIO input pins
401 */
402
pin_to_sw(uint32_t pin_pos)403 static uint8_t pin_to_sw(uint32_t pin_pos)
404 {
405 switch (pin_pos) {
406 case BIT(DT_GPIO_PIN(DT_ALIAS(sw0), gpios)): return 0;
407 case BIT(DT_GPIO_PIN(DT_ALIAS(sw1), gpios)): return 1;
408 case BIT(DT_GPIO_PIN(DT_ALIAS(sw2), gpios)): return 2;
409 case BIT(DT_GPIO_PIN(DT_ALIAS(sw3), gpios)): return 3;
410 }
411
412 printk("No match for GPIO pin 0x%08x\n", pin_pos);
413 return 0;
414 }
415
button_pressed(const struct device * dev,struct gpio_callback * cb,uint32_t pin_pos)416 static void button_pressed(const struct device *dev, struct gpio_callback *cb,
417 uint32_t pin_pos)
418 {
419 /*
420 * One button press within a 1 second interval sends an on message
421 * More than one button press sends an off message
422 */
423
424 time = k_uptime_get_32();
425
426 /* debounce the switch */
427 if (time < last_time + BUTTON_DEBOUNCE_DELAY_MS) {
428 last_time = time;
429 return;
430 }
431
432 if (button_press_cnt == 0U) {
433 k_timer_start(&sw.button_timer, K_SECONDS(1), K_NO_WAIT);
434 }
435
436 printk("button_press_cnt 0x%02x\n", button_press_cnt);
437 button_press_cnt++;
438
439 /* The variable pin_pos is the pin position in the GPIO register,
440 * not the pin number. It's assumed that only one bit is set.
441 */
442
443 sw.sw_num = pin_to_sw(pin_pos);
444 last_time = time;
445 }
446
447 /*
448 * Button Count Timer Worker
449 */
450
button_cnt_timer(struct k_timer * work)451 static void button_cnt_timer(struct k_timer *work)
452 {
453 struct switch_data *button_sw = CONTAINER_OF(work, struct switch_data, button_timer);
454
455 button_sw->onoff_state = button_press_cnt == 1U ? 1 : 0;
456 printk("button_press_cnt 0x%02x onoff_state 0x%02x\n",
457 button_press_cnt, button_sw->onoff_state);
458 button_press_cnt = 0U;
459 k_work_submit(&sw.button_work);
460 }
461
462 /*
463 * Button Pressed Worker Task
464 */
465
button_pressed_worker(struct k_work * work)466 static void button_pressed_worker(struct k_work *work)
467 {
468 const struct bt_mesh_model *mod_cli, *mod_srv;
469 struct bt_mesh_model_pub *pub_cli, *pub_srv;
470 struct switch_data *button_sw = CONTAINER_OF(work, struct switch_data, button_work);
471 int err;
472 uint8_t sw_idx = button_sw->sw_num;
473
474 mod_cli = mod_cli_sw[sw_idx];
475 pub_cli = mod_cli->pub;
476
477 mod_srv = mod_srv_sw[sw_idx];
478 pub_srv = mod_srv->pub;
479
480 /* If unprovisioned, just call the set function.
481 * The intent is to have switch-like behavior
482 * prior to provisioning. Once provisioned,
483 * the button and its corresponding led are no longer
484 * associated and act independently. So, if a button is to
485 * control its associated led after provisioning, the button
486 * must be configured to either publish to the led's unicast
487 * address or a group to which the led is subscribed.
488 */
489
490 if (primary_addr == BT_MESH_ADDR_UNASSIGNED) {
491 NET_BUF_SIMPLE_DEFINE(msg, 1);
492 struct bt_mesh_msg_ctx ctx = {
493 .addr = sw_idx + primary_addr,
494 };
495
496 /* This is a dummy message sufficient
497 * for the led server
498 */
499
500 net_buf_simple_add_u8(&msg, button_sw->onoff_state);
501 (void)gen_onoff_set_unack(mod_srv, &ctx, &msg);
502 return;
503 }
504
505 if (pub_cli->addr == BT_MESH_ADDR_UNASSIGNED) {
506 return;
507 }
508
509 printk("publish to 0x%04x onoff 0x%04x sw_idx 0x%04x\n",
510 pub_cli->addr, button_sw->onoff_state, sw_idx);
511 bt_mesh_model_msg_init(pub_cli->msg,
512 BT_MESH_MODEL_OP_GEN_ONOFF_SET);
513 net_buf_simple_add_u8(pub_cli->msg, button_sw->onoff_state);
514 net_buf_simple_add_u8(pub_cli->msg, trans_id++);
515 err = bt_mesh_model_publish(mod_cli);
516 if (err) {
517 printk("bt_mesh_model_publish err %d\n", err);
518 }
519 }
520
521 /* Disable OOB security for SILabs Android app */
522
523 static const struct bt_mesh_prov prov = {
524 .uuid = dev_uuid,
525 #if 1
526 .output_size = 6,
527 .output_actions = (BT_MESH_DISPLAY_NUMBER | BT_MESH_DISPLAY_STRING),
528 .output_number = output_number,
529 .output_string = output_string,
530 #else
531 .output_size = 0,
532 .output_actions = 0,
533 .output_number = 0,
534 #endif
535 .complete = prov_complete,
536 .reset = prov_reset,
537 };
538
539 /*
540 * Bluetooth Ready Callback
541 */
542
bt_ready(int err)543 static void bt_ready(int err)
544 {
545 struct bt_le_oob oob;
546
547 if (err) {
548 printk("Bluetooth init failed (err %d)\n", err);
549 return;
550 }
551
552 printk("Bluetooth initialized\n");
553
554 err = bt_mesh_init(&prov, &comp);
555 if (err) {
556 printk("Initializing mesh failed (err %d)\n", err);
557 return;
558 }
559
560 if (IS_ENABLED(CONFIG_SETTINGS)) {
561 settings_load();
562 }
563
564 /* Use identity address as device UUID */
565 if (bt_le_oob_get_local(BT_ID_DEFAULT, &oob)) {
566 printk("Identity Address unavailable\n");
567 } else {
568 memcpy(dev_uuid, oob.addr.a.val, 6);
569 }
570
571 bt_mesh_prov_enable(BT_MESH_PROV_GATT | BT_MESH_PROV_ADV);
572
573 printk("Mesh initialized\n");
574 }
575
main(void)576 int main(void)
577 {
578 int err, i;
579
580 printk("Initializing...\n");
581
582 /* Initialize the button debouncer */
583 last_time = k_uptime_get_32();
584
585 /* Initialize button worker task*/
586 k_work_init(&sw.button_work, button_pressed_worker);
587
588 /* Initialize button count timer */
589 k_timer_init(&sw.button_timer, button_cnt_timer, NULL);
590
591 gpio_init_callback(&button_cb, button_pressed,
592 BIT(sw_device[0].pin) | BIT(sw_device[1].pin) |
593 BIT(sw_device[2].pin) | BIT(sw_device[3].pin));
594
595 for (i = 0; i < ARRAY_SIZE(sw_device); i++) {
596 if (!gpio_is_ready_dt(&sw_device[i])) {
597 printk("SW%d GPIO controller device is not ready\n", i);
598 return 0;
599 }
600 gpio_pin_configure_dt(&sw_device[i], GPIO_INPUT);
601 gpio_pin_interrupt_configure_dt(&sw_device[i], GPIO_INT_EDGE_TO_ACTIVE);
602 gpio_add_callback(sw_device[i].port, &button_cb);
603 }
604
605
606 /* Initialize LED's */
607 for (i = 0; i < ARRAY_SIZE(led_onoff_states); i++) {
608 if (!gpio_is_ready_dt(&led_onoff_states[i].led_device)) {
609 printk("LED%d GPIO controller device is not ready\n", i);
610 return 0;
611 }
612 gpio_pin_configure_dt(&led_onoff_states[i].led_device, GPIO_OUTPUT_INACTIVE);
613 }
614
615 /* Initialize the Bluetooth Subsystem */
616 err = bt_enable(bt_ready);
617 if (err) {
618 printk("Bluetooth init failed (err %d)\n", err);
619 }
620 return 0;
621 }
622