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