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
197
src/app.cpp
197
src/app.cpp
|
@ -24,25 +24,30 @@ void app::setup(uint32_t timeout) {
|
||||||
while (!Serial)
|
while (!Serial)
|
||||||
yield();
|
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();
|
resetSystem();
|
||||||
mSettings.setup();
|
mSettings.setup();
|
||||||
mSettings.getPtr(mConfig);
|
mSettings.getPtr(mConfig);
|
||||||
DPRINTLN(DBG_INFO, F("Settings valid: ") + String((mSettings.getValid()) ? F("true") : F("false")));
|
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 = new HmSystemType();
|
||||||
mSys->enableDebug();
|
mSys->enableDebug();
|
||||||
mSys->setup(mConfig->nrf.amplifierPower, mConfig->nrf.pinIrq, mConfig->nrf.pinCe, mConfig->nrf.pinCs);
|
mSys->setup(mConfig->nrf.amplifierPower, mConfig->nrf.pinIrq, mConfig->nrf.pinCe, mConfig->nrf.pinCs);
|
||||||
mSys->addInverters(&mConfig->inst);
|
mSys->addInverters(&mConfig->inst);
|
||||||
|
|
||||||
#if !defined(AP_ONLY)
|
#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
|
#endif
|
||||||
|
|
||||||
mWifi.setup(mConfig, &mUtcTimestamp);
|
mWifi.setup(mConfig, &mTimestamp);
|
||||||
|
|
||||||
mPayload.setup(mSys);
|
mPayload.setup(mSys);
|
||||||
mPayload.enableSerialDebug(mConfig->serial.debug);
|
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));
|
mPayload.addListener(std::bind(&PubMqttType::payloadEventListener, &mMqtt, std::placeholders::_1));
|
||||||
addListener(EVERY_SEC, std::bind(&PubMqttType::tickerSecond, &mMqtt));
|
addListener(EVERY_SEC, std::bind(&PubMqttType::tickerSecond, &mMqtt));
|
||||||
addListener(EVERY_MIN, std::bind(&PubMqttType::tickerMinute, &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));
|
mMqtt.setSubscriptionCb(std::bind(&app::mqttSubRxCb, this, std::placeholders::_1));
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
setupLed();
|
setupLed();
|
||||||
|
@ -64,7 +69,7 @@ void app::setup(uint32_t timeout) {
|
||||||
|
|
||||||
// Plugins
|
// Plugins
|
||||||
#if defined(ENA_NOKIA) || defined(ENA_SSD1306)
|
#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));
|
mPayload.addListener(std::bind(&MonoDisplayType::payloadEventListener, &mMonoDisplay, std::placeholders::_1));
|
||||||
addListener(EVERY_SEC, std::bind(&MonoDisplayType::tickerSecond, &mMonoDisplay));
|
addListener(EVERY_SEC, std::bind(&MonoDisplayType::tickerSecond, &mMonoDisplay));
|
||||||
#endif
|
#endif
|
||||||
|
@ -119,87 +124,95 @@ void app::loop(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
mMqtt.loop();
|
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
|
void app::tickCalcSunrise(void) {
|
||||||
if (!mLatestSunTimestamp) { // first call: calculate time zone from longitude to refresh at local midnight
|
if (0 == mTimestamp) {
|
||||||
mCalculatedTimezoneOffset = (int8_t)((mConfig->sun.lon >= 0 ? mConfig->sun.lon + 7.5 : mConfig->sun.lon - 7.5) / 15) * 3600;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
if (mConfig->serial.debug)
|
|
||||||
DPRINTLN(DBG_DEBUG, F("Free heap: 0x") + String(ESP.getFreeHeap(), HEX));
|
|
||||||
|
|
||||||
if (!mSys->BufCtrl.empty()) {
|
|
||||||
if (mConfig->serial.debug)
|
|
||||||
DPRINTLN(DBG_DEBUG, F("recbuf not empty! #") + String(mSys->BufCtrl.getFill()));
|
|
||||||
}
|
|
||||||
|
|
||||||
int8_t maxLoop = MAX_NUM_INVERTERS;
|
|
||||||
Inverter<> *iv = mSys->getInverterByPos(mSendLastIvId);
|
|
||||||
do {
|
|
||||||
mSendLastIvId = ((MAX_NUM_INVERTERS - 1) == mSendLastIvId) ? 0 : mSendLastIvId + 1;
|
|
||||||
iv = mSys->getInverterByPos(mSendLastIvId);
|
|
||||||
} while ((NULL == iv) && ((maxLoop--) > 0));
|
|
||||||
|
|
||||||
if (NULL != iv) {
|
|
||||||
if (!mPayload.isComplete(iv))
|
|
||||||
mPayload.process(false, mConfig->nrf.maxRetransPerPyld, &mStat);
|
|
||||||
|
|
||||||
if (!mPayload.isComplete(iv)) {
|
|
||||||
if (0 == mPayload.getMaxPacketId(iv))
|
|
||||||
mStat.rxFailNoAnser++;
|
|
||||||
else
|
|
||||||
mStat.rxFail++;
|
|
||||||
|
|
||||||
iv->setQueuedCmdFinished(); // command failed
|
|
||||||
if (mConfig->serial.debug)
|
|
||||||
DPRINTLN(DBG_INFO, F("enqueued cmd failed/timeout"));
|
|
||||||
if (mConfig->serial.debug) {
|
|
||||||
DPRINT(DBG_INFO, F("(#") + String(iv->id) + ") ");
|
|
||||||
DPRINTLN(DBG_INFO, F("no Payload received! (retransmits: ") + String(mPayload.getRetransmits(iv)) + ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mPayload.reset(iv, mUtcTimestamp);
|
|
||||||
mPayload.request(iv);
|
|
||||||
|
|
||||||
yield();
|
|
||||||
if (mConfig->serial.debug) {
|
|
||||||
DPRINTLN(DBG_DEBUG, F("app:loop WiFi WiFi.status ") + String(WiFi.status()));
|
|
||||||
DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") Requesting Inv SN ") + String(iv->config->serial.u64, HEX));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iv->devControlRequest) {
|
|
||||||
if (mConfig->serial.debug)
|
|
||||||
DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") Devcontrol request ") + String(iv->devControlCmd) + F(" power limit ") + String(iv->powerLimit[0]));
|
|
||||||
mSys->Radio.sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit);
|
|
||||||
mPayload.setTxCmd(iv, iv->devControlCmd);
|
|
||||||
iv->clearCmdQueue();
|
|
||||||
iv->enqueCommand<InfoCommand>(SystemConfigPara);
|
|
||||||
} else {
|
|
||||||
uint8_t cmd = iv->getQueuedCmd();
|
|
||||||
DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") sendTimePacket"));
|
|
||||||
mSys->Radio.sendTimePacket(iv->radioId.u64, cmd, mPayload.getTs(iv), iv->alarmMesIndex);
|
|
||||||
mPayload.setTxCmd(iv, cmd);
|
|
||||||
mRxTicker = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (mConfig->serial.debug)
|
|
||||||
DPRINTLN(DBG_WARN, F("Time not set or it is night time, therefore no communication to the inverter!"));
|
|
||||||
yield();
|
|
||||||
|
|
||||||
updateLed();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
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));
|
||||||
|
|
||||||
|
if (!mSys->BufCtrl.empty()) {
|
||||||
|
if (mConfig->serial.debug)
|
||||||
|
DPRINTLN(DBG_DEBUG, F("recbuf not empty! #") + String(mSys->BufCtrl.getFill()));
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t maxLoop = MAX_NUM_INVERTERS;
|
||||||
|
Inverter<> *iv = mSys->getInverterByPos(mSendLastIvId);
|
||||||
|
do {
|
||||||
|
mSendLastIvId = ((MAX_NUM_INVERTERS - 1) == mSendLastIvId) ? 0 : mSendLastIvId + 1;
|
||||||
|
iv = mSys->getInverterByPos(mSendLastIvId);
|
||||||
|
} while ((NULL == iv) && ((maxLoop--) > 0));
|
||||||
|
|
||||||
|
if (NULL != iv) {
|
||||||
|
if (!mPayload.isComplete(iv))
|
||||||
|
mPayload.process(false, mConfig->nrf.maxRetransPerPyld, &mStat);
|
||||||
|
|
||||||
|
if (!mPayload.isComplete(iv)) {
|
||||||
|
if (0 == mPayload.getMaxPacketId(iv))
|
||||||
|
mStat.rxFailNoAnser++;
|
||||||
|
else
|
||||||
|
mStat.rxFail++;
|
||||||
|
|
||||||
|
iv->setQueuedCmdFinished(); // command failed
|
||||||
|
if (mConfig->serial.debug)
|
||||||
|
DPRINTLN(DBG_INFO, F("enqueued cmd failed/timeout"));
|
||||||
|
if (mConfig->serial.debug) {
|
||||||
|
DPRINT(DBG_INFO, F("(#") + String(iv->id) + ") ");
|
||||||
|
DPRINTLN(DBG_INFO, F("no Payload received! (retransmits: ") + String(mPayload.getRetransmits(iv)) + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mPayload.reset(iv, mTimestamp);
|
||||||
|
mPayload.request(iv);
|
||||||
|
|
||||||
|
yield();
|
||||||
|
if (mConfig->serial.debug) {
|
||||||
|
DPRINTLN(DBG_DEBUG, F("app:loop WiFi WiFi.status ") + String(WiFi.status()));
|
||||||
|
DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") Requesting Inv SN ") + String(iv->config->serial.u64, HEX));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iv->devControlRequest) {
|
||||||
|
if (mConfig->serial.debug)
|
||||||
|
DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") Devcontrol request ") + String(iv->devControlCmd) + F(" power limit ") + String(iv->powerLimit[0]));
|
||||||
|
mSys->Radio.sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit);
|
||||||
|
mPayload.setTxCmd(iv, iv->devControlCmd);
|
||||||
|
iv->clearCmdQueue();
|
||||||
|
iv->enqueCommand<InfoCommand>(SystemConfigPara);
|
||||||
|
} else {
|
||||||
|
uint8_t cmd = iv->getQueuedCmd();
|
||||||
|
DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") sendTimePacket"));
|
||||||
|
mSys->Radio.sendTimePacket(iv->radioId.u64, cmd, mPayload.getTs(iv), iv->alarmMesIndex);
|
||||||
|
mPayload.setTxCmd(iv, cmd);
|
||||||
|
mRxTicker = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} 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);
|
snprintf(mVersion, 12, "%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
|
||||||
|
|
||||||
mShouldReboot = false;
|
mShouldReboot = false;
|
||||||
mUptimeSecs = 0;
|
|
||||||
mUpdateNtp = false;
|
mUpdateNtp = false;
|
||||||
mFlagSendDiscoveryConfig = false;
|
mFlagSendDiscoveryConfig = false;
|
||||||
|
|
||||||
#ifdef AP_ONLY
|
#ifdef AP_ONLY
|
||||||
mUtcTimestamp = 1;
|
mTimestamp = 1;
|
||||||
#else
|
#else
|
||||||
mUtcTimestamp = 0;
|
mTimestamp = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mSunrise = 0;
|
mSunrise = 0;
|
||||||
mSunset = 0;
|
mSunset = 0;
|
||||||
|
|
||||||
mSendTicker = 0xffff;
|
|
||||||
|
|
||||||
mTicker = 0;
|
|
||||||
mRxTicker = 0;
|
mRxTicker = 0;
|
||||||
|
|
||||||
mSendLastIvId = 0;
|
mSendLastIvId = 0;
|
||||||
|
@ -278,7 +287,7 @@ void app::updateLed(void) {
|
||||||
Inverter<> *iv = mSys->getInverterByPos(0);
|
Inverter<> *iv = mSys->getInverterByPos(0);
|
||||||
if (NULL != iv) {
|
if (NULL != iv) {
|
||||||
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
|
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
|
||||||
if(iv->isProducing(mUtcTimestamp, rec))
|
if(iv->isProducing(mTimestamp, rec))
|
||||||
digitalWrite(mConfig->led.led0, LOW); // LED on
|
digitalWrite(mConfig->led.led0, LOW); // LED on
|
||||||
else
|
else
|
||||||
digitalWrite(mConfig->led.led0, HIGH); // LED off
|
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) {
|
String getTimeStr(uint32_t offset = 0) {
|
||||||
char str[10];
|
char str[10];
|
||||||
if(0 == mUtcTimestamp)
|
if(0 == mTimestamp)
|
||||||
sprintf(str, "n/a");
|
sprintf(str, "n/a");
|
||||||
else
|
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);
|
return String(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint32_t getUptime(void) {
|
|
||||||
return mUptimeSecs;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline uint32_t getTimestamp(void) {
|
|
||||||
return mUtcTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setTimestamp(uint32_t newTime) {
|
void setTimestamp(uint32_t newTime) {
|
||||||
DPRINTLN(DBG_DEBUG, F("setTimestamp: ") + String(newTime));
|
DPRINTLN(DBG_DEBUG, F("setTimestamp: ") + String(newTime));
|
||||||
if(0 == newTime)
|
if(0 == newTime)
|
||||||
mUpdateNtp = true;
|
mUpdateNtp = true;
|
||||||
else
|
else
|
||||||
mUtcTimestamp = newTime;
|
Scheduler::setTimestamp(newTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint32_t getSunrise(void) {
|
inline uint32_t getSunrise(void) {
|
||||||
|
@ -121,9 +113,6 @@ class app : public ah::Scheduler {
|
||||||
inline uint32_t getSunset(void) {
|
inline uint32_t getSunset(void) {
|
||||||
return mSunset;
|
return mSunset;
|
||||||
}
|
}
|
||||||
inline uint32_t getLatestSunTimestamp(void) {
|
|
||||||
return mLatestSunTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool mqttIsConnected(void) { return mMqtt.isConnected(); }
|
inline bool mqttIsConnected(void) { return mMqtt.isConnected(); }
|
||||||
inline bool getSettingsValid(void) { return mSettings.getValid(); }
|
inline bool getSettingsValid(void) { return mSettings.getValid(); }
|
||||||
|
@ -142,11 +131,7 @@ class app : public ah::Scheduler {
|
||||||
void setupLed(void);
|
void setupLed(void);
|
||||||
void updateLed(void);
|
void updateLed(void);
|
||||||
|
|
||||||
void uptimeTick(void) {
|
void tickSecond(void) {
|
||||||
mUptimeSecs++;
|
|
||||||
if (0 != mUtcTimestamp)
|
|
||||||
mUtcTimestamp++;
|
|
||||||
|
|
||||||
if (mShouldReboot) {
|
if (mShouldReboot) {
|
||||||
DPRINTLN(DBG_INFO, F("Rebooting..."));
|
DPRINTLN(DBG_INFO, F("Rebooting..."));
|
||||||
ESP.restart();
|
ESP.restart();
|
||||||
|
@ -158,16 +143,19 @@ class app : public ah::Scheduler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void minuteTick(void) {
|
void tickMinute(void) {
|
||||||
if(0 == mUtcTimestamp) {
|
if(0 == mTimestamp) {
|
||||||
mUpdateNtp = true;
|
mUpdateNtp = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ntpUpdateTick(void) {
|
void tickNtpUpdate(void) {
|
||||||
mUpdateNtp = true;
|
mUpdateNtp = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tickCalcSunrise(void);
|
||||||
|
void tickSend(void);
|
||||||
|
|
||||||
void stats(void) {
|
void stats(void) {
|
||||||
DPRINTLN(DBG_VERBOSE, F("main.h:stats"));
|
DPRINTLN(DBG_VERBOSE, F("main.h:stats"));
|
||||||
#ifdef ESP8266
|
#ifdef ESP8266
|
||||||
|
@ -188,9 +176,6 @@ class app : public ah::Scheduler {
|
||||||
DPRINTLN(DBG_VERBOSE, F(" - frag: ") + String(frag));
|
DPRINTLN(DBG_VERBOSE, F(" - frag: ") + String(frag));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t mUptimeSecs;
|
|
||||||
|
|
||||||
uint32_t mUtcTimestamp;
|
|
||||||
bool mUpdateNtp;
|
bool mUpdateNtp;
|
||||||
|
|
||||||
bool mShowRebootRequest;
|
bool mShowRebootRequest;
|
||||||
|
@ -204,13 +189,11 @@ class app : public ah::Scheduler {
|
||||||
settings mSettings;
|
settings mSettings;
|
||||||
settings_t *mConfig;
|
settings_t *mConfig;
|
||||||
|
|
||||||
uint16_t mSendTicker;
|
|
||||||
uint8_t mSendLastIvId;
|
uint8_t mSendLastIvId;
|
||||||
|
|
||||||
statistics_t mStat;
|
statistics_t mStat;
|
||||||
|
|
||||||
// timer
|
// timer
|
||||||
uint32_t mTicker;
|
|
||||||
uint32_t mRxTicker;
|
uint32_t mRxTicker;
|
||||||
|
|
||||||
// mqtt
|
// mqtt
|
||||||
|
@ -220,7 +203,6 @@ class app : public ah::Scheduler {
|
||||||
// sun
|
// sun
|
||||||
int32_t mCalculatedTimezoneOffset;
|
int32_t mCalculatedTimezoneOffset;
|
||||||
uint32_t mSunrise, mSunset;
|
uint32_t mSunrise, mSunset;
|
||||||
uint32_t mLatestSunTimestamp;
|
|
||||||
|
|
||||||
// plugins
|
// plugins
|
||||||
#if defined(ENA_NOKIA) || defined(ENA_SSD1306)
|
#if defined(ENA_NOKIA) || defined(ENA_SSD1306)
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
#define VERSION_MAJOR 0
|
#define VERSION_MAJOR 0
|
||||||
#define VERSION_MINOR 5
|
#define VERSION_MINOR 5
|
||||||
#define VERSION_PATCH 47
|
#define VERSION_PATCH 48
|
||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef __MONOCHROME_DISPLAY__
|
#ifndef __MONOCHROME_DISPLAY__
|
||||||
#define __MONOCHROME_DISPLAY__
|
#define __MONOCHROME_DISPLAY__
|
||||||
|
|
||||||
|
#if defined(ENA_NOKIA) || defined(ENA_SSD1306)
|
||||||
#ifdef ENA_NOKIA
|
#ifdef ENA_NOKIA
|
||||||
#include <U8g2lib.h>
|
#include <U8g2lib.h>
|
||||||
#define DISP_PROGMEM U8X8_PROGMEM
|
#define DISP_PROGMEM U8X8_PROGMEM
|
||||||
|
@ -21,7 +22,6 @@ static uint8_t bmp_arrow[] DISP_PROGMEM = {
|
||||||
B01110000, B01110000, B00110000, B00111000, B00011000, B01111111, B00111111,
|
B01110000, B01110000, B00110000, B00111000, B00011000, B01111111, B00111111,
|
||||||
B00011110, B00001110, B00000110, B00000000, B00000000, B00000000, B00000000} ;
|
B00011110, B00001110, B00000110, B00000000, B00000000, B00000000, B00000000} ;
|
||||||
|
|
||||||
#if defined(ENA_NOKIA) || defined(ENA_SSD1306)
|
|
||||||
template<class HMSYSTEM>
|
template<class HMSYSTEM>
|
||||||
class MonochromeDisplay {
|
class MonochromeDisplay {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -89,11 +89,33 @@ class PubMqtt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void tickerHour() {
|
void tickerSun() {
|
||||||
publish("sunrise", String(*mSunrise).c_str(), true);
|
publish("sunrise", String(*mSunrise).c_str(), true);
|
||||||
publish("sunset", String(*mSunset).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) {
|
void publish(const char *subTopic, const char *payload, bool retained = false, bool addTopic = true) {
|
||||||
char topic[MQTT_TOPIC_LEN + 2];
|
char topic[MQTT_TOPIC_LEN + 2];
|
||||||
snprintf(topic, (MQTT_TOPIC_LEN + 2), "%s/%s", mCfgMqtt->topic, subTopic);
|
snprintf(topic, (MQTT_TOPIC_LEN + 2), "%s/%s", mCfgMqtt->topic, subTopic);
|
||||||
|
@ -110,11 +132,6 @@ class PubMqtt {
|
||||||
mClient.subscribe(topic, QOS_0);
|
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) {
|
void setSubscriptionCb(subscriptionCb cb) {
|
||||||
mSubscriptionCb = cb;
|
mSubscriptionCb = cb;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,13 @@
|
||||||
enum {EVERY_SEC = 1, EVERY_MIN, EVERY_HR, EVERY_12H, EVERY_DAY};
|
enum {EVERY_SEC = 1, EVERY_MIN, EVERY_HR, EVERY_12H, EVERY_DAY};
|
||||||
typedef std::function<void()> SchedulerCb;
|
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 {
|
namespace ah {
|
||||||
class Scheduler {
|
class Scheduler {
|
||||||
public:
|
public:
|
||||||
|
@ -21,18 +28,25 @@ class Scheduler {
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
mPrevMillis = 0;
|
mPrevMillis = 0;
|
||||||
mSeconds = 0;
|
mSeconds = 0;
|
||||||
mMinutes = 0;
|
mMinutes = 0;
|
||||||
mHours = 0;
|
mHours = 0;
|
||||||
|
mUptime = 0;
|
||||||
|
mTimestamp = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
if (millis() - mPrevMillis >= 1000) {
|
if (millis() - mPrevMillis >= 1000) {
|
||||||
mPrevMillis += 1000;
|
mPrevMillis += 1000;
|
||||||
|
mUptime++;
|
||||||
|
if(0 != mTimestamp)
|
||||||
|
mTimestamp++;
|
||||||
notify(&mListSecond);
|
notify(&mListSecond);
|
||||||
|
onceFuncTick();
|
||||||
if(++mSeconds >= 60) {
|
if(++mSeconds >= 60) {
|
||||||
mSeconds = 0;
|
mSeconds = 0;
|
||||||
notify(&mListMinute);
|
notify(&mListMinute);
|
||||||
|
onceAtFuncTick();
|
||||||
if(++mMinutes >= 60) {
|
if(++mMinutes >= 60) {
|
||||||
mMinutes = 0;
|
mMinutes = 0;
|
||||||
notify(&mListHour);
|
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) {
|
void addListener(uint8_t every, SchedulerCb cb) {
|
||||||
switch(every) {
|
switch(every) {
|
||||||
case EVERY_SEC: mListSecond.push_back(cb); break;
|
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) {
|
virtual void notify(std::list<SchedulerCb> *lType) {
|
||||||
for(std::list<SchedulerCb>::iterator it = lType->begin(); it != lType->end(); ++it) {
|
for(std::list<SchedulerCb>::iterator it = lType->begin(); it != lType->end(); ++it) {
|
||||||
(*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> mListSecond;
|
||||||
std::list<SchedulerCb> mListMinute;
|
std::list<SchedulerCb> mListMinute;
|
||||||
std::list<SchedulerCb> mListHour;
|
std::list<SchedulerCb> mListHour;
|
||||||
std::list<SchedulerCb> mList12h;
|
std::list<SchedulerCb> mList12h;
|
||||||
std::list<SchedulerCb> mListDay;
|
std::list<SchedulerCb> mListDay;
|
||||||
|
|
||||||
private:
|
std::list<once_t> mOnce;
|
||||||
uint32_t mPrevMillis;
|
std::list<once_t> mOnceAt;
|
||||||
|
|
||||||
|
uint32_t mPrevMillis, mUptime;
|
||||||
uint8_t mSeconds, mMinutes, mHours;
|
uint8_t mSeconds, mMinutes, mHours;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,7 +134,7 @@
|
||||||
e.addEventListener("click", setTime);
|
e.addEventListener("click", setTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!obj["ts_sun_upd"]) {
|
if(0 == obj["ts_sunrise"]) {
|
||||||
var e = document.getElementById("sun");
|
var e = document.getElementById("sun");
|
||||||
if(null != e)
|
if(null != e)
|
||||||
e.parentNode.removeChild(e);
|
e.parentNode.removeChild(e);
|
||||||
|
|
|
@ -163,7 +163,6 @@ void webApi::getSysInfo(JsonObject obj) {
|
||||||
obj[F("ts_now")] = mApp->getTimestamp();
|
obj[F("ts_now")] = mApp->getTimestamp();
|
||||||
obj[F("ts_sunrise")] = mApp->getSunrise();
|
obj[F("ts_sunrise")] = mApp->getSunrise();
|
||||||
obj[F("ts_sunset")] = mApp->getSunset();
|
obj[F("ts_sunset")] = mApp->getSunset();
|
||||||
obj[F("ts_sun_upd")] = mApp->getLatestSunTimestamp();
|
|
||||||
obj[F("wifi_rssi")] = WiFi.RSSI();
|
obj[F("wifi_rssi")] = WiFi.RSSI();
|
||||||
obj[F("mac")] = WiFi.macAddress();
|
obj[F("mac")] = WiFi.macAddress();
|
||||||
obj[F("hostname")] = WiFi.getHostname();
|
obj[F("hostname")] = WiFi.getHostname();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue