Compare commits
4 Commits
f7cedf24e8
...
ccdc2bb63f
| Author | SHA1 | Date | |
|---|---|---|---|
|
ccdc2bb63f
|
|||
|
7d12d98ec9
|
|||
|
cdac9cbfb8
|
|||
|
1fade06bdb
|
@@ -13,4 +13,5 @@ idf_component_register(SRCS
|
|||||||
json
|
json
|
||||||
simulator
|
simulator
|
||||||
persistence-manager
|
persistence-manager
|
||||||
|
message-manager
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "api_handlers.h"
|
#include "api_handlers.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "message_manager.h"
|
||||||
|
|
||||||
#include <cJSON.h>
|
#include <cJSON.h>
|
||||||
#include <esp_http_server.h>
|
#include <esp_http_server.h>
|
||||||
@@ -255,7 +256,22 @@ esp_err_t api_light_power_handler(httpd_req_t *req)
|
|||||||
|
|
||||||
ESP_LOGI(TAG, "Received light power: %s", buf);
|
ESP_LOGI(TAG, "Received light power: %s", buf);
|
||||||
|
|
||||||
// TODO: Parse JSON and control light
|
cJSON *json = cJSON_Parse(buf);
|
||||||
|
if (json)
|
||||||
|
{
|
||||||
|
cJSON *active = cJSON_GetObjectItem(json, "on");
|
||||||
|
if (cJSON_IsBool(active))
|
||||||
|
{
|
||||||
|
message_t msg = {};
|
||||||
|
msg.type = MESSAGE_TYPE_SETTINGS;
|
||||||
|
msg.data.settings.type = SETTINGS_TYPE_BOOL;
|
||||||
|
strncpy(msg.data.settings.key, "light_active", sizeof(msg.data.settings.key) - 1);
|
||||||
|
msg.data.settings.value.bool_value = cJSON_IsTrue(active);
|
||||||
|
message_manager_post(&msg, pdMS_TO_TICKS(100));
|
||||||
|
}
|
||||||
|
cJSON_Delete(json);
|
||||||
|
}
|
||||||
|
|
||||||
set_cors_headers(req);
|
set_cors_headers(req);
|
||||||
return httpd_resp_sendstr(req, "{\"status\":\"ok\"}");
|
return httpd_resp_sendstr(req, "{\"status\":\"ok\"}");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,17 @@
|
|||||||
#include <cJSON.h>
|
#include <cJSON.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "persistence_manager.h"
|
||||||
|
|
||||||
// Gibt ein cJSON-Objekt with dem aktuellen Lichtstatus zurück
|
// Gibt ein cJSON-Objekt with dem aktuellen Lichtstatus zurück
|
||||||
cJSON *create_light_status_json(void)
|
cJSON *create_light_status_json(void)
|
||||||
{
|
{
|
||||||
|
persistence_manager_t pm;
|
||||||
|
persistence_manager_init(&pm, "config");
|
||||||
cJSON *json = cJSON_CreateObject();
|
cJSON *json = cJSON_CreateObject();
|
||||||
// TODO: Echte Werte einfügen, aktuell Dummy-Daten
|
|
||||||
cJSON_AddBoolToObject(json, "on", false);
|
bool light_active = persistence_manager_get_bool(&pm, "light_active", false);
|
||||||
|
cJSON_AddBoolToObject(json, "on", light_active);
|
||||||
cJSON_AddBoolToObject(json, "thunder", false);
|
cJSON_AddBoolToObject(json, "thunder", false);
|
||||||
cJSON_AddStringToObject(json, "mode", "day");
|
cJSON_AddStringToObject(json, "mode", "day");
|
||||||
cJSON_AddStringToObject(json, "schema", "schema_03.csv");
|
cJSON_AddStringToObject(json, "schema", "schema_03.csv");
|
||||||
@@ -16,5 +21,8 @@ cJSON *create_light_status_json(void)
|
|||||||
cJSON_AddNumberToObject(color, "g", 240);
|
cJSON_AddNumberToObject(color, "g", 240);
|
||||||
cJSON_AddNumberToObject(color, "b", 220);
|
cJSON_AddNumberToObject(color, "b", 220);
|
||||||
cJSON_AddItemToObject(json, "color", color);
|
cJSON_AddItemToObject(json, "color", color);
|
||||||
|
|
||||||
|
persistence_manager_deinit(&pm);
|
||||||
|
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include "websocket_handler.h"
|
#include "websocket_handler.h"
|
||||||
|
#include "api_server.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
#include "message_manager.h"
|
||||||
#include <esp_http_server.h>
|
#include <esp_http_server.h>
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -11,6 +13,16 @@ static const char *TAG = "websocket_handler";
|
|||||||
static int ws_clients[WS_MAX_CLIENTS];
|
static int ws_clients[WS_MAX_CLIENTS];
|
||||||
static int ws_client_count = 0;
|
static int ws_client_count = 0;
|
||||||
|
|
||||||
|
static void on_message_received(const message_t *msg)
|
||||||
|
{
|
||||||
|
cJSON *json = create_light_status_json();
|
||||||
|
cJSON_AddStringToObject(json, "type", "status");
|
||||||
|
char *response = cJSON_PrintUnformatted(json);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
api_server_ws_broadcast(response);
|
||||||
|
free(response);
|
||||||
|
}
|
||||||
|
|
||||||
static void ws_clients_init(void)
|
static void ws_clients_init(void)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < WS_MAX_CLIENTS; i++)
|
for (int i = 0; i < WS_MAX_CLIENTS; i++)
|
||||||
@@ -179,6 +191,8 @@ static void ws_async_send(void *arg)
|
|||||||
|
|
||||||
esp_err_t websocket_handler_init(httpd_handle_t server)
|
esp_err_t websocket_handler_init(httpd_handle_t server)
|
||||||
{
|
{
|
||||||
|
message_manager_register_listener(on_message_received);
|
||||||
|
|
||||||
ws_clients_init();
|
ws_clients_init();
|
||||||
// Register WebSocket URI handler
|
// Register WebSocket URI handler
|
||||||
httpd_uri_t ws_uri = {.uri = "/ws",
|
httpd_uri_t ws_uri = {.uri = "/ws",
|
||||||
|
|||||||
@@ -29,6 +29,18 @@ static EventGroupHandle_t s_wifi_event_group;
|
|||||||
|
|
||||||
static const char *TAG = "wifi_manager";
|
static const char *TAG = "wifi_manager";
|
||||||
|
|
||||||
|
static void led_status_reconnect()
|
||||||
|
{
|
||||||
|
led_behavior_t led_behavior = {
|
||||||
|
.on_time_ms = 250,
|
||||||
|
.off_time_ms = 100,
|
||||||
|
.color = {.red = 50, .green = 50, .blue = 0},
|
||||||
|
.index = 0,
|
||||||
|
.mode = LED_MODE_BLINK,
|
||||||
|
};
|
||||||
|
led_status_set_behavior(led_behavior);
|
||||||
|
}
|
||||||
|
|
||||||
static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
|
static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
|
||||||
{
|
{
|
||||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
|
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
|
||||||
@@ -40,6 +52,8 @@ static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t e
|
|||||||
{
|
{
|
||||||
ESP_LOGW(TAG, "WIFI_EVENT_STA_DISCONNECTED: Verbindung verloren, versuche erneut...");
|
ESP_LOGW(TAG, "WIFI_EVENT_STA_DISCONNECTED: Verbindung verloren, versuche erneut...");
|
||||||
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
||||||
|
led_status_reconnect();
|
||||||
|
esp_wifi_connect();
|
||||||
}
|
}
|
||||||
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
|
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
|
||||||
{
|
{
|
||||||
@@ -49,8 +63,10 @@ static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t e
|
|||||||
}
|
}
|
||||||
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_LOST_IP)
|
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_LOST_IP)
|
||||||
{
|
{
|
||||||
ESP_LOGW(TAG, "IP_EVENT_STA_LOST_IP: IP-Adresse verloren!");
|
ESP_LOGW(TAG, "IP_EVENT_STA_LOST_IP: IP-Adresse verloren! Versuche Reconnect...");
|
||||||
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
||||||
|
led_status_reconnect();
|
||||||
|
esp_wifi_connect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,14 +121,7 @@ void wifi_manager_init()
|
|||||||
|
|
||||||
if (have_ssid && have_password)
|
if (have_ssid && have_password)
|
||||||
{
|
{
|
||||||
led_behavior_t led_behavior = {
|
led_status_reconnect();
|
||||||
.on_time_ms = 250,
|
|
||||||
.off_time_ms = 100,
|
|
||||||
.color = {.red = 50, .green = 50, .blue = 0},
|
|
||||||
.index = 0,
|
|
||||||
.mode = LED_MODE_BLINK,
|
|
||||||
};
|
|
||||||
led_status_set_behavior(led_behavior);
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Found WiFi configuration: SSID='%s'", ssid);
|
ESP_LOGI(TAG, "Found WiFi configuration: SSID='%s'", ssid);
|
||||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||||
|
|||||||
@@ -18,4 +18,5 @@ idf_component_register(SRCS
|
|||||||
led-manager
|
led-manager
|
||||||
persistence-manager
|
persistence-manager
|
||||||
simulator
|
simulator
|
||||||
|
message-manager
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -175,6 +175,21 @@ class Menu : public Widget
|
|||||||
*/
|
*/
|
||||||
void toggle(const MenuItem &menuItem);
|
void toggle(const MenuItem &menuItem);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Setzt den Zustand eines Toggle-Menüeintrags explizit
|
||||||
|
* @param menuItem Der zu ändernde Toggle-Menüeintrag
|
||||||
|
* @param state Neuer Zustand (true = aktiviert, false = deaktiviert)
|
||||||
|
*
|
||||||
|
* @pre menuItem muss vom Typ TOGGLE sein
|
||||||
|
* @post Der Wert des Menüeintrags wird auf den angegebenen Zustand gesetzt
|
||||||
|
*
|
||||||
|
* @details Diese Methode setzt den Wert eines Toggle-Menüeintrags gezielt auf den gewünschten Zustand.
|
||||||
|
* Der geänderte Eintrag ersetzt das Original in der internen Menüstruktur.
|
||||||
|
*
|
||||||
|
* @note Diese Methode verändert direkt den internen Zustand des Menüs.
|
||||||
|
*/
|
||||||
|
void setToggle(const MenuItem &menuItem, const bool state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Changes the selected value of a selection menu item based on button input
|
* @brief Changes the selected value of a selection menu item based on button input
|
||||||
* @param menuItem The selection menu item to modify
|
* @param menuItem The selection menu item to modify
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "u8g2.h"
|
#include "u8g2.h"
|
||||||
|
|
||||||
#include "common/Common.h"
|
#include "common/Common.h"
|
||||||
|
#include "message_manager.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class Widget
|
* @class Widget
|
||||||
@@ -49,7 +50,9 @@ class Widget
|
|||||||
* @details Ensures that derived class destructors are called correctly when
|
* @details Ensures that derived class destructors are called correctly when
|
||||||
* a widget is destroyed through a base class pointer.
|
* a widget is destroyed through a base class pointer.
|
||||||
*/
|
*/
|
||||||
virtual ~Widget() = default;
|
virtual ~Widget();
|
||||||
|
|
||||||
|
virtual void onMessageReceived(const message_t *msg);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Called when the widget becomes active or enters the foreground
|
* @brief Called when the widget becomes active or enters the foreground
|
||||||
@@ -178,4 +181,8 @@ class Widget
|
|||||||
* the u8g2 context and assumes it is managed externally.
|
* the u8g2 context and assumes it is managed externally.
|
||||||
*/
|
*/
|
||||||
u8g2_t *u8g2;
|
u8g2_t *u8g2;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::vector<Widget *> s_instances;
|
||||||
|
static void globalMessageCallback(const message_t *msg);
|
||||||
};
|
};
|
||||||
@@ -120,6 +120,8 @@ class LightMenu final : public Menu
|
|||||||
*/
|
*/
|
||||||
void onButtonPressed(const MenuItem &menuItem, ButtonType button) override;
|
void onButtonPressed(const MenuItem &menuItem, ButtonType button) override;
|
||||||
|
|
||||||
|
void onMessageReceived(const message_t *msg);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Pointer to menu options configuration structure
|
* @brief Pointer to menu options configuration structure
|
||||||
* @details Stores a reference to the menu configuration passed during construction.
|
* @details Stores a reference to the menu configuration passed during construction.
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ constexpr int BOTTOM_OFFSET = 10;
|
|||||||
|
|
||||||
Menu::Menu(menu_options_t *options) : Widget(options->u8g2), m_options(options)
|
Menu::Menu(menu_options_t *options) : Widget(options->u8g2), m_options(options)
|
||||||
{
|
{
|
||||||
// Set up button callback using lambda to forward to member function
|
// Set up button callback using a lambda to forward to the member function
|
||||||
m_options->onButtonClicked = [this](const ButtonType button) { OnButtonClicked(button); };
|
m_options->onButtonClicked = [this](const ButtonType button) { OnButtonClicked(button); };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,6 +82,12 @@ void Menu::toggle(const MenuItem &menuItem)
|
|||||||
replaceItem(menuItem.getId(), item);
|
replaceItem(menuItem.getId(), item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Menu::setToggle(const MenuItem &menuItem, const bool state)
|
||||||
|
{
|
||||||
|
const auto item = menuItem.copyWith(state ? std::to_string(true) : std::to_string(false));
|
||||||
|
replaceItem(menuItem.getId(), item);
|
||||||
|
}
|
||||||
|
|
||||||
MenuItem Menu::switchValue(const MenuItem &menuItem, ButtonType button)
|
MenuItem Menu::switchValue(const MenuItem &menuItem, ButtonType button)
|
||||||
{
|
{
|
||||||
MenuItem result = menuItem;
|
MenuItem result = menuItem;
|
||||||
@@ -134,13 +140,13 @@ void Menu::Render()
|
|||||||
m_selected_item = 0;
|
m_selected_item = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Early return if no items to render
|
// Early return if there are no items to render
|
||||||
if (m_items.empty())
|
if (m_items.empty())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear screen with black background
|
// Clear the screen with a black background
|
||||||
u8g2_SetDrawColor(u8g2, 0);
|
u8g2_SetDrawColor(u8g2, 0);
|
||||||
u8g2_DrawBox(u8g2, 0, 0, u8g2->width, u8g2->height);
|
u8g2_DrawBox(u8g2, 0, 0, u8g2->width, u8g2->height);
|
||||||
|
|
||||||
@@ -151,7 +157,7 @@ void Menu::Render()
|
|||||||
drawScrollBar();
|
drawScrollBar();
|
||||||
drawSelectionBox();
|
drawSelectionBox();
|
||||||
|
|
||||||
// Calculate center position for main item
|
// Calculate center position for the main item
|
||||||
const int centerY = u8g2->height / 2 + 3;
|
const int centerY = u8g2->height / 2 + 3;
|
||||||
|
|
||||||
// Render the currently selected item (main/center item)
|
// Render the currently selected item (main/center item)
|
||||||
@@ -176,7 +182,7 @@ void Menu::Render()
|
|||||||
|
|
||||||
void Menu::renderWidget(const MenuItem *item, const uint8_t *font, const int x, const int y) const
|
void Menu::renderWidget(const MenuItem *item, const uint8_t *font, const int x, const int y) const
|
||||||
{
|
{
|
||||||
// Set font and draw main text
|
// Set font and draw the main text
|
||||||
u8g2_SetFont(u8g2, font);
|
u8g2_SetFont(u8g2, font);
|
||||||
u8g2_DrawStr(u8g2, x, y, item->getText().c_str());
|
u8g2_DrawStr(u8g2, x, y, item->getText().c_str());
|
||||||
|
|
||||||
@@ -206,7 +212,7 @@ void Menu::renderWidget(const MenuItem *item, const uint8_t *font, const int x,
|
|||||||
}
|
}
|
||||||
|
|
||||||
case MenuItemTypes::TOGGLE: {
|
case MenuItemTypes::TOGGLE: {
|
||||||
// Draw checkbox frame
|
// Draw the checkbox frame
|
||||||
const int frameX = u8g2->width - UIConstants::FRAME_BOX_SIZE - UIConstants::SELECTION_MARGIN;
|
const int frameX = u8g2->width - UIConstants::FRAME_BOX_SIZE - UIConstants::SELECTION_MARGIN;
|
||||||
const int frameY = y - UIConstants::FRAME_OFFSET;
|
const int frameY = y - UIConstants::FRAME_OFFSET;
|
||||||
u8g2_DrawFrame(u8g2, frameX, frameY, UIConstants::FRAME_BOX_SIZE, UIConstants::FRAME_BOX_SIZE);
|
u8g2_DrawFrame(u8g2, frameX, frameY, UIConstants::FRAME_BOX_SIZE, UIConstants::FRAME_BOX_SIZE);
|
||||||
@@ -272,7 +278,7 @@ void Menu::onPressedDown()
|
|||||||
if (m_items.empty())
|
if (m_items.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Wrap around to first item when at the end
|
// Wrap around to the first item when at the end
|
||||||
m_selected_item = (m_selected_item + 1) % m_items.size();
|
m_selected_item = (m_selected_item + 1) % m_items.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,7 +287,7 @@ void Menu::onPressedUp()
|
|||||||
if (m_items.empty())
|
if (m_items.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Wrap around to last item when at the beginning
|
// Wrap around to the last item when at the beginning
|
||||||
m_selected_item = (m_selected_item == 0) ? m_items.size() - 1 : m_selected_item - 1;
|
m_selected_item = (m_selected_item == 0) ? m_items.size() - 1 : m_selected_item - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,7 +320,7 @@ void Menu::onPressedSelect() const
|
|||||||
|
|
||||||
void Menu::onPressedBack() const
|
void Menu::onPressedBack() const
|
||||||
{
|
{
|
||||||
// Navigate back to previous screen if callback is available
|
// Navigate back to the previous screen if callback is available
|
||||||
if (m_options && m_options->popScreen)
|
if (m_options && m_options->popScreen)
|
||||||
{
|
{
|
||||||
m_options->popScreen();
|
m_options->popScreen();
|
||||||
|
|||||||
@@ -1,7 +1,20 @@
|
|||||||
#include "common/Widget.h"
|
#include "common/Widget.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
std::vector<Widget *> Widget::s_instances;
|
||||||
|
|
||||||
Widget::Widget(u8g2_t *u8g2) : u8g2(u8g2)
|
Widget::Widget(u8g2_t *u8g2) : u8g2(u8g2)
|
||||||
{
|
{
|
||||||
|
s_instances.push_back(this);
|
||||||
|
if (s_instances.size() == 1)
|
||||||
|
{
|
||||||
|
message_manager_register_listener(globalMessageCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget::~Widget()
|
||||||
|
{
|
||||||
|
s_instances.erase(std::remove(s_instances.begin(), s_instances.end(), this), s_instances.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Widget::onEnter()
|
void Widget::onEnter()
|
||||||
@@ -36,3 +49,15 @@ const char *Widget::getName() const
|
|||||||
{
|
{
|
||||||
return "Widget";
|
return "Widget";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Widget::onMessageReceived(const message_t *msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Widget::globalMessageCallback(const message_t *msg)
|
||||||
|
{
|
||||||
|
for (auto *w : s_instances)
|
||||||
|
{
|
||||||
|
w->onMessageReceived(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#include "ui/LightMenu.h"
|
#include "ui/LightMenu.h"
|
||||||
|
|
||||||
#include "led_strip_ws2812.h"
|
#include "led_strip_ws2812.h"
|
||||||
|
#include "message_manager.h"
|
||||||
#include "simulator.h"
|
#include "simulator.h"
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @namespace LightMenuItem
|
* @namespace LightMenuItem
|
||||||
@@ -71,12 +72,13 @@ void LightMenu::onButtonPressed(const MenuItem &menuItem, const ButtonType butto
|
|||||||
{
|
{
|
||||||
toggle(menuItem);
|
toggle(menuItem);
|
||||||
const auto value = getItem(menuItem.getId()).getValue() == "1";
|
const auto value = getItem(menuItem.getId()).getValue() == "1";
|
||||||
if (m_options && m_options->persistenceManager)
|
// Post change via message_manager
|
||||||
{
|
message_t msg = {};
|
||||||
persistence_manager_set_bool(m_options->persistenceManager, LightMenuOptions::LIGHT_ACTIVE, value);
|
msg.type = MESSAGE_TYPE_SETTINGS;
|
||||||
}
|
msg.data.settings.type = SETTINGS_TYPE_BOOL;
|
||||||
|
strncpy(msg.data.settings.key, LightMenuOptions::LIGHT_ACTIVE, sizeof(msg.data.settings.key) - 1);
|
||||||
start_simulation();
|
msg.data.settings.value.bool_value = value;
|
||||||
|
message_manager_post(&msg, pdMS_TO_TICKS(100));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -127,4 +129,15 @@ void LightMenu::onButtonPressed(const MenuItem &menuItem, const ButtonType butto
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LightMenu::onMessageReceived(const message_t *msg)
|
||||||
|
{
|
||||||
|
// Here you can react to messages, e.g. set toggle status
|
||||||
|
// Example: If light_active was changed, synchronize toggle
|
||||||
|
if (msg && msg->type == MESSAGE_TYPE_SETTINGS && msg->data.settings.type == SETTINGS_TYPE_BOOL &&
|
||||||
|
std::strcmp(msg->data.settings.key, "light_active") == 0)
|
||||||
|
{
|
||||||
|
setToggle(getItem(LightMenuItem::ACTIVATE), msg->data.settings.value.bool_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IMPLEMENT_GET_NAME(LightMenu)
|
IMPLEMENT_GET_NAME(LightMenu)
|
||||||
|
|||||||
6
firmware/components/message-manager/CMakeLists.txt
Normal file
6
firmware/components/message-manager/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
idf_component_register(
|
||||||
|
SRCS "src/message_manager.c"
|
||||||
|
INCLUDE_DIRS "include"
|
||||||
|
PRIV_REQUIRES
|
||||||
|
persistence-manager
|
||||||
|
)
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
MESSAGE_TYPE_SETTINGS,
|
||||||
|
MESSAGE_TYPE_BUTTON
|
||||||
|
} message_type_t;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
BUTTON_EVENT_PRESS,
|
||||||
|
BUTTON_EVENT_RELEASE
|
||||||
|
} button_event_type_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
button_event_type_t event_type;
|
||||||
|
uint8_t button_id;
|
||||||
|
} button_message_t;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
SETTINGS_TYPE_BOOL,
|
||||||
|
SETTINGS_TYPE_INT,
|
||||||
|
SETTINGS_TYPE_FLOAT,
|
||||||
|
SETTINGS_TYPE_STRING
|
||||||
|
} settings_type_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
settings_type_t type;
|
||||||
|
char key[32];
|
||||||
|
union {
|
||||||
|
bool bool_value;
|
||||||
|
int32_t int_value;
|
||||||
|
float float_value;
|
||||||
|
char string_value[64];
|
||||||
|
} value;
|
||||||
|
} settings_message_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
message_type_t type;
|
||||||
|
union {
|
||||||
|
settings_message_t settings;
|
||||||
|
button_message_t button;
|
||||||
|
} data;
|
||||||
|
} message_t;
|
||||||
|
|
||||||
|
// Observer Pattern: Listener-Typ und Registrierungsfunktionen
|
||||||
|
typedef void (*message_listener_t)(const message_t *msg);
|
||||||
|
void message_manager_register_listener(message_listener_t listener);
|
||||||
|
void message_manager_unregister_listener(message_listener_t listener);
|
||||||
|
void message_manager_init(void);
|
||||||
|
bool message_manager_post(const message_t *msg, TickType_t timeout);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
106
firmware/components/message-manager/src/message_manager.c
Normal file
106
firmware/components/message-manager/src/message_manager.c
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
#include "message_manager.h"
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/queue.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
#include <persistence_manager.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define MESSAGE_QUEUE_LENGTH 16
|
||||||
|
#define MESSAGE_QUEUE_ITEM_SIZE sizeof(message_t)
|
||||||
|
|
||||||
|
static const char *TAG = "message_manager";
|
||||||
|
static QueueHandle_t message_queue = NULL;
|
||||||
|
|
||||||
|
// Observer Pattern: Listener-Liste
|
||||||
|
#define MAX_MESSAGE_LISTENERS 8
|
||||||
|
static message_listener_t message_listeners[MAX_MESSAGE_LISTENERS] = {0};
|
||||||
|
static size_t message_listener_count = 0;
|
||||||
|
|
||||||
|
void message_manager_register_listener(message_listener_t listener) {
|
||||||
|
if (listener && message_listener_count < MAX_MESSAGE_LISTENERS) {
|
||||||
|
// Doppelte Registrierung vermeiden
|
||||||
|
for (size_t i = 0; i < message_listener_count; ++i) {
|
||||||
|
if (message_listeners[i] == listener) return;
|
||||||
|
}
|
||||||
|
message_listeners[message_listener_count++] = listener;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void message_manager_unregister_listener(message_listener_t listener) {
|
||||||
|
for (size_t i = 0; i < message_listener_count; ++i) {
|
||||||
|
if (message_listeners[i] == listener) {
|
||||||
|
// Nachfolgende Listener nach vorne schieben
|
||||||
|
for (size_t j = i; j < message_listener_count - 1; ++j) {
|
||||||
|
message_listeners[j] = message_listeners[j + 1];
|
||||||
|
}
|
||||||
|
message_listeners[--message_listener_count] = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void message_manager_task(void *param)
|
||||||
|
{
|
||||||
|
message_t msg;
|
||||||
|
persistence_manager_t pm;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (xQueueReceive(message_queue, &msg, portMAX_DELAY) == pdTRUE)
|
||||||
|
{
|
||||||
|
switch (msg.type)
|
||||||
|
{
|
||||||
|
case MESSAGE_TYPE_SETTINGS:
|
||||||
|
if (persistence_manager_init(&pm, "config") == ESP_OK)
|
||||||
|
{
|
||||||
|
switch (msg.data.settings.type)
|
||||||
|
{
|
||||||
|
case SETTINGS_TYPE_BOOL:
|
||||||
|
persistence_manager_set_bool(&pm, msg.data.settings.key, msg.data.settings.value.bool_value);
|
||||||
|
break;
|
||||||
|
case SETTINGS_TYPE_INT:
|
||||||
|
persistence_manager_set_int(&pm, msg.data.settings.key, msg.data.settings.value.int_value);
|
||||||
|
break;
|
||||||
|
case SETTINGS_TYPE_FLOAT:
|
||||||
|
persistence_manager_set_float(&pm, msg.data.settings.key, msg.data.settings.value.float_value);
|
||||||
|
break;
|
||||||
|
case SETTINGS_TYPE_STRING:
|
||||||
|
persistence_manager_set_string(&pm, msg.data.settings.key,
|
||||||
|
msg.data.settings.value.string_value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
persistence_manager_deinit(&pm);
|
||||||
|
ESP_LOGI(TAG, "Setting written: %s", msg.data.settings.key);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MESSAGE_TYPE_BUTTON:
|
||||||
|
ESP_LOGI(TAG, "Button event: id=%d, type=%d", msg.data.button.button_id, msg.data.button.event_type);
|
||||||
|
// TODO: Weiterverarbeitung/Callback für Button-Events
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Observer Pattern: Listener benachrichtigen
|
||||||
|
for (size_t i = 0; i < message_listener_count; ++i) {
|
||||||
|
if (message_listeners[i]) {
|
||||||
|
message_listeners[i](&msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void message_manager_init(void)
|
||||||
|
{
|
||||||
|
if (!message_queue)
|
||||||
|
{
|
||||||
|
message_queue = xQueueCreate(MESSAGE_QUEUE_LENGTH, MESSAGE_QUEUE_ITEM_SIZE);
|
||||||
|
xTaskCreate(message_manager_task, "message_manager_task", 4096, NULL, 5, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool message_manager_post(const message_t *msg, TickType_t timeout)
|
||||||
|
{
|
||||||
|
if (!message_queue)
|
||||||
|
return false;
|
||||||
|
ESP_LOGI(TAG, "Post: type=%d", msg->type);
|
||||||
|
return xQueueSend(message_queue, msg, timeout) == pdTRUE;
|
||||||
|
}
|
||||||
@@ -9,12 +9,20 @@ typedef struct
|
|||||||
int cycle_duration_minutes;
|
int cycle_duration_minutes;
|
||||||
} simulation_config_t;
|
} simulation_config_t;
|
||||||
|
|
||||||
char *get_time(void);
|
#ifdef __cplusplus
|
||||||
esp_err_t add_light_item(const char time[5], uint8_t red, uint8_t green, uint8_t blue, uint8_t white,
|
extern "C"
|
||||||
uint8_t brightness, uint8_t saturation);
|
{
|
||||||
void cleanup_light_items(void);
|
#endif
|
||||||
void start_simulate_day(void);
|
|
||||||
void start_simulate_night(void);
|
char *get_time(void);
|
||||||
void start_simulation_task(void);
|
esp_err_t add_light_item(const char time[5], uint8_t red, uint8_t green, uint8_t blue, uint8_t white,
|
||||||
void stop_simulation_task(void);
|
uint8_t brightness, uint8_t saturation);
|
||||||
void start_simulation(void);
|
void cleanup_light_items(void);
|
||||||
|
void start_simulate_day(void);
|
||||||
|
void start_simulate_night(void);
|
||||||
|
void start_simulation_task(void);
|
||||||
|
void stop_simulation_task(void);
|
||||||
|
void start_simulation(void);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -7,12 +7,14 @@
|
|||||||
#include "hal/u8g2_esp32_hal.h"
|
#include "hal/u8g2_esp32_hal.h"
|
||||||
#include "i2c_checker.h"
|
#include "i2c_checker.h"
|
||||||
#include "led_status.h"
|
#include "led_status.h"
|
||||||
|
#include "message_manager.h"
|
||||||
#include "persistence_manager.h"
|
#include "persistence_manager.h"
|
||||||
#include "simulator.h"
|
#include "simulator.h"
|
||||||
#include "ui/ClockScreenSaver.h"
|
#include "ui/ClockScreenSaver.h"
|
||||||
#include "ui/ScreenSaver.h"
|
#include "ui/ScreenSaver.h"
|
||||||
#include "ui/SplashScreen.h"
|
#include "ui/SplashScreen.h"
|
||||||
#include "wifi_manager.h"
|
#include "wifi_manager.h"
|
||||||
|
#include <cstring>
|
||||||
#include <driver/i2c.h>
|
#include <driver/i2c.h>
|
||||||
#include <esp_diagnostics.h>
|
#include <esp_diagnostics.h>
|
||||||
#include <esp_insights.h>
|
#include <esp_insights.h>
|
||||||
@@ -126,6 +128,15 @@ static void init_ui(void)
|
|||||||
u8g2_SendBuffer(&u8g2);
|
u8g2_SendBuffer(&u8g2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void on_message_received(const message_t *msg)
|
||||||
|
{
|
||||||
|
if (msg && msg->type == MESSAGE_TYPE_SETTINGS && msg->data.settings.type == SETTINGS_TYPE_BOOL &&
|
||||||
|
std::strcmp(msg->data.settings.key, "light_active") == 0)
|
||||||
|
{
|
||||||
|
start_simulation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void handle_button(uint8_t button)
|
static void handle_button(uint8_t button)
|
||||||
{
|
{
|
||||||
m_inactivityTracker->reset();
|
m_inactivityTracker->reset();
|
||||||
@@ -232,11 +243,15 @@ void app_task(void *args)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message_manager_init();
|
||||||
|
|
||||||
setup_buttons();
|
setup_buttons();
|
||||||
init_ui();
|
init_ui();
|
||||||
|
|
||||||
wifi_manager_init();
|
wifi_manager_init();
|
||||||
|
|
||||||
|
message_manager_register_listener(on_message_received);
|
||||||
|
|
||||||
start_simulation();
|
start_simulation();
|
||||||
|
|
||||||
auto oldTime = esp_timer_get_time();
|
auto oldTime = esp_timer_get_time();
|
||||||
|
|||||||
Reference in New Issue
Block a user