Feature additions (#434)
Lots of things "ported" over from the "enhanced" fork. With some adjustments here and there. KeyboardBacklight driver (for T-Deck only currently) Trackball driver (for T-Deck only currently) Keyboard backlight sleep/wake (for T-Deck only currently...also requires keyboard firmware update) Display sleep/wake Files - create file/folder Keyboard settings (for T-Deck only currently) Time & Date settings tweaks Locale settings tweaks Systeminfo additions Espnow wifi coexist initI2cDevices - moved to T-deck init.cpp / initBoot KeyboardInitService - removed, moved to T-deck init.cpp / initBoot Adjusted TIMER_UPDATE_INTERVAL to 2 seconds. Added lock to ActionCreateFolder Maybe missed some things in the list. Display wake could do with some kind of block on wake first touch to prevent UI elements being hit when waking device with touch. Same with encoder/trackball/keyboard press i guess. The original code was written by @cscott0108 at https://github.com/cscott0108/tactility-enhanced-t-deck
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
#include <Tactility/service/ServiceRegistration.h>
|
||||
#include <Tactility/service/ServiceManifest.h>
|
||||
#include <Tactility/service/ServiceContext.h>
|
||||
#include <Tactility/Timer.h>
|
||||
#include <Tactility/lvgl/LvglSync.h>
|
||||
#include <Tactility/settings/DisplaySettings.h>
|
||||
#include <Tactility/hal/display/DisplayDevice.h>
|
||||
|
||||
namespace tt::service::displayidle {
|
||||
|
||||
constexpr auto* TAG = "DisplayIdle";
|
||||
|
||||
class DisplayIdleService final : public Service {
|
||||
|
||||
std::unique_ptr<Timer> timer;
|
||||
bool displayDimmed = false;
|
||||
settings::display::DisplaySettings cachedDisplaySettings;
|
||||
|
||||
static std::shared_ptr<hal::display::DisplayDevice> getDisplay() {
|
||||
return hal::findFirstDevice<hal::display::DisplayDevice>(hal::Device::Type::Display);
|
||||
}
|
||||
|
||||
void tick() {
|
||||
// Settings are now cached and event-driven (no file I/O in timer callback!)
|
||||
// This prevents watchdog timeout from blocking the Timer Service task
|
||||
|
||||
// Query LVGL inactivity once for both checks
|
||||
uint32_t inactive_ms = 0;
|
||||
if (lvgl::lock(100)) {
|
||||
inactive_ms = lv_disp_get_inactive_time(nullptr);
|
||||
lvgl::unlock();
|
||||
}
|
||||
|
||||
// Handle display backlight
|
||||
auto display = getDisplay();
|
||||
if (display != nullptr && display->supportsBacklightDuty()) {
|
||||
// If timeout disabled, ensure backlight restored if we had dimmed it
|
||||
if (!cachedDisplaySettings.backlightTimeoutEnabled || cachedDisplaySettings.backlightTimeoutMs == 0) {
|
||||
if (displayDimmed) {
|
||||
display->setBacklightDuty(cachedDisplaySettings.backlightDuty);
|
||||
displayDimmed = false;
|
||||
}
|
||||
} else {
|
||||
if (!displayDimmed && inactive_ms >= cachedDisplaySettings.backlightTimeoutMs) {
|
||||
display->setBacklightDuty(0);
|
||||
displayDimmed = true;
|
||||
} else if (displayDimmed && inactive_ms < 100) {
|
||||
display->setBacklightDuty(cachedDisplaySettings.backlightDuty);
|
||||
displayDimmed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
bool onStart(TT_UNUSED ServiceContext& service) override {
|
||||
// Load settings once at startup and cache them
|
||||
// This eliminates file I/O from timer callback (prevents watchdog timeout)
|
||||
cachedDisplaySettings = settings::display::loadOrGetDefault();
|
||||
|
||||
// Note: Settings changes require service restart to take effect
|
||||
// TODO: Add DisplaySettingsChanged events for dynamic updates
|
||||
|
||||
timer = std::make_unique<Timer>(Timer::Type::Periodic, [this]{ this->tick(); });
|
||||
timer->setThreadPriority(Thread::Priority::Lower);
|
||||
timer->start(250); // check 4x per second for snappy restore
|
||||
return true;
|
||||
}
|
||||
|
||||
void onStop(TT_UNUSED ServiceContext& service) override {
|
||||
if (timer) {
|
||||
timer->stop();
|
||||
timer = nullptr;
|
||||
}
|
||||
// Ensure display restored on stop
|
||||
auto display = getDisplay();
|
||||
if (display && displayDimmed) {
|
||||
display->setBacklightDuty(cachedDisplaySettings.backlightDuty);
|
||||
displayDimmed = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
extern const ServiceManifest manifest = {
|
||||
.id = "DisplayIdle",
|
||||
.createService = create<DisplayIdleService>
|
||||
};
|
||||
|
||||
}
|
||||
@@ -34,18 +34,29 @@ static bool disableWifiService() {
|
||||
}
|
||||
|
||||
bool initWifi(const EspNowConfig& config) {
|
||||
// ESP-NOW can coexist with WiFi STA mode; only preserve WiFi state if already connected
|
||||
auto wifi_state = wifi::getRadioState();
|
||||
bool wifi_was_connected = (wifi_state == wifi::RadioState::ConnectionActive);
|
||||
|
||||
// If WiFi is off or in other states, temporarily disable it to initialize ESP-NOW
|
||||
// If WiFi is already connected, keep it running and just add ESP-NOW on top
|
||||
if (!wifi_was_connected && wifi_state != wifi::RadioState::Off && wifi_state != wifi::RadioState::OffPending) {
|
||||
if (!disableWifiService()) {
|
||||
TT_LOG_E(TAG, "Failed to disable wifi");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
wifi_mode_t mode;
|
||||
if (config.mode == Mode::Station) {
|
||||
// Use STA mode to allow coexistence with normal WiFi connection
|
||||
mode = wifi_mode_t::WIFI_MODE_STA;
|
||||
} else {
|
||||
mode = wifi_mode_t::WIFI_MODE_AP;
|
||||
}
|
||||
|
||||
// Only reinitialize WiFi if it's not already running
|
||||
if (wifi::getRadioState() == wifi::RadioState::Off) {
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
if (esp_wifi_init(&cfg) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "esp_wifi_init() failed");
|
||||
@@ -66,6 +77,7 @@ bool initWifi(const EspNowConfig& config) {
|
||||
TT_LOG_E(TAG, "esp_wifi_start() failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (esp_wifi_set_channel(config.channel, WIFI_SECOND_CHAN_NONE) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "esp_wifi_set_channel() failed");
|
||||
@@ -85,23 +97,19 @@ bool initWifi(const EspNowConfig& config) {
|
||||
}
|
||||
}
|
||||
|
||||
TT_LOG_I(TAG, "WiFi initialized for ESP-NOW (preserved existing connection: %s)", wifi_was_connected ? "yes" : "no");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool deinitWifi() {
|
||||
if (esp_wifi_stop() != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Failed to stop radio");
|
||||
return false;
|
||||
}
|
||||
// Don't deinitialize WiFi completely - just disable ESP-NOW
|
||||
// This allows normal WiFi connection to continue
|
||||
// Only stop/deinit if WiFi was originally off
|
||||
|
||||
if (esp_wifi_set_mode(WIFI_MODE_NULL) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Failed to unset mode");
|
||||
}
|
||||
|
||||
if (esp_wifi_deinit() != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Failed to deinit");
|
||||
}
|
||||
// Since we're only using WiFi for ESP-NOW, we can safely keep it in a minimal state
|
||||
// or shut it down. For now, keep it running to support STA + ESP-NOW coexistence.
|
||||
|
||||
TT_LOG_I(TAG, "ESP-NOW WiFi deinitialized (WiFi service continues independently)");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
#ifdef ESP_PLATFORM
|
||||
|
||||
#include <Tactility/service/ServiceRegistration.h>
|
||||
#include <Tactility/service/ServiceManifest.h>
|
||||
#include <Tactility/service/ServiceContext.h>
|
||||
#include <Tactility/Timer.h>
|
||||
#include <Tactility/lvgl/LvglSync.h>
|
||||
#include <Tactility/settings/KeyboardSettings.h>
|
||||
#include <Tactility/hal/keyboard/KeyboardDevice.h>
|
||||
|
||||
namespace keyboardbacklight {
|
||||
bool setBrightness(uint8_t brightness);
|
||||
}
|
||||
|
||||
namespace tt::service::keyboardidle {
|
||||
|
||||
constexpr auto* TAG = "KeyboardIdle";
|
||||
|
||||
class KeyboardIdleService final : public Service {
|
||||
|
||||
std::unique_ptr<Timer> timer;
|
||||
bool keyboardDimmed = false;
|
||||
settings::keyboard::KeyboardSettings cachedKeyboardSettings;
|
||||
|
||||
static std::shared_ptr<hal::keyboard::KeyboardDevice> getKeyboard() {
|
||||
return hal::findFirstDevice<hal::keyboard::KeyboardDevice>(hal::Device::Type::Keyboard);
|
||||
}
|
||||
|
||||
void tick() {
|
||||
// Settings are now cached and event-driven (no file I/O in timer callback!)
|
||||
// This prevents watchdog timeout from blocking the Timer Service task
|
||||
|
||||
// Query LVGL inactivity once for both checks
|
||||
uint32_t inactive_ms = 0;
|
||||
if (lvgl::lock(100)) {
|
||||
inactive_ms = lv_disp_get_inactive_time(nullptr);
|
||||
lvgl::unlock();
|
||||
}
|
||||
|
||||
// Handle keyboard backlight
|
||||
auto keyboard = getKeyboard();
|
||||
if (keyboard != nullptr && keyboard->isAttached()) {
|
||||
// If timeout disabled, ensure backlight restored if we had dimmed it
|
||||
if (!cachedKeyboardSettings.backlightTimeoutEnabled || cachedKeyboardSettings.backlightTimeoutMs == 0) {
|
||||
if (keyboardDimmed) {
|
||||
keyboardbacklight::setBrightness(cachedKeyboardSettings.backlightEnabled ? cachedKeyboardSettings.backlightBrightness : 0);
|
||||
keyboardDimmed = false;
|
||||
}
|
||||
} else {
|
||||
if (!keyboardDimmed && inactive_ms >= cachedKeyboardSettings.backlightTimeoutMs) {
|
||||
keyboardbacklight::setBrightness(0);
|
||||
keyboardDimmed = true;
|
||||
} else if (keyboardDimmed && inactive_ms < 100) {
|
||||
keyboardbacklight::setBrightness(cachedKeyboardSettings.backlightEnabled ? cachedKeyboardSettings.backlightBrightness : 0);
|
||||
keyboardDimmed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
bool onStart(TT_UNUSED ServiceContext& service) override {
|
||||
// Load settings once at startup and cache them
|
||||
// This eliminates file I/O from timer callback (prevents watchdog timeout)
|
||||
cachedKeyboardSettings = settings::keyboard::loadOrGetDefault();
|
||||
|
||||
// Note: Settings changes require service restart to take effect
|
||||
// TODO: Add KeyboardSettingsChanged events for dynamic updates
|
||||
|
||||
timer = std::make_unique<Timer>(Timer::Type::Periodic, [this]{ this->tick(); });
|
||||
timer->setThreadPriority(Thread::Priority::Lower);
|
||||
timer->start(250); // check 4x per second for snappy restore
|
||||
return true;
|
||||
}
|
||||
|
||||
void onStop(TT_UNUSED ServiceContext& service) override {
|
||||
if (timer) {
|
||||
timer->stop();
|
||||
timer = nullptr;
|
||||
}
|
||||
// Ensure keyboard restored on stop
|
||||
auto keyboard = getKeyboard();
|
||||
if (keyboard && keyboardDimmed) {
|
||||
keyboardbacklight::setBrightness(cachedKeyboardSettings.backlightEnabled ? cachedKeyboardSettings.backlightBrightness : 0);
|
||||
keyboardDimmed = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
extern const ServiceManifest manifest = {
|
||||
.id = "KeyboardIdle",
|
||||
.createService = create<KeyboardIdleService>
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -8,7 +8,7 @@
|
||||
namespace tt::service::memorychecker {
|
||||
|
||||
constexpr const char* TAG = "MemoryChecker";
|
||||
constexpr TickType_t TIMER_UPDATE_INTERVAL = 1000U / portTICK_PERIOD_MS;
|
||||
constexpr TickType_t TIMER_UPDATE_INTERVAL = 2000U / portTICK_PERIOD_MS;
|
||||
|
||||
// Total memory (in bytes) that should be free before warnings occur
|
||||
constexpr auto TOTAL_FREE_THRESHOLD = 10'000;
|
||||
|
||||
Reference in New Issue
Block a user