knxdisplay/main/main.cpp
2026-02-01 21:48:36 +01:00

144 lines
4.6 KiB
C++

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_lv_adapter.h"
#include "esp_lv_decoder.h"
#include "lvgl.h"
#include "Display.hpp"
#include "Touch.hpp"
#include "Gui.hpp"
#include "WidgetManager.hpp"
#include "Nvs.hpp"
#include "KnxWorker.hpp"
#include "Wifi.hpp"
#include "Hardware/Eth.hpp"
#include "WebServer.hpp"
#include "SdCard.hpp"
#include "Fonts.hpp"
#define TAG "App"
static void knx_task(void* arg) {
KnxWorker* worker = static_cast<KnxWorker*>(arg);
worker->init();
while (true) {
worker->loop();
vTaskDelay(pdMS_TO_TICKS(10));
}
}
// This is a simple wrapper for the application logic
class Application {
public:
void init() {
// Initialize hardware
nvs.init();
//Wifi::instance().init();
Eth::instance().init();
display.init();
touch.init();
// Initialize LVGL adapter
esp_lv_adapter_config_t cfg = ESP_LV_ADAPTER_DEFAULT_CONFIG();
cfg.stack_in_psram = true; // Use PSRAM for stack to save internal RAM
cfg.task_stack_size = 48 * 1024;
ESP_ERROR_CHECK(esp_lv_adapter_init(&cfg));
// Register display
esp_lv_adapter_display_config_t disp_cfg = ESP_LV_ADAPTER_DISPLAY_MIPI_DEFAULT_CONFIG(
display.getPanelHandle(),
NULL,
800, // Horizontal resolution
1280, // Vertical resolution
ESP_LV_ADAPTER_ROTATE_90 // Rotation
);
disp_cfg.profile.buffer_height = 40; // Reduced to 10 (~25KB) to fit in Internal RAM
disp_cfg.profile.use_psram = true;
lv_disp_t* lv_display = esp_lv_adapter_register_display(&disp_cfg);
assert(lv_display != NULL);
// Register touch
esp_lv_adapter_touch_config_t touch_cfg = ESP_LV_ADAPTER_TOUCH_DEFAULT_CONFIG(lv_display, touch.getTouchHandle());
lv_indev_t* lv_touch = esp_lv_adapter_register_touch(&touch_cfg);
assert(lv_touch != NULL);
lv_indev_set_user_data(lv_touch, &touch); // Set 'this' Touch object as user data
lv_indev_set_read_cb(lv_touch, Touch::lv_indev_read_cb); // Register the static callback
ESP_ERROR_CHECK(esp_lv_adapter_start());
// Test LVGL lock right after start
ESP_LOGI(TAG, "Testing LVGL lock after adapter start...");
vTaskDelay(pdMS_TO_TICKS(100));
if (esp_lv_adapter_lock(1000) == ESP_OK) {
ESP_LOGI(TAG, "LVGL lock OK after adapter start");
esp_lv_adapter_unlock();
} else {
ESP_LOGE(TAG, "LVGL lock BLOCKED right after adapter start!");
}
ESP_LOGI(TAG, "INIT SUCCESS");
}
void run() {
// Initialize SD card for web content and config storage
if (!SdCard::instance().init()) {
ESP_LOGW(TAG, "SD card not available, using defaults");
}
// Initialize hardware JPEG/PNG decoder for LVGL (uses ESP32-P4 hardware acceleration)
ESP_LOGI(TAG, "INIT IMAGE DECODER");
esp_lv_decoder_handle_t decoder_handle = NULL;
esp_err_t dec_err = esp_lv_decoder_init(&decoder_handle);
if (dec_err != ESP_OK) {
ESP_LOGW(TAG, "Failed to init image decoder: %s", esp_err_to_name(dec_err));
} else {
ESP_LOGI(TAG, "Hardware image decoder initialized");
}
ESP_LOGI(TAG, "START KNX");
BaseType_t knx_ok = xTaskCreatePinnedToCore(
knx_task, "knx", 4096, &Gui::knxWorker, 5, nullptr, 1);
if (knx_ok != pdPASS) {
ESP_LOGE(TAG, "Failed to start KNX task");
}
ESP_LOGI(TAG, "START WEBSERVER");
WebServer::instance().start();
ESP_LOGI(TAG, "INIT FONTS");
Fonts::init();
ESP_LOGI(TAG, "CREATE GUI");
gui.create();
// Force full screen redraw to initialize DMA2D hardware resources at startup
// This prevents lazy initialization during first button press which can cause freezes
ESP_LOGI(TAG, "Warming up display hardware...");
vTaskDelay(pdMS_TO_TICKS(100));
if (esp_lv_adapter_lock(1000) == ESP_OK) {
lv_obj_invalidate(lv_scr_act());
lv_refr_now(lv_display_get_default());
esp_lv_adapter_unlock();
}
vTaskDelay(pdMS_TO_TICKS(200)); // Let the refresh complete
ESP_LOGI(TAG, "Display hardware ready");
ESP_LOGI(TAG, "Application running");
while (true) {
vTaskDelay(pdMS_TO_TICKS(10));
}
}
private:
Display display;
Touch touch;
Gui gui;
Nvs nvs;
};
extern "C" void app_main(void)
{
ESP_LOGI(TAG, "Starting Application");
Application app;
app.init();
app.run();
}