memory optimization, only three tabs in memory at the same time

This commit is contained in:
KlausMu 2024-03-10 14:41:50 +01:00
parent 4227372859
commit b98a06e91c
32 changed files with 1114 additions and 1080 deletions

View File

@ -18,31 +18,83 @@ board_build.f_cpu = 240000000L
board_build.partitions = huge_app.csv
upload_speed = 1000000
lib_deps =
sparkfun/SparkFun LIS3DH Arduino Library@^1.0.3
crankyoldgit/IRremoteESP8266@^2.8.4
bodmer/TFT_eSPI@^2.5.23
adafruit/Adafruit FT6206 Library@^1.0.6
lvgl/lvgl@^8.3.4
bodmer/TFT_eSPI@^2.5.23
sparkfun/SparkFun LIS3DH Arduino Library@^1.0.3
crankyoldgit/IRremoteESP8266@^2.8.4
knolleary/PubSubClient@^2.8
h2zero/NimBLE-Arduino@^1.4.1
;chris--a/Keypad@^3.1.1
;t-vk/ESP32 BLE Keyboard@^0.3.2
build_flags =
-D DISABLE_ALL_LIBRARY_WARNINGS=1 ; for TFT_eSPI
-D USE_NIMBLE=1 ; for BLE Keyboard
-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
-D LV_CONF_PATH=../../../../src/gui_general_and_keys/lv_conf.h
;-- OMOTE -----------------------------------------------------------------
-D ENABLE_WIFI_AND_MQTT=1
-D ENABLE_BLUETOOTH=1
;-- Arduino log -----------------------------------------------------------
;-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE
;-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_ERROR
;-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_WARN
-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_INFO
;-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
;-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE
;-- lvgl ------------------------------------------------------------------
; lvgl variant 1:
; Don't use lv_conf.h. Tweak params via platfom.ini. See lv_conf_internal.h line 31. Don't change this line.
-D LV_CONF_SKIP=1
; use millis() from "Arduino.h" to tell the elapsed time in milliseconds
-D LV_TICK_CUSTOM=1
; dynamic memory. Takes as much as it gets from heap (DRAM). Needs approx. 25%-30% more memory than static memory.
;-D LV_MEM_CUSTOM=1
; static memory, will be allocated in static DRAM
-D LV_MEM_CUSTOM=0
-D LV_MEM_SIZE="(32U * 1024U)"
; fonts and theme
-D LV_FONT_MONTSERRAT_10=1
-D LV_FONT_MONTSERRAT_12=1
-D LV_FONT_MONTSERRAT_16=1
-D LV_FONT_MONTSERRAT_24=1
-D LV_THEME_DEFAULT_DARK=1
; don't build examples
-D LV_BUILD_EXAMPLES=0
; Enable the log module
-D LV_USE_LOG=1
-D LV_LOG_PRINTF=1
;-D LV_LOG_LEVEL=LV_LOG_LEVEL_TRACE
;-D LV_LOG_LEVEL=LV_LOG_LEVEL_INFO
;-D LV_LOG_LEVEL=LV_LOG_LEVEL_WARN
-D LV_LOG_LEVEL=LV_LOG_LEVEL_ERROR
;-D LV_LOG_LEVEL=LV_LOG_LEVEL_USER
;-D LV_LOG_LEVEL=LV_LOG_LEVEL_NONE
; trace really gives a lot of messages ...
;-D LV_LOG_LEVEL=LV_LOG_LEVEL_TRACE
; ---
; Enable asserts if an operation is failed or an invalid data is found.
; If LV_USE_LOG is enabled an error message will be printed on failure*/
; /*Check if the styles are properly initialized. (Very fast, recommended)*/
-D LV_USE_ASSERT_STYLE=1
; /*Check the integrity of `lv_mem` after critical operations. (Slow)*/
;-D LV_USE_ASSERT_MEM_INTEGRITY=1
; /*Check the object's type and existence (e.g. not deleted). (Slow)*/
;-D LV_USE_ASSERT_OBJ=1
; ---------
; lvgl variant 2:
; or define where lv_conf.h is, relative to the `lvgl` folder
;-D LV_CONF_PATH=../../../../src/gui_general_and_keys/lv_conf.h
;-- TFT_eSPI --------------------------------------------------------------
-D DISABLE_ALL_LIBRARY_WARNINGS=1
; The following lines replace the TFT_eSPI User_setup.h-file
-D USER_SETUP_LOADED=1
-D ILI9341_DRIVER=1
-D TFT_WIDTH=240
-D TFT_HEIGHT=320
;-D TFT_MISO
-D TFT_MOSI=23
-D TFT_SCLK=18
-D TFT_CS=5
-D TFT_DC=9
-D TFT_RST=-1
-D SPI_FREQUENCY=40000000 ; 40MHz default, some displays might support 80MHz
-D SPI_TOUCH_FREQUENCY=2500000
; TFT_eSPI fonts are disabled by default
;-D LOAD_GLCD=1
;-D LOAD_FONT2=1
@ -52,3 +104,5 @@ build_flags =
;-D LOAD_FONT8=1
;-D LOAD_GFXFF=1
;-D SMOOTH_FONT=1
;-- for BLE Keyboard. Don't change this line! -----------------------------
-D USE_NIMBLE=1

View File

@ -126,7 +126,7 @@ void executeCommandWithData(std::string command, commandData commandData, std::s
break;
}
#ifdef ENABLE_WIFI_AND_MQTT
#if ENABLE_WIFI_AND_MQTT == 1
case MQTT: {
auto current = commandData.commandPayloads.begin();
std::string topic = *current;

View File

@ -1,9 +1,6 @@
#ifndef __COMMANDHANDLER_H__
#define __COMMANDHANDLER_H__
#define ENABLE_WIFI_AND_MQTT // Comment out to disable WiFi
#define ENABLE_BLUETOOTH // Comment out to disable Bluetooth
#include <Arduino.h>
#include <string>
#include <list>
@ -104,7 +101,7 @@ enum commandHandlers {
IR_SONY,
IR_RC5,
IR_DENON,
#ifdef ENABLE_WIFI_AND_MQTT
#if ENABLE_WIFI_AND_MQTT == 1
MQTT,
#endif
#ifdef ENABLE_KEYBOARD_BLE

View File

@ -1,5 +1,6 @@
#include <lvgl.h>
#include "device_appleTV/device_appleTV.h"
#include "device_appleTV/gui_appleTV.h"
#include "gui_general_and_keys/guiBase.h"
#include "gui_general_and_keys/guiRegistry.h"
#include "hardware/tft.h"
@ -17,11 +18,9 @@ static void appleKey_event_cb(lv_event_t* e) {
Serial.println(50 + (int)e->user_data);
}
void init_gui_tab_appleTV(lv_obj_t* tabview) {
void create_tab_content_appleTV(lv_obj_t* tab) {
lv_obj_t* tab = lv_tabview_add_tab(tabview, "Apple TV");
// Add content to the Apple TV tab (3)
// Add content to the Apple TV tab
// Add a nice apple tv logo
lv_obj_t* appleImg = lv_img_create(tab);
lv_img_set_src(appleImg, &appleTvIcon);
@ -52,22 +51,15 @@ void init_gui_tab_appleTV(lv_obj_t* tabview) {
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);
}
void init_gui_pageIndicator_appleTV(lv_obj_t* panel) {
// Create actual (non-clickable) buttons for every tab
lv_obj_t* 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, "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);
void notify_tab_before_delete_appleTV(void) {
// remember to set all pointers to lvgl objects to NULL if they might be accessed from outside.
// They must check if object is NULL and must not use it if so
}
void register_gui_appleTV(void){
register_gui(& init_gui_tab_appleTV, & init_gui_pageIndicator_appleTV);
register_gui(std::string(tabName_appleTV), & create_tab_content_appleTV, & notify_tab_before_delete_appleTV);
}

View File

@ -3,6 +3,7 @@
#include <lvgl.h>
const char * const tabName_appleTV = "Apple TV";
void register_gui_appleTV(void);
#endif /*__GUI_APPLETV_H__*/

View File

@ -38,16 +38,16 @@ bool blinkBluetoothLabelIsOn = false;
void update_keyboard_ble_status() {
if (bleKeyboard.isConnected()) {
lv_label_set_text(BluetoothLabel, LV_SYMBOL_BLUETOOTH);
if (BluetoothLabel != NULL) {lv_label_set_text(BluetoothLabel, LV_SYMBOL_BLUETOOTH);}
bleKeyboard.setBatteryLevel(battery_percentage);
} else {
if(millis() - blinkBluetoothLabelLastChange >= 1000){
blinkBluetoothLabelIsOn = !blinkBluetoothLabelIsOn;
if (blinkBluetoothLabelIsOn) {
lv_label_set_text(BluetoothLabel, LV_SYMBOL_BLUETOOTH);
if (BluetoothLabel != NULL) {lv_label_set_text(BluetoothLabel, LV_SYMBOL_BLUETOOTH);}
} else {
lv_label_set_text(BluetoothLabel, "");
if (BluetoothLabel != NULL) {lv_label_set_text(BluetoothLabel, "");}
}
blinkBluetoothLabelLastChange = millis();
}

View File

@ -8,10 +8,8 @@
#ifdef ENABLE_KEYBOARD_BLE
#include "commandHandler.h"
#if defined(ENABLE_KEYBOARD_BLE) && !defined(ENABLE_BLUETOOTH)
static_assert(false, "You have to use \"#define ENABLE_BLUETOOTH\" in \"commandHandler.h\" when having \"#define ENABLE_KEYBOARD_BLE\"");
#if defined(ENABLE_KEYBOARD_BLE) && !(ENABLE_BLUETOOTH == 1)
static_assert(false, "You have to use \"-D ENABLE_BLUETOOTH=1\" in \"platformio.ini\" when having \"#define ENABLE_KEYBOARD_BLE\"");
#endif
#include <BleKeyboard.h>

View File

@ -10,10 +10,8 @@
// if you activate the MQTT keyboard, consider changing the mapping of the keyboard commands to the MQTT keyboard in file "commandHandler.h"
#include "commandHandler.h"
#if defined(ENABLE_KEYBOARD_MQTT) && !defined(ENABLE_WIFI_AND_MQTT)
static_assert(false, "You have to use \"#define ENABLE_WIFI_AND_MQTT\" in \"commandHandler.h\" when having \"#define ENABLE_KEYBOARD_MQTT\"");
#if defined(ENABLE_KEYBOARD_MQTT) && !(ENABLE_WIFI_AND_MQTT == 1)
static_assert(false, "You have to use \"-D ENABLE_WIFI_AND_MQTT=1\" in \"platformio.ini\" when having \"#define ENABLE_KEYBOARD_MQTT\"");
#endif
#define KEYBOARD_MQTT_UP "Keyboard_mqtt_up"

View File

@ -4,41 +4,50 @@
#include "gui_general_and_keys/guiRegistry.h"
#include "hardware/tft.h"
#include "device_smarthome/device_smarthome.h"
#include "device_smarthome/gui_smarthome.h"
#include "commandHandler.h"
// LVGL declarations
LV_IMG_DECLARE(lightbulb);
static lv_obj_t* lightToggleA;
static lv_obj_t* lightToggleB;
static lv_obj_t* sliderA;
static lv_obj_t* sliderB;
static bool lightToggleAstate = false;
static bool lightToggleBstate = false;
static int32_t sliderAvalue = 0;
static int32_t sliderBvalue = 0;
// Smart Home Toggle Event handler
static void smartHomeToggle_event_cb(lv_event_t * e){
static void smartHomeToggle_event_cb(lv_event_t* e){
std::string payload;
if (lv_obj_has_state(lv_event_get_target(e), LV_STATE_CHECKED)) payload = "true";
else payload = "false";
// Publish an MQTT message based on the event user data
#ifdef ENABLE_WIFI_AND_MQTT
#if ENABLE_WIFI_AND_MQTT == 1
if((int)e->user_data == 1) executeCommand(SMARTHOME_MQTT_BULB1_SET, payload);
if((int)e->user_data == 2) executeCommand(SMARTHOME_MQTT_BULB2_SET, payload);
#endif
}
// Smart Home Slider Event handler
static void smartHomeSlider_event_cb(lv_event_t * e){
static 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);
std::string payload_str(payload);
// Publish an MQTT message based on the event user data
#ifdef ENABLE_WIFI_AND_MQTT
#if ENABLE_WIFI_AND_MQTT == 1
if((int)e->user_data == 1) executeCommand(SMARTHOME_MQTT_BULB1_BRIGHTNESS_SET, payload);
if((int)e->user_data == 2) executeCommand(SMARTHOME_MQTT_BULB2_BRIGHTNESS_SET, payload);
#endif
}
void init_gui_tab_smarthome(lv_obj_t* tabview) {
void create_tab_content_smarthome(lv_obj_t* tab) {
lv_obj_t* tab = lv_tabview_add_tab(tabview, "Smart Home");
// Add content to the smart home tab (4)
// Add content to the smart home tab
lv_obj_set_layout(tab, LV_LAYOUT_FLEX);
lv_obj_set_flex_flow(tab, LV_FLEX_FLOW_COLUMN);
lv_obj_set_scrollbar_mode(tab, LV_SCROLLBAR_MODE_ACTIVE);
@ -61,25 +70,30 @@ void init_gui_tab_smarthome(lv_obj_t* tabview) {
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);
lightToggleA = lv_switch_create(menuBox);
if (lightToggleAstate) {
lv_obj_add_state(lightToggleA, LV_STATE_CHECKED);
} else {
// lv_obj_clear_state(lightToggleA, LV_STATE_CHECKED);
}
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);
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(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);
sliderA = lv_slider_create(menuBox);
lv_slider_set_range(sliderA, 0, 100);
lv_obj_set_style_bg_color(sliderA, lv_color_lighten(lv_color_black(), 30), LV_PART_INDICATOR);
lv_obj_set_style_bg_grad_color(sliderA, lv_color_lighten(lv_palette_main(LV_PALETTE_AMBER), 180), LV_PART_INDICATOR);
lv_obj_set_style_bg_grad_dir(sliderA, LV_GRAD_DIR_HOR, LV_PART_INDICATOR);
lv_obj_set_style_bg_color(sliderA, lv_color_white(), LV_PART_KNOB);
lv_obj_set_style_bg_opa(sliderA, 255, LV_PART_MAIN);
lv_obj_set_style_bg_color(sliderA, lv_color_lighten(color_primary, 50), LV_PART_MAIN);
lv_slider_set_value(sliderA, sliderAvalue, LV_ANIM_OFF);
lv_obj_set_size(sliderA, lv_pct(90), 10);
lv_obj_align(sliderA, LV_ALIGN_TOP_MID, 0, 37);
lv_obj_add_event_cb(sliderA, smartHomeSlider_event_cb, LV_EVENT_VALUE_CHANGED, (void*)1);
// Add another menu box for a second appliance
menuBox = lv_obj_create(tab);
@ -96,25 +110,30 @@ void init_gui_tab_smarthome(lv_obj_t* tabview) {
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);
lightToggleB = lv_switch_create(menuBox);
if (lightToggleBstate) {
lv_obj_add_state(lightToggleB, LV_STATE_CHECKED);
} else {
// lv_obj_clear_state(lightToggleB, LV_STATE_CHECKED);
}
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);
sliderB = lv_slider_create(menuBox);
lv_slider_set_range(sliderB, 0, 100);
lv_obj_set_style_bg_color(sliderB, lv_color_lighten(lv_color_black(), 30), LV_PART_INDICATOR);
lv_obj_set_style_bg_grad_color(sliderB, lv_color_lighten(lv_palette_main(LV_PALETTE_AMBER), 180), LV_PART_INDICATOR);
lv_obj_set_style_bg_grad_dir(sliderB, LV_GRAD_DIR_HOR, LV_PART_INDICATOR);
lv_obj_set_style_bg_color(sliderB, lv_color_white(), LV_PART_KNOB);
lv_obj_set_style_bg_opa(sliderB, 255, LV_PART_MAIN);
lv_obj_set_style_bg_color(sliderB, lv_color_lighten(color_primary, 50), LV_PART_MAIN);
lv_slider_set_value(sliderB, sliderBvalue, LV_ANIM_OFF);
lv_obj_set_size(sliderB, lv_pct(90), 10);
lv_obj_align(sliderB, LV_ALIGN_TOP_MID, 0, 37);
lv_obj_add_event_cb(sliderB, smartHomeSlider_event_cb, LV_EVENT_VALUE_CHANGED, (void*)2);
// Add another room (empty for now)
@ -128,19 +147,15 @@ void init_gui_tab_smarthome(lv_obj_t* tabview) {
}
void init_gui_pageIndicator_smarthome(lv_obj_t* panel) {
// Create actual (non-clickable) buttons for every tab
lv_obj_t* 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, "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);
void notify_tab_before_delete_smarthome(void) {
// remember to set all pointers to lvgl objects to NULL if they might be accessed from outside.
// They must check if object is NULL and must not use it if so
lightToggleAstate = lv_obj_has_state(lightToggleA, LV_STATE_CHECKED);
lightToggleBstate = lv_obj_has_state(lightToggleB, LV_STATE_CHECKED);
sliderAvalue = lv_slider_get_value(sliderA);
sliderBvalue = lv_slider_get_value(sliderB);
}
void register_gui_smarthome(void){
register_gui(& init_gui_tab_smarthome, & init_gui_pageIndicator_smarthome);
register_gui(std::string(tabName_smarthome), & create_tab_content_smarthome, & notify_tab_before_delete_smarthome);
}

View File

@ -3,6 +3,7 @@
#include <lvgl.h>
const char * const tabName_smarthome = "Smart Home";
void register_gui_smarthome(void);
#endif /*__GUI_SMARTHOME_H__*/

View File

@ -1,35 +1,93 @@
#include <lvgl.h>
#include "hardware/tft.h"
#include "hardware/sleep.h"
#include "hardware/memoryUsage.h"
#include "gui_general_and_keys/guiBase.h"
#include "gui_general_and_keys/guiRegistry.h"
#include "gui_general_and_keys/guiMemoryOptimizer.h"
lv_obj_t* panel;
lv_color_t color_primary = lv_color_hex(0x303030); // gray
lv_obj_t* MemoryUsageLabel = NULL;
lv_obj_t* WifiLabel = NULL;
lv_obj_t* BluetoothLabel;
lv_obj_t* objBattPercentage;
lv_obj_t* objBattIcon;
lv_obj_t* SceneLabel;
byte currentScreen = 1; // Current Device to control (allows switching mappings between devices)
lv_obj_t* BluetoothLabel = NULL;
lv_obj_t* BattPercentageLabel = NULL;
lv_obj_t* BattIconLabel = NULL;
lv_obj_t* SceneLabel = NULL;
uint32_t currentTabID = -1; // id of the current tab
uint32_t oldTabID = -1;
// LVGL declarations
LV_IMG_DECLARE(gradientLeft);
LV_IMG_DECLARE(gradientRight);
lv_obj_t* tabview = NULL;
// page indicator
lv_obj_t* panel = NULL;
lv_obj_t* img1 = NULL;
lv_obj_t* img2 = NULL;
static bool global_styles_already_initialized = false;
lv_style_t panel_style;
#ifdef drawRedBorderAroundMainWidgets
lv_style_t style_red_border;
#endif
// 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){
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);
lv_obj_scroll_to_x(panel, lv_obj_get_scroll_x(screen) * bias - offset, LV_ANIM_OFF);
// Set the page indicator (panel) scroll position relative to the tabview content scroll position
// this is a callback if the CONTENT of the tabview is scrolled (LV_EVENT_SCROLL)
void tabview_content_is_scrolling_event_cb(lv_event_t* e){
if (panel == NULL) { return;}
float bias = float(150.0 + 8.0) / 240.0;
// screenwidth indicator ?? 2 small hidden buttons ??
int offset = 240 / 2 - 150 / 2 - 8 - 2*50 / 2 -4;
// get the object to which the event is sent
lv_obj_t* tabviewContent = lv_event_get_target(e);
// Get the x coordinate of object and scroll panel accordingly
int16_t tabviewX = lv_obj_get_scroll_x(tabviewContent);
// the last events we receive are 0, 238 and 476. But should be 0, 240 and 480. Otherwise page indicator jumps a litte bit after recreation of tabs.
if ((tabviewX >= 237) && (tabviewX <= 240)) {tabviewX = 240;}
if ((tabviewX >= 475) && (tabviewX <= 480)) {tabviewX = 480;}
// we need 158 more (the size of one page indicator), because we always have one more page indicator at the beginning and at the end (so normally 5 when having 3 tabs)
int16_t panelX = tabviewX * bias - offset + 158;
// Serial.printf("scroll %d to %d\r\n", tabviewX, panelX);
lv_obj_scroll_to_x(panel, panelX, LV_ANIM_OFF);
// lv_obj_scroll_to_x(panel, lv_obj_get_scroll_x(tabviewContent) * bias - offset, LV_ANIM_OFF);
}
// Update current screen when the tabview page is changes
static void tabview_device_event_cb(lv_event_t* e){
currentScreen = lv_tabview_get_tab_act(lv_event_get_target(e));
// -----------------------
static bool waitBeforeActionAfterSlidingAnimationEnded = false;
static unsigned long waitBeforeActionAfterSlidingAnimationEnded_timerStart;
// This is the callback when the animation of the tab sliding ended
void tabview_animation_ready_cb(lv_anim_t* a) {
// Unfortunately, the animation has not completely ended here. We cannot do the recreation of the tabs here.
// We have to wait some more milliseconds or at least one cycle of lv_timer_handler();
// calling lv_timer_handler(); here does not help
// lv_timer_handler();
// guis_doAfterSliding(oldTabID, currentTabID);
waitBeforeActionAfterSlidingAnimationEnded = true;
waitBeforeActionAfterSlidingAnimationEnded_timerStart = millis();
}
// Update currentTabID when a new tab is selected
// this is a callback if the tabview is changed (LV_EVENT_VALUE_CHANGED)
// Sent when a new tab is selected by sliding or clicking the tab button. lv_tabview_get_tab_act(tabview) returns the zero based index of the current tab.
void tabview_tab_changed_event_cb(lv_event_t* e){
if (lv_event_get_code(e) == LV_EVENT_VALUE_CHANGED) {
oldTabID = currentTabID;
currentTabID = lv_tabview_get_tab_act(lv_event_get_target(e));
// Wait until the animation ended, then call "guis_doAfterSliding(oldTabID, currentTabID);"
// https://forum.lvgl.io/t/delete-a-tab-after-the-tabview-scroll-animation-is-complete/3155/4
lv_obj_t* myTabview = lv_event_get_target(e);
lv_obj_t* tabContainer = lv_tabview_get_content(myTabview);
// https://docs.lvgl.io/8.3/overview/animation.html?highlight=lv_anim_get#_CPPv411lv_anim_getPv18lv_anim_exec_xcb_t
// (lv_anim_exec_xcb_t) lv_obj_set_x does not find an animation. NULL is good as well.
lv_anim_t* anim = lv_anim_get(tabContainer, NULL); // (lv_anim_exec_xcb_t) lv_obj_set_x);
if(anim) {
lv_anim_set_ready_cb(anim, tabview_animation_ready_cb);
}
}
}
// Display flushing
@ -38,8 +96,12 @@ void my_disp_flush( lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *colo
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.setAddrWindow(area->x1, area->y1, w, h);
#ifdef useTwoBuffersForlvgl
tft.pushPixelsDMA((uint16_t*)&color_p->full, w * h);
#else
tft.pushColors((uint16_t*)&color_p->full, w * h, true);
#endif
tft.endWrite();
lv_disp_flush_ready( disp );
@ -77,18 +139,23 @@ void my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data){
}
static lv_disp_draw_buf_t draw_buf;
// static lv_color_t bufA[ screenWidth * screenHeight / 10 ];
// static lv_color_t bufB[ screenWidth * screenHeight / 10 ];
void setMainWidgetsHeightAndPosition();
void init_gui_status_bar();
void init_gui_memoryUsage_bar();
void init_gui(void) {
// Setup LVGL ---------------------------------------------------------------------------------------------
lv_init();
#ifdef useTwoBuffersForlvgl
lv_color_t * bufA = (lv_color_t *) malloc(sizeof(lv_color_t) * screenWidth * screenHeight / 10);
lv_color_t * bufB = (lv_color_t *) malloc(sizeof(lv_color_t) * screenWidth * screenHeight / 10);
lv_disp_draw_buf_init( &draw_buf, bufA, bufB, screenWidth * screenHeight / 10 );
lv_disp_draw_buf_init(&draw_buf, bufA, bufB, screenWidth * screenHeight / 10);
#else
lv_color_t * bufA = (lv_color_t *) malloc(sizeof(lv_color_t) * screenWidth * screenHeight / 10);
lv_disp_draw_buf_init(&draw_buf, bufA, NULL, screenWidth * screenHeight / 10);
#endif
// Initialize the display driver --------------------------------------------------------------------------
static lv_disp_drv_t disp_drv;
@ -106,114 +173,187 @@ void init_gui(void) {
indev_drv.read_cb = my_touchpad_read;
lv_indev_drv_register( &indev_drv );
// --- LVGL UI Configuration ---
// -- draw red border around the main widgets -------------------------------------------------------------
if (!global_styles_already_initialized) {
// Style the panel background. Will be initialized only once and reused in fillPanelWithPageIndicator_strategyMax3
lv_style_init(&panel_style);
lv_style_set_pad_all(&panel_style, 3);
lv_style_set_border_width(&panel_style, 0);
lv_style_set_bg_opa(&panel_style, LV_OPA_TRANSP);
#ifdef drawRedBorderAroundMainWidgets
lv_style_init(&style_red_border);
lv_style_set_border_side(&style_red_border, LV_BORDER_SIDE_FULL);
lv_style_set_border_color(&style_red_border, lv_color_hex(0xff0000));
lv_style_set_border_width(&style_red_border, 1);
global_styles_already_initialized = true;
#endif
}
// Set the background color -------------------------------------------------------------------------------
lv_obj_set_style_bg_color(lv_scr_act(), lv_color_black(), LV_PART_MAIN);
// set default height and position of main widgets
setMainWidgetsHeightAndPosition();
// At startup, set current GUI according to currentGUIname, and create the content of that tab (and the previous and the next) for the first time
guis_doTabCreationAtStartup();
// memoryUsage bar
init_gui_memoryUsage_bar();
// status bar
init_gui_status_bar();
}
// 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);
int panelHeight;
int memoryUsageBarTop;
int memoryUsageBarHeight;
int statusbarTop;
int statusbarHeight;
int tabviewTop;
int tabviewHeight;
int labelsPositionTop;
// Add all the tabs here
create_gui_tabs_from_gui_registry(tabview);
void setMainWidgetsHeightAndPosition() {
panelHeight = 30;
memoryUsageBarTop = 0;
memoryUsageBarHeight = getShowMemoryUsage() ? 14 : 0;
statusbarTop = memoryUsageBarTop + memoryUsageBarHeight;
statusbarHeight = 20;
tabviewTop = statusbarTop + statusbarHeight;
tabviewHeight = 320 - memoryUsageBarHeight - statusbarHeight - panelHeight;
labelsPositionTop = -2;
}
// Set current page according to the current screen
lv_tabview_set_act(tabview, currentScreen, LV_ANIM_OFF);
lv_obj_t* memoryUsageBar;
lv_obj_t* statusbar;
void showMemoryUsageBar(bool showBar) {
setMainWidgetsHeightAndPosition();
// Create a page indicator at the bottom ------------------------------------------------------------------
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
create_gui_pageIndicators_from_gui_registry(panel);
// 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);
lv_obj_set_size(memoryUsageBar, 240, memoryUsageBarHeight);
lv_obj_align(memoryUsageBar, LV_ALIGN_TOP_MID, 0, memoryUsageBarTop);
lv_obj_set_size(statusbar, 240, statusbarHeight);
lv_obj_align(statusbar, LV_ALIGN_TOP_MID, 0, statusbarTop);
lv_obj_set_size(tabview, screenWidth, tabviewHeight);
lv_obj_align(tabview, LV_ALIGN_TOP_MID, 0, tabviewTop);
// 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);
return;
// 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);
if (showBar) {
// lv_obj_clear_flag(memoryUsageBar, LV_OBJ_FLAG_HIDDEN);
lv_obj_set_size(memoryUsageBar, 240, memoryUsageBarHeight);
lv_obj_align(memoryUsageBar, LV_ALIGN_TOP_MID, 0, memoryUsageBarTop);
lv_obj_set_size(statusbar, 240, statusbarHeight);
lv_obj_align(statusbar, LV_ALIGN_TOP_MID, 0, statusbarTop);
lv_obj_set_size(tabview, screenWidth, tabviewHeight);
lv_obj_align(tabview, LV_ALIGN_TOP_MID, 0, tabviewTop);
} else {
// lv_obj_add_flag(memoryUsageBar, LV_OBJ_FLAG_HIDDEN);
}
}
void init_gui_memoryUsage_bar() {
// Create a memory status text bar at the top -------------------------------------------------------------
memoryUsageBar = lv_btn_create(lv_scr_act());
lv_obj_set_size(memoryUsageBar, 240, memoryUsageBarHeight);
lv_obj_set_style_shadow_width(memoryUsageBar, 0, LV_PART_MAIN);
lv_obj_set_style_bg_color(memoryUsageBar, lv_color_black(), LV_PART_MAIN);
#ifdef drawRedBorderAroundMainWidgets
lv_obj_add_style(memoryUsageBar, &style_red_border, LV_PART_MAIN);
#endif
lv_obj_set_style_radius(memoryUsageBar, 0, LV_PART_MAIN);
lv_obj_set_style_pad_all(memoryUsageBar, 0, LV_PART_MAIN);
lv_obj_align(memoryUsageBar, LV_ALIGN_TOP_MID, 0, memoryUsageBarTop);
MemoryUsageLabel = lv_label_create(memoryUsageBar);
lv_label_set_text(MemoryUsageLabel, "");
lv_obj_align(MemoryUsageLabel, LV_ALIGN_TOP_LEFT, 0, labelsPositionTop);
lv_obj_set_style_text_font(MemoryUsageLabel, &lv_font_montserrat_12, LV_PART_MAIN);
lv_label_set_recolor(MemoryUsageLabel, true);
}
void init_gui_status_bar() {
// Create a status bar at the top -------------------------------------------------------------------------
lv_obj_t* statusbar = lv_btn_create(lv_scr_act());
lv_obj_set_size(statusbar, 240, 20);
statusbar = lv_btn_create(lv_scr_act());
lv_obj_set_size(statusbar, 240, statusbarHeight);
lv_obj_set_style_shadow_width(statusbar, 0, LV_PART_MAIN);
lv_obj_set_style_bg_color(statusbar, lv_color_black(), LV_PART_MAIN);
#ifdef drawRedBorderAroundMainWidgets
lv_obj_add_style(statusbar, &style_red_border, LV_PART_MAIN);
#endif
lv_obj_set_style_radius(statusbar, 0, LV_PART_MAIN);
lv_obj_align(statusbar, LV_ALIGN_TOP_MID, 0, 0);
lv_obj_set_style_pad_all(statusbar, 0, LV_PART_MAIN);
lv_obj_align(statusbar, LV_ALIGN_TOP_MID, 0, statusbarTop);
int labelsPositionTopStatusbar = labelsPositionTop + 3;
// WiFi -------------------------------------------------------------------------
WifiLabel = lv_label_create(statusbar);
lv_label_set_text(WifiLabel, "");
lv_obj_align(WifiLabel, LV_ALIGN_LEFT_MID, -8, 0);
lv_obj_align(WifiLabel, LV_ALIGN_TOP_LEFT, 0, labelsPositionTopStatusbar +1);
lv_obj_set_style_text_font(WifiLabel, &lv_font_montserrat_12, LV_PART_MAIN);
// Bluetooth --------------------------------------------------------------------
BluetoothLabel = lv_label_create(statusbar);
lv_label_set_text(BluetoothLabel, "");
lv_obj_align(BluetoothLabel, LV_ALIGN_LEFT_MID, 12, 0);
lv_obj_align(BluetoothLabel, LV_ALIGN_TOP_LEFT, 20, labelsPositionTopStatusbar);
lv_obj_set_style_text_font(BluetoothLabel, &lv_font_montserrat_12, LV_PART_MAIN);
// Scene ------------------------------------------------------------------------
SceneLabel = lv_label_create(statusbar);
lv_label_set_text(SceneLabel, "");
lv_obj_align(SceneLabel, LV_ALIGN_CENTER, 0, 0);
lv_obj_align(SceneLabel, LV_ALIGN_TOP_MID, 0, labelsPositionTopStatusbar);
lv_obj_set_style_text_font(SceneLabel, &lv_font_montserrat_12, LV_PART_MAIN);
// Battery ----------------------------------------------------------------------
objBattPercentage = lv_label_create(statusbar);
lv_label_set_text(objBattPercentage, "");
// lv_obj_align(objBattPercentage, LV_ALIGN_RIGHT_MID, -16, 0);
lv_obj_align(objBattPercentage, LV_ALIGN_RIGHT_MID, -20, 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);
// --- End of LVGL configuration ---
BattPercentageLabel = lv_label_create(statusbar);
lv_label_set_text(BattPercentageLabel, "");
lv_obj_align(BattPercentageLabel, LV_ALIGN_TOP_RIGHT, -28, labelsPositionTopStatusbar);
lv_obj_set_style_text_font(BattPercentageLabel, &lv_font_montserrat_12, LV_PART_MAIN);
BattIconLabel = lv_label_create(statusbar);
lv_label_set_text(BattIconLabel, LV_SYMBOL_BATTERY_EMPTY);
lv_obj_align(BattIconLabel, LV_ALIGN_TOP_RIGHT, 0, labelsPositionTopStatusbar -1);
lv_obj_set_style_text_font(BattIconLabel, &lv_font_montserrat_16, LV_PART_MAIN);
}
static bool waitOneLoop = false;
void gui_loop(void) {
// after the sliding animation ended, we have to wait one cycle of gui_loop() before we can do the recreation of the tabs
if (waitBeforeActionAfterSlidingAnimationEnded) {
waitOneLoop = true;
waitBeforeActionAfterSlidingAnimationEnded = false;
} else if (waitOneLoop) {
waitOneLoop = false;
guis_doAfterSliding(oldTabID, currentTabID);
};
// // as alternative, we could wait some milliseconds. But one cycle of gui_loop() works well.
// if (waitBeforeActionAfterSlidingAnimationEnded) {
// if (millis() - waitBeforeActionAfterSlidingAnimationEnded_timerStart >= 5) {
// guis_doAfterSliding(oldTabID, currentTabID);
// waitBeforeActionAfterSlidingAnimationEnded = false;
// }
// }
lv_timer_handler();
}
void guis_doTabCreationAtStartup() {
gui_memoryOptimizer_prepare_startup();
guis_doAfterSliding(-1, -1);
}
void guis_doAfterSliding(int oldTabID, int newTabID) {
gui_memoryOptimizer_doAfterSliding_deletionAndCreation(&tabview, oldTabID, newTabID, &panel, &img1, &img2);
doLogMemoryUsage();
}
void setActiveTab(uint32_t index, lv_anim_enable_t anim_en) {
// unsigned long startTime = millis();
if (anim_en == LV_ANIM_ON) {
lv_tabview_set_act(tabview, index, LV_ANIM_ON);
// startTime = millis();
// while (millis() - startTime < 1000) {
// lv_timer_handler();
// }
} else {
lv_tabview_set_act(tabview, index, LV_ANIM_OFF);
// lv_timer_handler();
// log_memory();
}
}

View File

@ -4,20 +4,45 @@
#include <lvgl.h>
#include "../hardware/tft.h"
// -----------------------
// https://docs.lvgl.io/8.3/porting/display.html?highlight=lv_disp_draw_buf_init#buffering-modes
// With two buffers, the rendering and refreshing of the display become parallel operations
// Second buffer needs 15.360 bytes more memory in heap.
#define useTwoBuffersForlvgl
// LVGL declarations
LV_IMG_DECLARE(high_brightness);
LV_IMG_DECLARE(low_brightness);
extern lv_obj_t* objBattPercentage;
extern lv_obj_t* objBattIcon;
extern lv_obj_t* SceneLabel;
extern lv_color_t color_primary;
extern lv_obj_t* MemoryUsageLabel;
extern lv_obj_t* WifiLabel;
extern lv_obj_t* BluetoothLabel;
extern lv_obj_t* SceneLabel;
extern lv_obj_t* BattPercentageLabel;
extern lv_obj_t* BattIconLabel;
extern byte currentScreen; // Current screen that is shown
//#define drawRedBorderAroundMainWidgets
#ifdef drawRedBorderAroundMainWidgets
extern lv_style_t style_red_border;
#endif
extern lv_style_t panel_style;
extern int tabviewTop;
extern int tabviewHeight;
extern int panelHeight;
extern lv_color_t color_primary;
extern uint32_t currentTabID;
void init_gui(void);
void gui_loop(void);
void tabview_content_is_scrolling_event_cb(lv_event_t* e);
void tabview_tab_changed_event_cb(lv_event_t* e);
void guis_doTabCreationAtStartup();
void guis_doAfterSliding(int oldTabID, int newTabID);
void setActiveTab(uint32_t index, lv_anim_enable_t anim_en);
void showMemoryUsageBar(bool showBar);
#endif /*__GUIBASE_H__*/

View File

@ -0,0 +1,404 @@
#include <Arduino.h>
#include <lvgl.h>
#include "gui_general_and_keys/guiRegistry.h"
#include "gui_general_and_keys/guiBase.h"
struct tab_in_memory {
lv_obj_t* tab;
int listIndex;
};
tab_in_memory tabs_in_memory[3] = {{NULL, -1}, {NULL, -1}, {NULL, -1}};
int tabs_in_memory_previous_listIndex[3]= {-1 , -1, -1};
void notify_active_tabs_before_delete() {
Serial.printf(" Will notify tabs about deletion\r\n");
std::string nameOfTab;
for (int index=0; index <= 2; index++) {
if (tabs_in_memory[index].listIndex == -1) {
Serial.printf(" Will not notify tab %d about deletion because it does not exist\r\n", index);
continue;
}
nameOfTab = list_of_guis_to_be_shown.at(tabs_in_memory[index].listIndex);
if (nameOfTab == "") {
Serial.printf(" Will not notify tab %d about deletion because it is not set\r\n", index);
} else if (registered_guis_byName_map.count(nameOfTab) == 0) {
Serial.printf(" Can not notify tab %d about deletion because name \"%s\" was not found in registry\r\n", index, nameOfTab.c_str());
} else {
Serial.printf(" Will notify tab %d with name \"%s\" about deletion\r\n", index, nameOfTab.c_str());
registered_guis_byName_map.at(nameOfTab).this_notify_tab_before_delete();
}
}
}
void clear_tabview(lv_obj_t* tabview) {
if (tabview != NULL) {
// first remove events for the tabview
lv_obj_remove_event_cb(tabview, tabview_tab_changed_event_cb);
lv_obj_remove_event_cb(tabview, tabview_content_is_scrolling_event_cb);
// delete tabview
lv_obj_del(tabview);
tabview = NULL;
}
// set struct tabs_in_memory to NULL
tabs_in_memory[0] = {NULL, -1};
tabs_in_memory[1] = {NULL, -1};
tabs_in_memory[2] = {NULL, -1};
}
void clear_panel(lv_obj_t* panel, lv_obj_t* img1, lv_obj_t* img2) {
if (panel != NULL) {
lv_obj_del(panel);
panel = NULL;
}
if (img1 != NULL) {
lv_obj_del(img1);
img1 = NULL;
}
if (img2 != NULL) {
lv_obj_del(img2);
img2 = NULL;
}
}
lv_obj_t* create_tabview() {
// Setup a scrollable tabview for devices and settings ----------------------------------------------------
lv_obj_t* tabview = lv_tabview_create(lv_scr_act(), LV_DIR_TOP, 0); // Hide tab labels by setting their height to 0
#ifdef drawRedBorderAroundMainWidgets
lv_obj_add_style(tabview, &style_red_border, LV_PART_MAIN);
#endif
lv_obj_set_style_bg_color(tabview, lv_color_black(), LV_PART_MAIN);
lv_obj_set_size(tabview, screenWidth, tabviewHeight);
lv_obj_align(tabview, LV_ALIGN_TOP_MID, 0, tabviewTop);
return tabview;
}
lv_obj_t* create_panel() {
// Create a page indicator at the bottom ------------------------------------------------------------------
lv_obj_t* 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, panelHeight);
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);
return panel;
}
std::string get_name_of_gui_to_be_shown(int index) {
if (index == -1) {
return "";
} else if (index <= list_of_guis_to_be_shown.size() -1) {
return list_of_guis_to_be_shown.at(index);
} else {
return "";
}
}
void create_new_tab(lv_obj_t* tabview, uint32_t tabs_in_memory_index) {
std::string nameOfTab = get_name_of_gui_to_be_shown(tabs_in_memory[tabs_in_memory_index].listIndex);
if (nameOfTab == "") {
Serial.printf(" Will not create new tab at index %d because no name was provided\r\n", tabs_in_memory_index);
} else if (registered_guis_byName_map.count(nameOfTab) == 0) {
Serial.printf(" Will not create new tab at index %d because name %s was not found in registry\r\n", tabs_in_memory_index, nameOfTab.c_str());
} else {
Serial.printf(" Will create tab with name \"%s\" at index %d\r\n", nameOfTab.c_str(), tabs_in_memory_index);
// create tab and save pointer to tab in tabs_in_memory
tabs_in_memory[tabs_in_memory_index].tab = lv_tabview_add_tab(tabview, nameOfTab.c_str());
// let the gui create it's content
registered_guis_byName_map.at(nameOfTab).this_create_tab_content(tabs_in_memory[tabs_in_memory_index].tab);
}
}
void doTabCreation_strategyMax3(lv_obj_t* tabview, uint32_t oldTabID, uint32_t newTabID) {
// create up to three tabs and the content of the tabs
/*
example: list_of_guis_to_be_shown: 0 1 2 3 4
in memory active
0 1 -1 0 <- first state, special case - also the initial state
0 1 2 1
1 2 3 1
2 3 4 1
3 4 -1 1 <- last state, special case
*/
int tabToBeActivated = -1;
int oldListIndex = -1;
if ((oldTabID == -1) && (newTabID == -1)) {
// This is the initialization after the ESP32 has booted.
if ((tabs_in_memory_previous_listIndex[0] < list_of_guis_to_be_shown.size()) && (tabs_in_memory_previous_listIndex[0] != -1)) {
// In gui_memoryOptimizer_prepare_startup, the index of currentGUIname in list_of_guis_to_be_shown was saved, if found.
// We can resume at this old state.
oldListIndex = tabs_in_memory_previous_listIndex[0] ;
if (oldListIndex == 0) {
// first state
Serial.printf(" Startup: will resume where we went to sleep with \"first state\"\r\n");
tabs_in_memory[0] = {NULL, 0};
// take care if there is only one gui in list
tabs_in_memory[1] = {NULL, list_of_guis_to_be_shown.size() >= 2 ? 1 : -1};
tabs_in_memory[2] = {NULL, -1};
tabToBeActivated = 0;
} else if (oldListIndex == list_of_guis_to_be_shown.size() -1) {
// last state
Serial.printf(" Startup: will resume where we went to sleep with \"last state\"\r\n");
tabs_in_memory[0] = {NULL, oldListIndex -1};
tabs_in_memory[1] = {NULL, oldListIndex};
tabs_in_memory[2] = {NULL, -1};
tabToBeActivated = 1;
} else {
// any other state
Serial.printf(" Startup: will resume where we went to sleep with \"state between\"\r\n");
tabs_in_memory[0] = {NULL, oldListIndex -1};
tabs_in_memory[1] = {NULL, oldListIndex};
tabs_in_memory[2] = {NULL, oldListIndex +1};
tabToBeActivated = 1;
}
} else {
Serial.printf(" Startup: cannot resume old state, so we will show the first tabs from \"list_of_guis_to_be_shown\" as initial state\r\n");
// take care if there is no gui in list
tabs_in_memory[0] = {NULL, list_of_guis_to_be_shown.size() != 0 ? 0 : -1};
// take care if there is only one gui in list
tabs_in_memory[1] = {NULL, list_of_guis_to_be_shown.size() >= 2 ? 1 : -1};
tabs_in_memory[2] = {NULL, -1};
tabToBeActivated = 0;
}
} else if (oldTabID > newTabID) {
// swipe to previous item in list
Serial.printf(" Will swipe to previous item in list\r\n");
oldListIndex = tabs_in_memory_previous_listIndex[1];
if ((oldListIndex == 1)) {
// next state is the "first state"
tabs_in_memory[0] = {NULL, 0};
tabs_in_memory[1] = {NULL, 1};
tabs_in_memory[2] = {NULL, -1};
tabToBeActivated = 0;
} else {
tabs_in_memory[0] = {NULL, oldListIndex -2};
tabs_in_memory[1] = {NULL, oldListIndex -1};
tabs_in_memory[2] = {NULL, oldListIndex};
tabToBeActivated = 1;
}
} else {
// swipe to next item in list
Serial.printf(" Will swipe to next item in list\r\n");
if (tabs_in_memory_previous_listIndex[2] == -1) {
// last state was the first state
oldListIndex = tabs_in_memory_previous_listIndex[0]; // is always 0
} else {
oldListIndex = tabs_in_memory_previous_listIndex[1];
}
if (oldListIndex == list_of_guis_to_be_shown.size() -2) {
// next state is the "last state"
tabs_in_memory[0] = {NULL, oldListIndex};
tabs_in_memory[1] = {NULL, oldListIndex +1};
tabs_in_memory[2] = {NULL, -1};
tabToBeActivated = 1;
} else {
tabs_in_memory[0] = {NULL, oldListIndex};
tabs_in_memory[1] = {NULL, oldListIndex +1};
tabs_in_memory[2] = {NULL, oldListIndex +2};
tabToBeActivated = 1;
}
}
// create the tabs
Serial.printf(" Will create tabs. List indices of the three tabs are %d, %d, %d, tab nr %d will be activated\r\n", tabs_in_memory[0].listIndex, tabs_in_memory[1].listIndex, tabs_in_memory[2].listIndex, tabToBeActivated);
for (int i=0; i<3; i++) {
create_new_tab(tabview, i);
}
if (list_of_guis_to_be_shown.size() > 0) {
std::string nameOfNewActiveTab = list_of_guis_to_be_shown.at(tabs_in_memory[tabToBeActivated].listIndex);
Serial.printf(" New visible tab is \"%s\"\r\n", nameOfNewActiveTab.c_str());
// set the tab we swiped to as active
setActiveTab(tabToBeActivated, LV_ANIM_OFF);
currentGUIname = nameOfNewActiveTab;
currentTabID = tabToBeActivated;
}
}
LV_IMG_DECLARE(gradientLeft);
LV_IMG_DECLARE(gradientRight);
void fillPanelWithPageIndicator_strategyMax3(lv_obj_t* panel, lv_obj_t* img1, lv_obj_t* img2) {
Serial.printf(" Will fill panel with page indicators\r\n");
if (list_of_guis_to_be_shown.size() == 0) {
Serial.printf(" no tab available, so no page indicators\r\n");
// at least add the style
lv_obj_add_style(panel, &panel_style, 0);
#ifdef drawRedBorderAroundMainWidgets
lv_obj_add_style(panel, &style_red_border, LV_PART_MAIN);
#endif
return;
}
// 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);
/*
There needs to be two more screen indicators because of their different size (158 for page indicator, 240 for whole tab)
In some cases they need to have color black, if they are before the first tab or after the last tab.
In all other cases, they have color "color_primary". See this list:
example: list_of_guis_to_be_shown: 0 1 2 3 4
in memory color active
0 1 -1 b p p p 0 <- first state, special case - also the initial state
0 1 2 b p p p p 1
1 2 3 p p p p p 1
2 3 4 p p p p b 1
3 4 -1 p p p b 1 <- last state, special case
*/
// first page indicator before the first tab
btn = lv_btn_create(panel);
lv_obj_clear_flag(btn, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_size(btn, 150, lv_pct(100));
if (tabs_in_memory[0].listIndex == 0) {
lv_obj_set_style_bg_color(btn, lv_color_black(), LV_PART_MAIN);
} else {
lv_obj_set_style_bg_color(btn, color_primary, LV_PART_MAIN);
}
// create the panel content for the three guis (or less) which are currently in memory
std::string nameOfTab;
for (int i=0; i<3; i++) {
if (tabs_in_memory[i].listIndex != -1) {
nameOfTab = get_name_of_gui_to_be_shown(tabs_in_memory[i].listIndex);
// Create actual (non-clickable) buttons for every tab
lv_obj_t* 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, nameOfTab.c_str());
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);
}
}
// last page indicator after the last tab
btn = lv_btn_create(panel);
lv_obj_clear_flag(btn, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_size(btn, 150, lv_pct(100));
// 4 at last position 4 at middle position only one tab available overall
if ((tabs_in_memory[2].listIndex == list_of_guis_to_be_shown.size()-1) || (tabs_in_memory[1].listIndex == list_of_guis_to_be_shown.size()-1) || (tabs_in_memory[1].listIndex == -1)) {
lv_obj_set_style_bg_color(btn, lv_color_black(), LV_PART_MAIN);
} else {
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);
// creation of style was moved to init_gui(void)
// otherwise repeated calls of lv_style_init will lead to a memory leak of about 46 bytes each time
// https://docs.lvgl.io/8.3/overview/style.html?highlight=lv_style_t#initialize-styles-and-set-get-properties
lv_obj_add_style(panel, &panel_style, 0);
#ifdef drawRedBorderAroundMainWidgets
lv_obj_add_style(panel, &style_red_border, LV_PART_MAIN);
#endif
// Make the indicator fade out at the sides using gradient bitmaps
// Bitmaps are above the buttons and labels
// don't create it here
// 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, panelHeight, panelHeight); // stretch the 1-pixel high image to 30px
#ifdef drawRedBorderAroundMainWidgets
lv_obj_add_style(img1, &style_red_border, LV_PART_MAIN);
#endif
// don't create it here
// 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, panelHeight, panelHeight);
#ifdef drawRedBorderAroundMainWidgets
lv_obj_add_style(img2, &style_red_border, LV_PART_MAIN);
#endif
}
void gui_memoryOptimizer_prepare_startup() {
// find index of currentGUIname in list_of_guis_to_be_shown
for (int i=0; i<list_of_guis_to_be_shown.size(); i++) {
if (list_of_guis_to_be_shown[i] == currentGUIname) {
Serial.printf("Startup: found GUI with name \"%s\" in \"list_of_guis_to_be_shown\" at position %d\r\n", currentGUIname.c_str(), i);
// save position so that "guis_doAfterSliding" can use it
tabs_in_memory[0].listIndex = i;
}
}
}
void gui_memoryOptimizer_doAfterSliding_deletionAndCreation(lv_obj_t** tabview, int oldTabID, int newTabID, lv_obj_t** panel, lv_obj_t** img1, lv_obj_t** img2) {
// Here the magic for dynamic creation and deletion of lvgl objects happens to keep memory usage low.
// The next and previous tab must always be available in the tabview, because they can already been seen during the animation.
// And you always need 3 tabs, otherwise you even could not slide to the next or previous tab.
// So we always have 3 tabs.
// After the animation, the tabview and hence all tabs are deleted and recreated.
bool isInitialization = ((oldTabID == -1) && (newTabID == -1));
if (isInitialization) {
Serial.printf("Startup: will initially create the tabs to be shown\r\n");
} else {
Serial.printf("Changing from oldTabID %d \"%s\" to newTabID %d \"%s\"\r\n",
oldTabID, list_of_guis_to_be_shown.at(tabs_in_memory[oldTabID].listIndex).c_str(),
newTabID, list_of_guis_to_be_shown.at(tabs_in_memory[newTabID].listIndex).c_str());
}
// save the ids of the tabs we had in memory before
for (int i=0; i<3; i++) {
tabs_in_memory_previous_listIndex[i] = tabs_in_memory[i].listIndex;
}
// the old tabs need to be notified that they will be deleted so that they can persist their state if needed
if (!isInitialization) notify_active_tabs_before_delete();
// clear current tabview
clear_tabview(*tabview);
// clear current panel for page indicator
clear_panel(*panel, *img1, *img2);
// only optional: delete and create the whole screen. Not necessary.
// Only used for a test. init_gui_status_bar() would need to be called again at a suitable place, because the status bar would also be deleted.
// lv_obj_t * oldscr = lv_scr_act();
// // create new screen
// lv_obj_t * newscr = lv_obj_create(NULL);
// // load this new screen
// lv_scr_load(newscr);
// lv_obj_del(oldscr);
// recreate the tabview
lv_obj_t* newTabview = create_tabview();
*tabview = newTabview;
// Create the tabs. Use strategy "3 tabs at maximum" to keep memory usage low.
// Set the tab we swiped to as active
doTabCreation_strategyMax3(*tabview, oldTabID, newTabID);
// Create the panel for the page indicator. Panel itself takes about 2136 bytes for three tabs.
lv_obj_t* newPanel = create_panel();
*panel = newPanel;
*img1 = lv_img_create(lv_scr_act());
*img2 = lv_img_create(lv_scr_act());
fillPanelWithPageIndicator_strategyMax3(*panel, *img1, *img2);
// now, as the correct tab is active, register again the events for the tabview
lv_obj_add_event_cb(*tabview, tabview_tab_changed_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_add_event_cb(lv_tabview_get_content(*tabview), tabview_content_is_scrolling_event_cb, LV_EVENT_SCROLL, NULL);
// Initialize scroll position of the page indicator
lv_event_send(lv_tabview_get_content(*tabview), LV_EVENT_SCROLL, NULL);
}

View File

@ -0,0 +1,7 @@
#ifndef __GUIMEMORYOPTIMIZER_H__
#define __GUIMEMORYOPTIMIZER_H__
void gui_memoryOptimizer_prepare_startup();
void gui_memoryOptimizer_doAfterSliding_deletionAndCreation(lv_obj_t** tabview, int oldTabID, int newTabID, lv_obj_t** panel, lv_obj_t** img1, lv_obj_t** img2);
#endif /*__GUIMEMORYOPTIMIZER_H__*/

View File

@ -1,31 +1,37 @@
#include <Arduino.h>
#include <map>
#include <string>
#include <list>
#include <map>
#include <lvgl.h>
#include "guiRegistry.h"
#include "gui_general_and_keys/guiBase.h"
// https://stackoverflow.com/questions/840501/how-do-function-pointers-in-c-work
struct gui_definition {
init_gui_tab this_init_gui_tab;
init_gui_pageIndicator this_init_gui_pageIndicator;
};
// ------------------------------------------------------------------------------------
// this is a map of the registered_guis that can be accessed by name
std::map<std::string, gui_definition> registered_guis_byName_map;
// This is the list of the guis that we want to be available when swiping. Need not to be all the guis that have been registered, can be only a subset.
// You can swipe through these guis. Will be in the order you place them here in the vector.
std::vector<std::string> list_of_guis_to_be_shown;
std::string currentGUIname = "";
std::list<gui_definition> registered_guis;
// ------------------------------------------------------------------------------------
void register_gui(init_gui_tab a_init_gui_tab, init_gui_pageIndicator a_gui_pageIndicator) {
registered_guis.push_back(gui_definition{a_init_gui_tab, a_gui_pageIndicator});
}
void create_gui_tabs_from_gui_registry(lv_obj_t* tabview) {
std::list<gui_definition>::iterator it;
for (it = registered_guis.begin(); it != registered_guis.end(); ++it) {
it->this_init_gui_tab(tabview);
void register_gui(std::string a_name, create_tab_content a_create_tab_content, notify_tab_before_delete a_notify_tab_before_delete) {
if (registered_guis_byName_map.count(a_name) > 0) {
Serial.printf("ERROR!!!: you cannot register two guis having the same name '%s'\r\n", a_name.c_str());
return;
}
}
void create_gui_pageIndicators_from_gui_registry(lv_obj_t* panel) {
std::list<gui_definition>::iterator it;
for (it = registered_guis.begin(); it != registered_guis.end(); ++it) {
it->this_init_gui_pageIndicator(panel);
}
gui_definition new_gui_definition = gui_definition{a_name, a_create_tab_content, a_notify_tab_before_delete};
// put the gui_definition in a map that can be accessed by name
registered_guis_byName_map[a_name] = new_gui_definition;
// By default, put all registered guis in the sequence of guis to be shown
// Later we will have scene specific sequences of guis
list_of_guis_to_be_shown.insert(list_of_guis_to_be_shown.end(), {std::string(a_name)});
}

View File

@ -2,7 +2,7 @@
#define __GUIREGISTRY_H__
/*
If you want to create a new GUI for the touch screen, then
If you want to create a new GUI (tab in terms of lvgl) for the touch screen, then
- copy one of the existing GUIs, e.g. gui_numpad.cpp and gui_numpad.h
- place the files in the right folder:
- in folder "gui_general_and_keys" if they are of general purpose, not only for a specific device
@ -12,18 +12,34 @@
- rename the functions, they must not have the same name as the ones in the file you copied from
- call "register_gui_<nameOfGUI>();" in main.cpp
Technically spoken, to register a GUI, you need to provide two function pointers:
- one for creating the tab
- one for creating the page indicator at the bottom of the tab
register_gui(std::string(tabName), & create_tab_content_settings, & notify_tab_before_delete_settings);
Technically spoken, to register a GUI, you need to provide several function pointers:
- one for creating the content of the tab
- one to persist the state of the gui before it will be deleted, if needed
- and the name of the tab
*/
#include <string>
#include <vector>
#include <map>
#include <lvgl.h>
typedef void (*init_gui_tab)(lv_obj_t* tabview);
typedef void (*init_gui_pageIndicator)(lv_obj_t* tabview);
typedef void (*create_tab_content)(lv_obj_t* tab);
typedef void (*notify_tab_before_delete)(void);
void register_gui(init_gui_tab a_init_gui_tab, init_gui_pageIndicator a_gui_pageIndicator);
void create_gui_tabs_from_gui_registry(lv_obj_t* tabview);
void create_gui_pageIndicators_from_gui_registry(lv_obj_t* panel);
// https://stackoverflow.com/questions/840501/how-do-function-pointers-in-c-work
struct gui_definition {
std::string this_name;
create_tab_content this_create_tab_content;
notify_tab_before_delete this_notify_tab_before_delete;
uint32_t this_tabID;
lv_obj_t* this_tab;
};
extern std::map<std::string, gui_definition> registered_guis_byName_map;
extern std::vector<std::string> list_of_guis_to_be_shown;
extern std::string currentGUIname;
void register_gui(std::string a_name, create_tab_content a_create_tab_content, notify_tab_before_delete a_notify_tab_before_delete);
#endif /*__GUIREGISTRY_H__*/

View File

@ -6,6 +6,7 @@
#include "hardware/infrared_receiver.h"
#include "gui_general_and_keys/guiBase.h"
#include "gui_general_and_keys/guiRegistry.h"
#include "gui_general_and_keys/gui_irReceiver.h"
lv_obj_t* menuBoxToggle;
lv_obj_t* menuBoxMessages;
@ -65,7 +66,7 @@ void showNewIRmessage(String message) {
}
// IR receiver on Switch Event handler
static void IRReceiverOnSetting_event_cb(lv_event_t * e){
static void IRReceiverOnSetting_event_cb(lv_event_t* e){
irReceiverEnabled = lv_obj_has_state(lv_event_get_target(e), LV_STATE_CHECKED);
if (irReceiverEnabled) {
Serial.println("will turn on IR receiver");
@ -83,11 +84,9 @@ static void IRReceiverOnSetting_event_cb(lv_event_t * e){
}
}
void init_gui_tab_irReceiver(lv_obj_t* tabview) {
void create_tab_content_irReceiver(lv_obj_t* tab) {
lv_obj_t* tab = lv_tabview_add_tab(tabview, "IR Receiver");
// Add content to the settings tab
// Add content to the irReceiver tab
// With a flex layout, setting groups/boxes will position themselves automatically
lv_obj_set_layout(tab, LV_LAYOUT_FLEX);
lv_obj_set_flex_flow(tab, LV_FLEX_FLOW_COLUMN);
@ -120,20 +119,15 @@ void init_gui_tab_irReceiver(lv_obj_t* tabview) {
lv_obj_align(irReceivedMessage[i], LV_ALIGN_TOP_LEFT, 0, 0 + i*11);
}
printReceivedMessages(true);
}
void init_gui_pageIndicator_irReceiver(lv_obj_t* panel) {
// Create actual (non-clickable) buttons for every tab
lv_obj_t* 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, "IR Receiver");
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);
void notify_tab_before_delete_irReceiver(void) {
// remember to set all pointers to lvgl objects to NULL if they might be accessed from outside.
// They must check if object is NULL and must not use it if so
}
void register_gui_irReceiver(void){
register_gui(& init_gui_tab_irReceiver, & init_gui_pageIndicator_irReceiver);
register_gui(std::string(tabName_irReceiver), & create_tab_content_irReceiver, & notify_tab_before_delete_irReceiver);
}

View File

@ -4,6 +4,7 @@
#include <lvgl.h>
#include <string>
const char * const tabName_irReceiver = "IR Receiver";
void register_gui_irReceiver(void);
void showNewIRmessage(String);

View File

@ -3,6 +3,7 @@
#include "device_samsungTV/device_samsungTV.h"
#include "gui_general_and_keys/guiBase.h"
#include "gui_general_and_keys/guiRegistry.h"
#include "gui_general_and_keys/gui_numpad.h"
#include "commandHandler.h"
#include "scenes/sceneHandler.h"
#include "scenes/scene_TV.h"
@ -28,9 +29,7 @@ static void virtualKeypad_event_cb(lv_event_t* e) {
}
}
void init_gui_tab_numpad(lv_obj_t* tabview) {
lv_obj_t* tab = lv_tabview_add_tab(tabview, "Numpad");
void create_tab_content_numpad(lv_obj_t* tab) {
// 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
@ -79,23 +78,15 @@ void init_gui_tab_numpad(lv_obj_t* tabview) {
}
// Create a shared event for all button inside container
lv_obj_add_event_cb(cont, virtualKeypad_event_cb, LV_EVENT_CLICKED, NULL);
}
void init_gui_pageIndicator_numpad(lv_obj_t* panel) {
// Create actual (non-clickable) buttons for every tab
lv_obj_t* 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, "Numpad");
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);
void notify_tab_before_delete_numpad(void) {
// remember to set all pointers to lvgl objects to NULL if they might be accessed from outside.
// They must check if object is NULL and must not use it if so
}
void register_gui_numpad(void){
register_gui(& init_gui_tab_numpad, & init_gui_pageIndicator_numpad);
register_gui(std::string(tabName_numpad), & create_tab_content_numpad, & notify_tab_before_delete_numpad);
}

View File

@ -3,6 +3,7 @@
#include <lvgl.h>
const char * const tabName_numpad = "Numpad";
void register_gui_numpad(void);
#endif /*__GUI_NUMPAD_H__*/

View File

@ -2,26 +2,28 @@
#include "preferencesStorage.h"
#include "hardware/tft.h"
#include "hardware/sleep.h"
#include "hardware/memoryUsage.h"
#include "gui_general_and_keys/guiBase.h"
#include "gui_general_and_keys/guiRegistry.h"
#include "gui_general_and_keys/gui_settings.h"
lv_obj_t* objBattSettingsVoltage;
lv_obj_t* objBattSettingsPercentage;
//lv_obj_t* objBattSettingsIscharging;
// Slider Event handler
static void bl_slider_event_cb(lv_event_t * e){
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);
}
// Wakeup by IMU Switch Event handler
static void WakeEnableSetting_event_cb(lv_event_t * e){
static void WakeEnableSetting_event_cb(lv_event_t* e){
wakeupByIMUEnabled = lv_obj_has_state(lv_event_get_target(e), LV_STATE_CHECKED);
}
// timout event handler
static void timout_event_cb(lv_event_t * e){
static void timout_event_cb(lv_event_t* e){
lv_obj_t * drop = lv_event_get_target(e);
uint16_t selected = lv_dropdown_get_selected(drop);
switch (selected) {
@ -39,10 +41,12 @@ static void timout_event_cb(lv_event_t * e){
save_preferences();
}
void init_gui_tab_settings(lv_obj_t* tabview) {
lv_obj_t* tab = lv_tabview_add_tab(tabview, "Settings");
// show memory usage event handler
static void showMemoryUsage_event_cb(lv_event_t* e) {
setShowMemoryUsage(lv_obj_has_state(lv_event_get_target(e), LV_STATE_CHECKED));
}
void create_tab_content_settings(lv_obj_t* tab) {
// Add content to the settings tab
// With a flex layout, setting groups/boxes will position themselves automatically
@ -50,7 +54,7 @@ void init_gui_tab_settings(lv_obj_t* tabview) {
lv_obj_set_flex_flow(tab, LV_FLEX_FLOW_COLUMN);
lv_obj_set_scrollbar_mode(tab, LV_SCROLLBAR_MODE_ACTIVE);
// Add a label, then a box for the display settings
// Add a label, then a box for the display settings -----------------------------------------
lv_obj_t* menuLabel = lv_label_create(tab);
lv_label_set_text(menuLabel, "Display");
@ -87,7 +91,11 @@ void init_gui_tab_settings(lv_obj_t* tabview) {
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
if (wakeupByIMUEnabled) {
lv_obj_add_state(wakeToggle, LV_STATE_CHECKED);
} else {
// lv_obj_clear_state(wakeToggle, LV_STATE_CHECKED);
}
menuLabel = lv_label_create(menuBox);
lv_label_set_text(menuLabel, "Timeout");
@ -141,7 +149,7 @@ void init_gui_tab_settings(lv_obj_t* tabview) {
// lv_label_set_text(menuLabel, LV_SYMBOL_RIGHT);
// lv_obj_align(menuLabel, LV_ALIGN_TOP_RIGHT, 0, 32);
// Another setting for the battery
// Another setting for the battery ----------------------------------------------------------
menuLabel = lv_label_create(tab);
lv_label_set_text(menuLabel, "Battery");
menuBox = lv_obj_create(tab);
@ -158,21 +166,38 @@ void init_gui_tab_settings(lv_obj_t* tabview) {
// objBattSettingsIscharging = lv_label_create(menuBox);
// lv_label_set_text(objBattSettingsIscharging, "Is charging:");
// lv_obj_align(objBattSettingsIscharging, LV_ALIGN_TOP_LEFT, 0, 64);
// Memory statistics ------------------------------------------------------------------------
menuLabel = lv_label_create(tab);
lv_label_set_text(menuLabel, "Memory usage");
menuBox = lv_obj_create(tab);
lv_obj_set_size(menuBox, lv_pct(100), 48);
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, "Show mem usage");
lv_obj_align(menuLabel, LV_ALIGN_TOP_LEFT, 0, 3);
lv_obj_t* memoryUsageToggle = lv_switch_create(menuBox);
lv_obj_set_size(memoryUsageToggle, 40, 22);
lv_obj_align(memoryUsageToggle, LV_ALIGN_TOP_RIGHT, 0, 0);
lv_obj_set_style_bg_color(memoryUsageToggle, lv_color_hex(0x505050), LV_PART_MAIN);
lv_obj_add_event_cb(memoryUsageToggle, showMemoryUsage_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
if (getShowMemoryUsage()) {
lv_obj_add_state(memoryUsageToggle, LV_STATE_CHECKED);
} else {
// lv_obj_clear_state(memoryUsageToggle, LV_STATE_CHECKED);
}
}
void init_gui_pageIndicator_settings(lv_obj_t* panel) {
// Create actual (non-clickable) buttons for every tab
lv_obj_t* 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);
void notify_tab_before_delete_settings(void) {
// remember to set all pointers to lvgl objects to NULL if they might be accessed from outside.
// They must check if object is NULL and must not use it if so
objBattSettingsVoltage = NULL;
objBattSettingsPercentage = NULL;
}
void register_gui_settings(void){
register_gui(& init_gui_tab_settings, & init_gui_pageIndicator_settings);
register_gui(std::string(tabName_settings), & create_tab_content_settings, & notify_tab_before_delete_settings);
}

View File

@ -7,6 +7,7 @@ extern lv_obj_t* objBattSettingsVoltage;
extern lv_obj_t* objBattSettingsPercentage;
//extern lv_obj_t* objBattSettingsIscharging;
const char * const tabName_settings = "Settings";
void register_gui_settings(void);
#endif /*__GUI_SETTINGS_H__*/

View File

@ -1,762 +0,0 @@
/**
* @file lv_conf.h
* Configuration file for v8.3.5
*/
/*
* Copy this file as `lv_conf.h`
* 1. simply next to the `lvgl` folder
* 2. or any other places and
* - define `LV_CONF_INCLUDE_SIMPLE`
* - add the path as include path
*/
/* clang-format off */
#if 1 /*Set it to "1" to enable content*/
#ifndef LV_CONF_H
#define LV_CONF_H
#include <stdint.h>
/*====================
COLOR SETTINGS
*====================*/
/*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/
#define LV_COLOR_DEPTH 16
/*Swap the 2 bytes of RGB565 color. Useful if the display has an 8-bit interface (e.g. SPI)*/
#define LV_COLOR_16_SWAP 0
/*Enable features to draw on transparent background.
*It's required if opa, and transform_* style properties are used.
*Can be also used if the UI is above another layer, e.g. an OSD menu or video player.*/
#define LV_COLOR_SCREEN_TRANSP 0
/* Adjust color mix functions rounding. GPUs might calculate color mix (blending) differently.
* 0: round down, 64: round up from x.75, 128: round up from half, 192: round up from x.25, 254: round up */
#define LV_COLOR_MIX_ROUND_OFS 0
/*Images pixels with this color will not be drawn if they are chroma keyed)*/
#define LV_COLOR_CHROMA_KEY lv_color_hex(0x00ff00) /*pure green*/
/*=========================
MEMORY SETTINGS
*=========================*/
/*1: use custom malloc/free, 0: use the built-in `lv_mem_alloc()` and `lv_mem_free()`*/
#define LV_MEM_CUSTOM 0
#if LV_MEM_CUSTOM == 0
/*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/
#define LV_MEM_SIZE (48U * 1024U) /*[bytes]*/
/*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/
#define LV_MEM_ADR 0 /*0: unused*/
/*Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc*/
#if LV_MEM_ADR == 0
#undef LV_MEM_POOL_INCLUDE
#undef LV_MEM_POOL_ALLOC
#endif
#else /*LV_MEM_CUSTOM*/
#define LV_MEM_CUSTOM_INCLUDE <stdlib.h> /*Header for the dynamic memory function*/
#define LV_MEM_CUSTOM_ALLOC malloc
#define LV_MEM_CUSTOM_FREE free
#define LV_MEM_CUSTOM_REALLOC realloc
#endif /*LV_MEM_CUSTOM*/
/*Number of the intermediate memory buffer used during rendering and other internal processing mechanisms.
*You will see an error log message if there wasn't enough buffers. */
#define LV_MEM_BUF_MAX_NUM 16
/*Use the standard `memcpy` and `memset` instead of LVGL's own functions. (Might or might not be faster).*/
#define LV_MEMCPY_MEMSET_STD 0
/*====================
HAL SETTINGS
*====================*/
/*Default display refresh period. LVG will redraw changed areas with this period time*/
#define LV_DISP_DEF_REFR_PERIOD 30 /*[ms]*/
/*Input device read period in milliseconds*/
#define LV_INDEV_DEF_READ_PERIOD 30 /*[ms]*/
/*Use a custom tick source that tells the elapsed time in milliseconds.
*It removes the need to manually update the tick with `lv_tick_inc()`)*/
#define LV_TICK_CUSTOM 1
#if LV_TICK_CUSTOM
#define LV_TICK_CUSTOM_INCLUDE "Arduino.h" /*Header for the system time function*/
#define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current system time in ms*/
/*If using lvgl as ESP32 component*/
// #define LV_TICK_CUSTOM_INCLUDE "esp_timer.h"
// #define LV_TICK_CUSTOM_SYS_TIME_EXPR ((esp_timer_get_time() / 1000LL))
#endif /*LV_TICK_CUSTOM*/
/*Default Dot Per Inch. Used to initialize default sizes such as widgets sized, style paddings.
*(Not so important, you can adjust it to modify default sizes and spaces)*/
#define LV_DPI_DEF 130 /*[px/inch]*/
/*=======================
* FEATURE CONFIGURATION
*=======================*/
/*-------------
* Drawing
*-----------*/
/*Enable complex draw engine.
*Required to draw shadow, gradient, rounded corners, circles, arc, skew lines, image transformations or any masks*/
#define LV_DRAW_COMPLEX 1
#if LV_DRAW_COMPLEX != 0
/*Allow buffering some shadow calculation.
*LV_SHADOW_CACHE_SIZE is the max. shadow size to buffer, where shadow size is `shadow_width + radius`
*Caching has LV_SHADOW_CACHE_SIZE^2 RAM cost*/
#define LV_SHADOW_CACHE_SIZE 0
/* Set number of maximally cached circle data.
* The circumference of 1/4 circle are saved for anti-aliasing
* radius * 4 bytes are used per circle (the most often used radiuses are saved)
* 0: to disable caching */
#define LV_CIRCLE_CACHE_SIZE 4
#endif /*LV_DRAW_COMPLEX*/
/**
* "Simple layers" are used when a widget has `style_opa < 255` to buffer the widget into a layer
* and blend it as an image with the given opacity.
* Note that `bg_opa`, `text_opa` etc don't require buffering into layer)
* The widget can be buffered in smaller chunks to avoid using large buffers.
*
* - LV_LAYER_SIMPLE_BUF_SIZE: [bytes] the optimal target buffer size. LVGL will try to allocate it
* - LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE: [bytes] used if `LV_LAYER_SIMPLE_BUF_SIZE` couldn't be allocated.
*
* Both buffer sizes are in bytes.
* "Transformed layers" (where transform_angle/zoom properties are used) use larger buffers
* and can't be drawn in chunks. So these settings affects only widgets with opacity.
*/
#define LV_LAYER_SIMPLE_BUF_SIZE (24 * 1024)
#define LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE (3 * 1024)
/*Default image cache size. Image caching keeps the images opened.
*If only the built-in image formats are used there is no real advantage of caching. (I.e. if no new image decoder is added)
*With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images.
*However the opened images might consume additional RAM.
*0: to disable caching*/
#define LV_IMG_CACHE_DEF_SIZE 0
/*Number of stops allowed per gradient. Increase this to allow more stops.
*This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/
#define LV_GRADIENT_MAX_STOPS 2
/*Default gradient buffer size.
*When LVGL calculates the gradient "maps" it can save them into a cache to avoid calculating them again.
*LV_GRAD_CACHE_DEF_SIZE sets the size of this cache in bytes.
*If the cache is too small the map will be allocated only while it's required for the drawing.
*0 mean no caching.*/
#define LV_GRAD_CACHE_DEF_SIZE 0
/*Allow dithering the gradients (to achieve visual smooth color gradients on limited color depth display)
*LV_DITHER_GRADIENT implies allocating one or two more lines of the object's rendering surface
*The increase in memory consumption is (32 bits * object width) plus 24 bits * object width if using error diffusion */
#define LV_DITHER_GRADIENT 0
#if LV_DITHER_GRADIENT
/*Add support for error diffusion dithering.
*Error diffusion dithering gets a much better visual result, but implies more CPU consumption and memory when drawing.
*The increase in memory consumption is (24 bits * object's width)*/
#define LV_DITHER_ERROR_DIFFUSION 0
#endif
/*Maximum buffer size to allocate for rotation.
*Only used if software rotation is enabled in the display driver.*/
#define LV_DISP_ROT_MAX_BUF (10*1024)
/*-------------
* GPU
*-----------*/
/*Use Arm's 2D acceleration library Arm-2D */
#define LV_USE_GPU_ARM2D 0
/*Use STM32's DMA2D (aka Chrom Art) GPU*/
#define LV_USE_GPU_STM32_DMA2D 0
#if LV_USE_GPU_STM32_DMA2D
/*Must be defined to include path of CMSIS header of target processor
e.g. "stm32f769xx.h" or "stm32f429xx.h"*/
#define LV_GPU_DMA2D_CMSIS_INCLUDE
#endif
/*Use SWM341's DMA2D GPU*/
#define LV_USE_GPU_SWM341_DMA2D 0
#if LV_USE_GPU_SWM341_DMA2D
#define LV_GPU_SWM341_DMA2D_INCLUDE "SWM341.h"
#endif
/*Use NXP's PXP GPU iMX RTxxx platforms*/
#define LV_USE_GPU_NXP_PXP 0
#if LV_USE_GPU_NXP_PXP
/*1: Add default bare metal and FreeRTOS interrupt handling routines for PXP (lv_gpu_nxp_pxp_osa.c)
* and call lv_gpu_nxp_pxp_init() automatically during lv_init(). Note that symbol SDK_OS_FREE_RTOS
* has to be defined in order to use FreeRTOS OSA, otherwise bare-metal implementation is selected.
*0: lv_gpu_nxp_pxp_init() has to be called manually before lv_init()
*/
#define LV_USE_GPU_NXP_PXP_AUTO_INIT 0
#endif
/*Use NXP's VG-Lite GPU iMX RTxxx platforms*/
#define LV_USE_GPU_NXP_VG_LITE 0
/*Use SDL renderer API*/
#define LV_USE_GPU_SDL 0
#if LV_USE_GPU_SDL
#define LV_GPU_SDL_INCLUDE_PATH <SDL2/SDL.h>
/*Texture cache size, 8MB by default*/
#define LV_GPU_SDL_LRU_SIZE (1024 * 1024 * 8)
/*Custom blend mode for mask drawing, disable if you need to link with older SDL2 lib*/
#define LV_GPU_SDL_CUSTOM_BLEND_MODE (SDL_VERSION_ATLEAST(2, 0, 6))
#endif
/*-------------
* Logging
*-----------*/
/*Enable the log module*/
#define LV_USE_LOG 0
#if LV_USE_LOG
/*How important log should be added:
*LV_LOG_LEVEL_TRACE A lot of logs to give detailed information
*LV_LOG_LEVEL_INFO Log important events
*LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem
*LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail
*LV_LOG_LEVEL_USER Only logs added by the user
*LV_LOG_LEVEL_NONE Do not log anything*/
#define LV_LOG_LEVEL LV_LOG_LEVEL_WARN
/*1: Print the log with 'printf';
*0: User need to register a callback with `lv_log_register_print_cb()`*/
#define LV_LOG_PRINTF 0
/*Enable/disable LV_LOG_TRACE in modules that produces a huge number of logs*/
#define LV_LOG_TRACE_MEM 1
#define LV_LOG_TRACE_TIMER 1
#define LV_LOG_TRACE_INDEV 1
#define LV_LOG_TRACE_DISP_REFR 1
#define LV_LOG_TRACE_EVENT 1
#define LV_LOG_TRACE_OBJ_CREATE 1
#define LV_LOG_TRACE_LAYOUT 1
#define LV_LOG_TRACE_ANIM 1
#endif /*LV_USE_LOG*/
/*-------------
* Asserts
*-----------*/
/*Enable asserts if an operation is failed or an invalid data is found.
*If LV_USE_LOG is enabled an error message will be printed on failure*/
#define LV_USE_ASSERT_NULL 1 /*Check if the parameter is NULL. (Very fast, recommended)*/
#define LV_USE_ASSERT_MALLOC 1 /*Checks is the memory is successfully allocated or no. (Very fast, recommended)*/
#define LV_USE_ASSERT_STYLE 0 /*Check if the styles are properly initialized. (Very fast, recommended)*/
#define LV_USE_ASSERT_MEM_INTEGRITY 0 /*Check the integrity of `lv_mem` after critical operations. (Slow)*/
#define LV_USE_ASSERT_OBJ 0 /*Check the object's type and existence (e.g. not deleted). (Slow)*/
/*Add a custom handler when assert happens e.g. to restart the MCU*/
#define LV_ASSERT_HANDLER_INCLUDE <stdint.h>
#define LV_ASSERT_HANDLER while(1); /*Halt by default*/
/*-------------
* Others
*-----------*/
/*1: Show CPU usage and FPS count*/
#define LV_USE_PERF_MONITOR 0
#if LV_USE_PERF_MONITOR
#define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT
#endif
/*1: Show the used memory and the memory fragmentation
* Requires LV_MEM_CUSTOM = 0*/
#define LV_USE_MEM_MONITOR 0
#if LV_USE_MEM_MONITOR
#define LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_LEFT
#endif
/*1: Draw random colored rectangles over the redrawn areas*/
#define LV_USE_REFR_DEBUG 0
/*Change the built in (v)snprintf functions*/
#define LV_SPRINTF_CUSTOM 0
#if LV_SPRINTF_CUSTOM
#define LV_SPRINTF_INCLUDE <stdio.h>
#define lv_snprintf snprintf
#define lv_vsnprintf vsnprintf
#else /*LV_SPRINTF_CUSTOM*/
#define LV_SPRINTF_USE_FLOAT 0
#endif /*LV_SPRINTF_CUSTOM*/
#define LV_USE_USER_DATA 1
/*Garbage Collector settings
*Used if lvgl is bound to higher level language and the memory is managed by that language*/
#define LV_ENABLE_GC 0
#if LV_ENABLE_GC != 0
#define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/
#endif /*LV_ENABLE_GC*/
/*=====================
* COMPILER SETTINGS
*====================*/
/*For big endian systems set to 1*/
#define LV_BIG_ENDIAN_SYSTEM 0
/*Define a custom attribute to `lv_tick_inc` function*/
#define LV_ATTRIBUTE_TICK_INC
/*Define a custom attribute to `lv_timer_handler` function*/
#define LV_ATTRIBUTE_TIMER_HANDLER
/*Define a custom attribute to `lv_disp_flush_ready` function*/
#define LV_ATTRIBUTE_FLUSH_READY
/*Required alignment size for buffers*/
#define LV_ATTRIBUTE_MEM_ALIGN_SIZE 1
/*Will be added where memories needs to be aligned (with -Os data might not be aligned to boundary by default).
* E.g. __attribute__((aligned(4)))*/
#define LV_ATTRIBUTE_MEM_ALIGN
/*Attribute to mark large constant arrays for example font's bitmaps*/
#define LV_ATTRIBUTE_LARGE_CONST
/*Compiler prefix for a big array declaration in RAM*/
#define LV_ATTRIBUTE_LARGE_RAM_ARRAY
/*Place performance critical functions into a faster memory (e.g RAM)*/
#define LV_ATTRIBUTE_FAST_MEM
/*Prefix variables that are used in GPU accelerated operations, often these need to be placed in RAM sections that are DMA accessible*/
#define LV_ATTRIBUTE_DMA
/*Export integer constant to binding. This macro is used with constants in the form of LV_<CONST> that
*should also appear on LVGL binding API such as Micropython.*/
#define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning /*The default value just prevents GCC warning*/
/*Extend the default -32k..32k coordinate range to -4M..4M by using int32_t for coordinates instead of int16_t*/
#define LV_USE_LARGE_COORD 0
/*==================
* FONT USAGE
*===================*/
/*Montserrat fonts with ASCII range and some symbols using bpp = 4
*https://fonts.google.com/specimen/Montserrat*/
#define LV_FONT_MONTSERRAT_8 0
#define LV_FONT_MONTSERRAT_10 1
#define LV_FONT_MONTSERRAT_12 1
#define LV_FONT_MONTSERRAT_14 1
#define LV_FONT_MONTSERRAT_16 1
#define LV_FONT_MONTSERRAT_18 0
#define LV_FONT_MONTSERRAT_20 0
#define LV_FONT_MONTSERRAT_22 0
#define LV_FONT_MONTSERRAT_24 1
#define LV_FONT_MONTSERRAT_26 0
#define LV_FONT_MONTSERRAT_28 0
#define LV_FONT_MONTSERRAT_30 0
#define LV_FONT_MONTSERRAT_32 0
#define LV_FONT_MONTSERRAT_34 0
#define LV_FONT_MONTSERRAT_36 0
#define LV_FONT_MONTSERRAT_38 0
#define LV_FONT_MONTSERRAT_40 0
#define LV_FONT_MONTSERRAT_42 0
#define LV_FONT_MONTSERRAT_44 0
#define LV_FONT_MONTSERRAT_46 0
#define LV_FONT_MONTSERRAT_48 0
/*Demonstrate special features*/
#define LV_FONT_MONTSERRAT_12_SUBPX 0
#define LV_FONT_MONTSERRAT_28_COMPRESSED 0 /*bpp = 3*/
#define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 0 /*Hebrew, Arabic, Persian letters and all their forms*/
#define LV_FONT_SIMSUN_16_CJK 0 /*1000 most common CJK radicals*/
/*Pixel perfect monospace fonts*/
#define LV_FONT_UNSCII_8 0
#define LV_FONT_UNSCII_16 0
/*Optionally declare custom fonts here.
*You can use these fonts as default font too and they will be available globally.
*E.g. #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) LV_FONT_DECLARE(my_font_2)*/
#define LV_FONT_CUSTOM_DECLARE
/*Always set a default font*/
#define LV_FONT_DEFAULT &lv_font_montserrat_14
/*Enable handling large font and/or fonts with a lot of characters.
*The limit depends on the font size, font face and bpp.
*Compiler error will be triggered if a font needs it.*/
#define LV_FONT_FMT_TXT_LARGE 0
/*Enables/disables support for compressed fonts.*/
#define LV_USE_FONT_COMPRESSED 0
/*Enable subpixel rendering*/
#define LV_USE_FONT_SUBPX 0
#if LV_USE_FONT_SUBPX
/*Set the pixel order of the display. Physical order of RGB channels. Doesn't matter with "normal" fonts.*/
#define LV_FONT_SUBPX_BGR 0 /*0: RGB; 1:BGR order*/
#endif
/*Enable drawing placeholders when glyph dsc is not found*/
#define LV_USE_FONT_PLACEHOLDER 1
/*=================
* TEXT SETTINGS
*=================*/
/**
* Select a character encoding for strings.
* Your IDE or editor should have the same character encoding
* - LV_TXT_ENC_UTF8
* - LV_TXT_ENC_ASCII
*/
#define LV_TXT_ENC LV_TXT_ENC_UTF8
/*Can break (wrap) texts on these chars*/
#define LV_TXT_BREAK_CHARS " ,.;:-_"
/*If a word is at least this long, will break wherever "prettiest"
*To disable, set to a value <= 0*/
#define LV_TXT_LINE_BREAK_LONG_LEN 0
/*Minimum number of characters in a long word to put on a line before a break.
*Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/
#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3
/*Minimum number of characters in a long word to put on a line after a break.
*Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/
#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3
/*The control character to use for signalling text recoloring.*/
#define LV_TXT_COLOR_CMD "#"
/*Support bidirectional texts. Allows mixing Left-to-Right and Right-to-Left texts.
*The direction will be processed according to the Unicode Bidirectional Algorithm:
*https://www.w3.org/International/articles/inline-bidi-markup/uba-basics*/
#define LV_USE_BIDI 0
#if LV_USE_BIDI
/*Set the default direction. Supported values:
*`LV_BASE_DIR_LTR` Left-to-Right
*`LV_BASE_DIR_RTL` Right-to-Left
*`LV_BASE_DIR_AUTO` detect texts base direction*/
#define LV_BIDI_BASE_DIR_DEF LV_BASE_DIR_AUTO
#endif
/*Enable Arabic/Persian processing
*In these languages characters should be replaced with an other form based on their position in the text*/
#define LV_USE_ARABIC_PERSIAN_CHARS 0
/*==================
* WIDGET USAGE
*================*/
/*Documentation of the widgets: https://docs.lvgl.io/latest/en/html/widgets/index.html*/
#define LV_USE_ARC 1
#define LV_USE_BAR 1
#define LV_USE_BTN 1
#define LV_USE_BTNMATRIX 1
#define LV_USE_CANVAS 1
#define LV_USE_CHECKBOX 1
#define LV_USE_DROPDOWN 1 /*Requires: lv_label*/
#define LV_USE_IMG 1 /*Requires: lv_label*/
#define LV_USE_LABEL 1
#if LV_USE_LABEL
#define LV_LABEL_TEXT_SELECTION 1 /*Enable selecting text of the label*/
#define LV_LABEL_LONG_TXT_HINT 1 /*Store some extra info in labels to speed up drawing of very long texts*/
#endif
#define LV_USE_LINE 1
#define LV_USE_ROLLER 1 /*Requires: lv_label*/
#if LV_USE_ROLLER
#define LV_ROLLER_INF_PAGES 7 /*Number of extra "pages" when the roller is infinite*/
#endif
#define LV_USE_SLIDER 1 /*Requires: lv_bar*/
#define LV_USE_SWITCH 1
#define LV_USE_TEXTAREA 1 /*Requires: lv_label*/
#if LV_USE_TEXTAREA != 0
#define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500 /*ms*/
#endif
#define LV_USE_TABLE 1
/*==================
* EXTRA COMPONENTS
*==================*/
/*-----------
* Widgets
*----------*/
#define LV_USE_ANIMIMG 1
#define LV_USE_CALENDAR 0
#if LV_USE_CALENDAR
#define LV_CALENDAR_WEEK_STARTS_MONDAY 0
#if LV_CALENDAR_WEEK_STARTS_MONDAY
#define LV_CALENDAR_DEFAULT_DAY_NAMES {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"}
#else
#define LV_CALENDAR_DEFAULT_DAY_NAMES {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"}
#endif
#define LV_CALENDAR_DEFAULT_MONTH_NAMES {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}
#define LV_USE_CALENDAR_HEADER_ARROW 1
#define LV_USE_CALENDAR_HEADER_DROPDOWN 1
#endif /*LV_USE_CALENDAR*/
#define LV_USE_CHART 1
#define LV_USE_COLORWHEEL 1
#define LV_USE_IMGBTN 0
#define LV_USE_KEYBOARD 1
#define LV_USE_LED 1
#define LV_USE_LIST 1
#define LV_USE_MENU 1
#define LV_USE_METER 1
#define LV_USE_MSGBOX 1
#define LV_USE_SPAN 1
#if LV_USE_SPAN
/*A line text can contain maximum num of span descriptor */
#define LV_SPAN_SNIPPET_STACK_SIZE 64
#endif
#define LV_USE_SPINBOX 1
#define LV_USE_SPINNER 1
#define LV_USE_TABVIEW 1
#define LV_USE_TILEVIEW 1
#define LV_USE_WIN 0
/*-----------
* Themes
*----------*/
/*A simple, impressive and very complete theme*/
#define LV_USE_THEME_DEFAULT 1
#if LV_USE_THEME_DEFAULT
/*0: Light mode; 1: Dark mode*/
#define LV_THEME_DEFAULT_DARK 1
/*1: Enable grow on press*/
#define LV_THEME_DEFAULT_GROW 1
/*Default transition time in [ms]*/
#define LV_THEME_DEFAULT_TRANSITION_TIME 80
#endif /*LV_USE_THEME_DEFAULT*/
/*A very simple theme that is a good starting point for a custom theme*/
#define LV_USE_THEME_BASIC 1
/*A theme designed for monochrome displays*/
#define LV_USE_THEME_MONO 1
/*-----------
* Layouts
*----------*/
/*A layout similar to Flexbox in CSS.*/
#define LV_USE_FLEX 1
/*A layout similar to Grid in CSS.*/
#define LV_USE_GRID 1
/*---------------------
* 3rd party libraries
*--------------------*/
/*File system interfaces for common APIs */
/*API for fopen, fread, etc*/
#define LV_USE_FS_STDIO 0
#if LV_USE_FS_STDIO
#define LV_FS_STDIO_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
#define LV_FS_STDIO_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/
#define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
#endif
/*API for open, read, etc*/
#define LV_USE_FS_POSIX 0
#if LV_USE_FS_POSIX
#define LV_FS_POSIX_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
#define LV_FS_POSIX_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/
#define LV_FS_POSIX_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
#endif
/*API for CreateFile, ReadFile, etc*/
#define LV_USE_FS_WIN32 0
#if LV_USE_FS_WIN32
#define LV_FS_WIN32_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
#define LV_FS_WIN32_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/
#define LV_FS_WIN32_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
#endif
/*API for FATFS (needs to be added separately). Uses f_open, f_read, etc*/
#define LV_USE_FS_FATFS 0
#if LV_USE_FS_FATFS
#define LV_FS_FATFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
#define LV_FS_FATFS_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
#endif
/*PNG decoder library*/
#define LV_USE_PNG 0
/*BMP decoder library*/
#define LV_USE_BMP 0
/* JPG + split JPG decoder library.
* Split JPG is a custom format optimized for embedded systems. */
#define LV_USE_SJPG 0
/*GIF decoder library*/
#define LV_USE_GIF 0
/*QR code library*/
#define LV_USE_QRCODE 0
/*FreeType library*/
#define LV_USE_FREETYPE 0
#if LV_USE_FREETYPE
/*Memory used by FreeType to cache characters [bytes] (-1: no caching)*/
#define LV_FREETYPE_CACHE_SIZE (16 * 1024)
#if LV_FREETYPE_CACHE_SIZE >= 0
/* 1: bitmap cache use the sbit cache, 0:bitmap cache use the image cache. */
/* sbit cache:it is much more memory efficient for small bitmaps(font size < 256) */
/* if font size >= 256, must be configured as image cache */
#define LV_FREETYPE_SBIT_CACHE 0
/* Maximum number of opened FT_Face/FT_Size objects managed by this cache instance. */
/* (0:use system defaults) */
#define LV_FREETYPE_CACHE_FT_FACES 0
#define LV_FREETYPE_CACHE_FT_SIZES 0
#endif
#endif
/*Rlottie library*/
#define LV_USE_RLOTTIE 0
/*FFmpeg library for image decoding and playing videos
*Supports all major image formats so do not enable other image decoder with it*/
#define LV_USE_FFMPEG 0
#if LV_USE_FFMPEG
/*Dump input information to stderr*/
#define LV_FFMPEG_DUMP_FORMAT 0
#endif
/*-----------
* Others
*----------*/
/*1: Enable API to take snapshot for object*/
#define LV_USE_SNAPSHOT 0
/*1: Enable Monkey test*/
#define LV_USE_MONKEY 0
/*1: Enable grid navigation*/
#define LV_USE_GRIDNAV 0
/*1: Enable lv_obj fragment*/
#define LV_USE_FRAGMENT 0
/*1: Support using images as font in label or span widgets */
#define LV_USE_IMGFONT 0
/*1: Enable a published subscriber based messaging system */
#define LV_USE_MSG 0
/*1: Enable Pinyin input method*/
/*Requires: lv_keyboard*/
#define LV_USE_IME_PINYIN 0
#if LV_USE_IME_PINYIN
/*1: Use default thesaurus*/
/*If you do not use the default thesaurus, be sure to use `lv_ime_pinyin` after setting the thesauruss*/
#define LV_IME_PINYIN_USE_DEFAULT_DICT 1
/*Set the maximum number of candidate panels that can be displayed*/
/*This needs to be adjusted according to the size of the screen*/
#define LV_IME_PINYIN_CAND_TEXT_NUM 6
/*Use 9 key input(k9)*/
#define LV_IME_PINYIN_USE_K9_MODE 1
#if LV_IME_PINYIN_USE_K9_MODE == 1
#define LV_IME_PINYIN_K9_CAND_TEXT_NUM 3
#endif // LV_IME_PINYIN_USE_K9_MODE
#endif
/*==================
* EXAMPLES
*==================*/
/*Enable the examples to be built with the library*/
#define LV_BUILD_EXAMPLES 0
/*===================
* DEMO USAGE
====================*/
/*Show some widget. It might be required to increase `LV_MEM_SIZE` */
#define LV_USE_DEMO_WIDGETS 0
#if LV_USE_DEMO_WIDGETS
#define LV_DEMO_WIDGETS_SLIDESHOW 0
#endif
/*Demonstrate the usage of encoder and keyboard*/
#define LV_USE_DEMO_KEYPAD_AND_ENCODER 0
/*Benchmark your system*/
#define LV_USE_DEMO_BENCHMARK 0
#if LV_USE_DEMO_BENCHMARK
/*Use RGB565A8 images with 16 bit color depth instead of ARGB8565*/
#define LV_DEMO_BENCHMARK_RGB565A8 0
#endif
/*Stress test for LVGL*/
#define LV_USE_DEMO_STRESS 0
/*Music player demo*/
#define LV_USE_DEMO_MUSIC 0
#if LV_USE_DEMO_MUSIC
#define LV_DEMO_MUSIC_SQUARE 0
#define LV_DEMO_MUSIC_LANDSCAPE 0
#define LV_DEMO_MUSIC_ROUND 0
#define LV_DEMO_MUSIC_LARGE 0
#define LV_DEMO_MUSIC_AUTO_PLAY 0
#endif
/*--END OF LV_CONF_H--*/
#endif /*LV_CONF_H*/
#endif /*End of "Content enable"*/

View File

@ -37,8 +37,8 @@ void update_battery_stats(void) {
sprintf(buffer1, "Voltage: %.2f V", (float)battery_voltage / 1000);
// GUI settings
lv_label_set_text_fmt(objBattSettingsVoltage, buffer1);
lv_label_set_text_fmt(objBattSettingsPercentage, "Percentage: %d%%", battery_percentage);
if (objBattSettingsVoltage != NULL) {lv_label_set_text_fmt(objBattSettingsVoltage, buffer1);}
if (objBattSettingsPercentage != NULL) {lv_label_set_text_fmt(objBattSettingsPercentage, "Percentage: %d%%", battery_percentage);}
//lv_label_set_text_fmt(objBattSettingsIscharging, "Is charging: %s", battery_ischarging ? "yes" : "no");
// GUI status bar at the top
@ -55,21 +55,23 @@ void update_battery_stats(void) {
}
// if (battery_ischarging /*|| (!battery_ischarging && battery_voltage > 4350)*/){
// // lv_label_set_text(objBattPercentage, "");
// // lv_label_set_text_fmt(objBattPercentage, "%d%%", battery_percentage);
// // lv_label_set_text_fmt(objBattPercentage, "%.1f, %d%%", (float)battery_voltage / 1000, battery_percentage);
// lv_label_set_text(objBattPercentage, buffer2);
// lv_label_set_text(objBattIcon, LV_SYMBOL_USB);
// // if (BattPercentageLabel != NULL) {lv_label_set_text(BattPercentageLabel, "");}
// // lv_label_set_text_fmt(BattPercentageLabel, "%d%%", battery_percentage);
// // lv_label_set_text_fmt(BattPercentageLabel, "%.1f, %d%%", (float)battery_voltage / 1000, battery_percentage);
// if (BattPercentageLabel != NULL) {lv_label_set_text(BattPercentageLabel, buffer2);}
// if (BattIconLabel != NULL) {lv_label_set_text(BattIconLabel, LV_SYMBOL_USB);}
// } else
{
// Update status bar battery indicator
// lv_label_set_text_fmt(objBattPercentage, "%.1f, %d%%", (float)battery_voltage / 1000, battery_percentage);
lv_label_set_text(objBattPercentage, buffer2);
if(battery_percentage > 95) lv_label_set_text(objBattIcon, LV_SYMBOL_BATTERY_FULL);
else if(battery_percentage > 75) lv_label_set_text(objBattIcon, LV_SYMBOL_BATTERY_3);
else if(battery_percentage > 50) lv_label_set_text(objBattIcon, LV_SYMBOL_BATTERY_2);
else if(battery_percentage > 25) lv_label_set_text(objBattIcon, LV_SYMBOL_BATTERY_1);
else lv_label_set_text(objBattIcon, LV_SYMBOL_BATTERY_EMPTY);
// lv_label_set_text_fmt(BattPercentageLabel, "%.1f, %d%%", (float)battery_voltage / 1000, battery_percentage);
if (BattPercentageLabel != NULL) {lv_label_set_text(BattPercentageLabel, buffer2);}
if (BattIconLabel != NULL) {
if(battery_percentage > 95) lv_label_set_text(BattIconLabel, LV_SYMBOL_BATTERY_FULL);
else if(battery_percentage > 75) lv_label_set_text(BattIconLabel, LV_SYMBOL_BATTERY_3);
else if(battery_percentage > 50) lv_label_set_text(BattIconLabel, LV_SYMBOL_BATTERY_2);
else if(battery_percentage > 25) lv_label_set_text(BattIconLabel, LV_SYMBOL_BATTERY_1);
else lv_label_set_text(BattIconLabel, LV_SYMBOL_BATTERY_EMPTY);
}
}
}

View File

@ -0,0 +1,117 @@
#include <Arduino.h>
#include <lvgl.h>
#include "gui_general_and_keys/guiBase.h"
bool showMemoryUsage = 0;
#if LV_MEM_CUSTOM == 0
lv_mem_monitor_t mon;
#endif
static unsigned long updateSerialLogTimer = 0;
bool getShowMemoryUsage() {
return showMemoryUsage;
}
void setShowMemoryUsage(bool aShowMemoryUsage) {
showMemoryUsage = aShowMemoryUsage;
showMemoryUsageBar(showMemoryUsage);
}
void doLogMemoryUsage() {
// Serial.println("inside doLogMemoryUsage");
int thresholdForESP32HeapFreeWarning; // in bytes free in heap
#if (ENABLE_WIFI_AND_MQTT == 1) && (ENABLE_BLUETOOTH == 1)
thresholdForESP32HeapFreeWarning = 15000;
#elif ENABLE_WIFI_AND_MQTT == 1
thresholdForESP32HeapFreeWarning = 11000;
#elif ENABLE_BLUETOOTH == 1
thresholdForESP32HeapFreeWarning = 5000;
#elif ENABLE_WIFI_AND_MQTT == 1
thresholdForESP32HeapFreeWarning = 5000;
#endif
bool doESPHeapWarning = (ESP.getFreeHeap() < thresholdForESP32HeapFreeWarning);
bool doLVGLMemoryWarning = false;
#if LV_MEM_CUSTOM == 0
int thresholdForLVGLmemoryFreeWarning = 20; // in percentage free
lv_mem_monitor(&mon);
doLVGLMemoryWarning = ((100 - mon.used_pct) < thresholdForLVGLmemoryFreeWarning);
#endif
// Serial log every 5 sec
if(millis() - updateSerialLogTimer >= 5000) {
// Serial.println("inside doLogMemoryUsage: will do serial log");
updateSerialLogTimer = millis();
if (doESPHeapWarning) {
Serial.println("WARNING: ESP heap is getting low. You might encounter weird behaviour of your OMOTE, especially when using WiFi and/or BLE.");
}
Serial.printf(
"ESP32 heap: size: %6lu, used: %6lu (%2.0f%%), free: %6lu (%2.0f%%), heapMax: %6lu, heapMin: %6lu\r\n",
ESP.getHeapSize(),
ESP.getHeapSize() - ESP.getFreeHeap(), float(ESP.getHeapSize() - ESP.getFreeHeap()) / ESP.getHeapSize() * 100,
ESP.getFreeHeap(), float(ESP.getFreeHeap()) / ESP.getHeapSize() * 100,
ESP.getMaxAllocHeap(), ESP.getMinFreeHeap());
#if LV_MEM_CUSTOM == 0
if (doLVGLMemoryWarning) {
Serial.println("WARNING: LVGL memory is getting low. You GUI might stop working. In that case, increase \"-D LV_MEM_SIZE\" in platformio.ini");
}
Serial.printf(
"lvgl memory: size: %6lu, used: %6lu (%2d%%), free: %6lu (%2d%%), max_used: %6lu, free_biggest: %6lu, frag_pct: %2d%%\r\n",
mon.total_size,
mon.total_size - mon.free_size, mon.used_pct,
mon.free_size, 100-mon.used_pct,
mon.max_used, mon.free_biggest_size, mon.frag_pct);
#endif
} else {
// Serial.println("inside doLogMemoryUsage: serial log skipped");
}
if (showMemoryUsage) {
char buffer[80];
std::string ESP32HeapWarnBegin = "#00ff00 "; // green
std::string ESP32HeapWarnEnd = "#";
std::string LVGLMemorWarnBegin = "#00ff00 "; // green
std::string LVGLMemorWarnEnd = "#";
if (doESPHeapWarning) {
ESP32HeapWarnBegin = "#ff0000 "; // red
ESP32HeapWarnEnd = "#";
}
if (doLVGLMemoryWarning) {
LVGLMemorWarnBegin = "#ff0000 "; // red
LVGLMemorWarnEnd = "#";
}
#if LV_MEM_CUSTOM != 0
#ifdef SHOW_USED_MEMORY_INSTEAD_OF_FREE_IN_GUI
sprintf(buffer, ESP32HeapWarnBegin.append("%lu/%lu (%.0f%%)").append(ESP32HeapWarnEnd).c_str() , ESP.getHeapSize()-ESP.getFreeHeap(), ESP.getHeapSize(), float(ESP.getHeapSize()-ESP.getFreeHeap()) / ESP.getHeapSize() * 100);
#else
sprintf(buffer, ESP32HeapWarnBegin.append("%lu/%lu (%.0f%%)").append(ESP32HeapWarnEnd).c_str() , ESP.getFreeHeap(), ESP.getHeapSize(), float(ESP.getFreeHeap()) / ESP.getHeapSize() * 100);
#endif
#else
#ifdef SHOW_USED_MEMORY_INSTEAD_OF_FREE_IN_GUI
sprintf(buffer, ESP32HeapWarnBegin.append("%lu/%lu (%.0f%%)").append(ESP32HeapWarnEnd).append(" / ").append(LVGLMemorWarnBegin).append("%lu/%lu (%d%%)").append(LVGLMemorWarnEnd).c_str(), ESP.getHeapSize()-ESP.getFreeHeap(), ESP.getHeapSize(), float(ESP.getHeapSize()-ESP.getFreeHeap()) / ESP.getHeapSize() * 100, mon.total_size - mon.free_size, mon.total_size, mon.used_pct);
#else
sprintf(buffer, ESP32HeapWarnBegin.append("%lu/%lu (%.0f%%)").append(ESP32HeapWarnEnd).append(" / ").append(LVGLMemorWarnBegin).append("%lu/%lu (%d%%)").append(LVGLMemorWarnEnd).c_str(), ESP.getFreeHeap(), ESP.getHeapSize(), float(ESP.getFreeHeap()) / ESP.getHeapSize() * 100, mon.free_size, mon.total_size, 100-mon.used_pct);
#endif
#endif
for (int i=0; i<strlen(buffer); i++) {
if (buffer[i] == '.') {
buffer[i] = ',';
}
}
if (MemoryUsageLabel != NULL) {
// Serial.printf("inside doLogMemoryUsage: will do GUI log %s\r\n", buffer);
lv_label_set_text(MemoryUsageLabel, buffer);
}
} else {
if (MemoryUsageLabel != NULL) {
lv_label_set_text(MemoryUsageLabel, "");
}
}
}

View File

@ -0,0 +1,10 @@
#ifndef __MEMORYUSAGE_H__
#define __MEMORYUSAGE_H__
//#define SHOW_USED_MEMORY_INSTEAD_OF_FREE_IN_GUI // comment it out to see free memory instead of used memory in GUI. Serial log will always show both.
bool getShowMemoryUsage();
void setShowMemoryUsage(bool aShowMemoryUsage);
void doLogMemoryUsage(void);
#endif /*__MEMORYUSAGE_H__*/

View File

@ -5,7 +5,7 @@
#include "secrets.h"
#include "commandHandler.h"
#ifdef ENABLE_WIFI_AND_MQTT
#if ENABLE_WIFI_AND_MQTT == 1
WiFiClient espClient;
PubSubClient mqttClient(espClient);
@ -22,6 +22,7 @@ void WiFiEvent(WiFiEvent_t event){
// Set status bar icon based on WiFi status
if (event == ARDUINO_EVENT_WIFI_STA_GOT_IP || event == ARDUINO_EVENT_WIFI_STA_GOT_IP6) {
if (WifiLabel != NULL) {lv_label_set_text(WifiLabel, LV_SYMBOL_WIFI);}
Serial.printf("WiFi connected, IP address: %s\r\n", WiFi.localIP().toString().c_str());
} else if (event == ARDUINO_EVENT_WIFI_STA_DISCONNECTED) {
if (WifiLabel != NULL) {lv_label_set_text(WifiLabel, "");}

View File

@ -3,7 +3,7 @@
#include "commandHandler.h"
#ifdef ENABLE_WIFI_AND_MQTT
#if ENABLE_WIFI_AND_MQTT == 1
#include "WiFi.h"
#include <PubSubClient.h>

View File

@ -108,7 +108,7 @@ void enterSleep(){
configIMUInterrupts();
IMU.readRegister(&intDataRead, LIS3DH_INT1_SRC);//really clear interrupt
#ifdef ENABLE_WIFI_AND_MQTT
#if ENABLE_WIFI_AND_MQTT == 1
// Power down modem
WiFi.disconnect();
WiFi.mode(WIFI_OFF);

View File

@ -11,16 +11,18 @@
#include "hardware/mqtt.h"
#include "hardware/infrared_sender.h"
#include "hardware/infrared_receiver.h"
#include "hardware/memoryUsage.h"
// devices
#include "device_samsungTV/device_samsungTV.h"
#include "device_yamahaAmp/device_yamahaAmp.h"
#include "device_denonAvr/device_denonAvr.h"
#include "device_smarthome/device_smarthome.h"
#include "device_appleTV/device_appleTV.h"
#include "device_keyboard_mqtt/device_keyboard_mqtt.h"
#include "device_keyboard_ble/device_keyboard_ble.h"
#include "device_denonAvr/device_denonAvr.h"
// gui and keys
#include "gui_general_and_keys/guiBase.h"
#include "gui_general_and_keys/guiRegistry.h"
#include "gui_general_and_keys/gui_irReceiver.h"
#include "gui_general_and_keys/gui_settings.h"
#include "gui_general_and_keys/gui_numpad.h"
@ -64,6 +66,7 @@ void setup() {
// register commands for the devices
register_device_samsung();
register_device_yamaha();
register_device_denon();
register_device_smarthome();
register_device_appleTV();
#ifdef ENABLE_KEYBOARD_MQTT
@ -72,10 +75,9 @@ void setup() {
#ifdef ENABLE_KEYBOARD_BLE
register_device_keyboard_ble();
#endif
register_device_denon();
register_specialCommands();
// register the GUIs. They will be displayed in the order they are registered.
// register the GUIs. They will be displayed in the order they have been registered.
register_gui_irReceiver();
register_gui_settings();
register_gui_numpad();
@ -93,27 +95,29 @@ void setup() {
setLabelCurrentScene();
// init WiFi - needs to be after gui because WifiLabel must be available
#ifdef ENABLE_WIFI_AND_MQTT
#if ENABLE_WIFI_AND_MQTT == 1
init_mqtt();
#endif
Serial.print("Setup finished in ");
Serial.print(millis());
Serial.println("ms.");
Serial.printf("Setup finished in %lu ms.\r\n", millis());
}
// Loop ------------------------------------------------------------------------------------------------------------------------------------
void loop() {
void loop() {
// Update Backlight brightness
update_backligthBrighness();
// Update LVGL UI
gui_loop();
// Blink debug LED at 1 Hz
update_userled();
// Keypad Handling
keypad_loop();
// IR receiver
if (irReceiverEnabled) {
infraredReceiver_loop();
}
// Refresh IMU data at 10Hz
static unsigned long IMUTaskTimer = millis();
@ -124,26 +128,18 @@ void loop() {
// Update battery and BLE stats at 1Hz
static unsigned long updateStatusTimer = millis();
if(millis() - updateStatusTimer >= 1000){
update_battery_stats();
if(millis() - updateStatusTimer >= 1000) {
updateStatusTimer = millis();
update_battery_stats();
#ifdef ENABLE_BLUETOOTH
#if ENABLE_BLUETOOTH == 1
// adjust this if you implement other bluetooth devices than the BLE keyboard
#ifdef ENABLE_KEYBOARD_BLE
update_keyboard_ble_status();
#endif
#endif
// Serial.printf("heapSize: %lu, heapFree: %lu, heapMin: %lu, heapMax: %lu\r\n", ESP.getHeapSize(), ESP.getFreeHeap(), ESP.getMinFreeHeap(), ESP.getMaxAllocHeap());
}
// Keypad Handling
keypad_loop();
if (irReceiverEnabled) {
infraredReceiver_loop();
doLogMemoryUsage();
}
}

View File

@ -2,6 +2,7 @@
#include "hardware/sleep.h"
#include "hardware/tft.h"
#include "gui_general_and_keys/guiBase.h"
#include "gui_general_and_keys/guiRegistry.h"
#include "commandHandler.h"
#include "scenes/sceneHandler.h"
@ -14,10 +15,10 @@ void init_preferences(void) {
wakeupByIMUEnabled = preferences.getBool("wkpByIMU");
actualSleepTimeout = preferences.getUInt("slpTimeout");
backlight_brightness = preferences.getUChar("blBrightness");
currentScreen = preferences.getUChar("currentScreen");
currentScene = std::string(preferences.getString("currentScene").c_str());
currentGUIname = std::string(preferences.getString("currentGUIname").c_str());
// Serial.printf("Preferences restored: brightness %d, screen %d, scene %s\r\n", backlight_brightness, currentScreen, currentScene.c_str());
// Serial.printf("Preferences restored: brightness %d, GUI %s, scene %s\r\n", backlight_brightness, currentGUIname.c_str(), currentScene.c_str());
} else {
// Serial.printf("No preferences to restore\r\n");
}
@ -29,8 +30,8 @@ void save_preferences(void) {
preferences.putBool("wkpByIMU", wakeupByIMUEnabled);
preferences.putUInt("slpTimeout", actualSleepTimeout);
preferences.putUChar("blBrightness", backlight_brightness);
preferences.putUChar("currentScreen", currentScreen);
preferences.putString("currentScene", currentScene.c_str());
preferences.putString("currentGUIname", currentGUIname.c_str());
if(!preferences.getBool("alreadySetUp")) preferences.putBool("alreadySetUp", true);
preferences.end();
}

View File

@ -24,7 +24,7 @@ void handleScene(std::string command, commandData commandData, std::string addit
Serial.printf("scene: will switch from old scene %s to new scene %s\r\n", currentScene.c_str(), scene_name.c_str());
}
lv_label_set_text(SceneLabel, "changing...");
if (SceneLabel != NULL) {lv_label_set_text(SceneLabel, "changing...");}
gui_loop();
// end old scene
@ -45,11 +45,13 @@ void handleScene(std::string command, commandData commandData, std::string addit
currentScene = scene_name;
lv_label_set_text(SceneLabel, currentScene.c_str());
if (SceneLabel != NULL) {lv_label_set_text(SceneLabel, currentScene.c_str());}
Serial.printf("scene: scene handling finished, new scene %s is active\r\n", currentScene.c_str());
}
void setLabelCurrentScene() {
lv_label_set_text(SceneLabel, currentScene.c_str());
if ((SceneLabel != NULL) && sceneExists(currentScene)) {
lv_label_set_text(SceneLabel, currentScene.c_str());
}
}