diff --git a/Boards/LilygoTdeck/Source/hal/TdeckDisplay.cpp b/Boards/LilygoTdeck/Source/hal/TdeckDisplay.cpp index 8910a233..eec548dd 100644 --- a/Boards/LilygoTdeck/Source/hal/TdeckDisplay.cpp +++ b/Boards/LilygoTdeck/Source/hal/TdeckDisplay.cpp @@ -4,6 +4,7 @@ #include "Log.h" #include +#include #include "driver/ledc.h" #include "driver/spi_master.h" @@ -193,14 +194,38 @@ void TdeckDisplay::setBacklightDuty(uint8_t backlightDuty) { isBacklightInitialized = true; } - if (setBacklight(backlightDuty)) { - TT_LOG_I(TAG, "Backlight set: %d", backlightDuty); - lastBacklightDuty = backlightDuty; - } else { + if (!setBacklight(backlightDuty)) { TT_LOG_E(TAG, "Failed to configure display backlight"); } } +void TdeckDisplay::setGammaCurve(uint8_t index) { + uint8_t gamma_curve; + switch (index) { + case 0: + gamma_curve = 0x01; + break; + case 1: + gamma_curve = 0x04; + break; + case 2: + gamma_curve = 0x02; + break; + case 3: + gamma_curve = 0x08; + break; + default: + return; + } + const uint8_t param[] = { + gamma_curve + }; + + if (esp_lcd_panel_io_tx_param(ioHandle , LCD_CMD_GAMSET, param, 1) != ESP_OK) { + TT_LOG_E(TAG, "Failed to set gamma"); + } +} + tt::hal::Display* createDisplay() { return static_cast(new TdeckDisplay()); } diff --git a/Boards/LilygoTdeck/Source/hal/TdeckDisplay.h b/Boards/LilygoTdeck/Source/hal/TdeckDisplay.h index feb71a50..e5311951 100644 --- a/Boards/LilygoTdeck/Source/hal/TdeckDisplay.h +++ b/Boards/LilygoTdeck/Source/hal/TdeckDisplay.h @@ -13,7 +13,6 @@ private: esp_lcd_panel_io_handle_t ioHandle = nullptr; esp_lcd_panel_handle_t panelHandle = nullptr; lv_display_t* displayHandle = nullptr; - uint8_t lastBacklightDuty = 255; bool poweredOn = false; public: @@ -29,9 +28,11 @@ public: tt::hal::Touch* _Nullable createTouch() override; void setBacklightDuty(uint8_t backlightDuty) override; - uint8_t getBacklightDuty() const override { return lastBacklightDuty; } bool supportsBacklightDuty() const override { return true; } + void setGammaCurve(uint8_t index) override; + uint8_t getGammaCurveCount() const override { return 4; }; + lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; } private: diff --git a/Boards/M5stackShared/Source/hal/M5stackDisplay.h b/Boards/M5stackShared/Source/hal/M5stackDisplay.h index 961b7e61..e4e50f08 100644 --- a/Boards/M5stackShared/Source/hal/M5stackDisplay.h +++ b/Boards/M5stackShared/Source/hal/M5stackDisplay.h @@ -23,10 +23,6 @@ public: tt::hal::Touch* _Nullable createTouch() override; - void setBacklightDuty(uint8_t backlightDuty) override {} - uint8_t getBacklightDuty() const override { return 255; } - bool supportsBacklightDuty() const override { return false; } - lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; } }; diff --git a/Boards/Simulator/Source/hal/SdlDisplay.h b/Boards/Simulator/Source/hal/SdlDisplay.h index d4489415..6ff17407 100644 --- a/Boards/Simulator/Source/hal/SdlDisplay.h +++ b/Boards/Simulator/Source/hal/SdlDisplay.h @@ -19,10 +19,6 @@ public: tt::hal::Touch* _Nullable createTouch() override { return dynamic_cast(new SdlTouch()); } - void setBacklightDuty(uint8_t backlightDuty) override {} - uint8_t getBacklightDuty() const override { return 255; } - bool supportsBacklightDuty() const override { return false; } - lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; } }; diff --git a/Boards/YellowBoard/Source/hal/YellowDisplay.cpp b/Boards/YellowBoard/Source/hal/YellowDisplay.cpp index 571b701e..331802da 100644 --- a/Boards/YellowBoard/Source/hal/YellowDisplay.cpp +++ b/Boards/YellowBoard/Source/hal/YellowDisplay.cpp @@ -4,6 +4,7 @@ #include "Log.h" #include +#include #include "driver/gpio.h" #include "driver/ledc.h" @@ -109,6 +110,7 @@ bool YellowDisplay::start() { const lvgl_port_display_cfg_t disp_cfg = { .io_handle = ioHandle, .panel_handle = panelHandle, + .control_handle = nullptr, .buffer_size = TWODOTFOUR_LCD_DRAW_BUFFER_SIZE, .double_buffer = false, .trans_size = 0, @@ -120,11 +122,14 @@ bool YellowDisplay::start() { .mirror_x = true, .mirror_y = false, }, + .color_format = LV_COLOR_FORMAT_RGB565, .flags = { .buff_dma = true, .buff_spiram = false, .sw_rotate = false, - .swap_bytes = true + .swap_bytes = true, + .full_refresh = false, + .direct_mode = false } }; @@ -156,13 +161,48 @@ void YellowDisplay::setBacklightDuty(uint8_t backlightDuty) { isBacklightInitialized = true; } - if (setBacklight(backlightDuty)) { - TT_LOG_I(TAG, "Backlight set: %d", backlightDuty); - lastBacklightDuty = backlightDuty; - } else { + if (!setBacklight(backlightDuty)) { TT_LOG_E(TAG, "Failed to configure display backlight"); } } + +/** + * Note: + * The datasheet implies this should work, but it doesn't: + * https://www.digikey.com/htmldatasheets/production/1640716/0/0/1/ILI9341-Datasheet.pdf + * + * This repo claims it only has 1 curve: + * https://github.com/brucemack/hello-ili9341 + * + * I'm leaving it in as I'm not sure if it's just my hardware that's problematic. + */ +void YellowDisplay::setGammaCurve(uint8_t index) { + uint8_t gamma_curve; + switch (index) { + case 0: + gamma_curve = 0x01; + break; + case 1: + gamma_curve = 0x04; + break; + case 2: + gamma_curve = 0x02; + break; + case 3: + gamma_curve = 0x08; + break; + default: + return; + } + const uint8_t param[] = { + gamma_curve + }; + + if (esp_lcd_panel_io_tx_param(ioHandle , LCD_CMD_GAMSET, param, 1) != ESP_OK) { + TT_LOG_E(TAG, "Failed to set gamma"); + } +} + tt::hal::Touch* _Nullable YellowDisplay::createTouch() { return static_cast(new YellowTouch()); } diff --git a/Boards/YellowBoard/Source/hal/YellowDisplay.h b/Boards/YellowBoard/Source/hal/YellowDisplay.h index 2a9fa147..9a702731 100644 --- a/Boards/YellowBoard/Source/hal/YellowDisplay.h +++ b/Boards/YellowBoard/Source/hal/YellowDisplay.h @@ -13,7 +13,6 @@ private: esp_lcd_panel_io_handle_t ioHandle = nullptr; esp_lcd_panel_handle_t panelHandle = nullptr; lv_display_t* displayHandle = nullptr; - uint8_t lastBacklightDuty = 255; public: @@ -28,9 +27,11 @@ public: tt::hal::Touch* _Nullable createTouch() override; void setBacklightDuty(uint8_t backlightDuty) override; - uint8_t getBacklightDuty() const override { return lastBacklightDuty; } bool supportsBacklightDuty() const override { return true; } + void setGammaCurve(uint8_t index) override; + uint8_t getGammaCurveCount() const override { return 4; }; + lv_display_t* _Nullable getLvglDisplay() const override { return displayHandle; } }; diff --git a/Boards/YellowBoard/Source/hal/YellowTouch.cpp b/Boards/YellowBoard/Source/hal/YellowTouch.cpp index c8fa028b..72a02a95 100644 --- a/Boards/YellowBoard/Source/hal/YellowTouch.cpp +++ b/Boards/YellowBoard/Source/hal/YellowTouch.cpp @@ -42,7 +42,8 @@ bool YellowTouch::start(lv_display_t* display) { }, .process_coordinates = nullptr, .interrupt_callback = nullptr, - .user_data = nullptr + .user_data = nullptr, + .driver_data = nullptr }; if (esp_lcd_touch_new_i2c_cst816s(ioHandle, &config, &touchHandle) != ESP_OK) { diff --git a/Documentation/ideas.md b/Documentation/ideas.md index d2364dfd..82e8940f 100644 --- a/Documentation/ideas.md +++ b/Documentation/ideas.md @@ -1,24 +1,25 @@ # Bugs +- elf_loader, lvgl and TactilityC in TactilitySDK should include their license files - I2C Scanner is on M5Stack devices is broken - Fix screenshot app on ESP32: it currently blocks when allocating memory (its cmakelists.txt also needs a fix, see TODO in there) -- Commit fix to esp_lvgl_port to have `esp_lvgl_port_disp.c` user driver_data instead of user_data -- WiFi bug: when pressing disconnect while between `WIFI_EVENT_STA_START` and `IP_EVENT_STA_GOT_IP`, then auto-connect becomes activate again. -- elf_loader, lvgl and TactilityC in TactilitySDK should include their license files -- ESP32 (CYD) runs out of memory when: - - loading splash (even in Files app) - - WiFi is on and navigating back to Desktop - Suggested mitigation: When no PSRAM is available, use simplified desktop buttons -- WiFi fails quietly when there isn't enough memory. Add statusbar icon for memory pressure. Show error in WiFi screen (e.g. AlertDialog when SPI is not enabled and available memory is below a certain amount) +- WiFi bug: when pressing disconnect while between `WIFI_EVENT_STA_START` and `IP_EVENT_STA_GOT_IP`, then auto-connect becomes active again. +- ESP32 (CYD) memory issues (or any device without PSRAM): + - Boot app doesn't show logo + - WiFi is on and navigating back to Desktop makes desktop icons disappear + - WiFi might fail quiety when trying to enable it: this shows no feedback (force it by increasing LVGL buffers to 100kB) + Possible mitigations: + - When no PSRAM is available, use simplified desktop buttons + - Add statusbar icon for memory pressure. + - Show error in WiFi screen (e.g. AlertDialog when SPI is not enabled and available memory is below a certain amount) - WiFi details "forget" button should be hidden when WiFi credentials are not stores yet. # TODOs -- When WiFi is on, but there is no connection, it sort of seems like WiFi is off. Find better icon? Gnome uses a grayed-out 100% connectivity one. - Create different partitions files for different ESP flash size targets (N4, N8, N16, N32) - Rewrite `sdcard` HAL to class - Attach ELF data to wrapper app (as app data) (check that app state is "running"!) so you can run more than 1 external apps at a time. We'll need to keep track of all manifest instances, so that the wrapper can look up the relevant manifest for the relevant callbacks. - T-Deck: Clear screen before turning on blacklight -- Single wire audio +- Audio player app - Audio recording app - Files app: file operations: rename, delete, copy, paste (long press?), create folder - T-Deck: Use knob for UI selection @@ -41,7 +42,7 @@ - T-Deck Plus: Create separate board config? - Support for displays with different DPI. Consider the layer-based system like on Android. - Make firmwares available via web serial website -- If present, use LED to show boot status +- If present, use LED to show boot/wifi status - T-Deck Power: capacity estimation uses linear voltage curve, but it should use some sort of battery discharge curve. # App Ideas diff --git a/Tactility/Source/app/display/Display.cpp b/Tactility/Source/app/display/Display.cpp index e0bb3cb6..305fe501 100644 --- a/Tactility/Source/app/display/Display.cpp +++ b/Tactility/Source/app/display/Display.cpp @@ -13,7 +13,15 @@ namespace tt::app::display { static bool backlight_duty_set = false; static uint8_t backlight_duty = 255; -static void onSliderEvent(lv_event_t* event) { +static bool gamma_set = false; +static uint8_t gamma = 255; + +#define ROTATION_DEFAULT 0 +#define ROTATION_180 1 +#define ROTATION_270 2 +#define ROTATION_90 3 + +static void onBacklightSliderEvent(lv_event_t* event) { auto* slider = static_cast(lv_event_get_target(event)); auto* lvgl_display = lv_display_get_default(); tt_assert(lvgl_display != nullptr); @@ -30,10 +38,30 @@ static void onSliderEvent(lv_event_t* event) { } } -#define ROTATION_DEFAULT 0 -#define ROTATION_180 1 -#define ROTATION_270 2 -#define ROTATION_90 3 +static void onGammaSliderEvent(lv_event_t* event) { + auto* slider = static_cast(lv_event_get_target(event)); + auto* lvgl_display = lv_display_get_default(); + tt_assert(lvgl_display != nullptr); + auto* hal_display = (tt::hal::Display*)lv_display_get_user_data(lvgl_display); + tt_assert(hal_display != nullptr); + + if (hal_display->getGammaCurveCount() > 0) { + int32_t slider_value = lv_slider_get_value(slider); + + gamma = (uint8_t)slider_value; + gamma_set = true; + + hal_display->setGammaCurve(gamma); + } +} + +static tt::hal::Display* getHalDisplay(lv_obj_t* widget) { + auto* lvgl_display = lv_obj_get_display(widget); + tt_assert(lvgl_display != nullptr); + auto* hal_display = (tt::hal::Display*)lv_display_get_user_data(lvgl_display); + tt_assert(hal_display != nullptr); + return hal_display; +} static lv_display_rotation_t orientationSettingToDisplayRotation(uint32_t setting) { if (setting == ROTATION_180) { @@ -92,11 +120,19 @@ static void onShow(AppContext& app, lv_obj_t* parent) { lv_obj_set_width(brightness_slider, LV_PCT(50)); lv_obj_align(brightness_slider, LV_ALIGN_TOP_RIGHT, -8, 0); lv_slider_set_range(brightness_slider, 0, 255); - lv_obj_add_event_cb(brightness_slider, onSliderEvent, LV_EVENT_VALUE_CHANGED, nullptr); + lv_obj_add_event_cb(brightness_slider, onBacklightSliderEvent, LV_EVENT_VALUE_CHANGED, nullptr); - auto* lvgl_display = lv_obj_get_display(parent); - tt_assert(lvgl_display != nullptr); - auto* hal_display = (tt::hal::Display*)lv_display_get_user_data(lvgl_display); + lv_obj_t* gamma_label = lv_label_create(wrapper); + lv_label_set_text(gamma_label, "Gamma"); + lv_obj_set_y(gamma_label, 40); + + lv_obj_t* gamma_slider = lv_slider_create(wrapper); + lv_obj_set_width(gamma_slider, LV_PCT(50)); + lv_obj_align(gamma_slider, LV_ALIGN_TOP_RIGHT, -8, 40); + lv_slider_set_range(gamma_slider, 0, getHalDisplay(parent)->getGammaCurveCount()); + lv_obj_add_event_cb(gamma_slider, onGammaSliderEvent, LV_EVENT_VALUE_CHANGED, nullptr); + + auto* hal_display = getHalDisplay(parent); tt_assert(hal_display != nullptr); if (!hal_display->supportsBacklightDuty()) { @@ -107,10 +143,13 @@ static void onShow(AppContext& app, lv_obj_t* parent) { lv_slider_set_value(brightness_slider, value, LV_ANIM_OFF); } + lv_slider_set_value(gamma_slider, 128, LV_ANIM_OFF); + lv_obj_t* orientation_label = lv_label_create(wrapper); lv_label_set_text(orientation_label, "Orientation"); - lv_obj_align(orientation_label, LV_ALIGN_TOP_LEFT, 0, 40); + lv_obj_align(orientation_label, LV_ALIGN_TOP_LEFT, 0, 80); + auto lvgl_display = lv_obj_get_display(parent); auto horizontal_px = lv_display_get_horizontal_resolution(lvgl_display); auto vertical_px = lv_display_get_vertical_resolution(lvgl_display); bool is_landscape_display = horizontal_px > vertical_px; @@ -122,7 +161,7 @@ static void onShow(AppContext& app, lv_obj_t* parent) { lv_dropdown_set_options(orientation_dropdown, "Portrait\nPortrait (flipped)\nLandscape Left\nLandscape Right"); } - lv_obj_align(orientation_dropdown, LV_ALIGN_TOP_RIGHT, 0, 32); + lv_obj_align(orientation_dropdown, LV_ALIGN_TOP_RIGHT, 0, 72); lv_obj_add_event_cb(orientation_dropdown, onOrientationSet, LV_EVENT_VALUE_CHANGED, nullptr); uint32_t orientation_selected = dipslayOrientationToOrientationSetting( lv_display_get_rotation(lv_display_get_default()) diff --git a/TactilityHeadless/Source/hal/Display.h b/TactilityHeadless/Source/hal/Display.h index 912a8ca9..b8ec7515 100644 --- a/TactilityHeadless/Source/hal/Display.h +++ b/TactilityHeadless/Source/hal/Display.h @@ -18,9 +18,12 @@ public: virtual Touch* _Nullable createTouch() = 0; /** Set a value in the range [0, 255] */ - virtual void setBacklightDuty(uint8_t backlightDuty) = 0; - virtual uint8_t getBacklightDuty() const = 0; - virtual bool supportsBacklightDuty() const = 0; + virtual void setBacklightDuty(uint8_t backlightDuty) { /* NO-OP */ } + virtual bool supportsBacklightDuty() const { return false; } + + /** Set a value in the range [0, 255] */ + virtual void setGammaCurve(uint8_t index) { /* NO-OP */ } + virtual uint8_t getGammaCurveCount() const { return 0; }; /** After start() returns true, this should return a valid pointer until stop() is called and returns true */ virtual lv_display_t* _Nullable getLvglDisplay() const = 0;