add new ClockScreenSaver
Some checks failed
ESP-IDF Build / build (esp32s3, latest) (push) Failing after 22s
ESP-IDF Build / build (esp32s3, release-v5.4) (push) Failing after 18s
ESP-IDF Build / build (esp32s3, release-v5.5) (push) Failing after 21s

Signed-off-by: Peter Siegmund <developer@mars3142.org>
This commit is contained in:
2025-08-21 23:11:42 +02:00
parent f199f0d781
commit 1ead66520b
23 changed files with 939 additions and 40 deletions

View File

@@ -8,6 +8,7 @@ set(SOURCE_FILES
src/ui/LightMenu.cpp
src/ui/LightSettingsMenu.cpp
src/ui/MainMenu.cpp
src/ui/ClockScreenSaver.cpp
src/ui/ScreenSaver.cpp
src/ui/SettingsMenu.cpp
src/ui/SplashScreen.cpp

View File

@@ -0,0 +1,69 @@
/**
* @file ClockScreenSaver.h
* @brief Animated clock screensaver implementation with bouncing time display
* @details This header defines the ClockScreenSaver class which provides an animated
* clock screensaver that shows the system time bouncing around the screen.
* The screensaver prevents screen burn-in while displaying useful information.
* @author System Control Team
* @date 2025-06-14
*/
#pragma once
#include "MenuOptions.h"
#include "common/Widget.h"
/**
* @class ClockScreenSaver
* @brief Animated clock screensaver widget for system idle periods
*/
class ClockScreenSaver final : public Widget
{
public:
explicit ClockScreenSaver(menu_options_t *options);
void update(uint64_t dt) override;
void render() override;
void onButtonClicked(ButtonType button) override;
private:
static constexpr int MOVE_INTERVAL = 50; // milliseconds between movements
static constexpr int X_VELOCITY = 1; // horizontal movement speed
static constexpr int Y_VELOCITY = 1; // vertical movement speed
static constexpr int TEXT_PADDING = 0; // padding around text for bounds checking
static constexpr const uint8_t *FONT = u8g2_font_profont15_tf; // font for time display
menu_options_t *m_options;
uint64_t m_moveTimer;
// Position and movement
int m_posX;
int m_posY;
int m_velocityX;
int m_velocityY;
// Text dimensions (calculated once)
int m_textWidth;
int m_textHeight;
/**
* @brief Initialize position and movement
*/
void initPosition();
/**
* @brief Update the text dimensions based on current time
*/
void updateTextDimensions();
/**
* @brief Get the current time as a formatted string
* @param buffer Buffer to store the formatted time
* @param bufferSize Size of the buffer
*/
void getCurrentTimeString(char *buffer, size_t bufferSize) const;
/**
* @brief Check and handle collision with screen boundaries
*/
void checkBoundaryCollision();
};

View File

@@ -0,0 +1,122 @@
#include "ui/ClockScreenSaver.h"
#include <cstring>
#include <ctime>
ClockScreenSaver::ClockScreenSaver(menu_options_t *options)
: Widget(options->u8g2), m_options(options), m_moveTimer(0), m_posX(0), m_posY(0), m_velocityX(X_VELOCITY),
m_velocityY(Y_VELOCITY), m_textWidth(0), m_textHeight(0)
{
initPosition();
}
void ClockScreenSaver::initPosition()
{
// Start in the center of the screen
updateTextDimensions();
m_posX = (u8g2->width - m_textWidth) / 2;
m_posY = (u8g2->height - m_textHeight) / 2;
// Set initial velocity
m_velocityX = X_VELOCITY;
m_velocityY = Y_VELOCITY;
}
void ClockScreenSaver::updateTextDimensions()
{
char timeBuffer[32];
getCurrentTimeString(timeBuffer, sizeof(timeBuffer));
// Set font (use a large font for better visibility)
u8g2_SetFont(u8g2, FONT);
// Calculate text dimensions
m_textWidth = u8g2_GetStrWidth(u8g2, timeBuffer);
m_textHeight = u8g2_GetAscent(u8g2) - u8g2_GetDescent(u8g2);
}
void ClockScreenSaver::getCurrentTimeString(char *buffer, size_t bufferSize) const
{
time_t rawtime;
struct tm *timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
// Format time as HH:MM:SS
strftime(buffer, bufferSize, "%H:%M:%S", timeinfo);
}
void ClockScreenSaver::update(const uint64_t dt)
{
m_moveTimer += dt;
// Move the clock position at regular intervals
if (m_moveTimer > MOVE_INTERVAL)
{
m_moveTimer = 0;
// Update text dimensions (in case time format changes)
updateTextDimensions();
// Update position
m_posX += m_velocityX;
m_posY += m_velocityY;
// Check for collisions with screen boundaries
checkBoundaryCollision();
}
}
void ClockScreenSaver::checkBoundaryCollision()
{
// Check horizontal boundaries
if (m_posX <= TEXT_PADDING)
{
m_posX = TEXT_PADDING;
m_velocityX = X_VELOCITY; // Bounce right
}
else if (m_posX + m_textWidth >= u8g2->width - TEXT_PADDING)
{
m_posX = u8g2->width - m_textWidth - TEXT_PADDING;
m_velocityX = -X_VELOCITY; // Bounce left
}
// Check vertical boundaries
if (m_posY <= TEXT_PADDING + m_textHeight)
{
m_posY = TEXT_PADDING + m_textHeight;
m_velocityY = Y_VELOCITY; // Bounce down
}
else if (m_posY >= u8g2->height - TEXT_PADDING)
{
m_posY = u8g2->height - TEXT_PADDING;
m_velocityY = -Y_VELOCITY; // Bounce up
}
}
void ClockScreenSaver::render()
{
// Clear screen with a black background
u8g2_SetDrawColor(u8g2, 0);
u8g2_DrawBox(u8g2, 0, 0, u8g2->width, u8g2->height);
u8g2_SetDrawColor(u8g2, 1);
// Get current time
char timeBuffer[32];
getCurrentTimeString(timeBuffer, sizeof(timeBuffer));
// Set font
u8g2_SetFont(u8g2, FONT);
// Draw the time at current position
u8g2_DrawStr(u8g2, m_posX, m_posY, timeBuffer);
}
void ClockScreenSaver::onButtonClicked(ButtonType button)
{
// Exit screensaver on any button press
if (m_options && m_options->popScreen)
{
m_options->popScreen();
}
}

View File

@@ -9,15 +9,15 @@
*/
namespace LightMenuItem
{
constexpr uint8_t ACTIVATE = 0; ///< ID for the light activation toggle
constexpr uint8_t MODE = 1; ///< ID for the light mode selection
constexpr uint8_t LED_SETTINGS = 2; ///< ID for the LED settings menu item
constexpr auto ACTIVATE = 0; ///< ID for the light activation toggle
constexpr auto MODE = 1; ///< ID for the light mode selection
constexpr auto LED_SETTINGS = 2; ///< ID for the LED settings menu item
} // namespace LightMenuItem
namespace LightMenuOptions
{
constexpr std::string LIGHT_ACTIVE = "light_active";
constexpr std::string LIGHT_MODE = "light_mode";
constexpr auto LIGHT_ACTIVE = "light_active";
constexpr auto LIGHT_MODE = "light_mode";
} // namespace LightMenuOptions
LightMenu::LightMenu(menu_options_t *options) : Menu(options), m_options(options)
@@ -57,7 +57,8 @@ void LightMenu::onButtonPressed(const MenuItem &menuItem, const ButtonType butto
if (button == ButtonType::SELECT)
{
toggle(menuItem);
if (getItem(menuItem.getId()).getValue() == "1")
const auto value = getItem(menuItem.getId()).getValue() == "1";
if (value)
{
led_event_data_t payload = {.value = 42};
send_event(EVENT_LED_ON, &payload);
@@ -69,7 +70,6 @@ void LightMenu::onButtonPressed(const MenuItem &menuItem, const ButtonType butto
}
if (m_options && m_options->persistenceManager)
{
const auto value = getItem(menuItem.getId()).getValue() == "1";
m_options->persistenceManager->SetValue(LightMenuOptions::LIGHT_ACTIVE, value);
m_options->persistenceManager->Save();
}
@@ -82,9 +82,12 @@ void LightMenu::onButtonPressed(const MenuItem &menuItem, const ButtonType butto
const auto item = switchValue(menuItem, button);
if (button == ButtonType::LEFT || button == ButtonType::RIGHT)
{
const auto value = getItem(item.getId()).getIndex();
led_event_data_t payload = {.value = value};
send_event(EVENT_LED_DAY + value, &payload);
if (m_options && m_options->persistenceManager)
{
const auto value = getItem(item.getId()).getIndex();
m_options->persistenceManager->SetValue(LightMenuOptions::LIGHT_MODE, value);
m_options->persistenceManager->Save();
}

View File

@@ -1,6 +1,7 @@
if (DEFINED ENV{IDF_PATH})
idf_component_register(SRCS
src/hal_esp32/led_manager.cpp
src/hal_esp32/led_status.cpp
INCLUDE_DIRS "include"
PRIV_REQUIRES
u8g2

View File

@@ -0,0 +1,3 @@
#pragma once
void led_status_init();

View File

@@ -21,7 +21,7 @@ uint64_t wled_init(void)
led_strip_config_t strip_config = {.strip_gpio_num = CONFIG_WLED_DIN_PIN,
.max_leds = 64,
.led_model = LED_MODEL_WS2812,
.color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_RGB,
.color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB,
.flags = {
.invert_out = false,
}};
@@ -35,9 +35,9 @@ uint64_t wled_init(void)
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
for (uint32_t i = 0; i < 64; i++)
for (uint32_t i = 0; i < 3; i++)
{
led_strip_set_pixel(led_strip, i, 0, 0, 0);
led_strip_set_pixel(led_strip, i, 10, 10, 0);
}
led_strip_refresh(led_strip);

View File

@@ -0,0 +1,7 @@
#include "led_status.h"
void led_status_init()
{
// This function is intentionally left empty for the native implementation.
// It serves as a placeholder to maintain compatibility with the HAL interface.
}

View File

@@ -0,0 +1,7 @@
#include "led_status.h"
void led_status_init()
{
// This function is intentionally left empty for the native implementation.
// It serves as a placeholder to maintain compatibility with the HAL interface.
}