diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index ed86440fb2..1f48ca27b5 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -510,6 +510,13 @@ choice DISPLAY_OLED_TYPE bool "SH1106 128*64" endchoice +config OLED_AUTO_POWER_SAVE + bool "Enable OLED Auto Power Save" + depends on BOARD_TYPE_BREAD_COMPACT_WIFI + help + Automatically turn off the OLED display after a period of inactivity. + The screen wakes up on button press or voice activity. + choice DISPLAY_LCD_TYPE depends on BOARD_TYPE_BREAD_COMPACT_WIFI_LCD || BOARD_TYPE_BREAD_COMPACT_ESP32_LCD || BOARD_TYPE_CGC || BOARD_TYPE_WAVESHARE_P4_NANO || BOARD_TYPE_WAVESHARE_P4_WIFI6_TOUCH_LCD_XC || BOARD_TYPE_BREAD_COMPACT_WIFI_CAM prompt "LCD Type" diff --git a/main/boards/bread-compact-wifi/compact_wifi_board.cc b/main/boards/bread-compact-wifi/compact_wifi_board.cc index 04d71910ad..5a38c54932 100644 --- a/main/boards/bread-compact-wifi/compact_wifi_board.cc +++ b/main/boards/bread-compact-wifi/compact_wifi_board.cc @@ -9,6 +9,9 @@ #include "lamp_controller.h" #include "led/single_led.h" #include "assets/lang_config.h" +#ifdef CONFIG_OLED_AUTO_POWER_SAVE +#include "power_save_timer.h" +#endif #include #include @@ -21,6 +24,8 @@ #define TAG "CompactWifiBoard" +static constexpr int kPowerSaveTimeoutSeconds = 60; + class CompactWifiBoard : public WifiBoard { private: i2c_master_bus_handle_t display_i2c_bus_; @@ -31,6 +36,20 @@ class CompactWifiBoard : public WifiBoard { Button touch_button_; Button volume_up_button_; Button volume_down_button_; +#ifdef CONFIG_OLED_AUTO_POWER_SAVE + PowerSaveTimer* power_save_timer_; + + void InitializePowerSaveTimer() { + power_save_timer_ = new PowerSaveTimer(-1, kPowerSaveTimeoutSeconds, -1); + power_save_timer_->OnEnterSleepMode([this]() { + GetDisplay()->SetPowerSaveMode(true); + }); + power_save_timer_->OnExitSleepMode([this]() { + GetDisplay()->SetPowerSaveMode(false); + }); + power_save_timer_->SetEnabled(true); + } +#endif void InitializeDisplayI2c() { i2c_master_bus_config_t bus_config = { @@ -102,6 +121,9 @@ class CompactWifiBoard : public WifiBoard { void InitializeButtons() { boot_button_.OnClick([this]() { +#ifdef CONFIG_OLED_AUTO_POWER_SAVE + power_save_timer_->WakeUp(); +#endif auto& app = Application::GetInstance(); if (app.GetDeviceState() == kDeviceStateStarting) { EnterWifiConfigMode(); @@ -110,13 +132,22 @@ class CompactWifiBoard : public WifiBoard { app.ToggleChatState(); }); touch_button_.OnPressDown([this]() { +#ifdef CONFIG_OLED_AUTO_POWER_SAVE + power_save_timer_->WakeUp(); +#endif Application::GetInstance().StartListening(); }); touch_button_.OnPressUp([this]() { +#ifdef CONFIG_OLED_AUTO_POWER_SAVE + power_save_timer_->WakeUp(); +#endif Application::GetInstance().StopListening(); }); volume_up_button_.OnClick([this]() { +#ifdef CONFIG_OLED_AUTO_POWER_SAVE + power_save_timer_->WakeUp(); +#endif auto codec = GetAudioCodec(); auto volume = codec->output_volume() + 10; if (volume > 100) { @@ -127,11 +158,17 @@ class CompactWifiBoard : public WifiBoard { }); volume_up_button_.OnLongPress([this]() { +#ifdef CONFIG_OLED_AUTO_POWER_SAVE + power_save_timer_->WakeUp(); +#endif GetAudioCodec()->SetOutputVolume(100); GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME); }); volume_down_button_.OnClick([this]() { +#ifdef CONFIG_OLED_AUTO_POWER_SAVE + power_save_timer_->WakeUp(); +#endif auto codec = GetAudioCodec(); auto volume = codec->output_volume() - 10; if (volume < 0) { @@ -142,6 +179,9 @@ class CompactWifiBoard : public WifiBoard { }); volume_down_button_.OnLongPress([this]() { +#ifdef CONFIG_OLED_AUTO_POWER_SAVE + power_save_timer_->WakeUp(); +#endif GetAudioCodec()->SetOutputVolume(0); GetDisplay()->ShowNotification(Lang::Strings::MUTED); }); @@ -160,6 +200,9 @@ class CompactWifiBoard : public WifiBoard { volume_down_button_(VOLUME_DOWN_BUTTON_GPIO) { InitializeDisplayI2c(); InitializeSsd1306Display(); +#ifdef CONFIG_OLED_AUTO_POWER_SAVE + InitializePowerSaveTimer(); +#endif InitializeButtons(); InitializeTools(); } @@ -183,6 +226,20 @@ class CompactWifiBoard : public WifiBoard { virtual Display* GetDisplay() override { return display_; } + + virtual Backlight* GetBacklight() override { + static OledBacklight backlight(static_cast(display_)); + return &backlight; + } + +#ifdef CONFIG_OLED_AUTO_POWER_SAVE + virtual void SetPowerSaveLevel(PowerSaveLevel level) override { + if (level != PowerSaveLevel::LOW_POWER) { + power_save_timer_->WakeUp(); + } + WifiBoard::SetPowerSaveLevel(level); + } +#endif }; DECLARE_BOARD(CompactWifiBoard); diff --git a/main/boards/common/backlight.h b/main/boards/common/backlight.h index 5c09b3d22e..3f015ca015 100644 --- a/main/boards/common/backlight.h +++ b/main/boards/common/backlight.h @@ -13,7 +13,7 @@ class Backlight { ~Backlight(); void RestoreBrightness(); - void SetBrightness(uint8_t brightness, bool permanent = false); + virtual void SetBrightness(uint8_t brightness, bool permanent = false); inline uint8_t brightness() const { return brightness_; } protected: diff --git a/main/display/oled_display.cc b/main/display/oled_display.cc index 74469148a1..a2ae753974 100644 --- a/main/display/oled_display.cc +++ b/main/display/oled_display.cc @@ -1,4 +1,5 @@ #include "oled_display.h" +#include "settings.h" #include "assets/lang_config.h" #include "lvgl_theme.h" #include "lvgl_font.h" @@ -394,3 +395,52 @@ void OledDisplay::SetTheme(Theme* theme) { auto screen = lv_screen_active(); lv_obj_set_style_text_font(screen, text_font, 0); } + +void OledDisplay::SetPowerSaveMode(bool on) { + LvglDisplay::SetPowerSaveMode(on); + if (panel_ != nullptr) { + esp_lcd_panel_disp_on_off(panel_, !on); + } +} + +void OledDisplay::SetContrast(uint8_t contrast) { + if (panel_io_ != nullptr) { + ESP_LOGI(TAG, "SetContrast: %d", contrast); + esp_lcd_panel_io_tx_param(panel_io_, 0x81, &contrast, 1); + } +} + +OledBacklight::OledBacklight(OledDisplay* display) : display_(display) { + brightness_ = 50; +} + +void OledBacklight::SetBrightness(uint8_t brightness, bool permanent) { + if (brightness > 100) { + brightness = 100; + } + if (brightness_ == brightness) { + return; + } + + if (permanent) { + Settings settings("display", true); + settings.SetInt("brightness", brightness); + } + + brightness_ = brightness; + target_brightness_ = brightness; + SetBrightnessImpl(brightness); + ESP_LOGI(TAG, "Set OLED brightness to %d", brightness); +} + +void OledBacklight::SetBrightnessImpl(uint8_t brightness) { + if (brightness <= 5) { + display_->SetPowerSaveMode(true); + } else { + display_->SetPowerSaveMode(false); + // Map 6-100 to 0-255 + // 5 is min brightness, so we map 5-100 to 0-255 roughly + uint8_t contrast = static_cast((brightness) * 255 / 100); + display_->SetContrast(contrast); + } +} diff --git a/main/display/oled_display.h b/main/display/oled_display.h index c7f4077958..d2a6b3c508 100644 --- a/main/display/oled_display.h +++ b/main/display/oled_display.h @@ -2,6 +2,7 @@ #define OLED_DISPLAY_H #include "lvgl_display.h" +#include "backlight.h" #include #include @@ -35,6 +36,18 @@ class OledDisplay : public LvglDisplay { virtual void SetChatMessage(const char* role, const char* content) override; virtual void SetEmotion(const char* emotion) override; virtual void SetTheme(Theme* theme) override; + virtual void SetPowerSaveMode(bool on) override; + void SetContrast(uint8_t contrast); +}; + +class OledBacklight : public Backlight { +public: + OledBacklight(OledDisplay* display); + void SetBrightness(uint8_t brightness, bool permanent = false) override; + void SetBrightnessImpl(uint8_t brightness) override; + +private: + OledDisplay* display_; }; #endif // OLED_DISPLAY_H