From 41ad880ab3eba0b6c74ab88adca54079ec3b4fbf Mon Sep 17 00:00:00 2001 From: Edgar W Date: Sun, 3 Aug 2025 19:02:07 +0200 Subject: [PATCH 01/13] feat: add eeprom version code to be able to set default settings, Fixes #66 --- wordclock_esp8266.ino | 96 +++++++++++++++++++++++++++++-------------- 1 file changed, 65 insertions(+), 31 deletions(-) diff --git a/wordclock_esp8266.ino b/wordclock_esp8266.ino index b8d9046..9435069 100644 --- a/wordclock_esp8266.ino +++ b/wordclock_esp8266.ino @@ -48,20 +48,36 @@ // CONSTANTS // ---------------------------------------------------------------------------------- -#define EEPROM_SIZE 30 // size of EEPROM to save persistent variables -#define ADR_NM_START_H 0 -#define ADR_NM_END_H 4 -#define ADR_NM_START_M 8 -#define ADR_NM_END_M 12 -#define ADR_BRIGHTNESS 16 -#define ADR_MC_RED 20 -#define ADR_MC_GREEN 22 -#define ADR_MC_BLUE 24 -#define ADR_STATE 26 -#define ADR_NM_ACTIVATED 27 -#define ADR_COLSHIFTSPEED 28 -#define ADR_COLSHIFTACTIVE 29 - +#define EEPROM_VERSION_CODE 2 // Change this value when defaults settings change + +// EEPROM address map (all uint8_t, 1 byte each) +#define EEPROM_SIZE 13 // size of EEPROM to save persistent variables +#define ADR_EEPROM_VERSION 0 // uint8_t +#define ADR_NM_START_H 1 // uint8_t +#define ADR_NM_END_H 2 // uint8_t +#define ADR_NM_START_M 3 // uint8_t +#define ADR_NM_END_M 4 // uint8_t +#define ADR_BRIGHTNESS 5 // uint8_t +#define ADR_MC_RED 6 // uint8_t +#define ADR_MC_GREEN 7 // uint8_t +#define ADR_MC_BLUE 8 // uint8_t +#define ADR_STATE 9 // uint8_t +#define ADR_NM_ACTIVATED 10 // uint8_t +#define ADR_COLSHIFTSPEED 11 // uint8_t +#define ADR_COLSHIFTACTIVE 12 // uint8_t + +// DEFAULT SETTINGS (if one changes this, also increment the EEPROM_VERSION_CODE, to ensure that the EEPROM is updated with the new defaults) +#define DEFAULT_NM_START_HOUR 22 // default start hour of nightmode (0-23) +#define DEFAULT_NM_START_MIN 5 // default start minute of nightmode (0-59) +#define DEFAULT_NM_END_HOUR 7 // default end hour of nightmode (0-23) +#define DEFAULT_NM_END_MIN 0 // default end minute of nightmode (0-59) +#define DEFAULT_BRIGHTNESS 40 // default brightness of LEDs (0-255) +#define DEFAULT_MC_RED 200 // default main color red value +#define DEFAULT_MC_GREEN 200 // default main color green value +#define DEFAULT_MC_BLUE 0 // default main color blue value +#define DEFAULT_NM_ACTIVATED 1 // if function nightmode is activated (0 = deactivated, 1 = activated) +#define DEFAULT_COLSHIFT_SPEED 1 // needs to be between larger than 0 (1 = slowest, 255 = fastest) +#define DEFAULT_COLSHIFT_ACTIVE 0 // if dynamic color shift is active (0 = deactivated, 1 = activated) #define NEOPIXELPIN 5 // pin to which the NeoPixels are attached #define BUTTONPIN 14 // pin to which the button is attached @@ -159,7 +175,7 @@ const uint32_t colors24bit[NUM_COLORS] = { LEDMatrix::Color24bit(0, 128, 0), LEDMatrix::Color24bit(0, 0, 255) }; -uint8_t brightness = 40; // current brightness of leds +uint8_t brightness = DEFAULT_BRIGHTNESS; // current brightness of leds bool sprialDir = false; // timestamp variables @@ -182,24 +198,24 @@ Tetris mytetris = Tetris(&ledmatrix, &logger); Snake mysnake = Snake(&ledmatrix, &logger); Pong mypong = Pong(&ledmatrix, &logger); -float filterFactor = DEFAULT_SMOOTHING_FACTOR;// stores smoothing factor for led transition -uint8_t currentState = st_clock; // stores current state -bool stateAutoChange = false; // stores state of automatic state change -bool nightMode = false; // stores state of nightmode -bool nightModeActivated = true; // stores if the function nightmode is activated (its not the state of nightmode) -bool ledOff = false; // stores state of led off -uint32_t maincolor_clock = colors24bit[2]; // color of the clock and digital clock -uint32_t maincolor_snake = colors24bit[1]; // color of the random snake animation -bool apmode = false; // stores if WiFi AP mode is active -bool dynColorShiftActive = false; // stores if dynamic color shift is active -uint8_t dynColorShiftPhase = 0; // stores the phase of the dynamic color shift -uint8_t dynColorShiftSpeed = 1; // stores the speed of the dynamic color shift -> used to calc update period +float filterFactor = DEFAULT_SMOOTHING_FACTOR; // stores smoothing factor for led transition +uint8_t currentState = st_clock; // stores current state +bool stateAutoChange = false; // stores state of automatic state change +bool nightMode = false; // stores state of nightmode +bool nightModeActivated = DEFAULT_NM_ACTIVATED; // stores if the function nightmode is activated (its not the state of nightmode) +bool ledOff = false; // stores state of led off +uint32_t maincolor_clock = colors24bit[2]; // color of the clock and digital clock +uint32_t maincolor_snake = colors24bit[1]; // color of the random snake animation +bool apmode = false; // stores if WiFi AP mode is active +bool dynColorShiftActive = DEFAULT_COLSHIFT_ACTIVE; // stores if dynamic color shift is active +uint8_t dynColorShiftPhase = 0; // stores the phase of the dynamic color shift +uint8_t dynColorShiftSpeed = DEFAULT_COLSHIFT_SPEED; // stores the speed of the dynamic color shift -> used to calc update period // nightmode settings -uint8_t nightModeStartHour = 22; -uint8_t nightModeStartMin = 0; -uint8_t nightModeEndHour = 7; -uint8_t nightModeEndMin = 0; +uint8_t nightModeStartHour = DEFAULT_NM_START_HOUR; +uint8_t nightModeStartMin = DEFAULT_NM_START_MIN; +uint8_t nightModeEndHour = DEFAULT_NM_END_HOUR; +uint8_t nightModeEndMin = DEFAULT_NM_END_MIN; // Watchdog counter to trigger restart if NTP update was not possible 30 times in a row (5min) int watchdogCounter = 30; @@ -221,6 +237,24 @@ void setup() { //Init EEPROM EEPROM.begin(EEPROM_SIZE); + // Check EEPROM version code + uint8_t storedVersion = EEPROM.read(ADR_EEPROM_VERSION); + if (storedVersion != EEPROM_VERSION_CODE) { + // Set new defaults + EEPROM.write(ADR_EEPROM_VERSION, EEPROM_VERSION_CODE); + EEPROM.write(ADR_NM_START_H, DEFAULT_NM_START_HOUR); + EEPROM.write(ADR_NM_START_M, DEFAULT_NM_START_MIN); + EEPROM.write(ADR_NM_END_H, DEFAULT_NM_END_HOUR); + EEPROM.write(ADR_NM_END_M, DEFAULT_NM_END_MIN); + EEPROM.write(ADR_BRIGHTNESS, DEFAULT_BRIGHTNESS); + setMainColor(DEFAULT_MC_RED, DEFAULT_MC_GREEN, DEFAULT_MC_BLUE); + EEPROM.write(ADR_STATE, st_clock); + EEPROM.write(ADR_NM_ACTIVATED, DEFAULT_NM_ACTIVATED); + EEPROM.write(ADR_COLSHIFTSPEED, DEFAULT_COLSHIFT_SPEED); + EEPROM.write(ADR_COLSHIFTACTIVE, DEFAULT_COLSHIFT_ACTIVE); + EEPROM.commit(); + } + // configure button pin as input pinMode(BUTTONPIN, INPUT_PULLUP); From 2c95a5f1a46972e79ce96fa75c884c90b61d6b48 Mon Sep 17 00:00:00 2001 From: Edgar W <36072504+techniccontroller@users.noreply.github.com> Date: Sun, 21 Sep 2025 17:17:03 +0200 Subject: [PATCH 02/13] Update README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f7bc04c..80599fd 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,8 @@ We've got some interesting branches in this repo inspired by user feedback. Thes ![compile esp8266 workflow](https://github.com/techniccontroller/wordclock_esp8266/actions/workflows/compile_esp8266.yml/badge.svg?branch=rgbw_leds) - [**static_background_pattern**](https://github.com/techniccontroller/wordclock_esp8266/tree/static_background_pattern): This branch allows to light up specific letters always during clock mode. E.G., to display some special words in another color. ![compile esp8266 workflow](https://github.com/techniccontroller/wordclock_esp8266/actions/workflows/compile_esp8266.yml/badge.svg?branch=static_background_pattern) +- [**frame_light**](https://github.com/techniccontroller/wordclock_esp8266/tree/frame_light): This branch allows to add an additional LEDs around the clock, as background/frame light it has the same color as the clock. Thanks to Sandro for the idea. +![compile esp8266 workflow](https://github.com/techniccontroller/wordclock_esp8266/actions/workflows/compile_esp8266.yml/badge.svg?branch=frame_light) ## Install needed Libraries From d407c1d0f558ac9b92fa9db873a5bed8796e97f3 Mon Sep 17 00:00:00 2001 From: Edgar W Date: Sat, 3 Jan 2026 14:41:05 +0100 Subject: [PATCH 03/13] feat: add night mode brightness setting --- data/index.html | 10 +++++++++- wordclock_esp8266.ino | 42 +++++++++++++++++++++++++++++++++++------- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/data/index.html b/data/index.html index 4bce60d..15700fd 100644 --- a/data/index.html +++ b/data/index.html @@ -243,7 +243,7 @@ } .show{ - height: 280px; + height: 320px; transition: height 1s; } @@ -280,6 +280,10 @@

WORDCLOCK 2.0

+
+ + +
@@ -536,6 +540,7 @@

WORDCLOCK 2.0

document.getElementById("nm_start").value = myVar.nightModeStart.replace("-", ":"); document.getElementById("nm_end").value = myVar.nightModeEnd.replace("-", ":"); document.getElementById("brightness").value = parseInt(myVar.brightness); + document.getElementById("nm_brightness").value = parseInt(myVar.nightModeBrightness); document.getElementById("colorshiftspeed").value = parseInt(myVar.colorshiftspeed); updateDisplay(parseInt(myVar.modeid)); @@ -606,6 +611,7 @@

WORDCLOCK 2.0

var nmStart = document.getElementById("nm_start"); var nmEnd = document.getElementById("nm_end"); var sld_brightness = document.getElementById("brightness"); + var sld_nm_brightness = document.getElementById("nm_brightness"); var sld_colorshiftspeed = document.getElementById("colorshiftspeed"); var ckb_resetWifi = document.querySelector('input[id="ResetWifi"]'); var cmdstr = "./cmd?setting="; @@ -616,6 +622,8 @@

WORDCLOCK 2.0

cmdstr += sld_brightness.value; cmdstr += "-"; cmdstr += sld_colorshiftspeed.value; + cmdstr += "-"; + cmdstr += sld_nm_brightness.value; console.log(cmdstr); sendCommand(cmdstr); if(ckb_resetWifi.checked) { diff --git a/wordclock_esp8266.ino b/wordclock_esp8266.ino index 9435069..2dc2dbb 100644 --- a/wordclock_esp8266.ino +++ b/wordclock_esp8266.ino @@ -48,10 +48,10 @@ // CONSTANTS // ---------------------------------------------------------------------------------- -#define EEPROM_VERSION_CODE 2 // Change this value when defaults settings change +#define EEPROM_VERSION_CODE 3 // Change this value when defaults settings change // EEPROM address map (all uint8_t, 1 byte each) -#define EEPROM_SIZE 13 // size of EEPROM to save persistent variables +#define EEPROM_SIZE 14 // size of EEPROM to save persistent variables #define ADR_EEPROM_VERSION 0 // uint8_t #define ADR_NM_START_H 1 // uint8_t #define ADR_NM_END_H 2 // uint8_t @@ -65,17 +65,19 @@ #define ADR_NM_ACTIVATED 10 // uint8_t #define ADR_COLSHIFTSPEED 11 // uint8_t #define ADR_COLSHIFTACTIVE 12 // uint8_t +#define ADR_NM_BRIGHTNESS 13 // uint8_t // DEFAULT SETTINGS (if one changes this, also increment the EEPROM_VERSION_CODE, to ensure that the EEPROM is updated with the new defaults) #define DEFAULT_NM_START_HOUR 22 // default start hour of nightmode (0-23) #define DEFAULT_NM_START_MIN 5 // default start minute of nightmode (0-59) #define DEFAULT_NM_END_HOUR 7 // default end hour of nightmode (0-23) #define DEFAULT_NM_END_MIN 0 // default end minute of nightmode (0-59) -#define DEFAULT_BRIGHTNESS 40 // default brightness of LEDs (0-255) +#define DEFAULT_BRIGHTNESS 40 // default brightness of LEDs (10-255) #define DEFAULT_MC_RED 200 // default main color red value #define DEFAULT_MC_GREEN 200 // default main color green value #define DEFAULT_MC_BLUE 0 // default main color blue value #define DEFAULT_NM_ACTIVATED 1 // if function nightmode is activated (0 = deactivated, 1 = activated) +#define DEFAULT_NM_BRIGHTNESS 0 // default brightness during night mode (0-255) #define DEFAULT_COLSHIFT_SPEED 1 // needs to be between larger than 0 (1 = slowest, 255 = fastest) #define DEFAULT_COLSHIFT_ACTIVE 0 // if dynamic color shift is active (0 = deactivated, 1 = activated) @@ -216,6 +218,7 @@ uint8_t nightModeStartHour = DEFAULT_NM_START_HOUR; uint8_t nightModeStartMin = DEFAULT_NM_START_MIN; uint8_t nightModeEndHour = DEFAULT_NM_END_HOUR; uint8_t nightModeEndMin = DEFAULT_NM_END_MIN; +uint8_t nightModeBrightness = DEFAULT_NM_BRIGHTNESS; // Watchdog counter to trigger restart if NTP update was not possible 30 times in a row (5min) int watchdogCounter = 30; @@ -252,6 +255,7 @@ void setup() { EEPROM.write(ADR_NM_ACTIVATED, DEFAULT_NM_ACTIVATED); EEPROM.write(ADR_COLSHIFTSPEED, DEFAULT_COLSHIFT_SPEED); EEPROM.write(ADR_COLSHIFTACTIVE, DEFAULT_COLSHIFT_ACTIVE); + EEPROM.write(ADR_NM_BRIGHTNESS, DEFAULT_NM_BRIGHTNESS); EEPROM.commit(); } @@ -394,6 +398,7 @@ void setup() { loadNightmodeSettingsFromEEPROM(); loadBrightnessSettingsFromEEPROM(); loadColorShiftStateFromEEPROM(); + loadNightmodeBrightnessFromEEPROM(); if(ESP.getResetReason().equals("Power On") || ESP.getResetReason().equals("External System")){ // test quickly each LED @@ -460,15 +465,23 @@ void loop() { } // handle state behaviours (trigger loopCycles of different states depending on current state) - if(!nightMode && !ledOff && (millis() - lastStep > behaviorUpdatePeriod) && (millis() - lastLEDdirect > TIMEOUT_LEDDIRECT)){ + if(!ledOff && (millis() - lastStep > behaviorUpdatePeriod) && (millis() - lastLEDdirect > TIMEOUT_LEDDIRECT)){ updateStateBehavior(currentState); lastStep = millis(); } - // Turn off LEDs if ledOff is true or nightmode is active - if((ledOff || nightMode) && !waitForTimeAfterReboot){ + // Turn off LEDs if ledOff is true + if(ledOff && !waitForTimeAfterReboot){ ledmatrix.gridFlush(); } + + // Apply night mode brightness + if(nightMode && !ledOff && !waitForTimeAfterReboot){ + ledmatrix.setBrightness(nightModeBrightness); + } + else if(!nightMode && !ledOff && !waitForTimeAfterReboot){ + ledmatrix.setBrightness(brightness); + } // periodically write colors to matrix if(millis() - lastAnimationStep > PERIOD_MATRIXUPDATE && !waitForTimeAfterReboot && (millis() - lastLEDdirect > TIMEOUT_LEDDIRECT)){ @@ -480,7 +493,7 @@ void loop() { handleButton(); // handle state changes - if(stateAutoChange && (millis() - lastStateChange > PERIOD_STATECHANGE) && !nightMode && !ledOff){ + if(stateAutoChange && (millis() - lastStateChange > PERIOD_STATECHANGE) && !ledOff){ // increment state variable and trigger state change stateChange((currentState + 1) % NUM_STATES, false); @@ -931,6 +944,16 @@ void loadColorShiftStateFromEEPROM() logger.logString("ColorShiftActive: " + String(dynColorShiftActive)); } +/** + * @brief Load the night mode brightness from EEPROM + * + */ +void loadNightmodeBrightnessFromEEPROM() +{ + nightModeBrightness = EEPROM.read(ADR_NM_BRIGHTNESS); + logger.logString("Night mode brightness: " + String(nightModeBrightness)); +} + /** * @brief Handler for handling commands sent to "/cmd" url * @@ -1003,6 +1026,7 @@ void handleCommand() { nightModeEndMin = split(timestr, '-', 3).toInt(); brightness = split(timestr, '-', 4).toInt(); dynColorShiftSpeed = split(timestr, '-', 5).toInt(); + nightModeBrightness = split(timestr, '-', 6).toInt(); if(nightModeStartHour < 0 || nightModeStartHour > 23) nightModeStartHour = 22; if(nightModeStartMin < 0 || nightModeStartMin > 59) nightModeStartMin = 0; if(nightModeEndHour < 0 || nightModeEndHour > 23) nightModeEndHour = 7; @@ -1015,11 +1039,13 @@ void handleCommand() { EEPROM.write(ADR_NM_END_M, nightModeEndMin); EEPROM.write(ADR_BRIGHTNESS, brightness); EEPROM.write(ADR_COLSHIFTSPEED, dynColorShiftSpeed); + EEPROM.write(ADR_NM_BRIGHTNESS, nightModeBrightness); EEPROM.commit(); logger.logString("Nightmode starts at: " + String(nightModeStartHour) + ":" + String(nightModeStartMin)); logger.logString("Nightmode ends at: " + String(nightModeEndHour) + ":" + String(nightModeEndMin)); logger.logString("Brightness: " + String(brightness)); logger.logString("ColorShiftSpeed: " + String(dynColorShiftSpeed)); + logger.logString("Night mode brightness: " + String(nightModeBrightness)); ledmatrix.setBrightness(brightness); lastNightmodeCheck = millis() - PERIOD_NIGHTMODECHECK; } @@ -1173,6 +1199,8 @@ void handleDataRequest() { message += ","; message += "\"brightness\":\"" + String(brightness) + "\""; message += ","; + message += "\"nightModeBrightness\":\"" + String(nightModeBrightness) + "\""; + message += ","; message += "\"colorshift\":\"" + String(dynColorShiftActive) + "\""; message += ","; message += "\"colorshiftspeed\":\"" + String(dynColorShiftSpeed) + "\""; From ee60e8dc740c2cfac6cb208d585b81872b4e070c Mon Sep 17 00:00:00 2001 From: Edgar W Date: Sat, 3 Jan 2026 14:51:34 +0100 Subject: [PATCH 04/13] feat: add getters for day of the month and month number --- ntp_client_plus.cpp | 20 ++++++++++++++++++++ ntp_client_plus.h | 2 ++ 2 files changed, 22 insertions(+) diff --git a/ntp_client_plus.cpp b/ntp_client_plus.cpp index 451bde3..2dadd9f 100644 --- a/ntp_client_plus.cpp +++ b/ntp_client_plus.cpp @@ -314,6 +314,26 @@ unsigned int NTPClientPlus::getDayOfWeek() return this->_dayOfWeek; } +/** + * @brief Getter for day of the month + * + * @return unsigned int + */ +unsigned int NTPClientPlus::getDayOfMonth() +{ + return this->_dateDay; +} + +/** + * @brief Getter for the month number + * + * @return unsigned int + */ +unsigned int NTPClientPlus::getMonthNumber() +{ + return this->_dateMonth; +} + /** * @brief Function to calc current year * diff --git a/ntp_client_plus.h b/ntp_client_plus.h index 847306c..7060f2c 100644 --- a/ntp_client_plus.h +++ b/ntp_client_plus.h @@ -33,6 +33,8 @@ class NTPClientPlus{ String getFormattedDate(); void calcDate(); unsigned int getDayOfWeek(); + unsigned int getDayOfMonth(); + unsigned int getMonthNumber(); unsigned int getYear(); bool isLeapYear(unsigned int year); int getMonth(int dayOfYear); From 1de68ec9edeafd1ecbab945480384028bab798b3 Mon Sep 17 00:00:00 2001 From: Edgar W Date: Sat, 3 Jan 2026 15:04:37 +0100 Subject: [PATCH 05/13] fix: change ZEHN VOR to ZWANZIG NACH in german version --- wordclockfunctions.ino | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wordclockfunctions.ino b/wordclockfunctions.ino index ecbdc6e..5720b03 100644 --- a/wordclockfunctions.ino +++ b/wordclockfunctions.ino @@ -114,7 +114,7 @@ String timeToString(uint8_t hours,uint8_t minutes){ } else if(minutes >= 20 && minutes < 25) { - message += "ZEHN VOR HALB "; + message += "ZWANZIG NACH "; } else if(minutes >= 25 && minutes < 30) { @@ -130,7 +130,7 @@ String timeToString(uint8_t hours,uint8_t minutes){ } else if(minutes >= 40 && minutes < 45) { - message += "ZEHN NACH HALB "; + message += "ZWANZIG VOR "; } else if(minutes >= 45 && minutes < 50) { @@ -150,7 +150,7 @@ String timeToString(uint8_t hours,uint8_t minutes){ { hours -= 12; } - if(minutes >= 20) + if(minutes >= 25) { hours++; } From 57b5d33b713a6dd3b20d9d12897fd1cfca88dfb9 Mon Sep 17 00:00:00 2001 From: Techniccontroller Date: Sat, 4 Apr 2026 12:17:27 +0200 Subject: [PATCH 06/13] fix: improve daylight saving time handling in updateSWChange method, fix #79 --- ntp_client_plus.cpp | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/ntp_client_plus.cpp b/ntp_client_plus.cpp index 2dadd9f..b35504b 100644 --- a/ntp_client_plus.cpp +++ b/ntp_client_plus.cpp @@ -585,6 +585,15 @@ bool NTPClientPlus::updateSWChange() unsigned int dateDay = this->_dateDay; unsigned int dateMonth = this->_dateMonth; + // Calculate local STANDARD time (without DST) for exact switch-hour handling. + // EU rule: DST starts on last Sunday in March at 02:00 (standard time), + // and ends on last Sunday in October at 03:00 local summer time + // (== 02:00 standard time). + unsigned long secsSince1900NoDST = this->_utcx * this->secondperminute + + this->_secsSince1900 + + ((millis() - this->_lastUpdate) / 1000); + unsigned int hourNoDST = (secsSince1900NoDST % this->secondperday) / this->secondperhour; + bool summertimeActive = false; if (this->_swChange) @@ -607,10 +616,19 @@ bool NTPClientPlus::updateSWChange() //Calculation: 31 - 30 = 1; 1 + 2 = 3; //Result: Last day in March is a Wednesday. Changeover to summer time already done => set summer time - // If today is Sunday (dayOfWeek == 7) then this is already the last sunday in march -> set summer time + // If today is Sunday (dayOfWeek == 7) then this is the last Sunday in March. + // Switch to summer time at 02:00 standard time (clock jumps to 03:00). if(dayOfWeek == 7){ - this->setSummertime(1); - summertimeActive = true; + if (hourNoDST >= 2) + { + this->setSummertime(1); + summertimeActive = true; + } + else + { + this->setSummertime(0); + summertimeActive = false; + } } //There follows within the last week in March one more Sunday => set winter time @@ -652,10 +670,20 @@ bool NTPClientPlus::updateSWChange() //Calculation: 31 - 26 = 5; 5 + 2 = 7; //Result: Last day in October is a Sunday. There follows another Sunday in October => set summer time - // If today is Sunday (dayOfWeek == 7) then this is already the last sunday in october -> winter time + // If today is Sunday (dayOfWeek == 7) then this is the last Sunday in October. + // Switch back to winter time at 02:00 standard time + // (which corresponds to 03:00 local summer time). if(dayOfWeek == 7){ - this->setSummertime(0); - summertimeActive = false; + if (hourNoDST >= 2) + { + this->setSummertime(0); + summertimeActive = false; + } + else + { + this->setSummertime(1); + summertimeActive = true; + } } // There follows within the last week in October one more Sunday => summer time From 7afb73c857117795a6cd0d174a4401247465cd1b Mon Sep 17 00:00:00 2001 From: Edgar W <36072504+techniccontroller@users.noreply.github.com> Date: Sat, 4 Apr 2026 12:35:25 +0200 Subject: [PATCH 07/13] fix signed time calculations Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- ntp_client_plus.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/ntp_client_plus.cpp b/ntp_client_plus.cpp index b35504b..8dc6742 100644 --- a/ntp_client_plus.cpp +++ b/ntp_client_plus.cpp @@ -589,10 +589,16 @@ bool NTPClientPlus::updateSWChange() // EU rule: DST starts on last Sunday in March at 02:00 (standard time), // and ends on last Sunday in October at 03:00 local summer time // (== 02:00 standard time). - unsigned long secsSince1900NoDST = this->_utcx * this->secondperminute + - this->_secsSince1900 + - ((millis() - this->_lastUpdate) / 1000); - unsigned int hourNoDST = (secsSince1900NoDST % this->secondperday) / this->secondperhour; + int64_t currentUtcSeconds = static_cast(this->_secsSince1900) + + static_cast((millis() - this->_lastUpdate) / 1000); + int64_t secsSince1900NoDST = currentUtcSeconds + + (static_cast(this->_utcx) * this->secondperminute); + int64_t secondsIntoDay = secsSince1900NoDST % this->secondperday; + if (secondsIntoDay < 0) + { + secondsIntoDay += this->secondperday; + } + unsigned int hourNoDST = static_cast(secondsIntoDay / this->secondperhour); bool summertimeActive = false; From 491c35f329b62b65ce7be21c59316c6edab29c61 Mon Sep 17 00:00:00 2001 From: Techniccontroller Date: Sat, 4 Apr 2026 12:41:41 +0200 Subject: [PATCH 08/13] fix: improve calculation of seconds since 1 Jan 1900 to consider signed/unsigned variables --- ntp_client_plus.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ntp_client_plus.cpp b/ntp_client_plus.cpp index 8dc6742..f272461 100644 --- a/ntp_client_plus.cpp +++ b/ntp_client_plus.cpp @@ -128,11 +128,12 @@ void NTPClientPlus::setPoolServerName(const char *poolServerName) * @return unsigned long seconds since 1. Jan. 1900 */ unsigned long NTPClientPlus::getSecsSince1900() const -{ - return this->_utcx * this->secondperminute + // UTC offset - this->_summertime * this->secondperhour + // Summer time offset - this->_secsSince1900 + // seconds returned by the NTP server - ((millis() - this->_lastUpdate) / 1000); // Time since last update +{ + int64_t secsSince1900WithDST = static_cast(this->_secsSince1900) + // seconds returned by the NTP server + static_cast((millis() - this->_lastUpdate) / 1000) + // Time since last update + this->_summertime * this->secondperhour + // Summer time offset + (static_cast(this->_utcx) * this->secondperminute); // UTC offset + return static_cast(secsSince1900WithDST); } /** From 6d3ebc36023d26bbe9c370f941e3812d0924628b Mon Sep 17 00:00:00 2001 From: Edgar W Date: Sun, 26 Apr 2026 19:20:40 +0200 Subject: [PATCH 09/13] fix: add timeout for WiFi disconnection handling --- wordclock_esp8266.ino | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/wordclock_esp8266.ino b/wordclock_esp8266.ino index 2dc2dbb..b3fe8c9 100644 --- a/wordclock_esp8266.ino +++ b/wordclock_esp8266.ino @@ -94,6 +94,7 @@ #define PERIOD_SNAKE 50 #define PERIOD_PONG 10 #define TIMEOUT_LEDDIRECT 5000 +#define TIMEOUT_WIFI_DISCONNECTED 30000 #define PERIOD_STATECHANGE 10000 #define PERIOD_NTPUPDATE 30000 #define PERIOD_TIMEVISUUPDATE 1000 @@ -189,6 +190,7 @@ long lastNTPUpdate = millis() - (PERIOD_NTPUPDATE-3000); // time of last NTP up long lastAnimationStep = millis(); // time of last Matrix update long lastNightmodeCheck = millis() - (PERIOD_NIGHTMODECHECK-3000); // time of last nightmode check long buttonPressStart = 0; // time of push button press start +long wifiDisconnectedSince = 0; // time since WiFi connection was lost (0 = connected) uint16_t behaviorUpdatePeriod = PERIOD_TIMEVISUUPDATE; // holdes the period in which the behavior should be updated // Create necessary global objects @@ -456,11 +458,22 @@ void loop() { lastheartbeat = millis(); // Check wifi status (only if no apmode) - if(!apmode && WiFi.status() != WL_CONNECTED){ - Serial.println("connection lost"); - ledmatrix.gridAddPixel(0, 5, colors24bit[1]); - ledmatrix.drawOnMatrixInstant(); - delay(1000); + if(!apmode){ + if(WiFi.status() != WL_CONNECTED){ + if(wifiDisconnectedSince == 0){ + wifiDisconnectedSince = millis(); + Serial.println("connection lost"); + } + + if(millis() - wifiDisconnectedSince >= TIMEOUT_WIFI_DISCONNECTED){ + ledmatrix.gridAddPixel(0, 5, colors24bit[1]); + ledmatrix.drawOnMatrixInstant(); + delay(1000); + } + } + else { + wifiDisconnectedSince = 0; + } } } From 26c47ad4ce1e0c6f6d244c8b2985bf7562cc703d Mon Sep 17 00:00:00 2001 From: deepstahl77 <129783157+deepstahl77@users.noreply.github.com> Date: Wed, 8 Jul 2026 23:28:52 +0200 Subject: [PATCH 10/13] Optimize getFormattedTime method with snprintf Refactor time formatting to use snprintf for efficiency and avoid heap fragmentation. --- ntp_client_plus.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/ntp_client_plus.cpp b/ntp_client_plus.cpp index f272461..6892941 100644 --- a/ntp_client_plus.cpp +++ b/ntp_client_plus.cpp @@ -200,15 +200,14 @@ int NTPClientPlus::getSeconds() const String NTPClientPlus::getFormattedTime() const { unsigned long rawTime = this->getEpochTime(); unsigned long hours = (rawTime % 86400L) / 3600; - String hoursStr = hours < 10 ? "0" + String(hours) : String(hours); - unsigned long minutes = (rawTime % 3600) / 60; - String minuteStr = minutes < 10 ? "0" + String(minutes) : String(minutes); - unsigned long seconds = rawTime % 60; - String secondStr = seconds < 10 ? "0" + String(seconds) : String(seconds); - return hoursStr + ":" + minuteStr + ":" + secondStr; + // Fester Puffer statt mehrfacher String-Verkettung, um Heap-Fragmentierung zu vermeiden + // Format identisch zu vorher: hh:mm:ss (jeweils 2-stellig, mit fuehrender Null) + char buffer[9]; // "hh:mm:ss" + '\0' + snprintf(buffer, sizeof(buffer), "%02lu:%02lu:%02lu", hours, minutes, seconds); + return String(buffer); } /** @@ -732,4 +731,4 @@ bool NTPClientPlus::updateSWChange() } return summertimeActive; -} \ No newline at end of file +} From f1f6db32626b84ba78e99422c93677a0c247478e Mon Sep 17 00:00:00 2001 From: deepstahl77 <129783157+deepstahl77@users.noreply.github.com> Date: Wed, 8 Jul 2026 23:47:42 +0200 Subject: [PATCH 11/13] Refactor logString method for efficiency Refactor logString to use snprintf for better performance and avoid heap fragmentation. --- udplogger.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/udplogger.cpp b/udplogger.cpp index 369af22..0e7c34a 100644 --- a/udplogger.cpp +++ b/udplogger.cpp @@ -16,15 +16,16 @@ void UDPLogger::setName(String name){ _name = name; } -void UDPLogger::logString(String logmessage){ +void UDPLogger::logString(const String& logmessage){ // wait 5 milliseconds if last send was less than 5 milliseconds before if(millis() < (_lastSend + 5)){ delay(5); } - logmessage = _name + ": " + logmessage; - Serial.println(logmessage); + // Fester Puffer statt String-Verkettung, um Heap-Fragmentierung zu vermeiden + // (identisches Format und identische Kuerzung bei 100 Zeichen wie vorher) + snprintf(_packetBuffer, sizeof(_packetBuffer), "%s: %s", _name.c_str(), logmessage.c_str()); + Serial.println(_packetBuffer); _Udp.beginPacketMulticast(_multicastAddr, _port, _interfaceAddr); - logmessage.toCharArray(_packetBuffer, 100); _Udp.print(_packetBuffer); _Udp.endPacket(); _lastSend=millis(); @@ -35,4 +36,4 @@ void UDPLogger::logColor24bit(uint32_t color){ uint8_t resultGreen = color >> 8 & 0xff; uint8_t resultBlue = color & 0xff; logString(String(resultRed) + ", " + String(resultGreen) + ", " + String(resultBlue)); -} \ No newline at end of file +} From d49386d8aabcf5628565a50a905bb8caf30a026c Mon Sep 17 00:00:00 2001 From: Edgar W Date: Sat, 11 Jul 2026 23:04:57 +0200 Subject: [PATCH 12/13] fix signature of logString() --- udplogger.cpp | 4 ++-- udplogger.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/udplogger.cpp b/udplogger.cpp index 0e7c34a..00ecb02 100644 --- a/udplogger.cpp +++ b/udplogger.cpp @@ -21,8 +21,8 @@ void UDPLogger::logString(const String& logmessage){ if(millis() < (_lastSend + 5)){ delay(5); } - // Fester Puffer statt String-Verkettung, um Heap-Fragmentierung zu vermeiden - // (identisches Format und identische Kuerzung bei 100 Zeichen wie vorher) + // use fixed buffer instead of string concatenation to avoid heap fragmentation + // cut message to 100 characters of length (buffer size) snprintf(_packetBuffer, sizeof(_packetBuffer), "%s: %s", _name.c_str(), logmessage.c_str()); Serial.println(_packetBuffer); _Udp.beginPacketMulticast(_multicastAddr, _port, _interfaceAddr); diff --git a/udplogger.h b/udplogger.h index c26fa49..e4cac28 100644 --- a/udplogger.h +++ b/udplogger.h @@ -22,7 +22,7 @@ class UDPLogger{ UDPLogger(); UDPLogger(IPAddress interfaceAddr, IPAddress multicastAddr, int port); void setName(String name); - void logString(String logmessage); + void logString(const String& logmessage); void logColor24bit(uint32_t color); private: String _name; From 18393ac9660680b111ce0ac29b48b90795224eae Mon Sep 17 00:00:00 2001 From: Edgar W Date: Sat, 11 Jul 2026 23:12:59 +0200 Subject: [PATCH 13/13] fix: initialize _lastSend in UDPLogger constructor and update logString comment --- udplogger.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/udplogger.cpp b/udplogger.cpp index 00ecb02..619afc7 100644 --- a/udplogger.cpp +++ b/udplogger.cpp @@ -10,6 +10,7 @@ UDPLogger::UDPLogger(IPAddress interfaceAddr, IPAddress multicastAddr, int port) _interfaceAddr = interfaceAddr; _name = "Log"; _Udp.beginMulticast(_interfaceAddr, _multicastAddr, _port); + _lastSend = 0; } void UDPLogger::setName(String name){ @@ -22,7 +23,7 @@ void UDPLogger::logString(const String& logmessage){ delay(5); } // use fixed buffer instead of string concatenation to avoid heap fragmentation - // cut message to 100 characters of length (buffer size) + // message will be truncated to fit into _packetBuffer (99 chars + NUL) snprintf(_packetBuffer, sizeof(_packetBuffer), "%s: %s", _name.c_str(), logmessage.c_str()); Serial.println(_packetBuffer); _Udp.beginPacketMulticast(_multicastAddr, _port, _interfaceAddr);