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 #include <zephyr/sys/printk.h>
10 
11 #include <zephyr/settings/settings.h>
12 #include <zephyr/devicetree.h>
13 #include <zephyr/device.h>
14 #include <zephyr/drivers/hwinfo.h>
15 #include <zephyr/sys/byteorder.h>
16 
17 #include <zephyr/bluetooth/bluetooth.h>
18 #include <zephyr/bluetooth/mesh.h>
19 
20 #include "board.h"
21 
22 #define OP_ONOFF_GET       BT_MESH_MODEL_OP_2(0x82, 0x01)
23 #define OP_ONOFF_SET       BT_MESH_MODEL_OP_2(0x82, 0x02)
24 #define OP_ONOFF_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x03)
25 #define OP_ONOFF_STATUS    BT_MESH_MODEL_OP_2(0x82, 0x04)
26 
attention_on(const struct bt_mesh_model * mod)27 static void attention_on(const struct bt_mesh_model *mod)
28 {
29 	board_led_set(true);
30 }
31 
attention_off(const struct bt_mesh_model * mod)32 static void attention_off(const struct bt_mesh_model *mod)
33 {
34 	board_led_set(false);
35 }
36 
37 static const struct bt_mesh_health_srv_cb health_cb = {
38 	.attn_on = attention_on,
39 	.attn_off = attention_off,
40 };
41 
42 static struct bt_mesh_health_srv health_srv = {
43 	.cb = &health_cb,
44 };
45 
46 BT_MESH_HEALTH_PUB_DEFINE(health_pub, 0);
47 
48 static const char *const onoff_str[] = { "off", "on" };
49 
50 static struct {
51 	bool val;
52 	uint8_t tid;
53 	uint16_t src;
54 	uint32_t transition_time;
55 	struct k_work_delayable work;
56 } onoff;
57 
58 /* OnOff messages' transition time and remaining time fields are encoded as an
59  * 8 bit value with a 6 bit step field and a 2 bit resolution field.
60  * The resolution field maps to:
61  * 0: 100 ms
62  * 1: 1 s
63  * 2: 10 s
64  * 3: 20 min
65  */
66 static const uint32_t time_res[] = {
67 	100,
68 	MSEC_PER_SEC,
69 	10 * MSEC_PER_SEC,
70 	10 * 60 * MSEC_PER_SEC,
71 };
72 
model_time_decode(uint8_t val)73 static inline int32_t model_time_decode(uint8_t val)
74 {
75 	uint8_t resolution = (val >> 6) & BIT_MASK(2);
76 	uint8_t steps = val & BIT_MASK(6);
77 
78 	if (steps == 0x3f) {
79 		return SYS_FOREVER_MS;
80 	}
81 
82 	return steps * time_res[resolution];
83 }
84 
model_time_encode(int32_t ms)85 static inline uint8_t model_time_encode(int32_t ms)
86 {
87 	if (ms == SYS_FOREVER_MS) {
88 		return 0x3f;
89 	}
90 
91 	for (int i = 0; i < ARRAY_SIZE(time_res); i++) {
92 		if (ms >= BIT_MASK(6) * time_res[i]) {
93 			continue;
94 		}
95 
96 		uint8_t steps = DIV_ROUND_UP(ms, time_res[i]);
97 
98 		return steps | (i << 6);
99 	}
100 
101 	return 0x3f;
102 }
103 
onoff_status_send(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx)104 static int onoff_status_send(const struct bt_mesh_model *model,
105 			     struct bt_mesh_msg_ctx *ctx)
106 {
107 	uint32_t remaining;
108 
109 	BT_MESH_MODEL_BUF_DEFINE(buf, OP_ONOFF_STATUS, 3);
110 	bt_mesh_model_msg_init(&buf, OP_ONOFF_STATUS);
111 
112 	remaining = k_ticks_to_ms_floor32(
113 			    k_work_delayable_remaining_get(&onoff.work)) +
114 		    onoff.transition_time;
115 
116 	/* Check using remaining time instead of "work pending" to make the
117 	 * onoff status send the right value on instant transitions. As the
118 	 * work item is executed in a lower priority than the mesh message
119 	 * handler, the work will be pending even on instant transitions.
120 	 */
121 	if (remaining) {
122 		net_buf_simple_add_u8(&buf, !onoff.val);
123 		net_buf_simple_add_u8(&buf, onoff.val);
124 		net_buf_simple_add_u8(&buf, model_time_encode(remaining));
125 	} else {
126 		net_buf_simple_add_u8(&buf, onoff.val);
127 	}
128 
129 	return bt_mesh_model_send(model, ctx, &buf, NULL, NULL);
130 }
131 
onoff_timeout(struct k_work * work)132 static void onoff_timeout(struct k_work *work)
133 {
134 	if (onoff.transition_time) {
135 		/* Start transition.
136 		 *
137 		 * The LED should be on as long as the transition is in
138 		 * progress, regardless of the target value, according to the
139 		 * Bluetooth Mesh Model specification, section 3.1.1.
140 		 */
141 		board_led_set(true);
142 
143 		k_work_reschedule(&onoff.work, K_MSEC(onoff.transition_time));
144 		onoff.transition_time = 0;
145 		return;
146 	}
147 
148 	board_led_set(onoff.val);
149 }
150 
151 /* Generic OnOff Server message handlers */
152 
gen_onoff_get(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)153 static int gen_onoff_get(const struct bt_mesh_model *model,
154 			 struct bt_mesh_msg_ctx *ctx,
155 			 struct net_buf_simple *buf)
156 {
157 	onoff_status_send(model, ctx);
158 	return 0;
159 }
160 
gen_onoff_set_unack(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)161 static int gen_onoff_set_unack(const struct bt_mesh_model *model,
162 			       struct bt_mesh_msg_ctx *ctx,
163 			       struct net_buf_simple *buf)
164 {
165 	uint8_t val = net_buf_simple_pull_u8(buf);
166 	uint8_t tid = net_buf_simple_pull_u8(buf);
167 	int32_t trans = 0;
168 	int32_t delay = 0;
169 
170 	if (buf->len) {
171 		trans = model_time_decode(net_buf_simple_pull_u8(buf));
172 		delay = net_buf_simple_pull_u8(buf) * 5;
173 	}
174 
175 	/* Only perform change if the message wasn't a duplicate and the
176 	 * value is different.
177 	 */
178 	if (tid == onoff.tid && ctx->addr == onoff.src) {
179 		/* Duplicate */
180 		return 0;
181 	}
182 
183 	if (val == onoff.val) {
184 		/* No change */
185 		return 0;
186 	}
187 
188 	printk("set: %s delay: %d ms time: %d ms\n", onoff_str[val], delay,
189 	       trans);
190 
191 	onoff.tid = tid;
192 	onoff.src = ctx->addr;
193 	onoff.val = val;
194 	onoff.transition_time = trans;
195 
196 	/* Schedule the next action to happen on the delay, and keep
197 	 * transition time stored, so it can be applied in the timeout.
198 	 */
199 	k_work_reschedule(&onoff.work, K_MSEC(delay));
200 
201 	return 0;
202 }
203 
gen_onoff_set(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)204 static int gen_onoff_set(const struct bt_mesh_model *model,
205 			 struct bt_mesh_msg_ctx *ctx,
206 			 struct net_buf_simple *buf)
207 {
208 	(void)gen_onoff_set_unack(model, ctx, buf);
209 	onoff_status_send(model, ctx);
210 
211 	return 0;
212 }
213 
214 static const struct bt_mesh_model_op gen_onoff_srv_op[] = {
215 	{ OP_ONOFF_GET,       BT_MESH_LEN_EXACT(0), gen_onoff_get },
216 	{ OP_ONOFF_SET,       BT_MESH_LEN_MIN(2),   gen_onoff_set },
217 	{ OP_ONOFF_SET_UNACK, BT_MESH_LEN_MIN(2),   gen_onoff_set_unack },
218 	BT_MESH_MODEL_OP_END,
219 };
220 
221 /* Generic OnOff Client */
222 
gen_onoff_status(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)223 static int gen_onoff_status(const struct bt_mesh_model *model,
224 			    struct bt_mesh_msg_ctx *ctx,
225 			    struct net_buf_simple *buf)
226 {
227 	uint8_t present = net_buf_simple_pull_u8(buf);
228 
229 	if (buf->len) {
230 		uint8_t target = net_buf_simple_pull_u8(buf);
231 		int32_t remaining_time =
232 			model_time_decode(net_buf_simple_pull_u8(buf));
233 
234 		printk("OnOff status: %s -> %s: (%d ms)\n", onoff_str[present],
235 		       onoff_str[target], remaining_time);
236 		return 0;
237 	}
238 
239 	printk("OnOff status: %s\n", onoff_str[present]);
240 
241 	return 0;
242 }
243 
244 static const struct bt_mesh_model_op gen_onoff_cli_op[] = {
245 	{OP_ONOFF_STATUS, BT_MESH_LEN_MIN(1), gen_onoff_status},
246 	BT_MESH_MODEL_OP_END,
247 };
248 
249 /* This application only needs one element to contain its models */
250 static const struct bt_mesh_model models[] = {
251 	BT_MESH_MODEL_CFG_SRV,
252 	BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
253 	BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op, NULL,
254 		      NULL),
255 	BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op, NULL,
256 		      NULL),
257 };
258 
259 static const struct bt_mesh_elem elements[] = {
260 	BT_MESH_ELEM(0, models, BT_MESH_MODEL_NONE),
261 };
262 
263 static const struct bt_mesh_comp comp = {
264 	.cid = BT_COMP_ID_LF,
265 	.elem = elements,
266 	.elem_count = ARRAY_SIZE(elements),
267 };
268 
269 /* Provisioning */
output_numeric(bt_mesh_output_action_t action,uint8_t * numeric,size_t size)270 static int output_numeric(bt_mesh_output_action_t action, uint8_t *numeric, size_t size)
271 {
272 	uint32_t number;
273 
274 	if (size != sizeof(number)) {
275 		printk("Wrong OOB size: %u\n", size);
276 		return -EINVAL;
277 	}
278 
279 	number = sys_get_le32(numeric);
280 	printk("OOB Number: %u\n", number);
281 
282 	board_output_number(action, number);
283 
284 	return 0;
285 }
286 
prov_complete(uint16_t net_idx,uint16_t addr)287 static void prov_complete(uint16_t net_idx, uint16_t addr)
288 {
289 	board_prov_complete();
290 }
291 
prov_reset(void)292 static void prov_reset(void)
293 {
294 	bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
295 }
296 
297 static uint8_t dev_uuid[16];
298 
299 static const struct bt_mesh_prov prov = {
300 	.uuid = dev_uuid,
301 	.output_size = 4,
302 	.output_actions = BT_MESH_DISPLAY_NUMBER,
303 	.output_numeric = output_numeric,
304 	.complete = prov_complete,
305 	.reset = prov_reset,
306 };
307 
308 /** Send an OnOff Set message from the Generic OnOff Client to all nodes. */
gen_onoff_send(bool val)309 static int gen_onoff_send(bool val)
310 {
311 	struct bt_mesh_msg_ctx ctx = {
312 		.app_idx = models[3].keys[0], /* Use the bound key */
313 		.addr = BT_MESH_ADDR_ALL_NODES,
314 		.send_ttl = BT_MESH_TTL_DEFAULT,
315 	};
316 	static uint8_t tid;
317 
318 	if (ctx.app_idx == BT_MESH_KEY_UNUSED) {
319 		printk("The Generic OnOff Client must be bound to a key before "
320 		       "sending.\n");
321 		return -ENOENT;
322 	}
323 
324 	BT_MESH_MODEL_BUF_DEFINE(buf, OP_ONOFF_SET_UNACK, 2);
325 	bt_mesh_model_msg_init(&buf, OP_ONOFF_SET_UNACK);
326 	net_buf_simple_add_u8(&buf, val);
327 	net_buf_simple_add_u8(&buf, tid++);
328 
329 	printk("Sending OnOff Set: %s\n", onoff_str[val]);
330 
331 	return bt_mesh_model_send(&models[3], &ctx, &buf, NULL, NULL);
332 }
333 
button_pressed(struct k_work * work)334 static void button_pressed(struct k_work *work)
335 {
336 	if (bt_mesh_is_provisioned()) {
337 		(void)gen_onoff_send(!onoff.val);
338 		return;
339 	}
340 
341 	/* Self-provision with an arbitrary address.
342 	 *
343 	 * NOTE: This should never be done in a production environment.
344 	 *       Addresses should be assigned by a provisioner, and keys should
345 	 *       be generated from true random numbers. It is done in this
346 	 *       sample to allow testing without a provisioner.
347 	 */
348 	static uint8_t net_key[16];
349 	static uint8_t dev_key[16];
350 	static uint8_t app_key[16];
351 	uint16_t addr;
352 	int err;
353 
354 	if (IS_ENABLED(CONFIG_HWINFO)) {
355 		addr = sys_get_le16(&dev_uuid[0]) & BIT_MASK(15);
356 	} else {
357 		addr = k_uptime_get_32() & BIT_MASK(15);
358 	}
359 
360 	printk("Self-provisioning with address 0x%04x\n", addr);
361 	err = bt_mesh_provision(net_key, 0, 0, 0, addr, dev_key);
362 	if (err) {
363 		printk("Provisioning failed (err: %d)\n", err);
364 		return;
365 	}
366 
367 	/* Add an application key to both Generic OnOff models: */
368 	err = bt_mesh_app_key_add(0, 0, app_key);
369 	if (err) {
370 		printk("App key add failed (err: %d)\n", err);
371 		return;
372 	}
373 
374 	/* Models must be bound to an app key to send and receive messages with
375 	 * it:
376 	 */
377 	models[2].keys[0] = 0;
378 	models[3].keys[0] = 0;
379 
380 	printk("Provisioned and configured!\n");
381 }
382 
bt_ready(int err)383 static void bt_ready(int err)
384 {
385 	if (err) {
386 		printk("Bluetooth init failed (err %d)\n", err);
387 		return;
388 	}
389 
390 	printk("Bluetooth initialized\n");
391 
392 	err = bt_mesh_init(&prov, &comp);
393 	if (err) {
394 		printk("Initializing mesh failed (err %d)\n", err);
395 		return;
396 	}
397 
398 	if (IS_ENABLED(CONFIG_SETTINGS)) {
399 		settings_load();
400 	}
401 
402 	/* This will be a no-op if settings_load() loaded provisioning info */
403 	bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
404 
405 	printk("Mesh initialized\n");
406 }
407 
main(void)408 int main(void)
409 {
410 	static struct k_work button_work;
411 	int err = -1;
412 
413 	printk("Initializing...\n");
414 
415 	if (IS_ENABLED(CONFIG_HWINFO)) {
416 		err = hwinfo_get_device_id(dev_uuid, sizeof(dev_uuid));
417 	}
418 
419 	if (err < 0) {
420 		dev_uuid[0] = 0xdd;
421 		dev_uuid[1] = 0xdd;
422 	}
423 
424 	k_work_init(&button_work, button_pressed);
425 
426 	err = board_init(&button_work);
427 	if (err) {
428 		printk("Board init failed (err: %d)\n", err);
429 		return 0;
430 	}
431 
432 	k_work_init_delayable(&onoff.work, onoff_timeout);
433 
434 	/* Initialize the Bluetooth Subsystem */
435 	err = bt_enable(bt_ready);
436 	if (err) {
437 		printk("Bluetooth init failed (err %d)\n", err);
438 	}
439 	return 0;
440 }
441