From b872a70a8e031623d889c0bbbffa122a01c8c619 Mon Sep 17 00:00:00 2001 From: Thomas Bittner Date: Mon, 24 Jul 2023 18:25:22 +0200 Subject: [PATCH] Refactored code base and added WIFI configuration menu Refactored code base: * Code moved to additional modules each with its individual responsibilities * Display module is responsible for input/output on the LCD screen * wifihandler is responsible for handling WIFI connection * Battey module is responsilbe to monitor the battery * Data exchange should only be done via public API Added WIFI configuration menu * WIFI no longer specified via defines * In settings tab, wifi submenu added to enter credentials * wifi credentials are stored in flash after a successful connection --- Platformio/.vscode/settings.json | 10 +- Platformio/img/WiFi_High_Signal.png | Bin 0 -> 552 bytes Platformio/img/WiFi_Low_Signal.png | Bin 0 -> 570 bytes Platformio/img/WiFi_Mid_Signal.png | Bin 0 -> 559 bytes Platformio/img/WiFi_No_Signal.png | Bin 0 -> 572 bytes Platformio/platformio.ini | 3 + Platformio/src/assets.c | 153 ++++++ Platformio/src/battery/battery.cpp | 44 ++ Platformio/src/battery/battery.hpp | 60 ++ Platformio/src/display/display.cpp | 566 +++++++++++++++++++ Platformio/src/display/display.hpp | 307 +++++++++++ Platformio/src/display/displaySettings.cpp | 81 +++ Platformio/src/display/wifiSettings.cpp | 289 ++++++++++ Platformio/src/main.cpp | 603 ++------------------- Platformio/src/wifiHandler/wifiHandler.cpp | 152 ++++++ Platformio/src/wifiHandler/wifiHandler.hpp | 83 +++ 16 files changed, 1788 insertions(+), 563 deletions(-) create mode 100644 Platformio/img/WiFi_High_Signal.png create mode 100644 Platformio/img/WiFi_Low_Signal.png create mode 100644 Platformio/img/WiFi_Mid_Signal.png create mode 100644 Platformio/img/WiFi_No_Signal.png create mode 100644 Platformio/src/battery/battery.cpp create mode 100644 Platformio/src/battery/battery.hpp create mode 100644 Platformio/src/display/display.cpp create mode 100644 Platformio/src/display/display.hpp create mode 100644 Platformio/src/display/displaySettings.cpp create mode 100644 Platformio/src/display/wifiSettings.cpp create mode 100644 Platformio/src/wifiHandler/wifiHandler.cpp create mode 100644 Platformio/src/wifiHandler/wifiHandler.hpp diff --git a/Platformio/.vscode/settings.json b/Platformio/.vscode/settings.json index a035777..94e1fbd 100644 --- a/Platformio/.vscode/settings.json +++ b/Platformio/.vscode/settings.json @@ -2,7 +2,15 @@ "cmake.configureOnOpen": false, "files.associations": { "random": "cpp", - "array": "cpp" + "array": "cpp", + "adafruit_ft6206.h": "c", + "deque": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "vector": "cpp", + "string_view": "cpp", + "initializer_list": "cpp" }, "cmake.sourceDirectory": "${workspaceFolder}/.pio/libdeps/esp32/Adafruit BusIO" } \ No newline at end of file diff --git a/Platformio/img/WiFi_High_Signal.png b/Platformio/img/WiFi_High_Signal.png new file mode 100644 index 0000000000000000000000000000000000000000..10f9ffb0d8e7d1a222b78e61fd35b2f838937f12 GIT binary patch literal 552 zcmV+@0@wYCP)P000#T1ONa486u6700004XF*Lt006O% z3;baP0003YX+uL$Nkc;*aB^>EX>4Tx04R~2kg-a`P!xv0R`tNZ(H4KsE<^UpVa0kpkPN~PDn6aWAK0%A)? zL;yMUh^smP000SaNLh0L01sgR01sgSs6VG^00007bV*G`2j>U_1~ohK=Q5Q5004YR zL_t(I%dJvL0stWhlK%f^=g>hZbehGR3yBbn!repwl=Z_3h*Lm-pvKYc29S%)IbCR$ zrvjqhmbIAx_lnJ3$W(d>V)7r6S%kd<)%A_P)P000#T1ONa486u6700004XF*Lt006O% z3;baP0003YX+uL$Nkc;*aB^>EX>4Tx04R~2kg-a`P!xv0R`tNZ(H4KsE<^UpVa0kpkPN~PDn6aWAK0%A)? zL;yMUh^smP000SaNLh0L01sgR01sgSs6VG^00007bV*G`2j>U_1~mvxH&cTE0053j zL_t(I%cWCG62njkg53YkbkTXE_?afDJ4HuDB+MiXMw;iz4$c@ONcLVH`+CgIN$tgf zlRO39rwj(?FiYqcO;)Qwf@r1ooL<%BE!akT$k^v_n6A_q| zY$~hQVn5k0SzdT-+@vwdb=f5kIBV|ObhtmP000#T1ONa486u6700004XF*Lt006O% z3;baP0003YX+uL$Nkc;*aB^>EX>4Tx04R~2kg-a`P!xv0R`tNZ(H4KsE<^UpVa0kpkPN~PDn6aWAK0%A)? zL;yMUh^smP000SaNLh0L01sgR01sgSs6VG^00007bV*G`2j>U_1~nvq?>}Gw004tY zL_t(I%cYY+62Kq`16lw7XL@iPL7-~O4FVGr5OJX@Nr20Ivj=mGS%k>tYdymzqcsjs z92f(hXL*>2?NW^c^}|#|m-aWu1{XVnBOhl2g~|EiB7*hgYLy*`Cd^8rrPj1E6IZr7 x%O=-BbsE{Z7FzcHwdxM*^?uOuU;fFregJ$_O%n%4$WQ=6I} literal 0 HcmV?d00001 diff --git a/Platformio/img/WiFi_No_Signal.png b/Platformio/img/WiFi_No_Signal.png new file mode 100644 index 0000000000000000000000000000000000000000..250b9d0ac616ffbeff1fb406bf6da1e96ec7ee75 GIT binary patch literal 572 zcmV-C0>k}@P)P000#T1ONa486u6700004XF*Lt006O% z3;baP0003YX+uL$Nkc;*aB^>EX>4Tx04R~2kg-a`P!xv0R`tNZ(H4KsE<^UpVa0kpkPN~PDn6aWAK0%A)? zL;yMUh^smP000SaNLh0L01sgR01sgSs6VG^00007bV*G`2j>SG3=JKXqXB^c0059l zL_t(I%biov4uC)ilGgwKGCj1WsH~N59x@dy>0000< KMNUMnLSTaM;P~DE literal 0 HcmV?d00001 diff --git a/Platformio/platformio.ini b/Platformio/platformio.ini index 89211cc..eca843f 100644 --- a/Platformio/platformio.ini +++ b/Platformio/platformio.ini @@ -26,6 +26,9 @@ lib_deps = build_flags = -D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG -I include ; Include the folder containing lv_conf.h + -I src/display + -I src/wifiHandler + -I src/battery ; The following lines replace the TFT_eSPI User_setup.h-file -D USER_SETUP_LOADED=1 -D ILI9341_DRIVER=1 diff --git a/Platformio/src/assets.c b/Platformio/src/assets.c index c782111..0c5e577 100644 --- a/Platformio/src/assets.c +++ b/Platformio/src/assets.c @@ -290,4 +290,157 @@ const lv_img_dsc_t lightbulb = { .header.h = 20, .data_size = 240, .data = lightbulb_map, +}; + +#ifndef LV_ATTRIBUTE_IMG_WIFI_HIGH_SIGNAL +#define LV_ATTRIBUTE_IMG_WIFI_HIGH_SIGNAL +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_WIFI_HIGH_SIGNAL uint8_t WiFi_High_Signal_map[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t WiFi_High_Signal = { + .header.cf = LV_IMG_CF_ALPHA_8BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 25, + .header.h = 21, + .data_size = 525, + .data = WiFi_High_Signal_map, +}; + +#ifndef LV_ATTRIBUTE_IMG_WIFI_LOW_SIGNAL +#define LV_ATTRIBUTE_IMG_WIFI_LOW_SIGNAL +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_WIFI_LOW_SIGNAL uint8_t WiFi_Low_Signal_map[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t WiFi_Low_Signal = { + .header.cf = LV_IMG_CF_ALPHA_8BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 25, + .header.h = 21, + .data_size = 525, + .data = WiFi_Low_Signal_map, +}; + + +#ifndef LV_ATTRIBUTE_IMG_WIFI_MID_SIGNAL +#define LV_ATTRIBUTE_IMG_WIFI_MID_SIGNAL +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_WIFI_MID_SIGNAL uint8_t WiFi_Mid_Signal_map[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t WiFi_Mid_Signal = { + .header.cf = LV_IMG_CF_ALPHA_8BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 25, + .header.h = 21, + .data_size = 525, + .data = WiFi_Mid_Signal_map, +}; + +#ifndef LV_ATTRIBUTE_IMG_WIFI_NO_SIGNAL +#define LV_ATTRIBUTE_IMG_WIFI_NO_SIGNAL +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_WIFI_NO_SIGNAL uint8_t WiFi_No_Signal_map[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t WiFi_No_Signal = { + .header.cf = LV_IMG_CF_ALPHA_8BIT, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 25, + .header.h = 21, + .data_size = 525, + .data = WiFi_No_Signal_map, }; \ No newline at end of file diff --git a/Platformio/src/battery/battery.cpp b/Platformio/src/battery/battery.cpp new file mode 100644 index 0000000..85b2a30 --- /dev/null +++ b/Platformio/src/battery/battery.cpp @@ -0,0 +1,44 @@ +#include "battery.hpp" +#include "display.hpp" +#include + +extern Display display; + +Battery::Battery(int adc_pin, int charging_pin) +{ + this->adc_pin = adc_pin; + this->charging_pin = charging_pin; +} + +void Battery::setup() +{ + // Power Pin Definition + pinMode(this->charging_pin, INPUT_PULLUP); + pinMode(this->adc_pin, INPUT); +} + +int Battery::getPercentage() +{ + return constrain(map(this->getVoltage(), 3700, 4200, 0, 100), 0, 100); +} + +bool Battery::isCharging() +{ + return !digitalRead(this->charging_pin); +} + +bool Battery::isConnected() +{ + return ((!this->isCharging()) && (this->getVoltage() < 4350)); +} + +int Battery::getVoltage() +{ + return analogRead(this->adc_pin)*2*3300/4095 + 350; +} + +void Battery::update() +{ + display.update_battery(this->getPercentage(), this->isCharging(), this->isConnected()); + +} \ No newline at end of file diff --git a/Platformio/src/battery/battery.hpp b/Platformio/src/battery/battery.hpp new file mode 100644 index 0000000..347fa41 --- /dev/null +++ b/Platformio/src/battery/battery.hpp @@ -0,0 +1,60 @@ + + +#ifndef _BATTERY_HPP_ +#define _BATTERY_HPP_ + +class Battery +{ + public: + Battery(int adc_pin, int charging_pin); + void setup(); + /** + * @brief Get the Percentage of the battery + * + * @return int Percentage of the battery + */ + int getPercentage(); + + /** + * @brief Function to determine if the battery is charging or not + * + * @return true Battery is currently charging + * @return false Battery is currently not charging + */ + bool isCharging(); + + /** + * @brief Function to determine if the battery is connected + * + * @return true Battery is connected + * @return false Battery is not connected + */ + bool isConnected(); + + /** + * @brief Function to update the battery status. This should be called on a regular basis + * + */ + void update(); + private: + /** + * @brief Function to get the current voltage of the battery + * + * @return int Voltage of the battery in mV + */ + int getVoltage(); + + /** + * @brief Variable to store which pin should be used for ADC + * + */ + int adc_pin; + + /** + * @brief Variable to store which pin is used to inidicate if the battery is currently charging or not + * + */ + int charging_pin; +}; + +#endif \ No newline at end of file diff --git a/Platformio/src/display/display.cpp b/Platformio/src/display/display.cpp new file mode 100644 index 0000000..c051892 --- /dev/null +++ b/Platformio/src/display/display.cpp @@ -0,0 +1,566 @@ + +#include +#include "display.hpp" +#include "Wire.h" +#include + + +extern bool wakeupByIMUEnabled; +extern Display display; +// LVGL declarations +LV_IMG_DECLARE(gradientLeft); +LV_IMG_DECLARE(gradientRight); +LV_IMG_DECLARE(appleTvIcon); +LV_IMG_DECLARE(appleDisplayIcon); +LV_IMG_DECLARE(appleBackIcon); +LV_IMG_DECLARE(lightbulb); +LV_IMG_DECLARE(WiFi_No_Signal); +extern lv_obj_t* panel; +static lv_disp_drv_t disp_drv; +/*TODO: get rid of global variable and use API functions instead*/ +extern Preferences preferences; +//TODO: fix callback function structure to pass it in and/or move it out of display class somehow else +void smartHomeSlider_event_cb(lv_event_t * e); +void smartHomeToggle_event_cb(lv_event_t * e); +void WakeEnableSetting_event_cb(lv_event_t * e); +void appleKey_event_cb(lv_event_t* e); +void virtualKeypad_event_cb(lv_event_t* e); +void bl_slider_event_cb(lv_event_t * e); +void tabview_device_event_cb(lv_event_t* e); +void store_scroll_value_event_cb(lv_event_t* e); +void my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data); + +// Display flushing +static void my_disp_flush( lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p ){ + display.flush(disp, area, color_p); +} + +Display::Display(int backlight_pin, int enable_pin, int width, int height) +{ + this->backlight_pin = backlight_pin; + this->enable_pin = enable_pin; + this->width = width; + this->height = height; + this->primary_color = lv_color_hex(0x303030); // gray + + /*Initialize the keybard as null*/ + this->kb = NULL; +} + +void Display::hide_keyboard() +{ + lv_obj_add_flag(this->kb, LV_OBJ_FLAG_HIDDEN); +} + +void Display::show_keyboard() +{ + lv_obj_clear_flag(this->kb, LV_OBJ_FLAG_HIDDEN); + lv_obj_move_foreground(this->kb); +} + +void Display::reset_settings_menu() +{ + lv_menu_set_page(this->settingsMenu, this->settingsMainPage); +} + +void Display::turnOff() +{ + digitalWrite(this->backlight_pin, HIGH); + digitalWrite(this->enable_pin, HIGH); + pinMode(this->backlight_pin, INPUT); + pinMode(this->enable_pin, INPUT); + gpio_hold_en((gpio_num_t) this->backlight_pin); + gpio_hold_en((gpio_num_t) this->enable_pin); + preferences.putUChar("blBrightness", this->backlight_brightness); +} + +void Display::setup() +{ + // LCD Pin Definition + pinMode(this->enable_pin, OUTPUT); + digitalWrite(this->enable_pin, HIGH); + pinMode(this->backlight_pin, OUTPUT); + digitalWrite(this->backlight_pin, HIGH); + + this->tft = TFT_eSPI(); + ledcSetup(LCD_BACKLIGHT_LEDC_CHANNEL, LCD_BACKLIGHT_LEDC_FREQUENCY, LCD_BACKLIGHT_LEDC_BIT_RESOLUTION); + ledcAttachPin(this->backlight_pin, LCD_BACKLIGHT_LEDC_CHANNEL); + ledcWrite(LCD_BACKLIGHT_LEDC_CHANNEL, 0); + + // Slowly charge the VSW voltage to prevent a brownout + // Workaround for hardware rev 1! + for(int i = 0; i < 100; i++){ + digitalWrite(this->enable_pin, HIGH); // LCD Logic off + delayMicroseconds(1); + digitalWrite(this->enable_pin, LOW); // LCD Logic on + } + + delay(100); // Wait for the LCD driver to power on + this->tft.init(); + this->tft.initDMA(); + this->tft.setRotation(0); + this->tft.fillScreen(TFT_BLACK); + this->tft.setSwapBytes(true); + + //TODO: move touchscreen handling out of Display class + // Setup touchscreen + this->touch = Adafruit_FT6206(); + Wire.begin(19, 22, 400000); // Configure i2c pins and set frequency to 400kHz + this->touch.begin(128); // Initialize touchscreen and set sensitivity threshold + + this->backlight_brightness = preferences.getUChar("blBrightness", DEFAULT_BACKLIGHT_BRIGHTNESS); + + // Setup LVGL + lv_init(); + +} + + static lv_disp_draw_buf_t draw_buf; +void Display::setup_ui(){ + this->bufA = (lv_color_t*) malloc((this->width*this->height / 10)* sizeof(lv_color_t)); + this->bufB = (lv_color_t*) malloc((this->width*this->height / 10)* sizeof(lv_color_t)); + + lv_disp_draw_buf_init( &draw_buf, this->bufA, this->bufB, this->width * this->height / 10 ); + // Initialize the display driver + lv_disp_drv_init( &disp_drv ); + disp_drv.hor_res = this->width; + disp_drv.ver_res = this->height; + disp_drv.flush_cb = my_disp_flush; + disp_drv.draw_buf = &draw_buf; + lv_disp_drv_register( &disp_drv ); + + //TODO: move touchscreen driver to its own module + // Initialize the touchscreen driver + static lv_indev_drv_t indev_drv; + lv_indev_drv_init( &indev_drv ); + indev_drv.type = LV_INDEV_TYPE_POINTER; + indev_drv.read_cb = my_touchpad_read; + lv_indev_drv_register( &indev_drv ); + // Set the background color + lv_obj_set_style_bg_color(lv_scr_act(), lv_color_black(), LV_PART_MAIN); + + this->create_keyboard(); + // Setup a scrollable tabview for devices and settings + lv_obj_t* tabview; + tabview = lv_tabview_create(lv_scr_act(), LV_DIR_TOP, 0); // Hide tab labels by setting their height to 0 + lv_obj_set_style_bg_color(tabview, lv_color_black(), LV_PART_MAIN); + lv_obj_set_size(tabview, this->width, 270); // 270 = screenHeight(320) - panel(30) - statusbar(20) + lv_obj_align(tabview, LV_ALIGN_TOP_MID, 0, 20); + + // Add 4 tabs (names are irrelevant since the labels are hidden) + lv_obj_t* tab1 = lv_tabview_add_tab(tabview, "Settings"); + lv_obj_t* tab2 = lv_tabview_add_tab(tabview, "Technisat"); + lv_obj_t* tab3 = lv_tabview_add_tab(tabview, "Apple TV"); + lv_obj_t* tab4 = lv_tabview_add_tab(tabview, "Smart Home"); + + // Configure number button grid + static lv_coord_t col_dsc[] = { LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST }; // equal x distribution + static lv_coord_t row_dsc[] = { 52, 52, 52, 52, LV_GRID_TEMPLATE_LAST }; // manual y distribution to compress the grid a bit + + // Create a container with grid for tab2 + lv_obj_set_style_pad_all(tab2, 0, LV_PART_MAIN); + lv_obj_t* cont = lv_obj_create(tab2); + lv_obj_set_style_shadow_width(cont, 0, LV_PART_MAIN); + lv_obj_set_style_bg_color(cont, lv_color_black(), LV_PART_MAIN); + lv_obj_set_style_border_width(cont, 0, LV_PART_MAIN); + lv_obj_set_style_grid_column_dsc_array(cont, col_dsc, 0); + lv_obj_set_style_grid_row_dsc_array(cont, row_dsc, 0); + lv_obj_set_size(cont, 240, 270); + lv_obj_set_layout(cont, LV_LAYOUT_GRID); + lv_obj_align(cont, LV_ALIGN_TOP_MID, 0, 0); + lv_obj_set_style_radius(cont, 0, LV_PART_MAIN); + + lv_obj_t* buttonLabel; + lv_obj_t* obj; + + // Iterate through grid buttons configure them + for (int i = 0; i < 12; i++) { + uint8_t col = i % 3; + uint8_t row = i / 3; + // Create the button object + if ((row == 3) && ((col == 0) || (col == 2))) continue; // Do not create a complete fourth row, only a 0 button + obj = lv_btn_create(cont); + lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_STRETCH, col, 1, LV_GRID_ALIGN_STRETCH, row, 1); + lv_obj_set_style_bg_color(obj, this->primary_color, LV_PART_MAIN); + lv_obj_set_style_radius(obj, 14, LV_PART_MAIN); + lv_obj_set_style_shadow_color(obj, lv_color_hex(0x404040), LV_PART_MAIN); + lv_obj_add_flag(obj, LV_OBJ_FLAG_EVENT_BUBBLE); // Clicking a button causes a event in its container + // Create Labels for each button + buttonLabel = lv_label_create(obj); + if(i < 9){ + lv_label_set_text_fmt(buttonLabel, std::to_string(i+1).c_str(), col, row); + lv_obj_set_user_data(obj, (void*)i); // Add user data so we can identify which button caused the container event + } + else{ + lv_label_set_text_fmt(buttonLabel, "0", col, row); + lv_obj_set_user_data(obj, (void*)9); + } + lv_obj_set_style_text_font(buttonLabel, &lv_font_montserrat_24, LV_PART_MAIN); + lv_obj_center(buttonLabel); + } + // Create a shared event for all button inside container + lv_obj_add_event_cb(cont, virtualKeypad_event_cb, LV_EVENT_CLICKED, NULL); + + + // Add content to the Apple TV tab (3) + // Add a nice apple tv logo + lv_obj_t* appleImg = lv_img_create(tab3); + lv_img_set_src(appleImg, &appleTvIcon); + lv_obj_align(appleImg, LV_ALIGN_CENTER, 0, -60); + // create two buttons and add their icons accordingly + lv_obj_t* button = lv_btn_create(tab3); + lv_obj_align(button, LV_ALIGN_BOTTOM_LEFT, 10, 0); + lv_obj_set_size(button, 60, 60); + lv_obj_set_style_radius(button, 30, LV_PART_MAIN); + lv_obj_set_style_bg_color(button, this->primary_color, LV_PART_MAIN); + lv_obj_add_event_cb(button, appleKey_event_cb, LV_EVENT_CLICKED, (void*)1); + + appleImg = lv_img_create(button); + lv_img_set_src(appleImg, &appleBackIcon); + lv_obj_set_style_img_recolor(appleImg, lv_color_white(), LV_PART_MAIN); + lv_obj_set_style_img_recolor_opa(appleImg, LV_OPA_COVER, LV_PART_MAIN); + lv_obj_align(appleImg, LV_ALIGN_CENTER, -3, 0); + + button = lv_btn_create(tab3); + lv_obj_align(button, LV_ALIGN_BOTTOM_RIGHT, -10, 0); + lv_obj_set_size(button, 60, 60); + lv_obj_set_style_radius(button, 30, LV_PART_MAIN); + lv_obj_set_style_bg_color(button, this->primary_color, LV_PART_MAIN); + lv_obj_add_event_cb(button, appleKey_event_cb, LV_EVENT_CLICKED, (void*)2); + + appleImg = lv_img_create(button); + lv_img_set_src(appleImg, &appleDisplayIcon); + lv_obj_set_style_img_recolor(appleImg, lv_color_white(), LV_PART_MAIN); + lv_obj_set_style_img_recolor_opa(appleImg, LV_OPA_COVER, LV_PART_MAIN); + lv_obj_align(appleImg, LV_ALIGN_CENTER, 0, 0); + + this->setup_settings(tab1); + + // Add content to the smart home tab (4) + lv_obj_set_layout(tab4, LV_LAYOUT_FLEX); + lv_obj_set_flex_flow(tab4, LV_FLEX_FLOW_COLUMN); + lv_obj_set_scrollbar_mode(tab4, LV_SCROLLBAR_MODE_ACTIVE); + + // Add a label, then a box for the light controls + lv_obj_t* menuLabel = lv_label_create(tab4); + lv_label_set_text(menuLabel, "Living Room"); + + lv_obj_t* menuBox = lv_obj_create(tab4); + lv_obj_set_size(menuBox, lv_pct(100), 79); + lv_obj_set_style_bg_color(menuBox, this->primary_color, LV_PART_MAIN); + lv_obj_set_style_border_width(menuBox, 0, LV_PART_MAIN); + + lv_obj_t* bulbIcon = lv_img_create(menuBox); + lv_img_set_src(bulbIcon, &lightbulb); + lv_obj_set_style_img_recolor(bulbIcon, lv_color_white(), LV_PART_MAIN); + lv_obj_set_style_img_recolor_opa(bulbIcon, LV_OPA_COVER, LV_PART_MAIN); + lv_obj_align(bulbIcon, LV_ALIGN_TOP_LEFT, 0, 0); + + menuLabel = lv_label_create(menuBox); + lv_label_set_text(menuLabel, "Floor Lamp"); + lv_obj_align(menuLabel, LV_ALIGN_TOP_LEFT, 22, 3); + lv_obj_t* lightToggleA = lv_switch_create(menuBox); + lv_obj_set_size(lightToggleA, 40, 22); + lv_obj_align(lightToggleA, LV_ALIGN_TOP_RIGHT, 0, 0); + lv_obj_set_style_bg_color(lightToggleA, lv_color_lighten(this->primary_color, 50), LV_PART_MAIN); + lv_obj_set_style_bg_color(lightToggleA, this->primary_color, LV_PART_INDICATOR); + lv_obj_add_event_cb(lightToggleA, smartHomeToggle_event_cb, LV_EVENT_VALUE_CHANGED, (void*)1); + + lv_obj_t *slider = lv_slider_create(menuBox); + lv_slider_set_range(slider, 0, 100); + lv_obj_set_style_bg_color(slider, lv_color_lighten(lv_color_black(), 30), LV_PART_INDICATOR); + lv_obj_set_style_bg_grad_color(slider, lv_color_lighten(lv_palette_main(LV_PALETTE_AMBER), 180), LV_PART_INDICATOR); + lv_obj_set_style_bg_grad_dir(slider, LV_GRAD_DIR_HOR, LV_PART_INDICATOR); + lv_obj_set_style_bg_color(slider, lv_color_white(), LV_PART_KNOB); + lv_obj_set_style_bg_opa(slider, 255, LV_PART_MAIN); + lv_obj_set_style_bg_color(slider, lv_color_lighten(this->primary_color, 50), LV_PART_MAIN); + lv_slider_set_value(slider, 255, LV_ANIM_OFF); + lv_obj_set_size(slider, lv_pct(90), 10); + lv_obj_align(slider, LV_ALIGN_TOP_MID, 0, 37); + lv_obj_add_event_cb(slider, smartHomeSlider_event_cb, LV_EVENT_VALUE_CHANGED, (void*)1); + + // Add another this->settingsMenu box for a second appliance + menuBox = lv_obj_create(tab4); + lv_obj_set_size(menuBox, lv_pct(100), 79); + lv_obj_set_style_bg_color(menuBox, this->primary_color, LV_PART_MAIN); + lv_obj_set_style_border_width(menuBox, 0, LV_PART_MAIN); + + bulbIcon = lv_img_create(menuBox); + lv_img_set_src(bulbIcon, &lightbulb); + lv_obj_set_style_img_recolor(bulbIcon, lv_color_white(), LV_PART_MAIN); + lv_obj_set_style_img_recolor_opa(bulbIcon, LV_OPA_COVER, LV_PART_MAIN); + lv_obj_align(bulbIcon, LV_ALIGN_TOP_LEFT, 0, 0); + + menuLabel = lv_label_create(menuBox); + lv_label_set_text(menuLabel, "Ceiling Light"); + lv_obj_align(menuLabel, LV_ALIGN_TOP_LEFT, 22, 3); + lv_obj_t* lightToggleB = lv_switch_create(menuBox); + lv_obj_set_size(lightToggleB, 40, 22); + lv_obj_align(lightToggleB, LV_ALIGN_TOP_RIGHT, 0, 0); + lv_obj_set_style_bg_color(lightToggleB, lv_color_lighten(this->primary_color, 50), LV_PART_MAIN); + lv_obj_set_style_bg_color(lightToggleB, this->primary_color, LV_PART_INDICATOR); + lv_obj_add_event_cb(lightToggleB, smartHomeToggle_event_cb, LV_EVENT_VALUE_CHANGED, (void*)2); + + slider = lv_slider_create(menuBox); + lv_slider_set_range(slider, 0, 100); + lv_obj_set_style_bg_color(slider, lv_color_lighten(lv_color_black(), 30), LV_PART_INDICATOR); + lv_obj_set_style_bg_grad_color(slider, lv_color_lighten(lv_palette_main(LV_PALETTE_AMBER), 180), LV_PART_INDICATOR); + lv_obj_set_style_bg_grad_dir(slider, LV_GRAD_DIR_HOR, LV_PART_INDICATOR); + lv_obj_set_style_bg_color(slider, lv_color_white(), LV_PART_KNOB); + lv_obj_set_style_bg_opa(slider, 255, LV_PART_MAIN); + lv_obj_set_style_bg_color(slider, lv_color_lighten(this->primary_color, 50), LV_PART_MAIN); + lv_slider_set_value(slider, 255, LV_ANIM_OFF); + lv_obj_set_size(slider, lv_pct(90), 10); + lv_obj_align(slider, LV_ALIGN_TOP_MID, 0, 37); + lv_obj_add_event_cb(slider, smartHomeSlider_event_cb, LV_EVENT_VALUE_CHANGED, (void*)2); + + + // Add another room (empty for now) + menuLabel = lv_label_create(tab4); + lv_label_set_text(menuLabel, "Kitchen"); + + menuBox = lv_obj_create(tab4); + lv_obj_set_size(menuBox, lv_pct(100), 79); + lv_obj_set_style_bg_color(menuBox, this->primary_color, LV_PART_MAIN); + lv_obj_set_style_border_width(menuBox, 0, LV_PART_MAIN); + + + // Set current page according to the current Device + lv_tabview_set_act(tabview, 0, LV_ANIM_OFF); + + + // Create a page indicator + panel = lv_obj_create(lv_scr_act()); + lv_obj_clear_flag(panel, LV_OBJ_FLAG_CLICKABLE); // This indicator will not be clickable + lv_obj_set_size(panel, this->width, 30); + lv_obj_set_flex_flow(panel, LV_FLEX_FLOW_ROW); + lv_obj_align(panel, LV_ALIGN_BOTTOM_MID, 0, 0); + lv_obj_set_scrollbar_mode(panel, LV_SCROLLBAR_MODE_OFF); + // This small hidden button enables the page indicator to scroll further + lv_obj_t* btn = lv_btn_create(panel); + lv_obj_set_size(btn, 50, lv_pct(100)); + lv_obj_set_style_shadow_width(btn, 0, LV_PART_MAIN); + lv_obj_set_style_opa(btn, LV_OPA_TRANSP, LV_PART_MAIN); + // Create actual (non-clickable) buttons for every tab + btn = lv_btn_create(panel); + lv_obj_clear_flag(btn, LV_OBJ_FLAG_CLICKABLE); + lv_obj_set_size(btn, 150, lv_pct(100)); + lv_obj_t* label = lv_label_create(btn); + lv_label_set_text_fmt(label, "Settings"); + lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); + lv_obj_set_style_shadow_width(btn, 0, LV_PART_MAIN); + lv_obj_set_style_bg_color(btn, this->primary_color, LV_PART_MAIN); + + btn = lv_btn_create(panel); + lv_obj_clear_flag(btn, LV_OBJ_FLAG_CLICKABLE); + lv_obj_set_size(btn, 150, lv_pct(100)); + label = lv_label_create(btn); + lv_label_set_text_fmt(label, "Technisat"); + lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); + lv_obj_set_style_shadow_width(btn, 0, LV_PART_MAIN); + lv_obj_set_style_bg_color(btn, this->primary_color, LV_PART_MAIN); + + btn = lv_btn_create(panel); + lv_obj_clear_flag(btn, LV_OBJ_FLAG_CLICKABLE); + lv_obj_set_size(btn, 150, lv_pct(100)); + label = lv_label_create(btn); + lv_label_set_text_fmt(label, "Apple TV"); + lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); + lv_obj_set_style_shadow_width(btn, 0, LV_PART_MAIN); + lv_obj_set_style_bg_color(btn, this->primary_color, LV_PART_MAIN); + + btn = lv_btn_create(panel); + lv_obj_clear_flag(btn, LV_OBJ_FLAG_CLICKABLE); + lv_obj_set_size(btn, 150, lv_pct(100)); + label = lv_label_create(btn); + lv_label_set_text_fmt(label, "Smart Home"); + lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); + lv_obj_set_style_shadow_width(btn, 0, LV_PART_MAIN); + lv_obj_set_style_bg_color(btn, this->primary_color, LV_PART_MAIN); + // This small hidden button enables the page indicator to scroll further + btn = lv_btn_create(panel); + lv_obj_set_size(btn, 50, lv_pct(100)); + lv_obj_set_style_shadow_width(btn, 0, LV_PART_MAIN); + lv_obj_set_style_opa(btn, LV_OPA_TRANSP, LV_PART_MAIN); + + // Make the indicator scroll together with the tabs by creating a scroll event + lv_obj_add_event_cb(lv_tabview_get_content(tabview), store_scroll_value_event_cb, LV_EVENT_SCROLL, NULL); + lv_obj_add_event_cb(tabview, tabview_device_event_cb, LV_EVENT_VALUE_CHANGED, NULL); + // Initialize scroll position for the indicator + lv_event_send(lv_tabview_get_content(tabview), LV_EVENT_SCROLL, NULL); + + // Style the panel background + static lv_style_t style_btn; + lv_style_init(&style_btn); + lv_style_set_pad_all(&style_btn, 3); + lv_style_set_border_width(&style_btn, 0); + lv_style_set_bg_opa(&style_btn, LV_OPA_TRANSP); + lv_obj_add_style(panel, &style_btn, 0); + + // Make the indicator fade out at the sides using gradient bitmaps + lv_obj_t* img1 = lv_img_create(lv_scr_act()); + lv_img_set_src(img1, &gradientLeft); + lv_obj_align(img1, LV_ALIGN_BOTTOM_LEFT, 0, 0); + lv_obj_set_size(img1, 30, 30); // stretch the 1-pixel high image to 30px + lv_obj_t* img2 = lv_img_create(lv_scr_act()); + lv_img_set_src(img2, &gradientRight); + lv_obj_align(img2, LV_ALIGN_BOTTOM_RIGHT, 0, 0); + lv_obj_set_size(img2, 30, 30); + + + // Create a status bar + lv_obj_t* statusbar = lv_btn_create(lv_scr_act()); + lv_obj_set_size(statusbar, 240, 20); + lv_obj_set_style_shadow_width(statusbar, 0, LV_PART_MAIN); + lv_obj_set_style_bg_color(statusbar, lv_color_black(), LV_PART_MAIN); + lv_obj_set_style_radius(statusbar, 0, LV_PART_MAIN); + lv_obj_align(statusbar, LV_ALIGN_TOP_MID, 0, 0); + + this->WifiLabel = lv_label_create(statusbar); + lv_label_set_text(this->WifiLabel, ""); + lv_obj_align(this->WifiLabel, LV_ALIGN_LEFT_MID, -8, 0); + lv_obj_set_style_text_font(this->WifiLabel, &lv_font_montserrat_12, LV_PART_MAIN); + + this->objBattPercentage = lv_label_create(statusbar); + lv_label_set_text(this->objBattPercentage, ""); + lv_obj_align(this->objBattPercentage, LV_ALIGN_RIGHT_MID, -16, 0); + lv_obj_set_style_text_font(this->objBattPercentage, &lv_font_montserrat_12, LV_PART_MAIN); + + this->objBattIcon = lv_label_create(statusbar); + lv_label_set_text(this->objBattIcon, LV_SYMBOL_BATTERY_EMPTY); + lv_obj_align(this->objBattIcon, LV_ALIGN_RIGHT_MID, 8, 0); + lv_obj_set_style_text_font(this->objBattIcon, &lv_font_montserrat_16, LV_PART_MAIN); + +} + + +lv_obj_t* wifi_subpage; +static lv_obj_t * kb; +static lv_obj_t * ta; + +/* Callback function to show and hide keybaord for attached textareas */ +static void ta_kb_event_cb(lv_event_t* e) +{ + lv_event_code_t code = lv_event_get_code(e); + lv_obj_t * ta = lv_event_get_target(e); + lv_obj_t * kb = (lv_obj_t*) lv_event_get_user_data(e); + switch(code){ + case LV_EVENT_FOCUSED: + lv_keyboard_set_textarea(kb, ta); + lv_obj_clear_flag(kb, LV_OBJ_FLAG_HIDDEN); + lv_obj_move_foreground(kb); + break; + case LV_EVENT_DEFOCUSED: + lv_keyboard_set_textarea(kb, NULL); + lv_obj_add_flag(kb, LV_OBJ_FLAG_HIDDEN); + break; + default: + break; + } +} + +void Display::create_keyboard() +{ + this->kb = lv_keyboard_create(lv_scr_act()); + lv_obj_add_flag(this->kb, LV_OBJ_FLAG_HIDDEN); + lv_obj_set_y(this->kb, 0); +} + +void Display::attach_keyboard(lv_obj_t* textarea) +{ + if (this->kb == NULL) + { + this->create_keyboard(); + } + lv_keyboard_set_textarea(this->kb, textarea); + lv_obj_add_event_cb(textarea, ta_kb_event_cb, LV_EVENT_FOCUSED, this->kb); + lv_obj_add_event_cb(textarea, ta_kb_event_cb, LV_EVENT_DEFOCUSED, this->kb); +} + +void Display::setup_settings(lv_obj_t* parent) +{ + // Add content to the settings tab + // With a flex layout, setting groups/boxes will position themselves automatically + lv_obj_set_layout(parent, LV_LAYOUT_FLEX); + lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); + lv_obj_set_scrollbar_mode(parent, LV_SCROLLBAR_MODE_ACTIVE); + // Add a label, then a box for the display settings + this->settingsMenu = lv_menu_create(parent); + lv_obj_set_width(this->settingsMenu, 210); + + /* Create main page for settings this->settingsMenu*/ + this->settingsMainPage = lv_menu_page_create(this->settingsMenu, NULL); + lv_obj_t* cont = lv_menu_cont_create(this->settingsMainPage); + lv_obj_set_layout(cont, LV_LAYOUT_FLEX); + lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN); + lv_obj_set_scrollbar_mode(cont, LV_SCROLLBAR_MODE_ACTIVE); + //lv_obj_set_width(cont, lv_obj_get_width(parent)); + this->display_settings(cont); + + this->create_wifi_settings(this->settingsMenu, cont); + + // Another setting for the battery + lv_obj_t* menuLabel = lv_label_create(cont); + lv_label_set_text(menuLabel, "Battery"); + lv_obj_t* menuBox = lv_obj_create(cont); + lv_obj_set_size(menuBox, lv_pct(100), 125); + lv_obj_set_style_bg_color(menuBox, this->primary_color, LV_PART_MAIN); + lv_obj_set_style_border_width(menuBox, 0, LV_PART_MAIN); + + lv_menu_set_page(this->settingsMenu, this->settingsMainPage); +} + +void Display::flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p ){ + uint32_t w = ( area->x2 - area->x1 + 1 ); + uint32_t h = ( area->y2 - area->y1 + 1 ); + + this->tft.startWrite(); + this->tft.setAddrWindow( area->x1, area->y1, w, h ); + this->tft.pushPixelsDMA( ( uint16_t * )&color_p->full, w * h); + this->tft.endWrite(); + + lv_disp_flush_ready( disp ); +} + +void Display::update() +{ + + static int fadeInTimer = millis(); // fadeInTimer = time after setup + if(millis() < fadeInTimer + backlight_brightness){ // Fade in the backlight brightness + ledcWrite(5, millis()-fadeInTimer); + } + else { // Dim Backlight before entering standby + //if(standbyTimer < 2000) ledcWrite(5, 85); // Backlight dim + //else ledcWrite(5, this->backlight_brightness); // Backlight on + ledcWrite(5, this->backlight_brightness); // Backlight on + } + // Update LVGL UI + lv_timer_handler(); +} + +void Display::update_battery(int percentage, bool isCharging, bool isConnected) +{ + if(isCharging || !isConnected) + { + Serial.println("charging?"); + lv_label_set_text(this->objBattPercentage, ""); + lv_label_set_text(this->objBattIcon, LV_SYMBOL_USB); + } + else { + Serial.println("Running on battery"); + if(percentage > 95){ + lv_label_set_text(this->objBattIcon, LV_SYMBOL_BATTERY_FULL); + } else if(percentage > 75) + { + lv_label_set_text(this->objBattIcon, LV_SYMBOL_BATTERY_3); + } else if(percentage > 50) + { + lv_label_set_text(this->objBattIcon, LV_SYMBOL_BATTERY_2); + } else if(percentage > 25) + { + lv_label_set_text(this->objBattIcon, LV_SYMBOL_BATTERY_1); + } else { + lv_label_set_text(this->objBattIcon, LV_SYMBOL_BATTERY_EMPTY); + } + } +} diff --git a/Platformio/src/display/display.hpp b/Platformio/src/display/display.hpp new file mode 100644 index 0000000..eb9d0bd --- /dev/null +++ b/Platformio/src/display/display.hpp @@ -0,0 +1,307 @@ +/** + * @file display.hpp + * @author Thomas Bittner + * @brief Header file describing the display class used to create the lvgl object to handle display input and outpu + * @version 0.1 + * @date 2023-08-01 + * + * @copyright Copyright (c) 2023 + * + */ +#ifndef _DISPLAY_HPP_ +#define _DISPLAY_HPP_ + +#include "driver/ledc.h" +#include // Hardware-specific library +#include +#include + +/*LEDC Channel to use for the LCD backlight*/ +#define LCD_BACKLIGHT_LEDC_CHANNEL LEDC_CHANNEL_5 + +#define LCD_BACKLIGHT_LEDC_FREQUENCY 640 + +#define LCD_BACKLIGHT_LEDC_BIT_RESOLUTION 8 + +#define DEFAULT_BACKLIGHT_BRIGHTNESS 128 + +class Display +{ + public: + Display(int backlight_pin, int enable_pin, int width, int height); + + /** + * @brief Function to setup the display. + * + */ + void setup(); + + /** + * @brief Function to setup the user interface. This function has to be called after setup. + * + */ + void setup_ui(); + + /** + * @brief Function to flush the display (update what is shown on the LCD). This function is called within + * the LVGL callback function + * + * @param disp + * @param area + * @param color_p + */ + void flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p ); + + /** + * @brief Function to add list of networks to the wifi selection page. + * + * @param SSIDS Pointer to the list of SSIDs found. The strings are moved out and the pointer does not have to + * life after the function returns. + * @param RSSI Pointer to the list of the RSSI per SSID. The ordering of SSID and RSSI list should be equal. The + * value is moved out of the array in the function, therefore the array does not have to be valid after the + * function returns. + * @param size Size of the SSIDS and RSSI array. + */ + void add_wifi_networks(String *SSIDS, int *RSSI, unsigned int size); + + /** + * @brief Clear the wifi networks listed in the wifi selection page. This function is called before new wifi + * networks are added to the list to avoid double listing + * + */ + void clear_wifi_networks(); + + /** + * @brief Update the wifi status. This function will update the wifi label in the status bar according to the + * parameter. + * + * @param connected Boolean parameter to indicate if a wifi connection is established or not. + */ + void update_wifi(bool connected); + + /** + * @brief Function to hide the keyboard. If the keyboard is attached to a text area, it will be hidden when the + * text area is defocused. This function can be used if the keyboard need to be hidden due to some script event. + * + */ + void hide_keyboard(); + + /** + * @brief Function to show the keyboard. If a text area needs the keybaord, it should be attached to the text area + * using the approbiate function. The keyboard will then show up when the text area is focused. This function is + * needed if the keyboard should be shown due to some script or other trigger. + * + */ + void show_keyboard(); + + /** + * @brief API function which needs to be called regularly to update the display + * + */ + void update(); + + /** + * @brief Function to change the settings menu to main page again. + * + */ + void reset_settings_menu(); + + /** + * @brief Function to update the battery indicator on the display + * + * @param percentage Battery percentage + * @param isCharging True if the battery is charging. False otherwise + * @param isConnected True if a battery is connected, false otherwise + */ + void update_battery(int percentage, bool isCharging, bool isConnected); + + void turnOff(); + private: + /** + * @brief Pin used for LCD backlight control + * + */ + int backlight_pin; + + /** + * @brief Pin used to enable the LCD + * + */ + int enable_pin; + + /** + * @brief Width of the display in pixel + * + */ + int width; + + /** + * @brief Height of the display in pixel + * + */ + int height; + + /** + * @brief Pointer to the buffer used for drawing on the screen + * + */ + lv_color_t *bufA; + + /** + * @brief Pointer to the buffer used for drawing on the screen + * + */ + lv_color_t *bufB; + + /** + * @brief Object of the touch input driver + * + */ + Adafruit_FT6206 touch; + /** + * @brief Keyboard object used whenever a keyboard is needed. + * + */ + lv_obj_t* kb; + + /** + * @brief Variable to store the primary color + * + */ + lv_color_t primary_color; + + /** + * @brief Object of the TFT driver + * + */ + TFT_eSPI tft; + + /** + * @brief Set the up settings object + * + * @param parent + */ + void setup_settings(lv_obj_t* parent); + + /** + * @brief Function to create the keyboard object which can then be attached to different text areas. + * + */ + void create_keyboard(); + + /** + * @brief Function to attach the keyboard to a text area. If the text area is selected, the keyboard + * is shown and if the textarea is defocused, the keyboard will be hidden again. + * + * @param textarea Textarea where the keyboard should be attached to. + */ + void attach_keyboard(lv_obj_t* textarea); + + /** + * @brief LVGL Menu for settings pages as needed. + * + */ + lv_obj_t* settingsMenu; + + /** + * @brief Main page of the settings menu + * + */ + lv_obj_t* settingsMainPage; + + /** + * @brief Battery percentage label + * + */ + lv_obj_t* objBattPercentage; + + /** + * @brief Battery icon object in the status bar + * + */ + lv_obj_t* objBattIcon; + +/************************************** WIFI Settings Menu *******************************************************/ + /** + * @brief Container within the wifi selection page + */ + lv_obj_t* wifi_setting_cont; + + /** + * @brief Wifi settings entry point on the settings tab + * + */ + lv_obj_t* wifiOverview; + + /** + * @brief Label in the wifi password page. This label is updated with the selected SSID when the credentials for + * a wifi network is entered. + * + */ + lv_obj_t* wifi_password_label; + + /** + * @brief Menu Subpage for the wifi password + */ + lv_obj_t* wifi_password_page; + + /** + * @brief Menu Subpage for wifi selection + */ + lv_obj_t* wifi_selection_page; + + /** + * @brief Wifi Label shown in the top status bar + */ + lv_obj_t* WifiLabel; + + /** + * @brief Create a wifi selection sub page object + * + * @param menu LVGL Menu where the sub page should be added to + * @return lv_obj_t* Menu sub page object pointer + */ + lv_obj_t* create_wifi_selection_page(lv_obj_t* menu); + + /** + * @brief Method to create the wifi password sub page + * + * @param menu Menu where the sub page should be created + * @return lv_obj_t* menu sub page object pointer + */ + lv_obj_t* create_wifi_password_page(lv_obj_t* menu); + + /** + * @brief Method to create the wifi settings on the main page + * + * @param parent lv object parent where the main settings page should be added to + */ + void create_wifi_main_page(lv_obj_t* parent); + + /** + * @brief Method to create wifi settings. This method will call the create_wifi_selection_page, + * the create_wifi_password_page, and the create_wifi_main_page + * + * @param menu Settings menu where the sub pages should be added to + * @param parent lv object parent where the main settings page should be added to + */ + void create_wifi_settings(lv_obj_t* menu, lv_obj_t* parent); + + +/************************************** Display settings menu ********************************************************/ + /** + * Variable to store the current backlight brightness level + */ + unsigned int backlight_brightness; + + /** + * @brief Function to create the display settings page. + * + * @param parent LVGL object acting as a parent for the display settings page + */ + void display_settings(lv_obj_t* parent); +}; + + + +#endif \ No newline at end of file diff --git a/Platformio/src/display/displaySettings.cpp b/Platformio/src/display/displaySettings.cpp new file mode 100644 index 0000000..7488602 --- /dev/null +++ b/Platformio/src/display/displaySettings.cpp @@ -0,0 +1,81 @@ +#include "display.hpp" +#include + +LV_IMG_DECLARE(high_brightness); +LV_IMG_DECLARE(low_brightness); + +/*TODO: get rid of global variable and use API functions instead*/ +extern bool wakeupByIMUEnabled; + +// Slider Event handler +void bl_slider_event_cb(lv_event_t * e){ + lv_obj_t * slider = lv_event_get_target(e); + unsigned int* backlight_brightness = (unsigned int*) lv_event_get_user_data(e); + *backlight_brightness = constrain(lv_slider_get_value(slider), 30, 255); +} + +// Wakeup by IMU Switch Event handler +void WakeEnableSetting_event_cb(lv_event_t * e){ + wakeupByIMUEnabled = lv_obj_has_state(lv_event_get_target(e), LV_STATE_CHECKED); +} + +void Display::display_settings(lv_obj_t* parent) +{ + + lv_obj_t* menuLabel = lv_label_create(parent); + lv_label_set_text(menuLabel, "Display"); + + lv_obj_t* menuBox = lv_obj_create(parent); + lv_obj_set_size(menuBox, lv_pct(100), 109); + lv_obj_set_style_bg_color(menuBox, this->primary_color, LV_PART_MAIN); + lv_obj_set_style_border_width(menuBox, 0, LV_PART_MAIN); + + lv_obj_t* brightnessIcon = lv_img_create(menuBox); + lv_img_set_src(brightnessIcon, &low_brightness); + lv_obj_set_style_img_recolor(brightnessIcon, lv_color_white(), LV_PART_MAIN); + lv_obj_set_style_img_recolor_opa(brightnessIcon, LV_OPA_COVER, LV_PART_MAIN); + lv_obj_align(brightnessIcon, LV_ALIGN_TOP_LEFT, 0, 0); + lv_obj_t* slider = lv_slider_create(menuBox); + lv_slider_set_range(slider, 30, 255); + lv_obj_set_style_bg_color(slider, lv_color_white(), LV_PART_KNOB); + lv_obj_set_style_bg_opa(slider, LV_OPA_COVER, LV_PART_MAIN); + lv_obj_set_style_bg_color(slider, lv_color_lighten(this->primary_color, 50), LV_PART_MAIN); + lv_slider_set_value(slider, this->backlight_brightness, LV_ANIM_OFF); + lv_obj_set_size(slider, lv_pct(66), 10); + lv_obj_align(slider, LV_ALIGN_TOP_MID, 0, 3); + brightnessIcon = lv_img_create(menuBox); + lv_img_set_src(brightnessIcon, &high_brightness); + lv_obj_set_style_img_recolor(brightnessIcon, lv_color_white(), LV_PART_MAIN); + lv_obj_set_style_img_recolor_opa(brightnessIcon, LV_OPA_COVER, LV_PART_MAIN); + lv_obj_align(brightnessIcon, LV_ALIGN_TOP_RIGHT, 0, -1); + lv_obj_add_event_cb(slider, bl_slider_event_cb, LV_EVENT_VALUE_CHANGED, &this->backlight_brightness); + + menuLabel = lv_label_create(menuBox); + lv_label_set_text(menuLabel, "Lift to Wake"); + lv_obj_align(menuLabel, LV_ALIGN_TOP_LEFT, 0, 32); + lv_obj_t* wakeToggle = lv_switch_create(menuBox); + lv_obj_set_size(wakeToggle, 40, 22); + lv_obj_align(wakeToggle, LV_ALIGN_TOP_RIGHT, 0, 29); + lv_obj_set_style_bg_color(wakeToggle, lv_color_hex(0x505050), LV_PART_MAIN); + lv_obj_add_event_cb(wakeToggle, WakeEnableSetting_event_cb, LV_EVENT_VALUE_CHANGED, NULL); + if(wakeupByIMUEnabled) lv_obj_add_state(wakeToggle, LV_STATE_CHECKED); // set default state + + menuLabel = lv_label_create(menuBox); + lv_label_set_text(menuLabel, "Timeout"); + lv_obj_align(menuLabel, LV_ALIGN_TOP_LEFT, 0, 64); + lv_obj_t* drop = lv_dropdown_create(menuBox); + lv_dropdown_set_options(drop, "10s\n" + "30s\n" + "1m\n" + "3m"); + lv_obj_align(drop, LV_ALIGN_TOP_RIGHT, 0, 61); + lv_obj_set_size(drop, 70, 22); + //lv_obj_set_style_text_font(drop, &lv_font_montserrat_12, LV_PART_MAIN); + //lv_obj_set_style_text_font(lv_dropdown_get_list(drop), &lv_font_montserrat_12, LV_PART_MAIN); + lv_obj_set_style_pad_top(drop, 1, LV_PART_MAIN); + lv_obj_set_style_bg_color(drop, this->primary_color, LV_PART_MAIN); + lv_obj_set_style_bg_color(lv_dropdown_get_list(drop), this->primary_color, LV_PART_MAIN); + lv_obj_set_style_border_width(lv_dropdown_get_list(drop), 1, LV_PART_MAIN); + lv_obj_set_style_border_color(lv_dropdown_get_list(drop), lv_color_hex(0x505050), LV_PART_MAIN); + +} \ No newline at end of file diff --git a/Platformio/src/display/wifiSettings.cpp b/Platformio/src/display/wifiSettings.cpp new file mode 100644 index 0000000..9b6a9c6 --- /dev/null +++ b/Platformio/src/display/wifiSettings.cpp @@ -0,0 +1,289 @@ +/** + * @file wifiSettings.cpp + * @author Thomas Bittner + * @brief Functions to create the wifi settings user interface for the Display class for the OMOTE universal + * remote control + * @version 0.1 + * @date 2023-08-01 + * + * @copyright Copyright (c) 2023 + * + */ +#include "display.hpp" +#include +#include "WiFi.h" +#include "wifiHandler.hpp" + +/********************************************************************************************************************* +************************************************* Local functions **************************************************** +**********************************************************************************************************************/ + +extern wifiHandler wifihandler; +extern Display display; +static char* ssid; + +LV_IMG_DECLARE(WiFi_No_Signal); +LV_IMG_DECLARE(WiFi_Low_Signal); +LV_IMG_DECLARE(WiFi_Mid_Signal); +LV_IMG_DECLARE(WiFi_High_Signal); + +/** + * @brief Callback function for the show password checkbox. Checking the box will show the password while unchecked the + * password will be shown as dots. + * + * @param e Pointer to event object for the event where this callback is called + */ +static void show_password_cb(lv_event_t* e) +{ + lv_obj_t* password_field = (lv_obj_t*) e->user_data; + if (lv_obj_has_state(e->target, LV_STATE_CHECKED)){ + lv_textarea_set_password_mode(password_field, false); + } + else{ + lv_textarea_set_password_mode(password_field, true); + } + +} + +/** + * @brief Textarea callback function for the password field. In case the enter key is pressed in the text area, the + * function will try to connect to the network with the provided password. + * + * @param e Pointer to event object for the event where this callback is called + */ +static void ta_event_cb(lv_event_t* e) +{ + lv_event_code_t code = lv_event_get_code(e); + lv_obj_t * ta = lv_event_get_target(e); + lv_obj_t * kb = (lv_obj_t*) lv_event_get_user_data(e); + + const char* password = lv_textarea_get_text(ta); + switch(code){ + case LV_EVENT_READY: + wifihandler.connect(ssid, password); + lv_obj_clear_state(ta, LV_STATE_FOCUSED); + display.hide_keyboard(); + display.reset_settings_menu(); + /* Fall through on purpose. Pressing enter should disable the keyboard as well*/ + default: + break; + + } +} + +/** + * @brief Callback function in case a wifi is selected. This callback function will change the label of the wifi password + * sub page to the selected wifi network. + * + * @param e Pointer to event object for the event where this callback is called + */ +static void wifi_selected_cb(lv_event_t* e) +{ + lv_obj_t* label = lv_obj_get_child(e->target, 0); + lv_label_set_text((lv_obj_t*) e->user_data, lv_label_get_text(label)); + ssid = lv_label_get_text(label); +} + +/** + * @brief Callback which is triggered when the wifi settings are opened (the wifi settings are pressed in the settings + * main page). This function will trigger the asynchronous scan for wifi networks and update the label of the wifi + * selection page to indicate that wifi networks are being searched for. The wifi event callback function then has to + * call the API function to fill the page with the found networks. + * + * @param event Pointer to event object for the event where this callback is called + */ +static void wifi_settings_cb(lv_event_t* event) +{ + lv_obj_t* cont = (lv_obj_t*) lv_event_get_user_data(event); + lv_obj_clean(cont); + lv_obj_t* label = lv_label_create(cont); + lv_label_set_text(label, "Searching for wifi networks"); + //This will trigger an asynchronouse network scan + wifihandler.scan(); +} + +/** + * @brief Callback which is triggered when clicking the connect button. It triggers the wifi connection. + * + * @param event Pointer to event object for the event where this callback is called + */ +static void connect_btn_cb(lv_event_t* event) +{ + lv_obj_t* ta = (lv_obj_t*) event->user_data; + const char* password = lv_textarea_get_text(ta); + + wifihandler.connect(ssid, password); + lv_obj_clear_state(ta, LV_STATE_FOCUSED); + display.hide_keyboard(); + display.reset_settings_menu(); +} + +/*********************************************************************************************************************** +****************************************************** Class Methods *************************************************** +***********************************************************************************************************************/ + +void Display::clear_wifi_networks() +{ + lv_obj_clean(this->wifi_setting_cont); +} + +void Display::add_wifi_networks(String *SSIDs, int *RSSI, unsigned int size) +{ + if (size == 0) + { + lv_obj_t* menuBox = lv_obj_create(this->wifi_setting_cont); + lv_obj_set_size(menuBox, lv_pct(100), 45); + lv_obj_set_scrollbar_mode(menuBox, LV_SCROLLBAR_MODE_OFF); + lv_obj_t* menuLabel = lv_label_create(menuBox); + lv_label_set_text(menuLabel, "no networks found"); + } + for (int i = 0; i < size; i++) + { + lv_obj_t* menuBox = lv_obj_create(this->wifi_setting_cont); + lv_obj_set_size(menuBox, lv_pct(100), 45); + lv_obj_set_scrollbar_mode(menuBox, LV_SCROLLBAR_MODE_OFF); + lv_obj_t* menuLabel = lv_label_create(menuBox); + lv_label_set_text_fmt(menuLabel, "%s", SSIDs[i]); + + #if 0 + menuLabel = lv_label_create(menuBox); + lv_label_set_text_fmt(menuLabel, "%d", RSSI[i]); + lv_obj_align(menuLabel, LV_ALIGN_TOP_RIGHT, 0, 0); + +#else + + lv_obj_t* wifi_image = lv_img_create(menuBox); + lv_obj_align(wifi_image, LV_ALIGN_TOP_RIGHT, 0, 0); + + if (RSSI[i] > -50) + { + lv_img_set_src(wifi_image, &WiFi_High_Signal); + } + else if (RSSI[i] > -60) + { + lv_img_set_src(wifi_image, &WiFi_Mid_Signal); + } + else if (RSSI[i] > -70) + { + lv_img_set_src(wifi_image, &WiFi_Low_Signal); + } + else + { + lv_img_set_src(wifi_image, &WiFi_No_Signal); + } + lv_obj_set_style_img_recolor(wifi_image, lv_color_white(), LV_PART_MAIN); + lv_obj_set_style_img_recolor_opa(wifi_image, LV_OPA_COVER, LV_PART_MAIN); +#endif + + lv_menu_set_load_page_event(this->settingsMenu, menuBox, this->wifi_password_page); + lv_obj_add_event_cb(menuBox, wifi_selected_cb, LV_EVENT_CLICKED, this->wifi_password_label); + } +} + +void Display::update_wifi(bool connected) +{ + + lv_obj_t* ip_label = lv_obj_get_child(this->wifiOverview, 3); + lv_obj_t* ssid_label = lv_obj_get_child(this->wifiOverview, 0); + if (connected) + { + lv_label_set_text(this->WifiLabel, LV_SYMBOL_WIFI); + lv_label_set_text(ssid_label, wifihandler.getSSID()); + lv_label_set_text(ip_label, wifihandler.getIP().c_str()); + } + else + { + lv_label_set_text(this->WifiLabel, ""); + lv_label_set_text(ssid_label, "Disconnected"); + lv_label_set_text(ip_label, "-"); + } +} + +lv_obj_t* Display::create_wifi_password_page(lv_obj_t* menu) +{ + lv_obj_t* ret_val = lv_menu_page_create(menu, NULL); + lv_obj_t* cont = lv_menu_cont_create(ret_val); + lv_obj_set_layout(cont, LV_LAYOUT_FLEX); + lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN); + lv_obj_set_scrollbar_mode(cont, LV_SCROLLBAR_MODE_ACTIVE); + + this->wifi_password_label = lv_label_create(cont); + lv_label_set_text(this->wifi_password_label, "Password"); + lv_obj_t* password_input = lv_textarea_create(cont); + lv_obj_set_width(password_input, lv_pct(100)); + lv_textarea_set_password_mode(password_input, true); + lv_textarea_set_one_line(password_input, true); + lv_textarea_set_placeholder_text(password_input, "Password"); + lv_obj_add_event_cb(password_input, ta_event_cb, LV_EVENT_READY, NULL ); + this->attach_keyboard(password_input); + + lv_obj_t* show_password = lv_checkbox_create(cont); + lv_checkbox_set_text(show_password, "Show password"); + lv_obj_add_event_cb(show_password, show_password_cb, LV_EVENT_VALUE_CHANGED, password_input); + + lv_obj_t* connect_button = lv_btn_create(cont); + lv_obj_t* label = lv_label_create(connect_button); + lv_label_set_text(label, "Connect"); + lv_obj_add_event_cb(connect_button, connect_btn_cb, LV_EVENT_CLICKED, password_input); + + return ret_val; +} + +lv_obj_t* Display::create_wifi_selection_page(lv_obj_t* menu) +{ + /* Create sub page for wifi*/ + lv_obj_t* subpage = lv_menu_page_create(menu, NULL); + this->wifi_setting_cont = lv_menu_cont_create(subpage); + lv_obj_set_layout(this->wifi_setting_cont, LV_LAYOUT_FLEX); + lv_obj_set_flex_flow(this->wifi_setting_cont, LV_FLEX_FLOW_COLUMN); + lv_obj_set_scrollbar_mode(this->wifi_setting_cont, LV_SCROLLBAR_MODE_ACTIVE); + + lv_obj_t* menuLabel = lv_label_create(this->wifi_setting_cont); + lv_label_set_text(menuLabel, "Searching for wifi networks"); + + + return subpage; +} + +void Display::create_wifi_main_page(lv_obj_t* parent) +{ + lv_obj_t* menuLabel = lv_label_create(parent); + lv_label_set_text(menuLabel, "Wi-Fi"); + this->wifiOverview = lv_obj_create(parent); + lv_obj_set_size(this->wifiOverview, lv_pct(100), 80); + lv_obj_set_style_bg_color(this->wifiOverview, this->primary_color, LV_PART_MAIN); + lv_obj_set_style_border_width(this->wifiOverview, 0, LV_PART_MAIN); + menuLabel = lv_label_create(this->wifiOverview); + + lv_obj_t* arrow = lv_label_create(this->wifiOverview); + lv_label_set_text(arrow, LV_SYMBOL_RIGHT); + lv_obj_align(arrow, LV_ALIGN_TOP_RIGHT, 0, 0); + + lv_obj_t* ip_label = lv_label_create(this->wifiOverview); + lv_label_set_text(ip_label, "IP:"); + lv_obj_align(ip_label, LV_ALIGN_BOTTOM_LEFT, 0, 0); + + lv_obj_t* ip = lv_label_create(this->wifiOverview); + lv_obj_align(ip, LV_ALIGN_BOTTOM_RIGHT, 0, 0); + + if (wifihandler.isConnected()) + { + lv_label_set_text(menuLabel, wifihandler.getSSID()); + lv_label_set_text(ip, wifihandler.getIP().c_str()); + } + else + { + lv_label_set_text(menuLabel, "Disconnected"); + lv_label_set_text(ip, "-"); + } + lv_menu_set_load_page_event(this->settingsMenu, this->wifiOverview, this->wifi_selection_page); + lv_obj_add_event_cb(this->wifiOverview, wifi_settings_cb, LV_EVENT_CLICKED, this->wifi_setting_cont); +} + + +void Display::create_wifi_settings(lv_obj_t* menu, lv_obj_t* parent) +{ + this->wifi_selection_page = this->create_wifi_selection_page(menu); + this->wifi_password_page = this->create_wifi_password_page(this->settingsMenu); + this->create_wifi_main_page(parent); +} \ No newline at end of file diff --git a/Platformio/src/main.cpp b/Platformio/src/main.cpp index 4e82c51..263bf0b 100644 --- a/Platformio/src/main.cpp +++ b/Platformio/src/main.cpp @@ -1,7 +1,6 @@ // OMOTE firmware for ESP32 // 2023 Maximilian Kern -#include // Hardware-specific library #include // modified for inverted logic #include #include "SparkFunLIS3DH.h" @@ -15,6 +14,9 @@ #include #include "driver/ledc.h" #include +#include "display.hpp" +#include "wifiHandler.hpp" +#include "battery.hpp" #define ENABLE_WIFI // Comment out to diable connected features @@ -53,6 +55,12 @@ // Variables and Object declarations ------------------------------------------------------------------------------------------------------ +// LCD declarations +#define screenWidth 240 +#define screenHeight 320 +Display display(LCD_BL, LCD_EN, screenWidth, screenHeight); +wifiHandler wifihandler; +Battery battery(ADC_BAT, CRG_STAT); // Battery declares int battery_voltage = 0; int battery_percentage = 100; @@ -66,21 +74,12 @@ int standbyTimer = SLEEP_TIMEOUT; bool wakeupByIMUEnabled = true; LIS3DH IMU(I2C_MODE, 0x19); // Default constructor is I2C, addr 0x19. -// LCD declarations -TFT_eSPI tft = TFT_eSPI(); -#define screenWidth 240 -#define screenHeight 320 Adafruit_FT6206 touch = Adafruit_FT6206(); TS_Point touchPoint; TS_Point oldPoint; int backlight_brightness = 255; // LVGL declarations -static lv_disp_draw_buf_t draw_buf; -static lv_color_t bufA[ screenWidth * screenHeight / 10 ]; -static lv_color_t bufB[ screenWidth * screenHeight / 10 ]; -lv_obj_t* objBattPercentage; -lv_obj_t* objBattIcon; LV_IMG_DECLARE(gradientLeft); LV_IMG_DECLARE(gradientRight); LV_IMG_DECLARE(appleTvIcon); @@ -90,7 +89,6 @@ LV_IMG_DECLARE(high_brightness); LV_IMG_DECLARE(low_brightness); LV_IMG_DECLARE(lightbulb); lv_obj_t* panel; -lv_color_t color_primary = lv_color_hex(0x303030); // gray // Keypad declarations const byte ROWS = 5; //four rows @@ -126,17 +124,15 @@ byte wakeup_reason; enum Wakeup_reasons{WAKEUP_BY_RESET, WAKEUP_BY_IMU, WAKEUP_BY_KEYPAD}; Preferences preferences; -#define WIFI_SSID "YOUR_WIFI_SSID" -#define WIFI_PASSWORD "YOUR_WIFI_PASSWORD" -#define MQTT_SERVER "YOUR_MQTT_SERVER_IP" lv_obj_t* WifiLabel; WiFiClient espClient; PubSubClient client(espClient); + // Helper Functions ----------------------------------------------------------------------------------------------------------------------- // Set the page indicator scroll position relative to the tabview scroll position -static void store_scroll_value_event_cb(lv_event_t* e){ +void store_scroll_value_event_cb(lv_event_t* e){ float bias = (150.0 + 8.0) / 240.0; int offset = 240 / 2 - 150 / 2 - 8 - 50 - 3; lv_obj_t* screen = lv_event_get_target(e); @@ -144,18 +140,13 @@ static void store_scroll_value_event_cb(lv_event_t* e){ } // Update current device when the tabview page is changes -static void tabview_device_event_cb(lv_event_t* e){ +void tabview_device_event_cb(lv_event_t* e){ currentDevice = lv_tabview_get_tab_act(lv_event_get_target(e)); } -// Slider Event handler -static void bl_slider_event_cb(lv_event_t * e){ - lv_obj_t * slider = lv_event_get_target(e); - backlight_brightness = constrain(lv_slider_get_value(slider), 60, 255); -} // Virtual Keypad Event handler -static void virtualKeypad_event_cb(lv_event_t* e) { +void virtualKeypad_event_cb(lv_event_t* e) { lv_obj_t* target = lv_event_get_target(e); lv_obj_t* cont = lv_event_get_current_target(e); if (target == cont) return; // stop if container was clicked @@ -165,19 +156,15 @@ static void virtualKeypad_event_cb(lv_event_t* e) { } // Apple Key Event handler -static void appleKey_event_cb(lv_event_t* e) { +void appleKey_event_cb(lv_event_t* e) { // Send IR command based on the event user data IrSender.sendSony(50 + (int)e->user_data, 15); Serial.println(50 + (int)e->user_data); } -// Wakeup by IMU Switch Event handler -static void WakeEnableSetting_event_cb(lv_event_t * e){ - wakeupByIMUEnabled = lv_obj_has_state(lv_event_get_target(e), LV_STATE_CHECKED); -} // Smart Home Toggle Event handler -static void smartHomeToggle_event_cb(lv_event_t * e){ +void smartHomeToggle_event_cb(lv_event_t * e){ char payload[8]; if(lv_obj_has_state(lv_event_get_target(e), LV_STATE_CHECKED)) strcpy(payload,"true"); else strcpy(payload,"false"); @@ -187,7 +174,7 @@ static void smartHomeToggle_event_cb(lv_event_t * e){ } // Smart Home Toggle Event handler -static void smartHomeSlider_event_cb(lv_event_t * e){ +void smartHomeSlider_event_cb(lv_event_t * e){ lv_obj_t * slider = lv_event_get_target(e); char payload[8]; dtostrf(lv_slider_get_value(slider), 1, 2, payload); @@ -196,18 +183,6 @@ static void smartHomeSlider_event_cb(lv_event_t * e){ if((int)e->user_data == 2) client.publish("bulb2_setbrightness", payload); } -// Display flushing -void my_disp_flush( lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p ){ - uint32_t w = ( area->x2 - area->x1 + 1 ); - uint32_t h = ( area->y2 - area->y1 + 1 ); - - tft.startWrite(); - tft.setAddrWindow( area->x1, area->y1, w, h ); - tft.pushPixelsDMA( ( uint16_t * )&color_p->full, w * h); - tft.endWrite(); - - lv_disp_flush_ready( disp ); -} // Read the touchpad void my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data){ @@ -316,12 +291,7 @@ void configIMUInterrupts() // Enter Sleep Mode void enterSleep(){ - // Save settings to internal flash memory - preferences.putBool("wkpByIMU", wakeupByIMUEnabled); - preferences.putUChar("blBrightness", backlight_brightness); - preferences.putUChar("currentDevice", currentDevice); - if(!preferences.getBool("alreadySetUp")) preferences.putBool("alreadySetUp", true); - preferences.end(); + Serial.println("enterSleep called"); // Configure IMU uint8_t intDataRead; @@ -330,18 +300,22 @@ void enterSleep(){ IMU.readRegister(&intDataRead, LIS3DH_INT1_SRC);//really clear interrupt #ifdef ENABLE_WIFI + wifihandler.turnOff(); // Power down modem - WiFi.disconnect(); - WiFi.mode(WIFI_OFF); #endif + display.turnOff(); + + // Save settings to internal flash memory + preferences.putBool("wkpByIMU", wakeupByIMUEnabled); + preferences.putUChar("currentDevice", currentDevice); + if(!preferences.getBool("alreadySetUp")) preferences.putBool("alreadySetUp", true); + preferences.end(); // Prepare IO states digitalWrite(LCD_DC, LOW); // LCD control signals off digitalWrite(LCD_CS, LOW); digitalWrite(LCD_MOSI, LOW); digitalWrite(LCD_SCK, LOW); - digitalWrite(LCD_EN, HIGH); // LCD logic off - digitalWrite(LCD_BL, HIGH); // LCD backlight off pinMode(CRG_STAT, INPUT); // Disable Pull-Up digitalWrite(IR_VCC, LOW); // IR Receiver off @@ -363,10 +337,6 @@ void enterSleep(){ gpio_hold_en((gpio_num_t)SW_5); // Force display pins to high impedance // Without this the display might not wake up from sleep - pinMode(LCD_BL, INPUT); - pinMode(LCD_EN, INPUT); - gpio_hold_en((gpio_num_t)LCD_BL); - gpio_hold_en((gpio_num_t)LCD_EN); gpio_deep_sleep_hold_en(); esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK, ESP_EXT1_WAKEUP_ANY_HIGH); @@ -376,24 +346,6 @@ void enterSleep(){ esp_deep_sleep_start(); } -#ifdef ENABLE_WIFI -// WiFi status event -void WiFiEvent(WiFiEvent_t event){ - //Serial.printf("[WiFi-event] event: %d\n", event); - if(event == ARDUINO_EVENT_WIFI_STA_GOT_IP){ - client.setServer(MQTT_SERVER, 1883); // MQTT initialization - client.connect("OMOTE"); // Connect using a client id - } - // Set status bar icon based on WiFi status - if(event == ARDUINO_EVENT_WIFI_STA_GOT_IP || event == ARDUINO_EVENT_WIFI_STA_GOT_IP6){ - lv_label_set_text(WifiLabel, LV_SYMBOL_WIFI); - } - else{ - lv_label_set_text(WifiLabel, ""); - } -} -#endif - // Setup ---------------------------------------------------------------------------------------------------------------------------------- void setup() { @@ -423,9 +375,7 @@ void setup() { pinMode(SW_D, INPUT); pinMode(SW_E, INPUT); - // Power Pin Definition - pinMode(CRG_STAT, INPUT_PULLUP); - pinMode(ADC_BAT, INPUT); + battery.setup(); // IR Pin Definition pinMode(IR_RX, INPUT); @@ -434,12 +384,6 @@ void setup() { digitalWrite(IR_LED, HIGH); // HIGH off - LOW on digitalWrite(IR_VCC, LOW); // HIGH on - LOW off - // LCD Pin Definition - pinMode(LCD_EN, OUTPUT); - digitalWrite(LCD_EN, HIGH); - pinMode(LCD_BL, OUTPUT); - digitalWrite(LCD_BL, HIGH); - // Other Pin Definition pinMode(ACC_INT, INPUT); pinMode(USER_LED, OUTPUT); @@ -455,485 +399,30 @@ void setup() { gpio_hold_dis((gpio_num_t)LCD_BL); gpio_deep_sleep_hold_dis(); - // Configure the backlight PWM - // Manual setup because ledcSetup() briefly turns on the backlight - ledc_channel_config_t ledc_channel_left; - ledc_channel_left.gpio_num = (gpio_num_t)LCD_BL; - ledc_channel_left.speed_mode = LEDC_HIGH_SPEED_MODE; - ledc_channel_left.channel = LEDC_CHANNEL_5; - ledc_channel_left.intr_type = LEDC_INTR_DISABLE; - ledc_channel_left.timer_sel = LEDC_TIMER_1; - ledc_channel_left.flags.output_invert = 1; // Can't do this with ledcSetup() - ledc_channel_left.duty = 0; - - ledc_timer_config_t ledc_timer; - ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE; - ledc_timer.duty_resolution = LEDC_TIMER_8_BIT; - ledc_timer.timer_num = LEDC_TIMER_1; - ledc_timer.freq_hz = 640; - - ledc_channel_config(&ledc_channel_left); - ledc_timer_config(&ledc_timer); + Serial.begin(115200); // --- Startup --- - Serial.begin(115200); // Restore settings from internal flash memory preferences.begin("settings", false); if(preferences.getBool("alreadySetUp")){ wakeupByIMUEnabled = preferences.getBool("wkpByIMU"); - backlight_brightness = preferences.getUChar("blBrightness"); currentDevice = preferences.getUChar("currentDevice"); } + #ifdef ENABLE_WIFI + wifihandler.begin(); + #endif + + display.setup(); // Setup TFT - // Slowly charge the VSW voltage to prevent a brownout - // Workaround for hardware rev 1! - for(int i = 0; i < 100; i++){ - digitalWrite(LCD_EN, HIGH); // LCD Logic off - delayMicroseconds(1); - digitalWrite(LCD_EN, LOW); // LCD Logic on - } - - delay(100); // Wait for the LCD driver to power on - tft.init(); - tft.initDMA(); - tft.setRotation(0); - tft.fillScreen(TFT_BLACK); - tft.setSwapBytes(true); - - // Setup touchscreen - Wire.begin(SDA, SCL, 400000); // Configure i2c pins and set frequency to 400kHz - touch.begin(128); // Initialize touchscreen and set sensitivity threshold - - // Setup LVGL - lv_init(); - lv_disp_draw_buf_init( &draw_buf, bufA, bufB, screenWidth * screenHeight / 10 ); - - // Initialize the display driver - static lv_disp_drv_t disp_drv; - lv_disp_drv_init( &disp_drv ); - disp_drv.hor_res = screenWidth; - disp_drv.ver_res = screenHeight; - disp_drv.flush_cb = my_disp_flush; - disp_drv.draw_buf = &draw_buf; - lv_disp_drv_register( &disp_drv ); - - // Initialize the touchscreen driver - static lv_indev_drv_t indev_drv; - lv_indev_drv_init( &indev_drv ); - indev_drv.type = LV_INDEV_TYPE_POINTER; - indev_drv.read_cb = my_touchpad_read; - lv_indev_drv_register( &indev_drv ); - // --- LVGL UI Configuration --- - - // Set the background color - lv_obj_set_style_bg_color(lv_scr_act(), lv_color_black(), LV_PART_MAIN); - - // Setup a scrollable tabview for devices and settings - lv_obj_t* tabview; - tabview = lv_tabview_create(lv_scr_act(), LV_DIR_TOP, 0); // Hide tab labels by setting their height to 0 - lv_obj_set_style_bg_color(tabview, lv_color_black(), LV_PART_MAIN); - lv_obj_set_size(tabview, screenWidth, 270); // 270 = screenHeight(320) - panel(30) - statusbar(20) - lv_obj_align(tabview, LV_ALIGN_TOP_MID, 0, 20); - - // Add 4 tabs (names are irrelevant since the labels are hidden) - lv_obj_t* tab1 = lv_tabview_add_tab(tabview, "Settings"); - lv_obj_t* tab2 = lv_tabview_add_tab(tabview, "Technisat"); - lv_obj_t* tab3 = lv_tabview_add_tab(tabview, "Apple TV"); - lv_obj_t* tab4 = lv_tabview_add_tab(tabview, "Smart Home"); - - // Configure number button grid - static lv_coord_t col_dsc[] = { LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST }; // equal x distribution - static lv_coord_t row_dsc[] = { 52, 52, 52, 52, LV_GRID_TEMPLATE_LAST }; // manual y distribution to compress the grid a bit - - // Create a container with grid for tab2 - lv_obj_set_style_pad_all(tab2, 0, LV_PART_MAIN); - lv_obj_t* cont = lv_obj_create(tab2); - lv_obj_set_style_shadow_width(cont, 0, LV_PART_MAIN); - lv_obj_set_style_bg_color(cont, lv_color_black(), LV_PART_MAIN); - lv_obj_set_style_border_width(cont, 0, LV_PART_MAIN); - lv_obj_set_style_grid_column_dsc_array(cont, col_dsc, 0); - lv_obj_set_style_grid_row_dsc_array(cont, row_dsc, 0); - lv_obj_set_size(cont, 240, 270); - lv_obj_set_layout(cont, LV_LAYOUT_GRID); - lv_obj_align(cont, LV_ALIGN_TOP_MID, 0, 0); - lv_obj_set_style_radius(cont, 0, LV_PART_MAIN); - - lv_obj_t* buttonLabel; - lv_obj_t* obj; - - // Iterate through grid buttons configure them - for (int i = 0; i < 12; i++) { - uint8_t col = i % 3; - uint8_t row = i / 3; - // Create the button object - if ((row == 3) && ((col == 0) || (col == 2))) continue; // Do not create a complete fourth row, only a 0 button - obj = lv_btn_create(cont); - lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_STRETCH, col, 1, LV_GRID_ALIGN_STRETCH, row, 1); - lv_obj_set_style_bg_color(obj, color_primary, LV_PART_MAIN); - lv_obj_set_style_radius(obj, 14, LV_PART_MAIN); - lv_obj_set_style_shadow_color(obj, lv_color_hex(0x404040), LV_PART_MAIN); - lv_obj_add_flag(obj, LV_OBJ_FLAG_EVENT_BUBBLE); // Clicking a button causes a event in its container - // Create Labels for each button - buttonLabel = lv_label_create(obj); - if(i < 9){ - lv_label_set_text_fmt(buttonLabel, std::to_string(i+1).c_str(), col, row); - lv_obj_set_user_data(obj, (void*)i); // Add user data so we can identify which button caused the container event - } - else{ - lv_label_set_text_fmt(buttonLabel, "0", col, row); - lv_obj_set_user_data(obj, (void*)9); - } - lv_obj_set_style_text_font(buttonLabel, &lv_font_montserrat_24, LV_PART_MAIN); - lv_obj_center(buttonLabel); - } - // Create a shared event for all button inside container - lv_obj_add_event_cb(cont, virtualKeypad_event_cb, LV_EVENT_CLICKED, NULL); - - - // Add content to the Apple TV tab (3) - // Add a nice apple tv logo - lv_obj_t* appleImg = lv_img_create(tab3); - lv_img_set_src(appleImg, &appleTvIcon); - lv_obj_align(appleImg, LV_ALIGN_CENTER, 0, -60); - // create two buttons and add their icons accordingly - lv_obj_t* button = lv_btn_create(tab3); - lv_obj_align(button, LV_ALIGN_BOTTOM_LEFT, 10, 0); - lv_obj_set_size(button, 60, 60); - lv_obj_set_style_radius(button, 30, LV_PART_MAIN); - lv_obj_set_style_bg_color(button, color_primary, LV_PART_MAIN); - lv_obj_add_event_cb(button, appleKey_event_cb, LV_EVENT_CLICKED, (void*)1); - - appleImg = lv_img_create(button); - lv_img_set_src(appleImg, &appleBackIcon); - lv_obj_set_style_img_recolor(appleImg, lv_color_white(), LV_PART_MAIN); - lv_obj_set_style_img_recolor_opa(appleImg, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_align(appleImg, LV_ALIGN_CENTER, -3, 0); - - button = lv_btn_create(tab3); - lv_obj_align(button, LV_ALIGN_BOTTOM_RIGHT, -10, 0); - lv_obj_set_size(button, 60, 60); - lv_obj_set_style_radius(button, 30, LV_PART_MAIN); - lv_obj_set_style_bg_color(button, color_primary, LV_PART_MAIN); - lv_obj_add_event_cb(button, appleKey_event_cb, LV_EVENT_CLICKED, (void*)2); - - appleImg = lv_img_create(button); - lv_img_set_src(appleImg, &appleDisplayIcon); - lv_obj_set_style_img_recolor(appleImg, lv_color_white(), LV_PART_MAIN); - lv_obj_set_style_img_recolor_opa(appleImg, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_align(appleImg, LV_ALIGN_CENTER, 0, 0); - - - - - // Add content to the settings tab - // With a flex layout, setting groups/boxes will position themselves automatically - lv_obj_set_layout(tab1, LV_LAYOUT_FLEX); - lv_obj_set_flex_flow(tab1, LV_FLEX_FLOW_COLUMN); - lv_obj_set_scrollbar_mode(tab1, LV_SCROLLBAR_MODE_ACTIVE); - - // Add a label, then a box for the display settings - lv_obj_t* menuLabel = lv_label_create(tab1); - lv_label_set_text(menuLabel, "Display"); - - lv_obj_t* menuBox = lv_obj_create(tab1); - lv_obj_set_size(menuBox, lv_pct(100), 109); - lv_obj_set_style_bg_color(menuBox, color_primary, LV_PART_MAIN); - lv_obj_set_style_border_width(menuBox, 0, LV_PART_MAIN); - - lv_obj_t* brightnessIcon = lv_img_create(menuBox); - lv_img_set_src(brightnessIcon, &low_brightness); - lv_obj_set_style_img_recolor(brightnessIcon, lv_color_white(), LV_PART_MAIN); - lv_obj_set_style_img_recolor_opa(brightnessIcon, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_align(brightnessIcon, LV_ALIGN_TOP_LEFT, 0, 0); - lv_obj_t* slider = lv_slider_create(menuBox); - lv_slider_set_range(slider, 60, 255); - lv_obj_set_style_bg_color(slider, lv_color_white(), LV_PART_KNOB); - lv_obj_set_style_bg_opa(slider, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_set_style_bg_color(slider, lv_color_lighten(color_primary, 50), LV_PART_MAIN); - lv_slider_set_value(slider, backlight_brightness, LV_ANIM_OFF); - lv_obj_set_size(slider, lv_pct(66), 10); - lv_obj_align(slider, LV_ALIGN_TOP_MID, 0, 3); - brightnessIcon = lv_img_create(menuBox); - lv_img_set_src(brightnessIcon, &high_brightness); - lv_obj_set_style_img_recolor(brightnessIcon, lv_color_white(), LV_PART_MAIN); - lv_obj_set_style_img_recolor_opa(brightnessIcon, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_align(brightnessIcon, LV_ALIGN_TOP_RIGHT, 0, -1); - lv_obj_add_event_cb(slider, bl_slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL); - - menuLabel = lv_label_create(menuBox); - lv_label_set_text(menuLabel, "Lift to Wake"); - lv_obj_align(menuLabel, LV_ALIGN_TOP_LEFT, 0, 32); - lv_obj_t* wakeToggle = lv_switch_create(menuBox); - lv_obj_set_size(wakeToggle, 40, 22); - lv_obj_align(wakeToggle, LV_ALIGN_TOP_RIGHT, 0, 29); - lv_obj_set_style_bg_color(wakeToggle, lv_color_hex(0x505050), LV_PART_MAIN); - lv_obj_add_event_cb(wakeToggle, WakeEnableSetting_event_cb, LV_EVENT_VALUE_CHANGED, NULL); - if(wakeupByIMUEnabled) lv_obj_add_state(wakeToggle, LV_STATE_CHECKED); // set default state - - menuLabel = lv_label_create(menuBox); - lv_label_set_text(menuLabel, "Timeout"); - lv_obj_align(menuLabel, LV_ALIGN_TOP_LEFT, 0, 64); - lv_obj_t* drop = lv_dropdown_create(menuBox); - lv_dropdown_set_options(drop, "10s\n" - "30s\n" - "1m\n" - "3m"); - lv_obj_align(drop, LV_ALIGN_TOP_RIGHT, 0, 61); - lv_obj_set_size(drop, 70, 22); - //lv_obj_set_style_text_font(drop, &lv_font_montserrat_12, LV_PART_MAIN); - //lv_obj_set_style_text_font(lv_dropdown_get_list(drop), &lv_font_montserrat_12, LV_PART_MAIN); - lv_obj_set_style_pad_top(drop, 1, LV_PART_MAIN); - lv_obj_set_style_bg_color(drop, color_primary, LV_PART_MAIN); - lv_obj_set_style_bg_color(lv_dropdown_get_list(drop), color_primary, LV_PART_MAIN); - lv_obj_set_style_border_width(lv_dropdown_get_list(drop), 1, LV_PART_MAIN); - lv_obj_set_style_border_color(lv_dropdown_get_list(drop), lv_color_hex(0x505050), LV_PART_MAIN); - - // Add another label, then a settings box for WiFi - menuLabel = lv_label_create(tab1); - lv_label_set_text(menuLabel, "Wi-Fi"); - menuBox = lv_obj_create(tab1); - lv_obj_set_size(menuBox, lv_pct(100), 80); - lv_obj_set_style_bg_color(menuBox, color_primary, LV_PART_MAIN); - lv_obj_set_style_border_width(menuBox, 0, LV_PART_MAIN); - menuLabel = lv_label_create(menuBox); - lv_label_set_text(menuLabel, "Network"); - menuLabel = lv_label_create(menuBox); - lv_label_set_text(menuLabel, LV_SYMBOL_RIGHT); - lv_obj_align(menuLabel, LV_ALIGN_TOP_RIGHT, 0, 0); - menuLabel = lv_label_create(menuBox); - lv_label_set_text(menuLabel, "Password"); - lv_obj_align(menuLabel, LV_ALIGN_TOP_LEFT, 0, 32); - menuLabel = lv_label_create(menuBox); - lv_label_set_text(menuLabel, LV_SYMBOL_RIGHT); - lv_obj_align(menuLabel, LV_ALIGN_TOP_RIGHT, 0, 32); - - // Another setting for the battery - menuLabel = lv_label_create(tab1); - lv_label_set_text(menuLabel, "Battery"); - menuBox = lv_obj_create(tab1); - lv_obj_set_size(menuBox, lv_pct(100), 125); - lv_obj_set_style_bg_color(menuBox, color_primary, LV_PART_MAIN); - lv_obj_set_style_border_width(menuBox, 0, LV_PART_MAIN); - - - - - - // Add content to the smart home tab (4) - lv_obj_set_layout(tab4, LV_LAYOUT_FLEX); - lv_obj_set_flex_flow(tab4, LV_FLEX_FLOW_COLUMN); - lv_obj_set_scrollbar_mode(tab4, LV_SCROLLBAR_MODE_ACTIVE); - - // Add a label, then a box for the light controls - menuLabel = lv_label_create(tab4); - lv_label_set_text(menuLabel, "Living Room"); - - menuBox = lv_obj_create(tab4); - lv_obj_set_size(menuBox, lv_pct(100), 79); - lv_obj_set_style_bg_color(menuBox, color_primary, LV_PART_MAIN); - lv_obj_set_style_border_width(menuBox, 0, LV_PART_MAIN); - - lv_obj_t* bulbIcon = lv_img_create(menuBox); - lv_img_set_src(bulbIcon, &lightbulb); - lv_obj_set_style_img_recolor(bulbIcon, lv_color_white(), LV_PART_MAIN); - lv_obj_set_style_img_recolor_opa(bulbIcon, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_align(bulbIcon, LV_ALIGN_TOP_LEFT, 0, 0); - - menuLabel = lv_label_create(menuBox); - lv_label_set_text(menuLabel, "Floor Lamp"); - lv_obj_align(menuLabel, LV_ALIGN_TOP_LEFT, 22, 3); - lv_obj_t* lightToggleA = lv_switch_create(menuBox); - lv_obj_set_size(lightToggleA, 40, 22); - lv_obj_align(lightToggleA, LV_ALIGN_TOP_RIGHT, 0, 0); - lv_obj_set_style_bg_color(lightToggleA, lv_color_lighten(color_primary, 50), LV_PART_MAIN); - lv_obj_set_style_bg_color(lightToggleA, color_primary, LV_PART_INDICATOR); - lv_obj_add_event_cb(lightToggleA, smartHomeToggle_event_cb, LV_EVENT_VALUE_CHANGED, (void*)1); - - slider = lv_slider_create(menuBox); - lv_slider_set_range(slider, 0, 100); - lv_obj_set_style_bg_color(slider, lv_color_lighten(lv_color_black(), 30), LV_PART_INDICATOR); - lv_obj_set_style_bg_grad_color(slider, lv_color_lighten(lv_palette_main(LV_PALETTE_AMBER), 180), LV_PART_INDICATOR); - lv_obj_set_style_bg_grad_dir(slider, LV_GRAD_DIR_HOR, LV_PART_INDICATOR); - lv_obj_set_style_bg_color(slider, lv_color_white(), LV_PART_KNOB); - lv_obj_set_style_bg_opa(slider, 255, LV_PART_MAIN); - lv_obj_set_style_bg_color(slider, lv_color_lighten(color_primary, 50), LV_PART_MAIN); - lv_slider_set_value(slider, 255, LV_ANIM_OFF); - lv_obj_set_size(slider, lv_pct(90), 10); - lv_obj_align(slider, LV_ALIGN_TOP_MID, 0, 37); - lv_obj_add_event_cb(slider, smartHomeSlider_event_cb, LV_EVENT_VALUE_CHANGED, (void*)1); - - // Add another menu box for a second appliance - menuBox = lv_obj_create(tab4); - lv_obj_set_size(menuBox, lv_pct(100), 79); - lv_obj_set_style_bg_color(menuBox, color_primary, LV_PART_MAIN); - lv_obj_set_style_border_width(menuBox, 0, LV_PART_MAIN); - - bulbIcon = lv_img_create(menuBox); - lv_img_set_src(bulbIcon, &lightbulb); - lv_obj_set_style_img_recolor(bulbIcon, lv_color_white(), LV_PART_MAIN); - lv_obj_set_style_img_recolor_opa(bulbIcon, LV_OPA_COVER, LV_PART_MAIN); - lv_obj_align(bulbIcon, LV_ALIGN_TOP_LEFT, 0, 0); - - menuLabel = lv_label_create(menuBox); - lv_label_set_text(menuLabel, "Ceiling Light"); - lv_obj_align(menuLabel, LV_ALIGN_TOP_LEFT, 22, 3); - lv_obj_t* lightToggleB = lv_switch_create(menuBox); - lv_obj_set_size(lightToggleB, 40, 22); - lv_obj_align(lightToggleB, LV_ALIGN_TOP_RIGHT, 0, 0); - lv_obj_set_style_bg_color(lightToggleB, lv_color_lighten(color_primary, 50), LV_PART_MAIN); - lv_obj_set_style_bg_color(lightToggleB, color_primary, LV_PART_INDICATOR); - lv_obj_add_event_cb(lightToggleB, smartHomeToggle_event_cb, LV_EVENT_VALUE_CHANGED, (void*)2); - - slider = lv_slider_create(menuBox); - lv_slider_set_range(slider, 0, 100); - lv_obj_set_style_bg_color(slider, lv_color_lighten(lv_color_black(), 30), LV_PART_INDICATOR); - lv_obj_set_style_bg_grad_color(slider, lv_color_lighten(lv_palette_main(LV_PALETTE_AMBER), 180), LV_PART_INDICATOR); - lv_obj_set_style_bg_grad_dir(slider, LV_GRAD_DIR_HOR, LV_PART_INDICATOR); - lv_obj_set_style_bg_color(slider, lv_color_white(), LV_PART_KNOB); - lv_obj_set_style_bg_opa(slider, 255, LV_PART_MAIN); - lv_obj_set_style_bg_color(slider, lv_color_lighten(color_primary, 50), LV_PART_MAIN); - lv_slider_set_value(slider, 255, LV_ANIM_OFF); - lv_obj_set_size(slider, lv_pct(90), 10); - lv_obj_align(slider, LV_ALIGN_TOP_MID, 0, 37); - lv_obj_add_event_cb(slider, smartHomeSlider_event_cb, LV_EVENT_VALUE_CHANGED, (void*)2); - - - // Add another room (empty for now) - menuLabel = lv_label_create(tab4); - lv_label_set_text(menuLabel, "Kitchen"); - - menuBox = lv_obj_create(tab4); - lv_obj_set_size(menuBox, lv_pct(100), 79); - lv_obj_set_style_bg_color(menuBox, color_primary, LV_PART_MAIN); - lv_obj_set_style_border_width(menuBox, 0, LV_PART_MAIN); - - - // Set current page according to the current Device - lv_tabview_set_act(tabview, currentDevice, LV_ANIM_OFF); - - - // Create a page indicator - panel = lv_obj_create(lv_scr_act()); - lv_obj_clear_flag(panel, LV_OBJ_FLAG_CLICKABLE); // This indicator will not be clickable - lv_obj_set_size(panel, screenWidth, 30); - lv_obj_set_flex_flow(panel, LV_FLEX_FLOW_ROW); - lv_obj_align(panel, LV_ALIGN_BOTTOM_MID, 0, 0); - lv_obj_set_scrollbar_mode(panel, LV_SCROLLBAR_MODE_OFF); - // This small hidden button enables the page indicator to scroll further - lv_obj_t* btn = lv_btn_create(panel); - lv_obj_set_size(btn, 50, lv_pct(100)); - lv_obj_set_style_shadow_width(btn, 0, LV_PART_MAIN); - lv_obj_set_style_opa(btn, LV_OPA_TRANSP, LV_PART_MAIN); - // Create actual (non-clickable) buttons for every tab - btn = lv_btn_create(panel); - lv_obj_clear_flag(btn, LV_OBJ_FLAG_CLICKABLE); - lv_obj_set_size(btn, 150, lv_pct(100)); - lv_obj_t* label = lv_label_create(btn); - lv_label_set_text_fmt(label, "Settings"); - lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); - lv_obj_set_style_shadow_width(btn, 0, LV_PART_MAIN); - lv_obj_set_style_bg_color(btn, color_primary, LV_PART_MAIN); - - btn = lv_btn_create(panel); - lv_obj_clear_flag(btn, LV_OBJ_FLAG_CLICKABLE); - lv_obj_set_size(btn, 150, lv_pct(100)); - label = lv_label_create(btn); - lv_label_set_text_fmt(label, "Technisat"); - lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); - lv_obj_set_style_shadow_width(btn, 0, LV_PART_MAIN); - lv_obj_set_style_bg_color(btn, color_primary, LV_PART_MAIN); - - btn = lv_btn_create(panel); - lv_obj_clear_flag(btn, LV_OBJ_FLAG_CLICKABLE); - lv_obj_set_size(btn, 150, lv_pct(100)); - label = lv_label_create(btn); - lv_label_set_text_fmt(label, "Apple TV"); - lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); - lv_obj_set_style_shadow_width(btn, 0, LV_PART_MAIN); - lv_obj_set_style_bg_color(btn, color_primary, LV_PART_MAIN); - - btn = lv_btn_create(panel); - lv_obj_clear_flag(btn, LV_OBJ_FLAG_CLICKABLE); - lv_obj_set_size(btn, 150, lv_pct(100)); - label = lv_label_create(btn); - lv_label_set_text_fmt(label, "Smart Home"); - lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); - lv_obj_set_style_shadow_width(btn, 0, LV_PART_MAIN); - lv_obj_set_style_bg_color(btn, color_primary, LV_PART_MAIN); - // This small hidden button enables the page indicator to scroll further - btn = lv_btn_create(panel); - lv_obj_set_size(btn, 50, lv_pct(100)); - lv_obj_set_style_shadow_width(btn, 0, LV_PART_MAIN); - lv_obj_set_style_opa(btn, LV_OPA_TRANSP, LV_PART_MAIN); - - // Make the indicator scroll together with the tabs by creating a scroll event - lv_obj_add_event_cb(lv_tabview_get_content(tabview), store_scroll_value_event_cb, LV_EVENT_SCROLL, NULL); - lv_obj_add_event_cb(tabview, tabview_device_event_cb, LV_EVENT_VALUE_CHANGED, NULL); - // Initialize scroll position for the indicator - lv_event_send(lv_tabview_get_content(tabview), LV_EVENT_SCROLL, NULL); - - // Style the panel background - static lv_style_t style_btn; - lv_style_init(&style_btn); - lv_style_set_pad_all(&style_btn, 3); - lv_style_set_border_width(&style_btn, 0); - lv_style_set_bg_opa(&style_btn, LV_OPA_TRANSP); - lv_obj_add_style(panel, &style_btn, 0); - - // Make the indicator fade out at the sides using gradient bitmaps - lv_obj_t* img1 = lv_img_create(lv_scr_act()); - lv_img_set_src(img1, &gradientLeft); - lv_obj_align(img1, LV_ALIGN_BOTTOM_LEFT, 0, 0); - lv_obj_set_size(img1, 30, 30); // stretch the 1-pixel high image to 30px - lv_obj_t* img2 = lv_img_create(lv_scr_act()); - lv_img_set_src(img2, &gradientRight); - lv_obj_align(img2, LV_ALIGN_BOTTOM_RIGHT, 0, 0); - lv_obj_set_size(img2, 30, 30); - - - // Create a status bar - lv_obj_t* statusbar = lv_btn_create(lv_scr_act()); - lv_obj_set_size(statusbar, 240, 20); - lv_obj_set_style_shadow_width(statusbar, 0, LV_PART_MAIN); - lv_obj_set_style_bg_color(statusbar, lv_color_black(), LV_PART_MAIN); - lv_obj_set_style_radius(statusbar, 0, LV_PART_MAIN); - lv_obj_align(statusbar, LV_ALIGN_TOP_MID, 0, 0); - - WifiLabel = lv_label_create(statusbar); - lv_label_set_text(WifiLabel, ""); - lv_obj_align(WifiLabel, LV_ALIGN_LEFT_MID, -8, 0); - lv_obj_set_style_text_font(WifiLabel, &lv_font_montserrat_12, LV_PART_MAIN); - - - - - - objBattPercentage = lv_label_create(statusbar); - lv_label_set_text(objBattPercentage, ""); - lv_obj_align(objBattPercentage, LV_ALIGN_RIGHT_MID, -16, 0); - lv_obj_set_style_text_font(objBattPercentage, &lv_font_montserrat_12, LV_PART_MAIN); - - objBattIcon = lv_label_create(statusbar); - lv_label_set_text(objBattIcon, LV_SYMBOL_BATTERY_EMPTY); - lv_obj_align(objBattIcon, LV_ALIGN_RIGHT_MID, 8, 0); - lv_obj_set_style_text_font(objBattIcon, &lv_font_montserrat_16, LV_PART_MAIN); - + display.setup_ui(); // --- End of LVGL configuration --- - #ifdef ENABLE_WIFI - // Setup WiFi - WiFi.setHostname("OMOTE"); //define hostname - WiFi.onEvent(WiFiEvent); - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); - WiFi.setSleep(true); - #endif - // Setup IMU IMU.settings.accelSampleRate = 50; //Hz. Can be: 0,1,10,25,50,100,200,400,1600,5000 Hz IMU.settings.accelRange = 2; //Max G force readable. Can be: 2, 4, 8, 16 @@ -965,19 +454,7 @@ void setup() { void loop() { - // Update Backlight brightness - static int fadeInTimer = millis(); // fadeInTimer = time after setup - if(millis() < fadeInTimer + backlight_brightness){ // Fade in the backlight brightness - ledcWrite(5, millis()-fadeInTimer); - } - else { // Dim Backlight before entering standby - if(standbyTimer < 2000) ledcWrite(5, 85); // Backlight dim - else ledcWrite(5, backlight_brightness); // Backlight on - } - - // Update LVGL UI - lv_timer_handler(); - + display.update(); // Blink debug LED at 1 Hz digitalWrite(USER_LED, millis() % 1000 > 500); @@ -992,9 +469,11 @@ void loop() { IMUTaskTimer = millis(); } +#if 1 // Update battery stats at 1Hz static unsigned long batteryTaskTimer = millis() + 1000; // add 1s to start immediately if(millis() - batteryTaskTimer >= 1000){ +#if 0 battery_voltage = analogRead(ADC_BAT)*2*3300/4095 + 350; // 350mV ADC offset battery_percentage = constrain(map(battery_voltage, 3700, 4200, 0, 100), 0, 100); batteryTaskTimer = millis(); @@ -1013,7 +492,12 @@ void loop() { else if(battery_percentage > 25) lv_label_set_text(objBattIcon, LV_SYMBOL_BATTERY_1); else lv_label_set_text(objBattIcon, LV_SYMBOL_BATTERY_EMPTY); } +#else + batteryTaskTimer = millis(); + battery.update(); +#endif } + #endif // Keypad Handling customKeypad.getKey(); // Populate key list @@ -1035,9 +519,4 @@ void loop() { // //tft.drawString(String(results.command) + " ", 80, 90, 1); // IrReceiver.resume(); // Enable receiving of the next value //} - - - - - } \ No newline at end of file diff --git a/Platformio/src/wifiHandler/wifiHandler.cpp b/Platformio/src/wifiHandler/wifiHandler.cpp new file mode 100644 index 0000000..89a43c7 --- /dev/null +++ b/Platformio/src/wifiHandler/wifiHandler.cpp @@ -0,0 +1,152 @@ +/** + * @file wifiHandler.cpp + * @author Thomas Bittner + * @brief Wifi handler is a wrapper for the arduino wifi function. This wrapper provides handling of wifi credetials and + * storing it over reset. + * @version 0.1 + * @date 2023-08-01 + * + * @copyright Copyright (c) 2023 + * + */ + +#include "wifiHandler.hpp" +#include +#include +#include "display.hpp" +#include + +/*TODO: rework to not use global variables*/ +extern wifiHandler wifihandler; +extern Preferences preferences; +extern Display display; +extern PubSubClient client; +const char* temporary_ssid; +const char* temporary_password; + +#define MQTT_SERVER "YOUR_MQTT_SERVER_IP" +// WiFi status event +void WiFiEvent(WiFiEvent_t event){ + int no_networks = 0; + switch (event) + { + case ARDUINO_EVENT_WIFI_SCAN_DONE: + Serial.println("WIFI scan done\n"); + no_networks = WiFi.scanComplete(); + if (no_networks < 0) + { + Serial.println("Scan failed"); + } + else + { + display.clear_wifi_networks(); + Serial.print(no_networks); + Serial.print(" found\n"); + String SSIDS[no_networks]; + int RSSIS[no_networks]; + for (int i = 0; i < no_networks; i++) + { + SSIDS[i] = WiFi.SSID(i); + RSSIS[i] = WiFi.RSSI(i); + } + display.add_wifi_networks(SSIDS, RSSIS, no_networks); + } + break; + case ARDUINO_EVENT_WIFI_STA_GOT_IP: + client.setServer(MQTT_SERVER, 1883); // MQTT initialization + client.connect("OMOTE"); // Connect using a client id + case ARDUINO_EVENT_WIFI_STA_GOT_IP6: + display.update_wifi(true); + wifihandler.update_credetials(temporary_ssid, temporary_password); + break; + case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: + case ARDUINO_EVENT_WIFI_STA_LOST_IP: + case ARDUINO_EVENT_WIFI_STA_STOP: + display.update_wifi(false); + default: + break; + } +} +wifiHandler::wifiHandler() +{ + this->password[0] = '\0'; + this->SSID[0] = '\0'; +} + + +void wifiHandler::update_credetials(const char* temporary_ssid, const char* temporary_password) +{ + if (strcmp(temporary_password, this->password) != 0 || strcmp(temporary_ssid, this->SSID) != 0) + { + strcpy(this->password, temporary_password); + strcpy(this->SSID, temporary_ssid); + + String tempString = this->password; + preferences.putString("password", tempString); + tempString = this->SSID; + preferences.putString("SSID", tempString); + } +} + +void wifiHandler::scan() +{ + WiFi.scanNetworks(true); +} + +void wifiHandler::begin() +{ + WiFi.setHostname("OMOTE"); + WiFi.mode(WIFI_STA); + WiFi.onEvent(WiFiEvent); + + String ssid = preferences.getString("SSID"); + String password = preferences.getString("password"); + + /* If the SSID is not empty, there was a value stored in the preferences and we try to use it.*/ + if (!ssid.isEmpty()) + { + Serial.print("Connecting to wifi "); + Serial.println(ssid); + strcpy(this->SSID, ssid.c_str()); + strcpy(this->password, password.c_str()); + this->connect(this->SSID, this->password); + } + else + { + Serial.println("no SSID or password stored"); + /*Set first character to \0 indicates an empty string*/ + this->SSID[0] = '\0'; + this->password[0] = '\0'; + WiFi.disconnect(); + } + + WiFi.setSleep(true); +} + +void wifiHandler::connect(const char* SSID, const char* password) +{ + temporary_password = password; + temporary_ssid = SSID; + WiFi.begin(SSID, password); +} + +void wifiHandler::turnOff() +{ + WiFi.disconnect(); + WiFi.mode(WIFI_OFF); +} + +bool wifiHandler::isConnected() +{ + return WiFi.isConnected(); +} + +char* wifiHandler::getSSID() +{ + return this->SSID; +} + +String wifiHandler::getIP() +{ + return WiFi.localIP().toString(); +} \ No newline at end of file diff --git a/Platformio/src/wifiHandler/wifiHandler.hpp b/Platformio/src/wifiHandler/wifiHandler.hpp new file mode 100644 index 0000000..94469f5 --- /dev/null +++ b/Platformio/src/wifiHandler/wifiHandler.hpp @@ -0,0 +1,83 @@ +#include + +class wifiHandler { + public: + wifiHandler(); + /** + * @brief Function to initialize the wifi handler + * + */ + void begin(); + + /** + * @brief Connect to the wifi using the provided credetials + * + * @param SSID + * @param password + */ + void connect(const char* SSID, const char* password); + + /** + * @brief Function to disconnect from the network + * + */ + void disconnect(); + + /** + * @brief Function to determine wether or not we are connected to a network + * + * @return true Device is connected to wifi network + * @return false Device is not connected to wifi network + */ + bool isConnected(); + + /** + * @brief Function to turn off wifi + * + */ + void turnOff(); + + /** + * @brief function to trigger asynchronous scan for wifi networks + * + */ + void scan(); + + /** + * @brief Function to get SSID of the currently connected wifi network + * + * @return char* SSID of the currently connected network + */ + char* getSSID(); + + /** + * @brief Function to update the wifi credentials. This function is called in the wifi event callback function + * after a connection is established. Only then is the new credentials stored and the old stored credentials + * overwritten. + * + * @param temporary_ssid + * @param temporary_password + */ + void update_credetials(const char* temporary_ssid, const char* temporary_password); + + /** + * @brief Function to get the IP address of this device + * + * @return String IP Address of the device + */ + String getIP(); + private: + + /** + * @brief Internal variable to store the wifi password + * + */ + char password[50]; + + /** + * @brief Internal variable to store the wifi SSID + * + */ + char SSID[50]; + +}; \ No newline at end of file