Implement device management (#199)

- Added `tt::hal::Device` and functions (de)register devices and search for them.
- Refactored apps: `Power` and `Display` settings apps now use the device API to find devices.
- Implemented the new API for all existing drivers for all devices, including the simulator.
- Updated HAL Configuration to return `std::shared_ptr` instead of raw pointers.
- Added test project for headless tests and implemented tests for the new code.
This commit is contained in:
Ken Van Hoeylandt
2025-02-02 15:16:51 +01:00
committed by GitHub
parent c87200a80d
commit cff0605b0a
54 changed files with 655 additions and 109 deletions
+8 -10
View File
@@ -22,8 +22,10 @@ static uint8_t gamma = 255;
#define ROTATION_270 2
#define ROTATION_90 3
static void onBacklightSliderEvent(lv_event_t* event) {
auto* slider = static_cast<lv_obj_t*>(lv_event_get_target(event));
auto* lvgl_display = lv_display_get_default();
assert(lvgl_display != nullptr);
auto* hal_display = (tt::hal::Display*)lv_display_get_user_data(lvgl_display);
@@ -55,12 +57,8 @@ static void onGammaSliderEvent(lv_event_t* event) {
}
}
static tt::hal::Display* getHalDisplay(lv_obj_t* widget) {
auto* lvgl_display = lv_obj_get_display(widget);
assert(lvgl_display != nullptr);
auto* hal_display = (tt::hal::Display*)lv_display_get_user_data(lvgl_display);
assert(hal_display != nullptr);
return hal_display;
static std::shared_ptr<tt::hal::Display> getHalDisplay() {
return hal::findFirstDevice<hal::Display>(hal::Device::Type::Display);
}
static lv_display_rotation_t orientationSettingToDisplayRotation(uint32_t setting) {
@@ -103,6 +101,9 @@ class DisplayApp : public App {
void onShow(AppContext& app, lv_obj_t* parent) override {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
auto hal_display = getHalDisplay();
assert(hal_display != nullptr);
lvgl::toolbar_create(parent, app);
lv_obj_t* main_wrapper = lv_obj_create(parent);
@@ -131,12 +132,9 @@ class DisplayApp : public App {
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_slider_set_range(gamma_slider, 0, hal_display->getGammaCurveCount());
lv_obj_add_event_cb(gamma_slider, onGammaSliderEvent, LV_EVENT_VALUE_CHANGED, nullptr);
auto* hal_display = getHalDisplay(parent);
assert(hal_display != nullptr);
if (!hal_display->supportsBacklightDuty()) {
lv_slider_set_value(brightness_slider, 255, LV_ANIM_OFF);
lv_obj_add_state(brightness_slider, LV_STATE_DISABLED);
+12 -1
View File
@@ -4,6 +4,7 @@
#include "Tactility/lvgl/Toolbar.h"
#include "Tactility/service/loader/Loader.h"
#include <Tactility/hal/Device.h>
#include <Tactility/Assets.h>
#include <Tactility/Tactility.h>
#include <Tactility/Timer.h>
@@ -33,7 +34,9 @@ class PowerApp : public App {
private:
Timer update_timer = Timer(Timer::Type::Periodic, &onTimer, nullptr);
std::shared_ptr<tt::hal::Power> power = getConfiguration()->hardware->power();
std::shared_ptr<hal::Power> power;
lv_obj_t* enableLabel = nullptr;
lv_obj_t* enableSwitch = nullptr;
lv_obj_t* batteryVoltageLabel = nullptr;
@@ -135,11 +138,19 @@ private:
public:
void onCreate(AppContext& app) override {
power = hal::findFirstDevice<hal::Power>(hal::Device::Type::Power);
}
void onShow(AppContext& app, lv_obj_t* parent) override {
lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN);
lvgl::toolbar_create(parent, app);
if (power == nullptr) {
return;
}
lv_obj_t* wrapper = lv_obj_create(parent);
lv_obj_set_width(wrapper, LV_PCT(100));
lv_obj_set_style_border_width(wrapper, 0, 0);
+33 -15
View File
@@ -1,6 +1,7 @@
#include "Tactility/lvgl/Toolbar.h"
#include <Tactility/Assets.h>
#include <Tactility/hal/Device.h>
#include <Tactility/Tactility.h>
#include <lvgl.h>
@@ -42,17 +43,17 @@ static size_t getSpiTotal() {
}
static void addMemoryBar(lv_obj_t* parent, const char* label, size_t used, size_t total) {
lv_obj_t* container = lv_obj_create(parent);
auto* container = lv_obj_create(parent);
lv_obj_set_size(container, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_pad_all(container, 0, 0);
lv_obj_set_style_border_width(container, 0, 0);
lv_obj_set_flex_flow(container, LV_FLEX_FLOW_ROW);
lv_obj_t* left_label = lv_label_create(container);
auto* left_label = lv_label_create(container);
lv_label_set_text(left_label, label);
lv_obj_set_width(left_label, 60);
lv_obj_t* bar = lv_bar_create(container);
auto* bar = lv_bar_create(container);
lv_obj_set_flex_grow(bar, 1);
if (total > 0) {
@@ -63,7 +64,7 @@ static void addMemoryBar(lv_obj_t* parent, const char* label, size_t used, size_
lv_bar_set_value(bar, (int32_t)used, LV_ANIM_OFF);
lv_obj_t* bottom_label = lv_label_create(parent);
auto* bottom_label = lv_label_create(parent);
lv_label_set_text_fmt(bottom_label, "%u / %u kB", (used / 1024), (total / 1024));
lv_obj_set_width(bottom_label, LV_PCT(100));
lv_obj_set_style_text_align(bottom_label, LV_TEXT_ALIGN_RIGHT, 0);
@@ -90,19 +91,18 @@ static const char* getTaskState(const TaskStatus_t& task) {
}
static void addRtosTask(lv_obj_t* parent, const TaskStatus_t& task) {
lv_obj_t* label = lv_label_create(parent);
auto* label = lv_label_create(parent);
const char* name = (task.pcTaskName == nullptr || task.pcTaskName[0] == 0) ? "(unnamed)" : task.pcTaskName;
lv_label_set_text_fmt(label, "%s (%s)", name, getTaskState(task));
}
static void addRtosTasks(lv_obj_t* parent) {
UBaseType_t count = uxTaskGetNumberOfTasks();
TaskStatus_t* tasks = (TaskStatus_t*)malloc(sizeof(TaskStatus_t) * count);
auto* tasks = (TaskStatus_t*)malloc(sizeof(TaskStatus_t) * count);
uint32_t totalRuntime = 0;
UBaseType_t actual = uxTaskGetSystemState(tasks, count, &totalRuntime);
for (int i = 0; i < actual; ++i) {
const TaskStatus_t& task = tasks[i];
TT_LOG_I(TAG, "Task: %s", task.pcTaskName);
addRtosTask(parent, task);
}
free(tasks);
@@ -110,6 +110,18 @@ static void addRtosTasks(lv_obj_t* parent) {
#endif
static void addDevice(lv_obj_t* parent, const std::shared_ptr<hal::Device>& device) {
auto* label = lv_label_create(parent);
lv_label_set_text(label, device->getName().c_str());
}
static void addDevices(lv_obj_t* parent) {
auto devices = hal::getDevices();
for (const auto& device: devices) {
addDevice(parent, device);
}
}
class SystemInfoApp : public App {
void onShow(AppContext& app, lv_obj_t* parent) override {
@@ -117,16 +129,16 @@ class SystemInfoApp : public App {
lvgl::toolbar_create(parent, app);
// This wrapper automatically has its children added vertically underneath eachother
lv_obj_t* wrapper = lv_obj_create(parent);
auto* wrapper = lv_obj_create(parent);
lv_obj_set_style_border_width(wrapper, 0, 0);
lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN);
lv_obj_set_width(wrapper, LV_PCT(100));
lv_obj_set_flex_grow(wrapper, 1);
// Wrapper for the memory usage bars
lv_obj_t* memory_label = lv_label_create(wrapper);
auto* memory_label = lv_label_create(wrapper);
lv_label_set_text(memory_label, "Memory usage");
lv_obj_t* memory_wrapper = lv_obj_create(wrapper);
auto* memory_wrapper = lv_obj_create(wrapper);
lv_obj_set_flex_flow(memory_wrapper, LV_FLEX_FLOW_COLUMN);
lv_obj_set_size(memory_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
@@ -134,23 +146,29 @@ class SystemInfoApp : public App {
addMemoryBar(memory_wrapper, "SPI", getSpiTotal() - getSpiFree(), getSpiTotal());
#if configUSE_TRACE_FACILITY
lv_obj_t* tasks_label = lv_label_create(wrapper);
auto* tasks_label = lv_label_create(wrapper);
lv_label_set_text(tasks_label, "Tasks");
lv_obj_t* tasks_wrapper = lv_obj_create(wrapper);
auto* tasks_wrapper = lv_obj_create(wrapper);
lv_obj_set_flex_flow(tasks_wrapper, LV_FLEX_FLOW_COLUMN);
lv_obj_set_size(tasks_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
addRtosTasks(tasks_wrapper);
#endif
auto* devices_label = lv_label_create(wrapper);
lv_label_set_text(devices_label, "Devices");
auto* devices_wrapper = lv_obj_create(wrapper);
lv_obj_set_flex_flow(devices_wrapper, LV_FLEX_FLOW_COLUMN);
lv_obj_set_size(devices_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
addDevices(devices_wrapper);
#ifdef ESP_PLATFORM
// Build info
lv_obj_t* build_info_label = lv_label_create(wrapper);
auto* build_info_label = lv_label_create(wrapper);
lv_label_set_text(build_info_label, "Build info");
lv_obj_t* build_info_wrapper = lv_obj_create(wrapper);
auto* build_info_wrapper = lv_obj_create(wrapper);
lv_obj_set_flex_flow(build_info_wrapper, LV_FLEX_FLOW_COLUMN);
lv_obj_set_size(build_info_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_t* esp_idf_version = lv_label_create(build_info_wrapper);
auto* esp_idf_version = lv_label_create(build_info_wrapper);
lv_label_set_text_fmt(esp_idf_version, "IDF version: %d.%d.%d", ESP_IDF_VERSION_MAJOR, ESP_IDF_VERSION_MINOR, ESP_IDF_VERSION_PATCH);
#endif
}
+21 -15
View File
@@ -11,14 +11,16 @@
namespace tt::lvgl {
#define TAG "lvglinit"
#define TAG "lvgl_init"
bool initDisplay(const hal::Configuration& config) {
static std::shared_ptr<tt::hal::Display> initDisplay(const hal::Configuration& config) {
assert(config.createDisplay);
auto* display = config.createDisplay();
auto display = config.createDisplay();
assert(display != nullptr);
if (!display->start()) {
TT_LOG_E(TAG, "Display start failed");
return false;
return nullptr;
}
lv_display_t* lvgl_display = display->getLvglDisplay();
@@ -32,17 +34,17 @@ bool initDisplay(const hal::Configuration& config) {
// esp_lvgl_port users user_data by default, so we have to modify the source
// this is a check for when we upgrade esp_lvgl_port and forget to modify it again
assert(existing_display_user_data == nullptr);
lv_display_set_user_data(lvgl_display, display);
lv_display_set_user_data(lvgl_display, display.get());
lv_display_rotation_t rotation = app::display::getRotation();
if (rotation != lv_disp_get_rotation(lv_disp_get_default())) {
lv_disp_set_rotation(lv_disp_get_default(), static_cast<lv_display_rotation_t>(rotation));
}
return true;
return display;
}
bool initTouch(hal::Display* display, hal::Touch* touch) {
static bool initTouch(const std::shared_ptr<hal::Display>& display, const std::shared_ptr<hal::Touch>& touch) {
TT_LOG_I(TAG, "Touch init");
assert(display);
assert(touch);
@@ -54,14 +56,14 @@ bool initTouch(hal::Display* display, hal::Touch* touch) {
}
}
bool initKeyboard(hal::Display* display, hal::Keyboard* keyboard) {
static bool initKeyboard(const std::shared_ptr<hal::Display>& display, const std::shared_ptr<hal::Keyboard>& keyboard) {
TT_LOG_I(TAG, "Keyboard init");
assert(display);
assert(keyboard);
if (keyboard->isAttached()) {
if (keyboard->start(display->getLvglDisplay())) {
lv_indev_t* keyboard_indev = keyboard->getLvglIndev();
lv_indev_set_user_data(keyboard_indev, keyboard);
lv_indev_set_user_data(keyboard_indev, keyboard.get());
tt::lvgl::keypad_set_indev(keyboard_indev);
TT_LOG_I(TAG, "Keyboard started");
return true;
@@ -85,20 +87,24 @@ void init(const hal::Configuration& config) {
return;
}
if (!initDisplay(config)) {
auto display = initDisplay(config);
if (display == nullptr) {
return;
}
hal::registerDevice(display);
hal::Display* display = getDisplay();
hal::Touch* touch = display->createTouch();
auto touch = display->createTouch();
if (touch != nullptr) {
hal::registerDevice(touch);
initTouch(display, touch);
}
if (config.createKeyboard) {
hal::Keyboard* keyboard = config.createKeyboard();
initKeyboard(display, keyboard);
auto keyboard = config.createKeyboard();
if (keyboard != nullptr) {
hal::registerDevice(keyboard);
initKeyboard(display, keyboard);
}
}
TT_LOG_I(TAG, "Finished");