mirror of
https://github.com/lumapu/ahoy.git
synced 2025-05-25 23:06:11 +02:00
fix #468 MQTT status at sunset
This commit is contained in:
parent
3c6e3221e8
commit
9da0fc4058
8 changed files with 222 additions and 138 deletions
71
src/app.cpp
71
src/app.cpp
|
@ -24,25 +24,30 @@ void app::setup(uint32_t timeout) {
|
|||
while (!Serial)
|
||||
yield();
|
||||
|
||||
addListener(EVERY_SEC, std::bind(&app::uptimeTick, this));
|
||||
addListener(EVERY_MIN, std::bind(&app::minuteTick, this));
|
||||
addListener(EVERY_12H, std::bind(&app::ntpUpdateTick, this));
|
||||
|
||||
resetSystem();
|
||||
mSettings.setup();
|
||||
mSettings.getPtr(mConfig);
|
||||
DPRINTLN(DBG_INFO, F("Settings valid: ") + String((mSettings.getValid()) ? F("true") : F("false")));
|
||||
|
||||
addListener(EVERY_SEC, std::bind(&app::tickSecond, this));
|
||||
addListener(EVERY_MIN, std::bind(&app::tickMinute, this));
|
||||
addListener(EVERY_12H, std::bind(&app::tickNtpUpdate, this));
|
||||
once(mConfig->nrf.sendInterval, std::bind(&app::tickSend, this), "tickSend");
|
||||
if((mConfig->sun.lat) && (mConfig->sun.lon)) {
|
||||
once(5, std::bind(&app::tickCalcSunrise, this));
|
||||
mCalculatedTimezoneOffset = (int8_t)((mConfig->sun.lon >= 0 ? mConfig->sun.lon + 7.5 : mConfig->sun.lon - 7.5) / 15) * 3600;
|
||||
}
|
||||
|
||||
mSys = new HmSystemType();
|
||||
mSys->enableDebug();
|
||||
mSys->setup(mConfig->nrf.amplifierPower, mConfig->nrf.pinIrq, mConfig->nrf.pinCe, mConfig->nrf.pinCs);
|
||||
mSys->addInverters(&mConfig->inst);
|
||||
|
||||
#if !defined(AP_ONLY)
|
||||
mMqtt.setup(&mConfig->mqtt, mConfig->sys.deviceName, mVersion, mSys, &mUtcTimestamp, &mSunrise, &mSunset);
|
||||
mMqtt.setup(&mConfig->mqtt, mConfig->sys.deviceName, mVersion, mSys, &mTimestamp, &mSunrise, &mSunset);
|
||||
#endif
|
||||
|
||||
mWifi.setup(mConfig, &mUtcTimestamp);
|
||||
mWifi.setup(mConfig, &mTimestamp);
|
||||
|
||||
mPayload.setup(mSys);
|
||||
mPayload.enableSerialDebug(mConfig->serial.debug);
|
||||
|
@ -51,8 +56,8 @@ void app::setup(uint32_t timeout) {
|
|||
mPayload.addListener(std::bind(&PubMqttType::payloadEventListener, &mMqtt, std::placeholders::_1));
|
||||
addListener(EVERY_SEC, std::bind(&PubMqttType::tickerSecond, &mMqtt));
|
||||
addListener(EVERY_MIN, std::bind(&PubMqttType::tickerMinute, &mMqtt));
|
||||
addListener(EVERY_HR, std::bind(&PubMqttType::tickerHour, &mMqtt));
|
||||
mMqtt.setSubscriptionCb(std::bind(&app::mqttSubRxCb, this, std::placeholders::_1));
|
||||
|
||||
}
|
||||
#endif
|
||||
setupLed();
|
||||
|
@ -64,7 +69,7 @@ void app::setup(uint32_t timeout) {
|
|||
|
||||
// Plugins
|
||||
#if defined(ENA_NOKIA) || defined(ENA_SSD1306)
|
||||
mMonoDisplay.setup(mSys, &mUtcTimestamp);
|
||||
mMonoDisplay.setup(mSys, &mTimestamp);
|
||||
mPayload.addListener(std::bind(&MonoDisplayType::payloadEventListener, &mMonoDisplay, std::placeholders::_1));
|
||||
addListener(EVERY_SEC, std::bind(&MonoDisplayType::tickerSecond, &mMonoDisplay));
|
||||
#endif
|
||||
|
@ -119,22 +124,29 @@ void app::loop(void) {
|
|||
}
|
||||
|
||||
mMqtt.loop();
|
||||
}
|
||||
|
||||
if (ah::checkTicker(&mTicker, 1000)) {
|
||||
if (mUtcTimestamp > 946684800 && mConfig->sun.lat && mConfig->sun.lon && (mUtcTimestamp + mCalculatedTimezoneOffset) / 86400 != (mLatestSunTimestamp + mCalculatedTimezoneOffset) / 86400) { // update on reboot or midnight
|
||||
if (!mLatestSunTimestamp) { // first call: calculate time zone from longitude to refresh at local midnight
|
||||
mCalculatedTimezoneOffset = (int8_t)((mConfig->sun.lon >= 0 ? mConfig->sun.lon + 7.5 : mConfig->sun.lon - 7.5) / 15) * 3600;
|
||||
//-----------------------------------------------------------------------------
|
||||
void app::tickCalcSunrise(void) {
|
||||
if (0 == mTimestamp) {
|
||||
once(5, std::bind(&app::tickCalcSunrise, this)); // check again in 5 secs
|
||||
return;
|
||||
}
|
||||
ah::calculateSunriseSunset(mUtcTimestamp, mCalculatedTimezoneOffset, mConfig->sun.lat, mConfig->sun.lon, &mSunrise, &mSunset);
|
||||
mLatestSunTimestamp = mUtcTimestamp;
|
||||
ah::calculateSunriseSunset(mTimestamp, mCalculatedTimezoneOffset, mConfig->sun.lat, mConfig->sun.lon, &mSunrise, &mSunset);
|
||||
|
||||
uint32_t nxtTrig = mTimestamp - (mTimestamp % 86400) + 86400; // next midnight
|
||||
onceAt(nxtTrig, std::bind(&app::tickCalcSunrise, this), "calc sunrise");
|
||||
onceAt(mSunrise, std::bind(&app::tickSend, this), "tickSend"); // register next event
|
||||
if (mConfig->mqtt.broker[0] > 0) {
|
||||
once(1, std::bind(&PubMqttType::tickerSun, &mMqtt), "MQTT-tickerSun");
|
||||
onceAt(mSunset, std::bind(&PubMqttType::tickSunset, &mMqtt));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (++mSendTicker >= mConfig->nrf.sendInterval) {
|
||||
mSendTicker = 0;
|
||||
|
||||
if (mUtcTimestamp > 946684800 && (!mConfig->sun.disNightCom || !mLatestSunTimestamp || (mUtcTimestamp >= mSunrise && mUtcTimestamp <= mSunset))) { // Timestamp is set and (inverter communication only during the day if the option is activated and sunrise/sunset is set)
|
||||
//-----------------------------------------------------------------------------
|
||||
void app::tickSend(void) {
|
||||
if ((mTimestamp > 0) && (!mConfig->sun.disNightCom || (mTimestamp >= mSunrise && mTimestamp <= mSunset))) { // Timestamp is set and (inverter communication only during the day if the option is activated and sunrise/sunset is set)
|
||||
once(mConfig->nrf.sendInterval, std::bind(&app::tickSend, this), "tickSend"); // register next event
|
||||
if (mConfig->serial.debug)
|
||||
DPRINTLN(DBG_DEBUG, F("Free heap: 0x") + String(ESP.getFreeHeap(), HEX));
|
||||
|
||||
|
@ -169,7 +181,7 @@ void app::loop(void) {
|
|||
}
|
||||
}
|
||||
|
||||
mPayload.reset(iv, mUtcTimestamp);
|
||||
mPayload.reset(iv, mTimestamp);
|
||||
mPayload.request(iv);
|
||||
|
||||
yield();
|
||||
|
@ -193,13 +205,14 @@ void app::loop(void) {
|
|||
mRxTicker = 0;
|
||||
}
|
||||
}
|
||||
} else if (mConfig->serial.debug)
|
||||
} else {
|
||||
once(3600, std::bind(&app::tickSend, this), "tickSend"); // register next event (one hour)
|
||||
if (mConfig->serial.debug)
|
||||
DPRINTLN(DBG_WARN, F("Time not set or it is night time, therefore no communication to the inverter!"));
|
||||
}
|
||||
yield();
|
||||
|
||||
updateLed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -224,22 +237,18 @@ void app::resetSystem(void) {
|
|||
snprintf(mVersion, 12, "%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
|
||||
|
||||
mShouldReboot = false;
|
||||
mUptimeSecs = 0;
|
||||
mUpdateNtp = false;
|
||||
mFlagSendDiscoveryConfig = false;
|
||||
|
||||
#ifdef AP_ONLY
|
||||
mUtcTimestamp = 1;
|
||||
mTimestamp = 1;
|
||||
#else
|
||||
mUtcTimestamp = 0;
|
||||
mTimestamp = 0;
|
||||
#endif
|
||||
|
||||
mSunrise = 0;
|
||||
mSunset = 0;
|
||||
|
||||
mSendTicker = 0xffff;
|
||||
|
||||
mTicker = 0;
|
||||
mRxTicker = 0;
|
||||
|
||||
mSendLastIvId = 0;
|
||||
|
@ -278,7 +287,7 @@ void app::updateLed(void) {
|
|||
Inverter<> *iv = mSys->getInverterByPos(0);
|
||||
if (NULL != iv) {
|
||||
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
|
||||
if(iv->isProducing(mUtcTimestamp, rec))
|
||||
if(iv->isProducing(mTimestamp, rec))
|
||||
digitalWrite(mConfig->led.led0, LOW); // LED on
|
||||
else
|
||||
digitalWrite(mConfig->led.led0, HIGH); // LED off
|
||||
|
|
38
src/app.h
38
src/app.h
|
@ -92,27 +92,19 @@ class app : public ah::Scheduler {
|
|||
|
||||
String getTimeStr(uint32_t offset = 0) {
|
||||
char str[10];
|
||||
if(0 == mUtcTimestamp)
|
||||
if(0 == mTimestamp)
|
||||
sprintf(str, "n/a");
|
||||
else
|
||||
sprintf(str, "%02d:%02d:%02d ", hour(mUtcTimestamp + offset), minute(mUtcTimestamp + offset), second(mUtcTimestamp + offset));
|
||||
sprintf(str, "%02d:%02d:%02d ", hour(mTimestamp + offset), minute(mTimestamp + offset), second(mTimestamp + offset));
|
||||
return String(str);
|
||||
}
|
||||
|
||||
inline uint32_t getUptime(void) {
|
||||
return mUptimeSecs;
|
||||
}
|
||||
|
||||
inline uint32_t getTimestamp(void) {
|
||||
return mUtcTimestamp;
|
||||
}
|
||||
|
||||
void setTimestamp(uint32_t newTime) {
|
||||
DPRINTLN(DBG_DEBUG, F("setTimestamp: ") + String(newTime));
|
||||
if(0 == newTime)
|
||||
mUpdateNtp = true;
|
||||
else
|
||||
mUtcTimestamp = newTime;
|
||||
Scheduler::setTimestamp(newTime);
|
||||
}
|
||||
|
||||
inline uint32_t getSunrise(void) {
|
||||
|
@ -121,9 +113,6 @@ class app : public ah::Scheduler {
|
|||
inline uint32_t getSunset(void) {
|
||||
return mSunset;
|
||||
}
|
||||
inline uint32_t getLatestSunTimestamp(void) {
|
||||
return mLatestSunTimestamp;
|
||||
}
|
||||
|
||||
inline bool mqttIsConnected(void) { return mMqtt.isConnected(); }
|
||||
inline bool getSettingsValid(void) { return mSettings.getValid(); }
|
||||
|
@ -142,11 +131,7 @@ class app : public ah::Scheduler {
|
|||
void setupLed(void);
|
||||
void updateLed(void);
|
||||
|
||||
void uptimeTick(void) {
|
||||
mUptimeSecs++;
|
||||
if (0 != mUtcTimestamp)
|
||||
mUtcTimestamp++;
|
||||
|
||||
void tickSecond(void) {
|
||||
if (mShouldReboot) {
|
||||
DPRINTLN(DBG_INFO, F("Rebooting..."));
|
||||
ESP.restart();
|
||||
|
@ -158,16 +143,19 @@ class app : public ah::Scheduler {
|
|||
}
|
||||
}
|
||||
|
||||
void minuteTick(void) {
|
||||
if(0 == mUtcTimestamp) {
|
||||
void tickMinute(void) {
|
||||
if(0 == mTimestamp) {
|
||||
mUpdateNtp = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ntpUpdateTick(void) {
|
||||
void tickNtpUpdate(void) {
|
||||
mUpdateNtp = true;
|
||||
}
|
||||
|
||||
void tickCalcSunrise(void);
|
||||
void tickSend(void);
|
||||
|
||||
void stats(void) {
|
||||
DPRINTLN(DBG_VERBOSE, F("main.h:stats"));
|
||||
#ifdef ESP8266
|
||||
|
@ -188,9 +176,6 @@ class app : public ah::Scheduler {
|
|||
DPRINTLN(DBG_VERBOSE, F(" - frag: ") + String(frag));
|
||||
}
|
||||
|
||||
uint32_t mUptimeSecs;
|
||||
|
||||
uint32_t mUtcTimestamp;
|
||||
bool mUpdateNtp;
|
||||
|
||||
bool mShowRebootRequest;
|
||||
|
@ -204,13 +189,11 @@ class app : public ah::Scheduler {
|
|||
settings mSettings;
|
||||
settings_t *mConfig;
|
||||
|
||||
uint16_t mSendTicker;
|
||||
uint8_t mSendLastIvId;
|
||||
|
||||
statistics_t mStat;
|
||||
|
||||
// timer
|
||||
uint32_t mTicker;
|
||||
uint32_t mRxTicker;
|
||||
|
||||
// mqtt
|
||||
|
@ -220,7 +203,6 @@ class app : public ah::Scheduler {
|
|||
// sun
|
||||
int32_t mCalculatedTimezoneOffset;
|
||||
uint32_t mSunrise, mSunset;
|
||||
uint32_t mLatestSunTimestamp;
|
||||
|
||||
// plugins
|
||||
#if defined(ENA_NOKIA) || defined(ENA_SSD1306)
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
//-------------------------------------
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MINOR 5
|
||||
#define VERSION_PATCH 47
|
||||
#define VERSION_PATCH 48
|
||||
|
||||
//-------------------------------------
|
||||
typedef struct {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef __MONOCHROME_DISPLAY__
|
||||
#define __MONOCHROME_DISPLAY__
|
||||
|
||||
#if defined(ENA_NOKIA) || defined(ENA_SSD1306)
|
||||
#ifdef ENA_NOKIA
|
||||
#include <U8g2lib.h>
|
||||
#define DISP_PROGMEM U8X8_PROGMEM
|
||||
|
@ -21,7 +22,6 @@ static uint8_t bmp_arrow[] DISP_PROGMEM = {
|
|||
B01110000, B01110000, B00110000, B00111000, B00011000, B01111111, B00111111,
|
||||
B00011110, B00001110, B00000110, B00000000, B00000000, B00000000, B00000000} ;
|
||||
|
||||
#if defined(ENA_NOKIA) || defined(ENA_SSD1306)
|
||||
template<class HMSYSTEM>
|
||||
class MonochromeDisplay {
|
||||
public:
|
||||
|
|
|
@ -89,11 +89,33 @@ class PubMqtt {
|
|||
}
|
||||
}
|
||||
|
||||
void tickerHour() {
|
||||
void tickerSun() {
|
||||
publish("sunrise", String(*mSunrise).c_str(), true);
|
||||
publish("sunset", String(*mSunset).c_str(), true);
|
||||
}
|
||||
|
||||
void tickSunset() {
|
||||
char topic[MAX_NAME_LENGTH + 15], val[32];
|
||||
for (uint8_t id = 0; id < mSys->getNumInverters(); id++) {
|
||||
Inverter<> *iv = mSys->getInverterByPos(id);
|
||||
if (NULL == iv)
|
||||
continue; // skip to next inverter
|
||||
|
||||
snprintf(topic, MAX_NAME_LENGTH + 15, "%s/available_text", iv->config->name);
|
||||
snprintf(val, 32, "not available and not producing");
|
||||
publish(topic, val, true);
|
||||
|
||||
snprintf(topic, MAX_NAME_LENGTH + 15, "%s/available", iv->config->name);
|
||||
snprintf(val, 32, "%d", MQTT_STATUS_NOT_AVAIL_NOT_PROD);
|
||||
publish(topic, val, true);
|
||||
}
|
||||
}
|
||||
|
||||
void payloadEventListener(uint8_t cmd) {
|
||||
if(mClient.connected()) // prevent overflow if MQTT broker is not reachable but set
|
||||
mSendList.push(cmd);
|
||||
}
|
||||
|
||||
void publish(const char *subTopic, const char *payload, bool retained = false, bool addTopic = true) {
|
||||
char topic[MQTT_TOPIC_LEN + 2];
|
||||
snprintf(topic, (MQTT_TOPIC_LEN + 2), "%s/%s", mCfgMqtt->topic, subTopic);
|
||||
|
@ -110,11 +132,6 @@ class PubMqtt {
|
|||
mClient.subscribe(topic, QOS_0);
|
||||
}
|
||||
|
||||
void payloadEventListener(uint8_t cmd) {
|
||||
if(mClient.connected()) // prevent overflow if MQTT broker is not reachable but set
|
||||
mSendList.push(cmd);
|
||||
}
|
||||
|
||||
void setSubscriptionCb(subscriptionCb cb) {
|
||||
mSubscriptionCb = cb;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,13 @@
|
|||
enum {EVERY_SEC = 1, EVERY_MIN, EVERY_HR, EVERY_12H, EVERY_DAY};
|
||||
typedef std::function<void()> SchedulerCb;
|
||||
|
||||
struct once_t {
|
||||
uint32_t n;
|
||||
SchedulerCb f;
|
||||
once_t(uint32_t a, SchedulerCb b) : n(a), f(b) {}
|
||||
once_t() : n(0), f(NULL) {}
|
||||
};
|
||||
|
||||
namespace ah {
|
||||
class Scheduler {
|
||||
public:
|
||||
|
@ -24,15 +31,22 @@ class Scheduler {
|
|||
mSeconds = 0;
|
||||
mMinutes = 0;
|
||||
mHours = 0;
|
||||
mUptime = 0;
|
||||
mTimestamp = 0;
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (millis() - mPrevMillis >= 1000) {
|
||||
mPrevMillis += 1000;
|
||||
mUptime++;
|
||||
if(0 != mTimestamp)
|
||||
mTimestamp++;
|
||||
notify(&mListSecond);
|
||||
onceFuncTick();
|
||||
if(++mSeconds >= 60) {
|
||||
mSeconds = 0;
|
||||
notify(&mListMinute);
|
||||
onceAtFuncTick();
|
||||
if(++mMinutes >= 60) {
|
||||
mMinutes = 0;
|
||||
notify(&mListHour);
|
||||
|
@ -48,6 +62,30 @@ class Scheduler {
|
|||
}
|
||||
}
|
||||
|
||||
// checked every second
|
||||
void once(uint32_t sec, SchedulerCb cb, const char *info = NULL) {
|
||||
if(NULL != info) {
|
||||
DPRINT(DBG_INFO, F("once in [s]: ") + String(sec));
|
||||
DBGPRINTLN(F(", ") + String(info));
|
||||
}
|
||||
mOnce.push_back(once_t(sec, cb));
|
||||
}
|
||||
|
||||
// checked every minute
|
||||
void onceAt(uint32_t timestamp, SchedulerCb cb, const char *info = NULL) {
|
||||
if(timestamp > mTimestamp) {
|
||||
if(NULL != info) {
|
||||
DPRINT(DBG_INFO, F("onceAt (UTC): ") + getDateTimeStr(timestamp));
|
||||
DBGPRINTLN(F(", ") + String(info));
|
||||
}
|
||||
mOnceAt.push_back(once_t(timestamp, cb));
|
||||
}
|
||||
}
|
||||
|
||||
virtual void setTimestamp(uint32_t ts) {
|
||||
mTimestamp = ts;
|
||||
}
|
||||
|
||||
void addListener(uint8_t every, SchedulerCb cb) {
|
||||
switch(every) {
|
||||
case EVERY_SEC: mListSecond.push_back(cb); break;
|
||||
|
@ -59,21 +97,60 @@ class Scheduler {
|
|||
}
|
||||
}
|
||||
|
||||
uint32_t getUptime(void) {
|
||||
return mUptime;
|
||||
}
|
||||
|
||||
uint32_t getTimestamp(void) {
|
||||
return mTimestamp;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void notify(std::list<SchedulerCb> *lType) {
|
||||
for(std::list<SchedulerCb>::iterator it = lType->begin(); it != lType->end(); ++it) {
|
||||
(*it)();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32_t mTimestamp;
|
||||
|
||||
private:
|
||||
void onceFuncTick(void) {
|
||||
if(mOnce.empty())
|
||||
return;
|
||||
for(std::list<once_t>::iterator it = mOnce.begin(); it != mOnce.end();) {
|
||||
if(((*it).n)-- == 0) {
|
||||
((*it).f)();
|
||||
it = mOnce.erase(it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void onceAtFuncTick(void) {
|
||||
if(mOnceAt.empty())
|
||||
return;
|
||||
for(std::list<once_t>::iterator it = mOnceAt.begin(); it != mOnceAt.end();) {
|
||||
if(((*it).n) < mTimestamp) {
|
||||
((*it).f)();
|
||||
it = mOnceAt.erase(it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
std::list<SchedulerCb> mListSecond;
|
||||
std::list<SchedulerCb> mListMinute;
|
||||
std::list<SchedulerCb> mListHour;
|
||||
std::list<SchedulerCb> mList12h;
|
||||
std::list<SchedulerCb> mListDay;
|
||||
|
||||
private:
|
||||
uint32_t mPrevMillis;
|
||||
std::list<once_t> mOnce;
|
||||
std::list<once_t> mOnceAt;
|
||||
|
||||
uint32_t mPrevMillis, mUptime;
|
||||
uint8_t mSeconds, mMinutes, mHours;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -134,7 +134,7 @@
|
|||
e.addEventListener("click", setTime);
|
||||
}
|
||||
|
||||
if(!obj["ts_sun_upd"]) {
|
||||
if(0 == obj["ts_sunrise"]) {
|
||||
var e = document.getElementById("sun");
|
||||
if(null != e)
|
||||
e.parentNode.removeChild(e);
|
||||
|
|
|
@ -163,7 +163,6 @@ void webApi::getSysInfo(JsonObject obj) {
|
|||
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();
|
||||
obj[F("mac")] = WiFi.macAddress();
|
||||
obj[F("hostname")] = WiFi.getHostname();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue