diff --git a/ntp_client_plus.cpp b/ntp_client_plus.cpp index 2dadd9f..6892941 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); } /** @@ -199,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); } /** @@ -585,6 +585,21 @@ 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). + 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; if (this->_swChange) @@ -607,10 +622,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 +676,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 @@ -697,4 +731,4 @@ bool NTPClientPlus::updateSWChange() } return summertimeActive; -} \ No newline at end of file +} diff --git a/udplogger.cpp b/udplogger.cpp index 369af22..619afc7 100644 --- a/udplogger.cpp +++ b/udplogger.cpp @@ -10,21 +10,23 @@ UDPLogger::UDPLogger(IPAddress interfaceAddr, IPAddress multicastAddr, int port) _interfaceAddr = interfaceAddr; _name = "Log"; _Udp.beginMulticast(_interfaceAddr, _multicastAddr, _port); + _lastSend = 0; } 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); + // use fixed buffer instead of string concatenation to avoid heap fragmentation + // 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); - logmessage.toCharArray(_packetBuffer, 100); _Udp.print(_packetBuffer); _Udp.endPacket(); _lastSend=millis(); @@ -35,4 +37,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 +} 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; 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; + } } }