Webserver

This commit is contained in:
Thomas Peterson 2026-01-24 09:25:30 +01:00
parent da30730029
commit f34eb810da
7 changed files with 2139 additions and 506 deletions

View File

@ -2,6 +2,7 @@
#include "driver/i2c_master.h"
#include "esp_log.h"
#include "esp_lcd_touch_gt911.h"
#include "WidgetManager.hpp"
// Common display resolutions, used for touch dimensions
#define LCD_H_RES 800
@ -75,6 +76,7 @@ void Touch::lv_indev_read_cb(lv_indev_t *indev, lv_indev_data_t *data)
data->point.x = x[0];
data->point.y = y[0];
data->state = LV_INDEV_STATE_PRESSED;
WidgetManager::instance().onUserActivity();
} else {
data->state = LV_INDEV_STATE_RELEASED;
}

View File

@ -6,14 +6,28 @@
// Maximum number of widgets
static constexpr size_t MAX_WIDGETS = 32;
static constexpr size_t MAX_SCREENS = 8;
static constexpr size_t MAX_TEXT_LEN = 32;
static constexpr size_t MAX_SCREEN_NAME_LEN = 24;
enum class WidgetType : uint8_t {
LABEL = 0,
BUTTON = 1,
LED = 2,
// Future: GAUGE, IMAGE, ARC, etc.
};
enum class ScreenMode : uint8_t {
FULLSCREEN = 0,
MODAL = 1,
};
enum class ButtonAction : uint8_t {
KNX = 0,
JUMP = 1,
BACK = 2,
};
// Text source: static text or KNX group address
enum class TextSource : uint8_t {
STATIC = 0, // Static text
@ -77,9 +91,11 @@ struct WidgetConfig {
// Button specific
bool isToggle; // For buttons: toggle mode
uint16_t knxAddressWrite; // KNX address to write on click
ButtonAction action; // Button action (KNX, Jump, Back)
uint8_t targetScreen; // Target screen ID for jump
// Serialization size (fixed for NVS storage)
static constexpr size_t SERIALIZED_SIZE = 64;
static constexpr size_t SERIALIZED_SIZE = 68;
void serialize(uint8_t* buf) const;
void deserialize(const uint8_t* buf);
@ -98,13 +114,29 @@ struct WidgetConfig {
// Screen configuration (holds all widgets)
struct ScreenConfig {
uint8_t id;
char name[MAX_SCREEN_NAME_LEN];
ScreenMode mode;
Color backgroundColor;
uint8_t widgetCount;
WidgetConfig widgets[MAX_WIDGETS];
void clear();
void clear(uint8_t newId = 0, const char* newName = nullptr);
int addWidget(const WidgetConfig& widget); // Returns widget ID or -1
bool removeWidget(uint8_t id);
WidgetConfig* findWidget(uint8_t id);
const WidgetConfig* findWidget(uint8_t id) const;
};
struct GuiConfig {
uint8_t screenCount;
ScreenConfig screens[MAX_SCREENS];
uint8_t startScreenId;
bool standbyEnabled;
uint8_t standbyScreenId;
uint16_t standbyMinutes;
void clear();
ScreenConfig* findScreen(uint8_t id);
const ScreenConfig* findScreen(uint8_t id) const;
};

File diff suppressed because it is too large Load Diff

View File

@ -27,14 +27,24 @@ public:
// Reset to factory defaults
void resetToDefaults();
// Periodic tasks (standby handling)
void loop();
// User activity (resets standby timer)
void onUserActivity();
// KNX value update (called from KnxWorker)
void onKnxValue(uint16_t groupAddr, float value);
void onKnxSwitch(uint16_t groupAddr, bool value);
void onKnxText(uint16_t groupAddr, const char* text);
// Button action handler
void handleButtonAction(const WidgetConfig& cfg, lv_obj_t* target);
void goBack();
// Direct config access
ScreenConfig& getConfig() { return config_; }
const ScreenConfig& getConfig() const { return config_; }
GuiConfig& getConfig() { return config_; }
const GuiConfig& getConfig() const { return config_; }
private:
WidgetManager();
@ -45,18 +55,38 @@ private:
void loadFromSdCard();
void saveToSdCard();
void destroyAllWidgets();
void createAllWidgets();
lv_obj_t* createWidget(const WidgetConfig& cfg);
void createAllWidgets(const ScreenConfig& screen, lv_obj_t* parent);
lv_obj_t* createWidget(const WidgetConfig& cfg, lv_obj_t* parent);
void applyStyle(lv_obj_t* obj, const WidgetConfig& cfg);
void applyLedStyle(lv_obj_t* obj, const WidgetConfig& cfg);
const lv_font_t* getFontBySize(uint8_t sizeIndex);
void createDefaultConfig();
void applyScreen(uint8_t screenId);
void showScreen(uint8_t screenId);
void showModalScreen(const ScreenConfig& screen);
void closeModal();
void enterStandby();
ScreenConfig* activeScreen();
const ScreenConfig* activeScreen() const;
static constexpr const char* CONFIG_FILE = "/sdcard/lvgl.json";
ScreenConfig config_;
GuiConfig config_;
uint8_t activeScreenId_ = 0;
uint8_t previousScreenId_ = 0xFF;
uint8_t standbyReturnScreenId_ = 0xFF;
uint8_t modalScreenId_ = 0xFF;
bool standbyActive_ = false;
bool standbyWakePending_ = false;
uint8_t standbyWakeTarget_ = 0xFF;
bool navPending_ = false;
ButtonAction navAction_ = ButtonAction::KNX;
uint8_t navTargetScreen_ = 0xFF;
int64_t lastActivityUs_ = 0;
// Runtime widget references (indexed by widget ID)
std::array<lv_obj_t*, MAX_WIDGETS> widgetObjects_;
lv_obj_t* screen_ = nullptr;
lv_obj_t* modalContainer_ = nullptr;
};

View File

@ -7,6 +7,7 @@
#include "Display.hpp"
#include "Touch.hpp"
#include "Gui.hpp"
#include "WidgetManager.hpp"
#include "Nvs.hpp"
#include "KnxWorker.hpp"
#include "Wifi.hpp"
@ -72,6 +73,7 @@ public:
while (true) {
vTaskDelay(pdMS_TO_TICKS(10));
knxWorker.loop();
WidgetManager::instance().loop();
}
}

View File

@ -6,13 +6,13 @@
static const char* TAG = "WebServer";
esp_err_t WebServer::getConfigHandler(httpd_req_t* req) {
char* buf = new char[8192];
char* buf = new char[32768];
if (buf == nullptr) {
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Out of memory");
return ESP_FAIL;
}
WidgetManager::instance().getConfigJson(buf, 8192);
WidgetManager::instance().getConfigJson(buf, 32768);
httpd_resp_set_type(req, "application/json");
httpd_resp_send(req, buf, strlen(buf));
@ -23,7 +23,7 @@ esp_err_t WebServer::getConfigHandler(httpd_req_t* req) {
esp_err_t WebServer::postConfigHandler(httpd_req_t* req) {
int total_len = req->content_len;
if (total_len > 8192) {
if (total_len > 32768) {
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Content too large");
return ESP_FAIL;
}

File diff suppressed because it is too large Load Diff