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.
This commit is contained in:
Scotty
2026-04-02 09:04:33 +02:00
parent 6836b6967a
commit 51dbfa1a4e
9 changed files with 119 additions and 5 deletions
+6
View File
@@ -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;
+5
View File
@@ -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
+30
View File
@@ -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 nearestinteger 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;
}
+16
View File
@@ -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 <Alt>+<B> 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 <Alt>+<B> (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
+19 -1
View File
@@ -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;
}
}
+8 -1
View File
@@ -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;
};
+8
View File
@@ -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",
+24 -3
View File
@@ -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);
+3
View File
@@ -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<lv_obj_t*> _rowObjs;