From 51dbfa1a4e1b3110ec470e877aaf861f1c92e7fb Mon Sep 17 00:00:00 2001 From: Scotty Date: Thu, 2 Apr 2026 09:04:33 +0200 Subject: [PATCH] Add keyboard backlight config and control - Add user config for brightness and auto-on/off control. - Auto-switch backlight on/off according to power state. --- src/config/UserConfig.cpp | 6 ++++++ src/config/UserConfig.h | 5 +++++ src/hal/Keyboard.cpp | 30 +++++++++++++++++++++++++++++ src/hal/Keyboard.h | 16 +++++++++++++++ src/hal/Power.cpp | 20 ++++++++++++++++++- src/hal/Power.h | 9 ++++++++- src/main.cpp | 8 ++++++++ src/ui/screens/LvSettingsScreen.cpp | 27 +++++++++++++++++++++++--- src/ui/screens/LvSettingsScreen.h | 3 +++ 9 files changed, 119 insertions(+), 5 deletions(-) diff --git a/src/config/UserConfig.cpp b/src/config/UserConfig.cpp index 43e41aa..e03cc6a 100644 --- a/src/config/UserConfig.cpp +++ b/src/config/UserConfig.cpp @@ -52,6 +52,9 @@ bool UserConfig::parseJson(const String& json) { if (rawBri > 100) rawBri = rawBri * 100 / 255; // Migrate from PWM to percentage _settings.brightness = constrain(rawBri, 1, 100); _settings.denseFontMode = doc["dense_font"] | false; + _settings.keyboardBrightness = doc["kb_brightness"] | 100; + _settings.keyboardAutoOn = doc["kb_auto_on"] | false; + _settings.keyboardAutoOff = doc["kb_auto_off"] | false; _settings.trackballSpeed = doc["trackball_speed"] | 3; _settings.touchSensitivity = doc["touch_sens"] | 3; _settings.bleEnabled = doc["ble_enabled"] | false; @@ -102,6 +105,9 @@ String UserConfig::serializeToJson() const { doc["screen_off"] = _settings.screenOffTimeout; doc["brightness"] = _settings.brightness; doc["dense_font"] = _settings.denseFontMode; + doc["kb_brightness"] = _settings.keyboardBrightness; + doc["kb_auto_on"] = _settings.keyboardAutoOn; + doc["kb_auto_off"] = _settings.keyboardAutoOff; doc["trackball_speed"] = _settings.trackballSpeed; doc["touch_sens"] = _settings.touchSensitivity; doc["ble_enabled"] = _settings.bleEnabled; diff --git a/src/config/UserConfig.h b/src/config/UserConfig.h index 186d634..7e2e56e 100644 --- a/src/config/UserConfig.h +++ b/src/config/UserConfig.h @@ -42,6 +42,11 @@ struct UserSettings { uint8_t brightness = 100; // Percentage 1-100 bool denseFontMode = false; // T-Deck Plus: adaptive font toggle + // Keyboard + uint8_t keyboardBrightness = 100; // Percentage 1-100 + bool keyboardAutoOn = false; // Backlight ON when switching to ACTIVE power state + bool keyboardAutoOff = false; // Backlight OFF when switching from ACTIVE power state + // Trackball uint8_t trackballSpeed = 3; // 1-5 sensitivity diff --git a/src/hal/Keyboard.cpp b/src/hal/Keyboard.cpp index 1708a03..03e7bba 100644 --- a/src/hal/Keyboard.cpp +++ b/src/hal/Keyboard.cpp @@ -91,3 +91,33 @@ void Keyboard::update() { _hasEvent = true; } + +bool Keyboard::setBacklightBrightness(uint8_t percent) { + percent = constrain(percent, 1, 100); + // [1, 100] % -> [31, 255] PWM + constexpr uint16_t SCALE = 255 - 31; + constexpr uint16_t DIV = 100 - 1; + uint16_t tmp = (uint16_t)(percent - 1) * SCALE; + tmp = (tmp + DIV / 2) / DIV; // +DIV/2 for nearest‑integer rounding + _backlightBrightness = (uint8_t)(31 + tmp); + + Wire.beginTransmission(KB_I2C_ADDR); + Wire.write(0x02); // LILYGO_KB_ALT_B_BRIGHTNESS_CMD + Wire.write(_backlightBrightness); + return Wire.endTransmission() == 0; +} + +bool Keyboard::backlightOn() { + return setBrightness(_backlightBrightness); +} + +bool Keyboard::backlightOff() { + return setBrightness(0); +} + +bool Keyboard::setBrightness(uint8_t pwm) { + Wire.beginTransmission(KB_I2C_ADDR); + Wire.write(0x01); // LILYGO_KB_BRIGHTNESS_CMD + Wire.write(pwm); + return Wire.endTransmission() == 0; +} diff --git a/src/hal/Keyboard.h b/src/hal/Keyboard.h index 2245c6f..74afd38 100644 --- a/src/hal/Keyboard.h +++ b/src/hal/Keyboard.h @@ -42,8 +42,23 @@ public: bool hasEvent() const { return _hasEvent; } const KeyEvent& getEvent() const { return _event; } + // Backlight control + // NOTES: + // - Backlight control has been added to the ESP32-C3 F/W on 2024-12-25, + // there's no way to detect if the installed F/W supports it + // (no I2C reads except for key states, I2C writes are always ACK'ed). + // - Backlight toggle via + is implemented in the ESP32-C3 F/W, + // we can't track the actual backlight ON/OFF state. + // - The ESP32-C3 F/W uses 2 brightness settings, one for + (which doesn't + // change the current brightness), the other one for the current brightness. + // The range for the 1st one is limited to [31, 255], we use it for the 2nd one, too. + bool setBacklightBrightness(uint8_t percent); // doesn't change the current brightness + bool backlightOn(); + bool backlightOff(); + private: uint8_t readKey(uint8_t* modOut); + static bool setBrightness(uint8_t pwm); InputMode _mode = InputMode::Navigation; KeyEvent _event = {}; @@ -51,6 +66,7 @@ private: uint8_t _lastKey = 0; bool _altHeld = false; // Software Alt tracking unsigned long _altPressTime = 0; // When Alt was detected + uint8_t _backlightBrightness = 255; // [31, 255] static Keyboard* _instance; static int _debugCount; // Log first N keypresses diff --git a/src/hal/Power.cpp b/src/hal/Power.cpp index 2724fe4..a55fdea 100644 --- a/src/hal/Power.cpp +++ b/src/hal/Power.cpp @@ -1,8 +1,10 @@ #include "Power.h" #include "hal/Display.h" +#include "hal/Keyboard.h" -// Forward declaration — display instance provided externally +// Forward declarations — display & keyboard instances provided externally extern Display display; +extern Keyboard keyboard; void Power::enablePeripherals() { // CRITICAL: GPIO 10 must be HIGH to enable all T-Deck Plus peripherals @@ -66,6 +68,13 @@ void Power::setBrightness(uint8_t percent) { } } +void Power::setKbBrightness(uint8_t percent, bool apply) { + keyboard.setBacklightBrightness(constrain(percent, 1, 100)); + if (apply) { // Show the new brightness + keyboard.backlightOn(); + } +} + void Power::loop() { unsigned long elapsed = millis() - _lastActivity; @@ -102,13 +111,22 @@ void Power::setState(State newState) { display.wakeup(); } display.setBrightness(percentToPWM(_brightnessPct)); + if (_kbAutoOn) { + keyboard.backlightOn(); + } break; case DIMMED: display.setBrightness(DIM_PWM); + if (_kbAutoOff) { + keyboard.backlightOff(); + } break; case SCREEN_OFF: display.setBrightness(0); display.sleep(); + if (_kbAutoOff) { + keyboard.backlightOff(); + } break; } } diff --git a/src/hal/Power.h b/src/hal/Power.h index 77a12f8..02e5545 100644 --- a/src/hal/Power.h +++ b/src/hal/Power.h @@ -20,11 +20,16 @@ public: float batteryVoltage() const; int batteryPercent() const; - // Backlight — accepts percentage 1-100 + // Display backlight — accepts percentage 1-100 void setBrightness(uint8_t percent); void setDimTimeout(uint16_t seconds) { _dimTimeout = seconds * 1000UL; } void setOffTimeout(uint16_t seconds) { _offTimeout = seconds * 1000UL; } + // Keyboard backlight — accepts percentage 1-100 + void setKbBrightness(uint8_t percent, bool apply=false); + void setKbAutoOn(bool enable) { _kbAutoOn = enable; } + void setKbAutoOff(bool enable) { _kbAutoOff = enable; } + enum State { ACTIVE, DIMMED, SCREEN_OFF }; State state() const { return _state; } bool isScreenOn() const { return _state != SCREEN_OFF; } @@ -40,4 +45,6 @@ private: unsigned long _offTimeout = 60000; uint8_t _brightnessPct = 100; // User brightness as 1-100% static constexpr uint8_t DIM_PWM = 40; // ~15% PWM when dimmed + bool _kbAutoOn = false; + bool _kbAutoOff = false; }; diff --git a/src/main.cpp b/src/main.cpp index b393e4e..81cf6ab 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -654,6 +654,9 @@ void setup() { powerMgr.setDimTimeout(userConfig.settings().screenDimTimeout); powerMgr.setOffTimeout(userConfig.settings().screenOffTimeout); powerMgr.setBrightness(userConfig.settings().brightness); + powerMgr.setKbBrightness(userConfig.settings().keyboardBrightness); + powerMgr.setKbAutoOn(userConfig.settings().keyboardAutoOn); + powerMgr.setKbAutoOff(userConfig.settings().keyboardAutoOff); // Step 24.5: GPS init #if HAS_GPS @@ -903,6 +906,11 @@ void setup() { } } + if (userConfig.settings().keyboardAutoOn) { + // We are in ACTIVE power state here, switch keyboard backlight ON + keyboard.backlightOn(); + } + Serial.println("[BOOT] RatDeck ready"); Serial.printf("[BOOT] Summary: radio=%s flash=%s sd=%s\n", radioOnline ? "ONLINE" : "OFFLINE", diff --git a/src/ui/screens/LvSettingsScreen.cpp b/src/ui/screens/LvSettingsScreen.cpp index bdc2188..e2247e8 100644 --- a/src/ui/screens/LvSettingsScreen.cpp +++ b/src/ui/screens/LvSettingsScreen.cpp @@ -218,18 +218,32 @@ void LvSettingsScreen::buildItems() { // Display & Input int dispStart = idx; - _items.push_back({"Brightness", SettingType::INTEGER, + _items.push_back({"Display Brightness", SettingType::INTEGER, [&s]() { return s.brightness; }, [&s](int v) { s.brightness = v; }, [](int v) { return String(v) + "%"; }, 5, 100, 5}); idx++; - _items.push_back({"Dim Timeout", SettingType::INTEGER, + _items.push_back({"Display Dim Timeout", SettingType::INTEGER, [&s]() { return s.screenDimTimeout; }, [&s](int v) { s.screenDimTimeout = v; }, [](int v) { return String(v) + "s"; }, 5, 300, 5}); idx++; - _items.push_back({"Off Timeout", SettingType::INTEGER, + _items.push_back({"Display Off Timeout", SettingType::INTEGER, [&s]() { return s.screenOffTimeout; }, [&s](int v) { s.screenOffTimeout = v; }, [](int v) { return String(v) + "s"; }, 10, 600, 10}); idx++; + _items.push_back({"Keyboard Backlight Brightness", SettingType::INTEGER, + [&s]() { return s.keyboardBrightness; }, [&s](int v) { s.keyboardBrightness = v; }, + [](int v) { return String(v) + "%"; }, 5, 100, 5}); + idx++; + _items.push_back({"Keyboard Backlight Auto-ON", SettingType::TOGGLE, + [&s]() { return s.keyboardAutoOn ? 1 : 0; }, + [&s](int v) { s.keyboardAutoOn = (v != 0); }, + [](int v) { return v ? String("ON") : String("OFF"); }}); + idx++; + _items.push_back({"Keyboard Backlight Auto-OFF", SettingType::TOGGLE, + [&s]() { return s.keyboardAutoOff ? 1 : 0; }, + [&s](int v) { s.keyboardAutoOff = (v != 0); }, + [](int v) { return v ? String("ON") : String("OFF"); }}); + idx++; _items.push_back({"Trackball Speed", SettingType::INTEGER, [&s]() { return s.trackballSpeed; }, [&s](int v) { s.trackballSpeed = v; }, [](int v) { return String(v); }, 1, 5, 1}); @@ -702,6 +716,7 @@ void LvSettingsScreen::onEnter() { _textEditing = false; _confirmingReset = false; _confirmingDevMode = false; + _kbBrightness = _cfg ? _cfg->settings().keyboardBrightness : 0; rebuildCategoryList(); } @@ -1334,6 +1349,12 @@ void LvSettingsScreen::applyAndSave() { _power->setBrightness(s.brightness); _power->setDimTimeout(s.screenDimTimeout); _power->setOffTimeout(s.screenOffTimeout); + if (_kbBrightness != s.keyboardBrightness) { + _power->setKbBrightness(s.keyboardBrightness, true); + _kbBrightness = s.keyboardBrightness; + } + _power->setKbAutoOn(s.keyboardAutoOn); + _power->setKbAutoOff(s.keyboardAutoOff); } if (_radio && _radio->isRadioOnline()) { _radio->setFrequency(s.loraFrequency); diff --git a/src/ui/screens/LvSettingsScreen.h b/src/ui/screens/LvSettingsScreen.h index 511f88c..ee29c42 100644 --- a/src/ui/screens/LvSettingsScreen.h +++ b/src/ui/screens/LvSettingsScreen.h @@ -166,6 +166,9 @@ private: void snapshotTCPSettings(); bool tcpSettingsChanged() const; + // Keyboard backlight change detection + uint8_t _kbBrightness = 0; + // LVGL widgets lv_obj_t* _scrollContainer = nullptr; std::vector _rowObjs;