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
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:
@@ -1,13 +0,0 @@
|
||||
ARG DOCKER_TAG=latest
|
||||
FROM espressif/idf:${DOCKER_TAG}
|
||||
|
||||
ENV LC_ALL=C.UTF-8
|
||||
ENV LANG=C.UTF-8
|
||||
|
||||
RUN apt-get update -y && apt-get install udev -y
|
||||
|
||||
RUN echo "source /opt/esp/idf/export.sh > /dev/null 2>&1" >> ~/.bashrc
|
||||
|
||||
ENTRYPOINT [ "/opt/esp/entrypoint.sh" ]
|
||||
|
||||
CMD ["/bin/bash", "-c"]
|
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"name": "ESP-IDF QEMU",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile"
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {
|
||||
"terminal.integrated.defaultProfile.linux": "bash",
|
||||
"idf.espIdfPath": "/opt/esp/idf",
|
||||
"idf.toolsPath": "/opt/esp",
|
||||
"idf.gitPath": "/usr/bin/git"
|
||||
},
|
||||
"extensions": [
|
||||
"espressif.esp-idf-extension",
|
||||
"espressif.esp-idf-web"
|
||||
]
|
||||
}
|
||||
},
|
||||
"runArgs": ["--privileged"]
|
||||
}
|
15
firmware/.vscode/launch.json
vendored
15
firmware/.vscode/launch.json
vendored
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "gdbtarget",
|
||||
"request": "attach",
|
||||
"name": "Eclipse CDT GDB Adapter"
|
||||
},
|
||||
{
|
||||
"type": "espidf",
|
||||
"name": "Launch",
|
||||
"request": "launch"
|
||||
}
|
||||
]
|
||||
}
|
@@ -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
|
||||
|
69
firmware/components/insa/include/ui/ClockScreenSaver.h
Normal file
69
firmware/components/insa/include/ui/ClockScreenSaver.h
Normal 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();
|
||||
};
|
122
firmware/components/insa/src/ui/ClockScreenSaver.cpp
Normal file
122
firmware/components/insa/src/ui/ClockScreenSaver.cpp
Normal 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();
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
|
@@ -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
|
||||
|
3
firmware/components/led-manager/include/led_status.h
Normal file
3
firmware/components/led-manager/include/led_status.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void led_status_init();
|
@@ -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);
|
||||
|
||||
|
@@ -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.
|
||||
}
|
@@ -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.
|
||||
}
|
Binary file not shown.
@@ -1,7 +1,6 @@
|
||||
idf_component_register(SRCS
|
||||
main.cpp
|
||||
app_task.cpp
|
||||
../components/persistence-manager/src/hal_esp32/PersistenceManager.cpp
|
||||
button_handling.c
|
||||
hal/u8g2_esp32_hal.c
|
||||
INCLUDE_DIRS "."
|
||||
|
@@ -1,7 +1,14 @@
|
||||
menu "Warnemuende Lighthouse"
|
||||
menu "System Control"
|
||||
config WLED_DIN_PIN
|
||||
int "WLED Data In Pin"
|
||||
default 14
|
||||
help
|
||||
The number of the WLED data in pin.
|
||||
|
||||
config STATUS_WLED_PIN
|
||||
int "Status WLED Pin"
|
||||
default 2
|
||||
help
|
||||
The number of the status WLED pin.
|
||||
|
||||
endmenu
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#include "button_handling.h"
|
||||
#include "common/InactivityTracker.h"
|
||||
#include "hal_esp32/PersistenceManager.h"
|
||||
#include "ui/ClockScreenSaver.h"
|
||||
#include "ui/ScreenSaver.h"
|
||||
#include "ui/SplashScreen.h"
|
||||
|
||||
@@ -103,7 +104,7 @@ static void init_ui(void)
|
||||
};
|
||||
m_widget = std::make_shared<SplashScreen>(&options);
|
||||
m_inactivityTracker = std::make_unique<InactivityTracker>(60000, []() {
|
||||
auto screensaver = std::make_shared<ScreenSaver>(&options);
|
||||
auto screensaver = std::make_shared<ClockScreenSaver>(&options);
|
||||
options.pushScreen(screensaver);
|
||||
});
|
||||
}
|
||||
|
@@ -43,7 +43,7 @@ static void button_event_cb(void *arg, void *usr_data)
|
||||
}
|
||||
}
|
||||
|
||||
static void create_button(uint8_t gpio, int index)
|
||||
static void init_button(uint8_t gpio, int index)
|
||||
{
|
||||
const button_config_t btn_cfg = {0};
|
||||
const button_gpio_config_t btn_gpio_cfg = {
|
||||
@@ -74,7 +74,7 @@ void setup_buttons(void)
|
||||
ESP_LOGI(TAG, "Button queue created successfully");
|
||||
for (int i = 0; i < sizeof(gpios) / sizeof(gpios[0]); i++)
|
||||
{
|
||||
create_button(gpios[i], i);
|
||||
init_button(gpios[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,8 @@
|
||||
# Name , Type , SubType , Offset , Size , Flags
|
||||
nvs , data , nvs , 0x9000 , 20k ,
|
||||
otadata , data , ota , 0xe000 , 8k ,
|
||||
otadata , data , ota , , 8k ,
|
||||
app0 , app , ota_0 , 0x10000 , 1024k ,
|
||||
app1 , app , ota_1 , , 1024k ,
|
||||
spiffs , data , spiffs , , 1536k ,
|
||||
phy , data , phy , , 4k ,
|
||||
coredump , data , coredump , , 64k ,
|
||||
spiffs , data , spiffs , , 1916k ,
|
||||
|
|
@@ -1,4 +1,4 @@
|
||||
# activate Bluetooth Low Energy (BLE)
|
||||
# Bluetooth
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BT_NIMBLE_ENABLED=y
|
||||
|
||||
@@ -13,7 +13,10 @@ CONFIG_ESPTOOLPY_FLASHSIZE="4MB"
|
||||
|
||||
# Partitions
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_OFFSET=0x8000
|
||||
CONFIG_PARTITION_TABLE_MD5=y
|
||||
|
||||
# Build type
|
||||
CONFIG_APP_REPRODUCIBLE_BUILD=y
|
||||
|
||||
# Core dump
|
||||
CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH=y
|
||||
CONFIG_ESP_COREDUMP_CAPTURE_DRAM=y
|
||||
|
@@ -6,6 +6,7 @@
|
||||
#include "MenuOptions.h"
|
||||
#include "common/InactivityTracker.h"
|
||||
#include "hal_native/PersistenceManager.h"
|
||||
#include "ui/ClockScreenSaver.h"
|
||||
#include "ui/ScreenSaver.h"
|
||||
#include "ui/SplashScreen.h"
|
||||
#include "ui/widgets/Button.h"
|
||||
@@ -56,15 +57,11 @@ Device::Device(void *appstate) : UIWidget(appstate)
|
||||
m_children.push_back(std::make_shared<Button>(
|
||||
GetContext(), U8G2_SCREEN_WIDTH * U8G2_SCREEN_FACTOR + 3 * U8G2_SCREEN_PADDING + DPAD_WIDTH,
|
||||
U8G2_SCREEN_HEIGHT * U8G2_SCREEN_FACTOR + U8G2_SCREEN_PADDING - BUTTON_WIDTH, BUTTON_WIDTH,
|
||||
[]() {
|
||||
PushKey(SDLK_RETURN);
|
||||
}));
|
||||
[]() { PushKey(SDLK_RETURN); }));
|
||||
m_children.push_back(std::make_shared<Button>(
|
||||
GetContext(), U8G2_SCREEN_WIDTH * U8G2_SCREEN_FACTOR + 4 * U8G2_SCREEN_PADDING + DPAD_WIDTH + BUTTON_WIDTH,
|
||||
U8G2_SCREEN_HEIGHT * U8G2_SCREEN_FACTOR + U8G2_SCREEN_PADDING - 2 * BUTTON_WIDTH, BUTTON_WIDTH,
|
||||
[]() {
|
||||
PushKey(SDLK_BACKSPACE);
|
||||
}));
|
||||
[]() { PushKey(SDLK_BACKSPACE); }));
|
||||
m_children.push_back(std::make_shared<D_Pad>(
|
||||
GetContext(), U8G2_SCREEN_WIDTH * U8G2_SCREEN_FACTOR + 2 * U8G2_SCREEN_PADDING,
|
||||
U8G2_SCREEN_HEIGHT * U8G2_SCREEN_FACTOR + U8G2_SCREEN_PADDING - DPAD_WIDTH, DPAD_WIDTH, dpad_callback));
|
||||
@@ -74,22 +71,16 @@ Device::Device(void *appstate) : UIWidget(appstate)
|
||||
|
||||
options = {
|
||||
.u8g2 = &u8g2,
|
||||
.setScreen = [this](const std::shared_ptr<Widget> &screen) {
|
||||
this->SetScreen(screen);
|
||||
},
|
||||
.pushScreen = [this](const std::shared_ptr<Widget> &screen) {
|
||||
this->PushScreen(screen);
|
||||
},
|
||||
.popScreen = [this]() {
|
||||
this->PopScreen();
|
||||
},
|
||||
.setScreen = [this](const std::shared_ptr<Widget> &screen) { this->SetScreen(screen); },
|
||||
.pushScreen = [this](const std::shared_ptr<Widget> &screen) { this->PushScreen(screen); },
|
||||
.popScreen = [this]() { this->PopScreen(); },
|
||||
.persistenceManager = std::make_shared<PersistenceManager>(),
|
||||
};
|
||||
options.persistenceManager->Load();
|
||||
|
||||
m_widget = std::make_shared<SplashScreen>(&options);
|
||||
m_inactivityTracker = std::make_unique<InactivityTracker>(60000, []() {
|
||||
const auto screensaver = std::make_shared<ScreenSaver>(&options);
|
||||
const auto screensaver = std::make_shared<ClockScreenSaver>(&options);
|
||||
options.pushScreen(screensaver);
|
||||
});
|
||||
}
|
||||
|
Reference in New Issue
Block a user