From 8bf90775c86e43be84e1b17e9ca4ed83aa4b919f Mon Sep 17 00:00:00 2001
From: DanielR92
Date: Wed, 12 Oct 2022 16:23:42 +0200
Subject: [PATCH] first patch from homeautomation22
---
tools/esp8266/ahoywifi.cpp | 22 --------
tools/esp8266/ahoywifi.h | 4 +-
tools/esp8266/app.cpp | 54 +++++++++++++++++--
tools/esp8266/app.h | 27 +++++++++-
tools/esp8266/html/index.html | 6 +++
tools/esp8266/sun.cpp | 99 +++++++++++++++++++++++++++++++++++
tools/esp8266/sun.h | 42 +++++++++++++++
tools/esp8266/webApi.cpp | 3 ++
8 files changed, 226 insertions(+), 31 deletions(-)
create mode 100644 tools/esp8266/sun.cpp
create mode 100644 tools/esp8266/sun.h
diff --git a/tools/esp8266/ahoywifi.cpp b/tools/esp8266/ahoywifi.cpp
index b3feb43c..436fd660 100644
--- a/tools/esp8266/ahoywifi.cpp
+++ b/tools/esp8266/ahoywifi.cpp
@@ -12,7 +12,6 @@
// NTP CONFIG
#define NTP_PACKET_SIZE 48
-#define TIMEZONE 1 // Central European time +1
//-----------------------------------------------------------------------------
@@ -185,7 +184,6 @@ time_t ahoywifi::getNtpTime(void) {
WiFi.hostByName(mConfig->ntpAddr, timeServer);
mUdp->begin(mConfig->ntpPort);
-
sendNTPpacket(timeServer);
while(retry++ < 5) {
@@ -200,7 +198,6 @@ time_t ahoywifi::getNtpTime(void) {
secsSince1900 |= (buf[43] );
date = secsSince1900 - 2208988800UL; // UTC time
- date += (TIMEZONE + offsetDayLightSaving(date)) * 3600;
break;
}
else
@@ -231,22 +228,3 @@ void ahoywifi::sendNTPpacket(IPAddress& address) {
mUdp->write(buf, NTP_PACKET_SIZE);
mUdp->endPacket();
}
-
-
-//-----------------------------------------------------------------------------
-// calculates the daylight saving time for middle Europe. Input: Unixtime in UTC
-// from: https://forum.arduino.cc/index.php?topic=172044.msg1278536#msg1278536
-time_t ahoywifi::offsetDayLightSaving (uint32_t local_t) {
- //DPRINTLN(DBG_VERBOSE, F("wifi::offsetDayLightSaving"));
- int m = month (local_t);
- if(m < 3 || m > 10) return 0; // no DSL in Jan, Feb, Nov, Dez
- if(m > 3 && m < 10) return 1; // DSL in Apr, May, Jun, Jul, Aug, Sep
- int y = year (local_t);
- int h = hour (local_t);
- int hToday = (h + 24 * day(local_t));
- if((m == 3 && hToday >= (1 + TIMEZONE + 24 * (31 - (5 * y /4 + 4) % 7)))
- || (m == 10 && hToday < (1 + TIMEZONE + 24 * (31 - (5 * y /4 + 1) % 7))) )
- return 1;
- else
- return 0;
-}
diff --git a/tools/esp8266/ahoywifi.h b/tools/esp8266/ahoywifi.h
index fdd3bff0..ba02c577 100644
--- a/tools/esp8266/ahoywifi.h
+++ b/tools/esp8266/ahoywifi.h
@@ -30,11 +30,9 @@ class ahoywifi {
bool setupStation(uint32_t timeout);
bool getApActive(void);
time_t getNtpTime(void);
-
+
private:
void sendNTPpacket(IPAddress& address);
- time_t offsetDayLightSaving (uint32_t local_t);
-
config_t *mConfig;
sysConfig_t *mSysCfg;
diff --git a/tools/esp8266/app.cpp b/tools/esp8266/app.cpp
index f2aa473a..17798bfc 100644
--- a/tools/esp8266/app.cpp
+++ b/tools/esp8266/app.cpp
@@ -44,6 +44,8 @@ void app::setup(uint32_t timeout) {
mWebInst = new web(this, &mSysConfig, &mConfig, &mStat, mVersion);
mWebInst->setup();
+
+ mSun.setLocalization(52.6479, 13.6922); // ToDo: add lan/lon to setup, can also in app::loadEEpconfig or somewhere else in app.cpp
}
//-----------------------------------------------------------------------------
@@ -56,6 +58,8 @@ void app::loop(void) {
if(millis() - mPrevMillis >= 1000) {
mPrevMillis += 1000;
mUptimeSecs++;
+ if(0 != mUtcTimestamp)
+ mUtcTimestamp++;
if(0 != mTimestamp)
mTimestamp++;
}
@@ -67,7 +71,8 @@ void app::loop(void) {
if(mUpdateNtp) {
mUpdateNtp = false;
- mTimestamp = mWifi->getNtpTime();
+ mUtcTimestamp = mWifi->getNtpTime();
+ mTimestamp = mUtcTimestamp + ((TIMEZONE + offsetDayLightSaving(mUtcTimestamp)) * 3600);
DPRINTLN(DBG_INFO, "[NTP]: " + getDateTimeStr(mTimestamp));
}
@@ -149,6 +154,12 @@ void app::loop(void) {
mMqtt.loop();
if(checkTicker(&mTicker, 1000)) {
+ if(mTimestamp > 946684800 && mSun.LocalizationIsSet() && mTimestamp / 86400 > mLatestSunTimestamp / 86400) // update on reboot or new day
+ {
+ CalculateSunriseSunset(mTimestamp, &mSunrise, &mSunset);
+ mLatestSunTimestamp = mTimestamp;
+ }
+
if((++mMqttTicker >= mMqttInterval) && (mMqttInterval != 0xffff) && mMqttActive) {
mMqttTicker = 0;
mMqtt.isConnected(true); // really needed? See comment from HorstG-57 #176
@@ -195,7 +206,8 @@ void app::loop(void) {
if(++mSendTicker >= mConfig.sendInterval) {
mSendTicker = 0;
- if(0 != mTimestamp) {
+ bool DisableNightCommunication = false; // ToDo: Add option in setup to disable inverter communication at night
+ if(mUtcTimestamp > 946684800 && (!DisableNightCommunication || !mLatestSunTimestamp || (mTimestamp >= mSunrise && mTimestamp <= mSunset))) { // Timestamp is set and (inverter communication only during the day if the option is activated and sunrise/sunset is set)
if(mConfig.serialDebug)
DPRINTLN(DBG_DEBUG, F("Free heap: 0x") + String(ESP.getFreeHeap(), HEX));
@@ -257,7 +269,7 @@ void app::loop(void) {
}
}
else if(mConfig.serialDebug)
- DPRINTLN(DBG_WARN, F("time not set, can't request inverter!"));
+ DPRINTLN(DBG_WARN, F("Time not set or it is night time, therefore no communication to the inverter!"));
yield();
}
}
@@ -379,7 +391,7 @@ void app::processPayload(bool retransmit) {
if(NULL == rec)
DPRINTLN(DBG_ERROR, F("record is NULL!"));
else {
- rec->ts = mPayload[iv->id].ts;
+ rec->ts = mPayload[iv->id].ts + ((TIMEZONE + offsetDayLightSaving(mUtcTimestamp)) * 3600);
for(uint8_t i = 0; i < rec->length; i++) {
iv->addValue(i, payload, rec);
yield();
@@ -679,8 +691,10 @@ void app::resetSystem(void) {
mNtpRefreshInterval = NTP_REFRESH_INTERVAL; // [ms]
#ifdef AP_ONLY
+ mUtcTimestamp = 1;
mTimestamp = 1;
#else
+ mUtcTimestamp = 0;
mTimestamp = 0;
#endif
@@ -857,5 +871,35 @@ void app::resetPayload(Inverter<>* iv) {
mPayload[iv->id].maxPackId = 0;
mPayload[iv->id].complete = false;
mPayload[iv->id].requested = false;
- mPayload[iv->id].ts = mTimestamp;
+ mPayload[iv->id].ts = mUtcTimestamp;
+}
+
+//-----------------------------------------------------------------------------
+// calculates the daylight saving time for middle Europe. Input: Unixtime in UTC
+// from: https://forum.arduino.cc/index.php?topic=172044.msg1278536#msg1278536
+uint8_t app::offsetDayLightSaving (uint32_t local_t) {
+ //DPRINTLN(DBG_VERBOSE, F("wifi::offsetDayLightSaving"));
+ int m = month (local_t);
+ if(m < 3 || m > 10) return 0; // no DSL in Jan, Feb, Nov, Dez
+ if(m > 3 && m < 10) return 1; // DSL in Apr, May, Jun, Jul, Aug, Sep
+ int y = year (local_t);
+ int h = hour (local_t);
+ int hToday = (h + 24 * day(local_t));
+ if((m == 3 && hToday >= (1 + TIMEZONE + 24 * (31 - (5 * y /4 + 4) % 7)))
+ || (m == 10 && hToday < (1 + TIMEZONE + 24 * (31 - (5 * y /4 + 1) % 7))) )
+ return 1;
+ else
+ return 0;
+}
+
+bool app::CalculateSunriseSunset(uint32_t localTimestamp, uint32_t *localSunrise, uint32_t *localSunset) { // true = day; false = night
+ uint32_t UTC_Timestamp_Sunrise, UTC_Timestamp_Sunset;
+ mSun.getRiseSet(localTimestamp, UTC_Timestamp_Sunrise, UTC_Timestamp_Sunset); // local timestamp is only used to calculate today's calendar day
+ *localSunrise = UTC_Timestamp_Sunrise + ((TIMEZONE + offsetDayLightSaving(UTC_Timestamp_Sunrise)) * 3600); // sunrise in local time, OPTIONAL: Add an offset of +-seconds to the end of the line
+ *localSunset = UTC_Timestamp_Sunset + ((TIMEZONE + offsetDayLightSaving(UTC_Timestamp_Sunset)) * 3600); // sunset in local time, OPTIONAL: Add an offset of +-seconds to the end of the line
+
+ if(localTimestamp >= *localSunrise && localTimestamp <= *localSunset)
+ return true; // it is day
+ else
+ return false; // it is night
}
diff --git a/tools/esp8266/app.h b/tools/esp8266/app.h
index fe92bb5e..5431909a 100644
--- a/tools/esp8266/app.h
+++ b/tools/esp8266/app.h
@@ -23,6 +23,9 @@
#include "mqtt.h"
#include "ahoywifi.h"
#include "web.h"
+#include "sun.h"
+
+#define TIMEZONE 1 // Central European time +1
// hier läst sich das Verhalten der app in Bezug auf MQTT
// durch PER-Conpiler defines anpassen
@@ -117,7 +120,20 @@ class app {
if(0 == newTime)
mUpdateNtp = true;
else
- mTimestamp = newTime;
+ {
+ mUtcTimestamp = newTime;
+ mTimestamp = mUtcTimestamp + ((TIMEZONE + offsetDayLightSaving(mUtcTimestamp)) * 3600);
+ }
+ }
+
+ inline uint32_t getSunrise(void) {
+ return mSunrise;
+ }
+ inline uint32_t getSunset(void) {
+ return mSunset;
+ }
+ inline uint32_t getLatestSunTimestamp(void) {
+ return mLatestSunTimestamp;
}
void eraseSettings(bool all = false) {
@@ -234,6 +250,8 @@ class app {
DPRINTLN(DBG_VERBOSE, F(" - frag: ") + String(frag));
}
+ uint8_t offsetDayLightSaving(uint32_t local_t);
+ bool CalculateSunriseSunset(uint32_t localTimestamp, uint32_t *localSunrise, uint32_t *localSunset);
uint32_t mUptimeSecs;
uint32_t mPrevMillis;
@@ -246,6 +264,7 @@ class app {
bool mSettingsValid;
eep *mEep;
+ uint32_t mUtcTimestamp;
uint32_t mTimestamp;
bool mUpdateNtp;
@@ -277,6 +296,12 @@ class app {
// serial
uint16_t mSerialTicker;
+
+ // sun
+ sun mSun;
+ uint32_t mSunrise;
+ uint32_t mSunset;
+ uint32_t mLatestSunTimestamp;
};
#endif /*__APP_H__*/
diff --git a/tools/esp8266/html/index.html b/tools/esp8266/html/index.html
index 48517201..759e4309 100644
--- a/tools/esp8266/html/index.html
+++ b/tools/esp8266/html/index.html
@@ -17,6 +17,8 @@
Uptime:
ESP-Time:
+ Sunrise:
+ Sunset:
RSSI: dBm
Statistics:
@@ -57,11 +59,15 @@
var hrs = parseInt(up / 3600) % 24;
var min = parseInt(up / 60) % 60;
var sec = up % 60;
+ var sunrise = new Date(obj["ts_sunrise"] * 1000);
+ var sunset = new Date(obj["ts_sunset"] * 1000);
document.getElementById("uptime").innerHTML = days + " Days, "
+ ("0"+hrs).substr(-2) + ":"
+ ("0"+min).substr(-2) + ":"
+ ("0"+sec).substr(-2);
document.getElementById("date").innerHTML = date.toLocaleString('de-DE', {timeZone: 'UTC'});
+ document.getElementById("sunrise").innerHTML = sunrise.toLocaleString('de-DE', {timeZone: 'UTC'});
+ document.getElementById("sunset").innerHTML = sunset.toLocaleString('de-DE', {timeZone: 'UTC'});
}
function parseStat(obj) {
diff --git a/tools/esp8266/sun.cpp b/tools/esp8266/sun.cpp
new file mode 100644
index 00000000..3dad7ab7
--- /dev/null
+++ b/tools/esp8266/sun.cpp
@@ -0,0 +1,99 @@
+/*
+ sun.cpp - Library for calculating sunrise and sunset times based on current local time and position on Earth.
+ Calculations are based on Sunrise equation: https://en.wikipedia.org/wiki/Sunrise_equation
+ Input and output times are in Unix Timestamp format (seconds since Jan 1, 1970, 00:00:00).
+
+ Copyright (C) 2017 Nejc Planinsek
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#include "sun.h"
+
+const float pi = 3.14159265;
+
+/*
+ * Constructor. Latitude and longitude must be provided.
+ */
+void sun::setLocalization(float lat, float lon) {
+ _lat = lat;
+ _lon = lon;
+}
+
+bool sun::LocalizationIsSet() {
+ if (_lat && _lon)
+ return true;
+ else
+ return false;
+}
+
+/*
+ * Calculates sunrise and sunset times based on current time. All times are in Unix Timestamp format.
+ */
+void sun::getRiseSet(uint32_t unixTime, uint32_t &sunrise, uint32_t &sunset) {
+ // Convert Julian day to Unix Timestamp
+ uint32_t Jdate = unixTime / 86400 + 2440588;
+ // Number of days since Jan 1st, 2000 12:00
+ float n = Jdate - 2451545.0 + 0.0008;
+ // Mean solar noon
+ float Jstar = -_lon / 360 + n;
+ // Solar mean anomaly
+ float M = fmod((357.5291 + 0.98560028 * Jstar), 360);
+ // Equation of the center
+ float C = 1.9148 * sin(M / 360 * 2 * pi) + 0.02 * sin(2 * M / 360 * 2 * pi) + 0.0003 * sin(3 * M * 360 * 2 * pi);
+ // Ecliptic longitude
+ float lambda = fmod((M + C + 180 + 102.9372), 360);
+ // Solar transit
+ float Jtransit = Jstar + (0.0053 * sin(M / 360.0 * 2.0 * pi) - 0.0069 * sin(2.0 * (lambda / 360.0 * 2.0 * pi)));
+ // Declination of the sun
+ float delta = asin(sin(lambda / 360 * 2 * pi) * sin(23.44 / 360 * 2 * pi)) / (2 * pi) * 360;
+ // Hour angle
+ float omega0 = 360 / (2 * pi) * acos((sin(-0.83 / 360 * 2 * pi) - sin(_lat / 360 * 2 * pi) * sin(delta / 360 * 2 * pi)) / (cos(_lat / 360 * 2 * pi) * cos(delta / 360 * 2 * pi)));
+ // Julian day sunrise, sunset
+ float Jset = Jtransit + omega0 / 360;
+ float Jrise = Jtransit - omega0 / 360;
+ // Convert to Unix Timestamp
+ uint32_t unixRise = Jrise * 86400.0 + 946728000; // Convert days (incl. fraction) to seconds + offset 1/1/2000 12:00
+ uint32_t unixSet = Jset * 86400.0 + 946728000; // Convert days (incl. fraction) to seconds + offset 1/1/2000 12:00
+ sunrise = unixRise;
+ sunset = unixSet;
+}
+
+uint32_t sun::getRise(uint32_t unixTime) {
+ uint32_t rise;
+ uint32_t set;
+ getRiseSet(unixTime, rise, set);
+ return rise;
+}
+
+uint32_t sun::getSet(uint32_t unixTime) {
+ uint32_t rise;
+ uint32_t set;
+ getRiseSet(unixTime, rise, set);
+ return set;
+}
+
+uint32_t sun::getDayLength(uint32_t unixTime) {
+ uint32_t rise;
+ uint32_t set;
+ getRiseSet(unixTime, rise, set);
+ return set - rise;
+}
+
+uint32_t sun::getNightLength(uint32_t unixTime) {
+ uint32_t rise;
+ uint32_t set;
+ getRiseSet(unixTime, rise, set);
+ return 86400 - (set - rise);
+}
diff --git a/tools/esp8266/sun.h b/tools/esp8266/sun.h
new file mode 100644
index 00000000..c614d18d
--- /dev/null
+++ b/tools/esp8266/sun.h
@@ -0,0 +1,42 @@
+/*
+ sun.h - Library for calculating sunrise and sunset times based on current local time and position on Earth.
+ Calculations are based on Sunrise equation: https://en.wikipedia.org/wiki/Sunrise_equation
+ Input and output times are in Unix Timestamp format (seconds since Jan 1, 1970, 00:00:00).
+
+ Copyright (C) 2017 Nejc Planinsek
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#ifndef __SUN_H__
+#define __SUN_H__
+
+#include "Arduino.h"
+
+class sun
+{
+ public:
+ void setLocalization(float lat, float lon);
+ bool LocalizationIsSet();
+ void getRiseSet(uint32_t unixTime, uint32_t &sunrise, uint32_t &sunset);
+ uint32_t getRise(uint32_t unixTime);
+ uint32_t getSet(uint32_t unixTime);
+ uint32_t getDayLength(uint32_t unixTime);
+ uint32_t getNightLength(uint32_t unixTime);
+ private:
+ float _lat = 0.0;
+ float _lon = 0.0;
+};
+
+#endif /*__SUN_H__*/
diff --git a/tools/esp8266/webApi.cpp b/tools/esp8266/webApi.cpp
index 7bf372ab..34ba1cbb 100644
--- a/tools/esp8266/webApi.cpp
+++ b/tools/esp8266/webApi.cpp
@@ -125,6 +125,9 @@ void webApi::getSystem(JsonObject obj) {
obj[F("build")] = String(AUTO_GIT_HASH);
obj[F("ts_uptime")] = mApp->getUptime();
obj[F("ts_now")] = mApp->getTimestamp();
+ obj[F("ts_sunrise")] = mApp->getSunrise();
+ obj[F("ts_sunset")] = mApp->getSunset();
+ obj[F("ts_sun_upd")] = mApp->getLatestSunTimestamp();
obj[F("wifi_rssi")] = WiFi.RSSI();
}