1 /*
2  * Copyright (c) 2018 Phytec Messtechnik GmbH
3  * Copyright (c) 2018 Intel Corporation
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <zephyr/kernel.h>
9 #include <zephyr/device.h>
10 #include <zephyr/devicetree.h>
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/display/cfb.h>
13 #include <zephyr/sys/printk.h>
14 #include <zephyr/drivers/flash.h>
15 #include <zephyr/storage/flash_map.h>
16 #include <zephyr/drivers/sensor.h>
17 
18 #include <string.h>
19 #include <stdio.h>
20 
21 #include <zephyr/bluetooth/bluetooth.h>
22 #include <zephyr/bluetooth/mesh/access.h>
23 
24 #include "mesh.h"
25 #include "board.h"
26 
27 #define STORAGE_PARTITION		storage_partition
28 #define STORAGE_PARTITION_DEV		FIXED_PARTITION_DEVICE(STORAGE_PARTITION)
29 #define STORAGE_PARTITION_OFFSET	FIXED_PARTITION_OFFSET(STORAGE_PARTITION)
30 #define STORAGE_PARTITION_SIZE		FIXED_PARTITION_SIZE(STORAGE_PARTITION)
31 
32 enum font_size {
33 	FONT_SMALL = 0,
34 	FONT_MEDIUM = 1,
35 	FONT_BIG = 2,
36 };
37 
38 enum screen_ids {
39 	SCREEN_MAIN = 0,
40 	SCREEN_SENSORS = 1,
41 	SCREEN_STATS = 2,
42 	SCREEN_LAST,
43 };
44 
45 struct font_info {
46 	uint8_t columns;
47 } fonts[] = {
48 	[FONT_BIG] =    { .columns = 12 },
49 	[FONT_MEDIUM] = { .columns = 16 },
50 	[FONT_SMALL] =  { .columns = 25 },
51 };
52 
53 #define LONG_PRESS_TIMEOUT K_SECONDS(1)
54 
55 #define STAT_COUNT 128
56 
57 static const struct device *const epd_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display));
58 static bool pressed;
59 static uint8_t screen_id = SCREEN_MAIN;
60 static struct k_work_delayable epd_work;
61 static struct k_work_delayable long_press_work;
62 static char str_buf[256];
63 
64 static const struct gpio_dt_spec leds[] = {
65 	GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios),
66 	GPIO_DT_SPEC_GET(DT_ALIAS(led1), gpios),
67 	GPIO_DT_SPEC_GET(DT_ALIAS(led2), gpios),
68 };
69 
70 static const struct gpio_dt_spec sw0_gpio = GPIO_DT_SPEC_GET(DT_ALIAS(sw0), gpios);
71 
72 struct k_work_delayable led_timer;
73 
print_line(enum font_size font_size,int row,const char * text,size_t len,bool center)74 static size_t print_line(enum font_size font_size, int row, const char *text,
75 			 size_t len, bool center)
76 {
77 	uint8_t font_height, font_width;
78 	uint8_t line[fonts[FONT_SMALL].columns + 1];
79 	int pad;
80 
81 	cfb_framebuffer_set_font(epd_dev, font_size);
82 
83 	len = MIN(len, fonts[font_size].columns);
84 	memcpy(line, text, len);
85 	line[len] = '\0';
86 
87 	if (center) {
88 		pad = (fonts[font_size].columns - len) / 2U;
89 	} else {
90 		pad = 0;
91 	}
92 
93 	cfb_get_font_size(epd_dev, font_size, &font_width, &font_height);
94 
95 	if (cfb_print(epd_dev, line, font_width * pad, font_height * row)) {
96 		printk("Failed to print a string\n");
97 	}
98 
99 	return len;
100 }
101 
get_len(enum font_size font,const char * text)102 static size_t get_len(enum font_size font, const char *text)
103 {
104 	const char *space = NULL;
105 	size_t i;
106 
107 	for (i = 0; i <= fonts[font].columns; i++) {
108 		switch (text[i]) {
109 		case '\n':
110 		case '\0':
111 			return i;
112 		case ' ':
113 			space = &text[i];
114 			break;
115 		default:
116 			continue;
117 		}
118 	}
119 
120 	/* If we got more characters than fits a line, and a space was
121 	 * encountered, fall back to the last space.
122 	 */
123 	if (space) {
124 		return space - text;
125 	}
126 
127 	return fonts[font].columns;
128 }
129 
board_blink_leds(void)130 void board_blink_leds(void)
131 {
132 	k_work_reschedule(&led_timer, K_MSEC(100));
133 }
134 
board_show_text(const char * text,bool center,k_timeout_t duration)135 void board_show_text(const char *text, bool center, k_timeout_t duration)
136 {
137 	int i;
138 
139 	cfb_framebuffer_clear(epd_dev, false);
140 
141 	for (i = 0; i < 3; i++) {
142 		size_t len;
143 
144 		while (*text == ' ' || *text == '\n') {
145 			text++;
146 		}
147 
148 		len = get_len(FONT_BIG, text);
149 		if (!len) {
150 			break;
151 		}
152 
153 		text += print_line(FONT_BIG, i, text, len, center);
154 		if (!*text) {
155 			break;
156 		}
157 	}
158 
159 	cfb_framebuffer_finalize(epd_dev);
160 
161 	if (!K_TIMEOUT_EQ(duration, K_FOREVER)) {
162 		k_work_reschedule(&epd_work, duration);
163 	}
164 }
165 
166 static struct stat {
167 	uint16_t addr;
168 	char name[9];
169 	uint8_t min_hops;
170 	uint8_t max_hops;
171 	uint16_t hello_count;
172 	uint16_t heartbeat_count;
173 } stats[STAT_COUNT] = {
174 	[0 ... (STAT_COUNT - 1)] = {
175 		.min_hops = BT_MESH_TTL_MAX,
176 		.max_hops = 0,
177 	},
178 };
179 
180 static uint32_t stat_count;
181 
182 #define NO_UPDATE -1
183 
add_hello(uint16_t addr,const char * name)184 static int add_hello(uint16_t addr, const char *name)
185 {
186 	int i;
187 
188 	for (i = 0; i < ARRAY_SIZE(stats); i++) {
189 		struct stat *stat = &stats[i];
190 
191 		if (!stat->addr) {
192 			stat->addr = addr;
193 			strncpy(stat->name, name, sizeof(stat->name) - 1);
194 			stat->hello_count = 1U;
195 			stat_count++;
196 			return i;
197 		}
198 
199 		if (stat->addr == addr) {
200 			/* Update name, incase it has changed */
201 			strncpy(stat->name, name, sizeof(stat->name) - 1);
202 
203 			if (stat->hello_count < 0xffff) {
204 				stat->hello_count++;
205 				return i;
206 			}
207 
208 			return NO_UPDATE;
209 		}
210 	}
211 
212 	return NO_UPDATE;
213 }
214 
add_heartbeat(uint16_t addr,uint8_t hops)215 static int add_heartbeat(uint16_t addr, uint8_t hops)
216 {
217 	int i;
218 
219 	for (i = 0; i < ARRAY_SIZE(stats); i++) {
220 		struct stat *stat = &stats[i];
221 
222 		if (!stat->addr) {
223 			stat->addr = addr;
224 			stat->heartbeat_count = 1U;
225 			stat->min_hops = hops;
226 			stat->max_hops = hops;
227 			stat_count++;
228 			return i;
229 		}
230 
231 		if (stat->addr == addr) {
232 			if (hops < stat->min_hops) {
233 				stat->min_hops = hops;
234 			} else if (hops > stat->max_hops) {
235 				stat->max_hops = hops;
236 			}
237 
238 			if (stat->heartbeat_count < 0xffff) {
239 				stat->heartbeat_count++;
240 				return i;
241 			}
242 
243 			return NO_UPDATE;
244 		}
245 	}
246 
247 	return NO_UPDATE;
248 }
249 
board_add_hello(uint16_t addr,const char * name)250 void board_add_hello(uint16_t addr, const char *name)
251 {
252 	uint32_t sort_i;
253 
254 	sort_i = add_hello(addr, name);
255 	if (sort_i != NO_UPDATE) {
256 	}
257 }
258 
board_add_heartbeat(uint16_t addr,uint8_t hops)259 void board_add_heartbeat(uint16_t addr, uint8_t hops)
260 {
261 	uint32_t sort_i;
262 
263 	sort_i = add_heartbeat(addr, hops);
264 	if (sort_i != NO_UPDATE) {
265 	}
266 }
267 
show_statistics(void)268 static void show_statistics(void)
269 {
270 	int top[4] = { -1, -1, -1, -1 };
271 	int len, i, line = 0;
272 	struct stat *stat;
273 	char str[32];
274 
275 	cfb_framebuffer_clear(epd_dev, false);
276 
277 	len = snprintk(str, sizeof(str),
278 		       "Own Address: 0x%04x", mesh_get_addr());
279 	print_line(FONT_SMALL, line++, str, len, false);
280 
281 	len = snprintk(str, sizeof(str),
282 		       "Node Count:  %u", stat_count + 1);
283 	print_line(FONT_SMALL, line++, str, len, false);
284 
285 	/* Find the top sender */
286 	for (i = 0; i < ARRAY_SIZE(stats); i++) {
287 		int j;
288 
289 		stat = &stats[i];
290 		if (!stat->addr) {
291 			break;
292 		}
293 
294 		if (!stat->hello_count) {
295 			continue;
296 		}
297 
298 		for (j = 0; j < ARRAY_SIZE(top); j++) {
299 			if (top[j] < 0) {
300 				top[j] = i;
301 				break;
302 			}
303 
304 			if (stat->hello_count <= stats[top[j]].hello_count) {
305 				continue;
306 			}
307 
308 			/* Move other elements down the list */
309 			if (j < ARRAY_SIZE(top) - 1) {
310 				memmove(&top[j + 1], &top[j],
311 					((ARRAY_SIZE(top) - j - 1) *
312 					 sizeof(top[j])));
313 			}
314 
315 			top[j] = i;
316 			break;
317 		}
318 	}
319 
320 	if (stat_count > 0) {
321 		len = snprintk(str, sizeof(str), "Most messages from:");
322 		print_line(FONT_SMALL, line++, str, len, false);
323 
324 		for (i = 0; i < ARRAY_SIZE(top); i++) {
325 			if (top[i] < 0) {
326 				break;
327 			}
328 
329 			stat = &stats[top[i]];
330 
331 			len = snprintk(str, sizeof(str), "%-3u 0x%04x %s",
332 				       stat->hello_count, stat->addr,
333 				       stat->name);
334 			print_line(FONT_SMALL, line++, str, len, false);
335 		}
336 	}
337 
338 	cfb_framebuffer_finalize(epd_dev);
339 }
340 
show_sensors_data(k_timeout_t interval)341 static void show_sensors_data(k_timeout_t interval)
342 {
343 	struct sensor_value val[3];
344 	uint8_t line = 0U;
345 	uint16_t len = 0U;
346 
347 	cfb_framebuffer_clear(epd_dev, false);
348 
349 	/* hdc1010 */
350 	if (get_hdc1010_val(val)) {
351 		goto _error_get;
352 	}
353 
354 	len = snprintf(str_buf, sizeof(str_buf), "Temperature:%d.%d C\n",
355 		       val[0].val1, val[0].val2 / 100000);
356 	print_line(FONT_SMALL, line++, str_buf, len, false);
357 
358 	len = snprintf(str_buf, sizeof(str_buf), "Humidity:%d%%\n",
359 		       val[1].val1);
360 	print_line(FONT_SMALL, line++, str_buf, len, false);
361 
362 	/* mma8652 */
363 	if (get_mma8652_val(val)) {
364 		goto _error_get;
365 	}
366 
367 	len = snprintf(str_buf, sizeof(str_buf), "AX :%10.3f\n",
368 		       sensor_value_to_double(&val[0]));
369 	print_line(FONT_SMALL, line++, str_buf, len, false);
370 
371 	len = snprintf(str_buf, sizeof(str_buf), "AY :%10.3f\n",
372 		       sensor_value_to_double(&val[1]));
373 	print_line(FONT_SMALL, line++, str_buf, len, false);
374 
375 	len = snprintf(str_buf, sizeof(str_buf), "AZ :%10.3f\n",
376 		       sensor_value_to_double(&val[2]));
377 	print_line(FONT_SMALL, line++, str_buf, len, false);
378 
379 	/* apds9960 */
380 	if (get_apds9960_val(val)) {
381 		goto _error_get;
382 	}
383 
384 	len = snprintf(str_buf, sizeof(str_buf), "Light :%d\n", val[0].val1);
385 	print_line(FONT_SMALL, line++, str_buf, len, false);
386 	len = snprintf(str_buf, sizeof(str_buf), "Proximity:%d\n", val[1].val1);
387 	print_line(FONT_SMALL, line++, str_buf, len, false);
388 
389 	cfb_framebuffer_finalize(epd_dev);
390 
391 	k_work_reschedule(&epd_work, interval);
392 
393 	return;
394 
395 _error_get:
396 	printk("Failed to get sensor data or print a string\n");
397 }
398 
show_main(void)399 static void show_main(void)
400 {
401 	char buf[CONFIG_BT_DEVICE_NAME_MAX];
402 	int i;
403 
404 	strncpy(buf, bt_get_name(), sizeof(buf) - 1);
405 	buf[sizeof(buf) - 1] = '\0';
406 
407 	/* Convert commas to newlines */
408 	for (i = 0; buf[i] != '\0'; i++) {
409 		if (buf[i] == ',') {
410 			buf[i] = '\n';
411 		}
412 	}
413 
414 	board_show_text(buf, true, K_FOREVER);
415 }
416 
epd_update(struct k_work * work)417 static void epd_update(struct k_work *work)
418 {
419 	switch (screen_id) {
420 	case SCREEN_STATS:
421 		show_statistics();
422 		return;
423 	case SCREEN_SENSORS:
424 		show_sensors_data(K_SECONDS(2));
425 		return;
426 	case SCREEN_MAIN:
427 		show_main();
428 		return;
429 	}
430 }
431 
long_press(struct k_work * work)432 static void long_press(struct k_work *work)
433 {
434 	/* Treat as release so actual release doesn't send messages */
435 	pressed = false;
436 	screen_id = (screen_id + 1) % SCREEN_LAST;
437 	printk("Change screen to id = %d\n", screen_id);
438 	board_refresh_display();
439 }
440 
button_is_pressed(void)441 static bool button_is_pressed(void)
442 {
443 	return gpio_pin_get_dt(&sw0_gpio) > 0;
444 }
445 
button_interrupt(const struct device * dev,struct gpio_callback * cb,uint32_t pins)446 static void button_interrupt(const struct device *dev,
447 			     struct gpio_callback *cb,
448 			     uint32_t pins)
449 {
450 	if (button_is_pressed() == pressed) {
451 		return;
452 	}
453 
454 	pressed = !pressed;
455 	printk("Button %s\n", pressed ? "pressed" : "released");
456 
457 	if (pressed) {
458 		k_work_reschedule(&long_press_work, LONG_PRESS_TIMEOUT);
459 		return;
460 	}
461 
462 	k_work_cancel_delayable(&long_press_work);
463 
464 	if (!mesh_is_initialized()) {
465 		return;
466 	}
467 
468 	/* Short press for views */
469 	switch (screen_id) {
470 	case SCREEN_SENSORS:
471 	case SCREEN_STATS:
472 		return;
473 	case SCREEN_MAIN:
474 		if (pins & BIT(sw0_gpio.pin)) {
475 			uint32_t uptime = k_uptime_get_32();
476 			static uint32_t bad_count, press_ts;
477 
478 			if (uptime - press_ts < 500) {
479 				bad_count++;
480 			} else {
481 				bad_count = 0U;
482 			}
483 
484 			press_ts = uptime;
485 
486 			if (bad_count) {
487 				if (bad_count > 5) {
488 					mesh_send_baduser();
489 					bad_count = 0U;
490 				} else {
491 					printk("Ignoring press\n");
492 				}
493 			} else {
494 				mesh_send_hello();
495 			}
496 		}
497 		return;
498 	default:
499 		return;
500 	}
501 }
502 
configure_button(void)503 static int configure_button(void)
504 {
505 	static struct gpio_callback button_cb;
506 
507 	if (!gpio_is_ready_dt(&sw0_gpio)) {
508 		printk("%s: device not ready.\n", sw0_gpio.port->name);
509 		return -ENODEV;
510 	}
511 
512 	gpio_pin_configure_dt(&sw0_gpio, GPIO_INPUT);
513 
514 	gpio_pin_interrupt_configure_dt(&sw0_gpio, GPIO_INT_EDGE_BOTH);
515 
516 	gpio_init_callback(&button_cb, button_interrupt, BIT(sw0_gpio.pin));
517 
518 	gpio_add_callback(sw0_gpio.port, &button_cb);
519 
520 	return 0;
521 }
522 
set_led_state(uint8_t id,bool state)523 int set_led_state(uint8_t id, bool state)
524 {
525 	return gpio_pin_set_dt(&leds[id], state);
526 }
527 
led_timeout(struct k_work * work)528 static void led_timeout(struct k_work *work)
529 {
530 	static int led_cntr;
531 	int i;
532 
533 	/* Disable all LEDs */
534 	for (i = 0; i < ARRAY_SIZE(leds); i++) {
535 		set_led_state(i, 0);
536 	}
537 
538 	/* Stop after 5 iterations */
539 	if (led_cntr >= (ARRAY_SIZE(leds) * 5)) {
540 		led_cntr = 0;
541 		return;
542 	}
543 
544 	/* Select and enable current LED */
545 	i = led_cntr++ % ARRAY_SIZE(leds);
546 	set_led_state(i, 1);
547 
548 	k_work_reschedule(&led_timer, K_MSEC(100));
549 }
550 
configure_leds(void)551 static int configure_leds(void)
552 {
553 	int i;
554 
555 	for (i = 0; i < ARRAY_SIZE(leds); i++) {
556 		if (!gpio_is_ready_dt(&leds[i])) {
557 			printk("%s: device not ready.\n", leds[i].port->name);
558 			return -ENODEV;
559 		}
560 
561 		gpio_pin_configure_dt(&leds[i], GPIO_OUTPUT_INACTIVE);
562 	}
563 
564 	k_work_init_delayable(&led_timer, led_timeout);
565 	return 0;
566 }
567 
erase_storage(void)568 static int erase_storage(void)
569 {
570 	const struct device *dev = STORAGE_PARTITION_DEV;
571 
572 	if (!device_is_ready(dev)) {
573 		printk("Flash device not ready\n");
574 		return -ENODEV;
575 	}
576 
577 	return flash_erase(dev, STORAGE_PARTITION_OFFSET, STORAGE_PARTITION_SIZE);
578 }
579 
board_refresh_display(void)580 void board_refresh_display(void)
581 {
582 	k_work_reschedule(&epd_work, K_NO_WAIT);
583 }
584 
board_init(void)585 int board_init(void)
586 {
587 	if (!device_is_ready(epd_dev)) {
588 		printk("%s: device not ready.\n", epd_dev->name);
589 		return -ENODEV;
590 	}
591 
592 	if (cfb_framebuffer_init(epd_dev)) {
593 		printk("Framebuffer initialization failed\n");
594 		return -EIO;
595 	}
596 
597 	cfb_framebuffer_clear(epd_dev, true);
598 
599 	if (configure_button()) {
600 		printk("Failed to configure button\n");
601 		return -EIO;
602 	}
603 
604 	if (configure_leds()) {
605 		printk("LED init failed\n");
606 		return -EIO;
607 	}
608 
609 	k_work_init_delayable(&epd_work, epd_update);
610 	k_work_init_delayable(&long_press_work, long_press);
611 
612 	pressed = button_is_pressed();
613 	if (pressed) {
614 		printk("Erasing storage\n");
615 		board_show_text("Resetting Device", false, K_SECONDS(4));
616 		erase_storage();
617 	}
618 
619 	return 0;
620 }
621