Browse Source

fix #480 AP Mode on fresh ESP

included #483 improvements
fix #468 last_success MQTT
fix #468 update available status at sunset
fix #447 reorderd enqueue commands to not have same payload length in a row
added ssd1306 and nokia display to build script
pull/487/head
lumapu 2 years ago
parent
commit
d9290d9fdf
  1. 2
      .github/workflows/compile_development.yml
  2. 2
      .github/workflows/compile_esp8266.yml
  3. 47
      src/app.cpp
  4. 2
      src/app.h
  5. 8
      src/config/settings.h
  6. 2
      src/defines.h
  7. 9
      src/hm/hmInverter.h
  8. 2
      src/main.cpp
  9. 3
      src/publisher/pubMqtt.h
  10. 13
      src/publisher/pubSerial.h
  11. 108
      src/utils/llist.h
  12. 182
      src/utils/scheduler.h
  13. 2
      src/utils/sun.h
  14. 10
      src/web/html/index.html
  15. 7
      src/wifi/ahoywifi.cpp

2
.github/workflows/compile_development.yml

@ -47,7 +47,7 @@ jobs:
run: python convert.py run: python convert.py
- name: Run PlatformIO - name: Run PlatformIO
run: pio run -d src --environment esp8266-release --environment esp8285-release --environment esp32-wroom32-release run: pio run -d src --environment esp8266-release --environment esp8285-release --environment esp8266-nokia5110 --environment esp8266-ssd1306 --environment esp32-wroom32-release
- name: Rename Binary files - name: Rename Binary files
id: rename-binary-files id: rename-binary-files

2
.github/workflows/compile_esp8266.yml

@ -49,7 +49,7 @@ jobs:
working-directory: src/web/html working-directory: src/web/html
run: python convert.py run: python convert.py
- name: Run PlatformIO - name: Run PlatformIO
run: pio run -d tools/esp8266 --environment esp8266-release --environment esp8285-release --environment esp32-wroom32-release run: pio run -d tools/esp8266 --environment esp8266-release --environment esp8285-release --environment esp8266-nokia5110 --environment esp8266-ssd1306 --environment esp32-wroom32-release
- name: Rename Binary files - name: Rename Binary files
id: rename-binary-files id: rename-binary-files

47
src/app.cpp

@ -19,24 +19,29 @@ app::app() : ah::Scheduler() {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void app::setup(uint32_t timeout) { void app::setup() {
Serial.begin(115200); Serial.begin(115200);
while (!Serial) while (!Serial)
yield(); yield();
ah::Scheduler::setup();
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)); everySec(std::bind(&app::tickSecond, this));
addListener(EVERY_12H, std::bind(&app::tickNtpUpdate, this)); everyMin(std::bind(&app::tickMinute, this));
once(mConfig->nrf.sendInterval, std::bind(&app::tickSend, this), "tickSend"); every12h(std::bind(&app::tickNtpUpdate, this));
every(std::bind(&app::tickSend, this), mConfig->nrf.sendInterval);
#if !defined(AP_ONLY)
if((mConfig->sun.lat) && (mConfig->sun.lon)) { 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; mCalculatedTimezoneOffset = (int8_t)((mConfig->sun.lon >= 0 ? mConfig->sun.lon + 7.5 : mConfig->sun.lon - 7.5) / 15) * 3600;
once(std::bind(&app::tickCalcSunrise, this), 5);
} }
#endif
mSys = new HmSystemType(); mSys = new HmSystemType();
mSys->enableDebug(); mSys->enableDebug();
@ -54,10 +59,9 @@ void app::setup(uint32_t timeout) {
#if !defined(AP_ONLY) #if !defined(AP_ONLY)
if (mConfig->mqtt.broker[0] > 0) { if (mConfig->mqtt.broker[0] > 0) {
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)); everySec(std::bind(&PubMqttType::tickerSecond, &mMqtt));
addListener(EVERY_MIN, std::bind(&PubMqttType::tickerMinute, &mMqtt)); everyMin(std::bind(&PubMqttType::tickerMinute, &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();
@ -65,16 +69,17 @@ void app::setup(uint32_t timeout) {
mWeb = new web(this, mConfig, &mStat, mVersion); mWeb = new web(this, mConfig, &mStat, mVersion);
mWeb->setup(); mWeb->setup();
mWeb->setProtection(strlen(mConfig->sys.adminPwd) != 0); mWeb->setProtection(strlen(mConfig->sys.adminPwd) != 0);
addListener(EVERY_SEC, std::bind(&web::tickSecond, mWeb)); everySec(std::bind(&web::tickSecond, mWeb));
// Plugins // Plugins
#if defined(ENA_NOKIA) || defined(ENA_SSD1306) #if defined(ENA_NOKIA) || defined(ENA_SSD1306)
mMonoDisplay.setup(mSys, &mTimestamp); 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)); everySec(std::bind(&MonoDisplayType::tickerSecond, &mMonoDisplay));
#endif #endif
//addListener(EVERY_MIN, std::bind(&PubSerialType::tickerMinute, &mPubSerial)); mPubSerial.setup(mConfig, mSys, &mTimestamp);
every(std::bind(&PubSerialType::tick, &mPubSerial), mConfig->serial.interval);
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -129,27 +134,26 @@ void app::loop(void) {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void app::tickCalcSunrise(void) { void app::tickCalcSunrise(void) {
if (0 == mTimestamp) { if (0 == mTimestamp) {
once(5, std::bind(&app::tickCalcSunrise, this)); // check again in 5 secs once(std::bind(&app::tickCalcSunrise, this), 5); // check again in 5 secs
return; return;
} }
ah::calculateSunriseSunset(mTimestamp, mCalculatedTimezoneOffset, mConfig->sun.lat, mConfig->sun.lon, &mSunrise, &mSunset); ah::calculateSunriseSunset(mTimestamp, mCalculatedTimezoneOffset, mConfig->sun.lat, mConfig->sun.lon, &mSunrise, &mSunset);
uint32_t nxtTrig = mTimestamp - (mTimestamp % 86400) + 86400; // next midnight uint32_t nxtTrig = mTimestamp - (mTimestamp % 86400) + 86400; // next midnight
onceAt(nxtTrig, std::bind(&app::tickCalcSunrise, this), "calc sunrise"); onceAt(std::bind(&app::tickCalcSunrise, this), nxtTrig);
onceAt(mSunrise, std::bind(&app::tickSend, this), "tickSend"); // register next event
if (mConfig->mqtt.broker[0] > 0) { if (mConfig->mqtt.broker[0] > 0) {
once(1, std::bind(&PubMqttType::tickerSun, &mMqtt), "MQTT-tickerSun"); once(std::bind(&PubMqttType::tickerSun, &mMqtt), 1);
onceAt(mSunset, std::bind(&PubMqttType::tickSunset, &mMqtt)); onceAt(std::bind(&PubMqttType::tickSunset, &mMqtt), mSunset);
} }
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void app::tickSend(void) { void app::tickSend(void) {
if(!mSys->Radio.isChipConnected()) {
DPRINTLN(DBG_WARN, "NRF24 not connected!");
return;
}
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) 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 (!mSys->BufCtrl.empty()) {
if (mConfig->serial.debug) if (mConfig->serial.debug)
DPRINTLN(DBG_DEBUG, F("recbuf not empty! #") + String(mSys->BufCtrl.getFill())); DPRINTLN(DBG_DEBUG, F("recbuf not empty! #") + String(mSys->BufCtrl.getFill()));
@ -206,7 +210,6 @@ void app::tickSend(void) {
} }
} }
} else { } else {
once(3600, std::bind(&app::tickSend, this), "tickSend"); // register next event (one hour)
if (mConfig->serial.debug) if (mConfig->serial.debug)
DPRINTLN(DBG_WARN, F("Time not set or it is night time, therefore no communication to the inverter!")); DPRINTLN(DBG_WARN, F("Time not set or it is night time, therefore no communication to the inverter!"));
} }

2
src/app.h

@ -52,7 +52,7 @@ class app : public ah::Scheduler {
app(); app();
~app() {} ~app() {}
void setup(uint32_t timeout); void setup(void);
void loop(void); void loop(void);
void handleIntr(void); void handleIntr(void);
void cbMqtt(char* topic, byte* payload, unsigned int length); void cbMqtt(char* topic, byte* payload, unsigned int length);

8
src/config/settings.h

@ -98,6 +98,7 @@ typedef struct {
cfgMqtt_t mqtt; cfgMqtt_t mqtt;
cfgLed_t led; cfgLed_t led;
cfgInst_t inst; cfgInst_t inst;
bool valid;
} settings_t; } settings_t;
class settings { class settings {
@ -107,7 +108,7 @@ class settings {
void setup() { void setup() {
DPRINTLN(DBG_INFO, F("Initializing FS ..")); DPRINTLN(DBG_INFO, F("Initializing FS .."));
mValid = false; mCfg.valid = false;
#if !defined(ESP32) #if !defined(ESP32)
LittleFSConfig cfg; LittleFSConfig cfg;
cfg.setAutoFormat(false); cfg.setAutoFormat(false);
@ -145,7 +146,7 @@ class settings {
} }
bool getValid(void) { bool getValid(void) {
return mValid; return mCfg.valid;
} }
void getInfo(uint32_t *used, uint32_t *size) { void getInfo(uint32_t *used, uint32_t *size) {
@ -173,7 +174,7 @@ class settings {
DynamicJsonDocument root(4096); DynamicJsonDocument root(4096);
DeserializationError err = deserializeJson(root, fp); DeserializationError err = deserializeJson(root, fp);
if(!err) { if(!err) {
mValid = true; mCfg.valid = true;
jsonWifi(root["wifi"]); jsonWifi(root["wifi"]);
jsonNrf(root["nrf"]); jsonNrf(root["nrf"]);
jsonNtp(root["ntp"]); jsonNtp(root["ntp"]);
@ -409,7 +410,6 @@ class settings {
} }
settings_t mCfg; settings_t mCfg;
bool mValid;
}; };
#endif /*__SETTINGS_H__*/ #endif /*__SETTINGS_H__*/

2
src/defines.h

@ -13,7 +13,7 @@
//------------------------------------- //-------------------------------------
#define VERSION_MAJOR 0 #define VERSION_MAJOR 0
#define VERSION_MINOR 5 #define VERSION_MINOR 5
#define VERSION_PATCH 48 #define VERSION_PATCH 49
//------------------------------------- //-------------------------------------
typedef struct { typedef struct {

9
src/hm/hmInverter.h

@ -160,18 +160,13 @@ class Inverter {
} }
uint8_t getQueuedCmd() { uint8_t getQueuedCmd() {
if (_commandQueue.empty()){ if (_commandQueue.empty()) {
// Fill with default commands
enqueCommand<InfoCommand>(RealTimeRunData_Debug);
if (fwVersion == 0) if (fwVersion == 0)
{ // info needed maybe after "one night" (=> DC>0 to DC=0 and to DC>0) or reboot
enqueCommand<InfoCommand>(InverterDevInform_All); enqueCommand<InfoCommand>(InverterDevInform_All);
} enqueCommand<InfoCommand>(RealTimeRunData_Debug);
if (actPowerLimit == 0xffff) if (actPowerLimit == 0xffff)
{ // info needed maybe after "one nigth" (=> DC>0 to DC=0 and to DC>0) or reboot
enqueCommand<InfoCommand>(SystemConfigPara); enqueCommand<InfoCommand>(SystemConfigPara);
} }
}
return _commandQueue.front().get()->getCmd(); return _commandQueue.front().get()->getCmd();
} }

2
src/main.cpp

@ -18,7 +18,7 @@ IRAM_ATTR void handleIntr(void) {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void setup() { void setup() {
myApp.setup(WIFI_TRY_CONNECT_TIME); myApp.setup();
// TODO: move to HmRadio // TODO: move to HmRadio
attachInterrupt(digitalPinToInterrupt(myApp.getIrqPin()), handleIntr, FALLING); attachInterrupt(digitalPinToInterrupt(myApp.getIrqPin()), handleIntr, FALLING);

3
src/publisher/pubMqtt.h

@ -95,6 +95,7 @@ class PubMqtt {
} }
void tickSunset() { void tickSunset() {
printf("tickSunset\n");
char topic[MAX_NAME_LENGTH + 15], val[32]; char topic[MAX_NAME_LENGTH + 15], val[32];
for (uint8_t id = 0; id < mSys->getNumInverters(); id++) { for (uint8_t id = 0; id < mSys->getNumInverters(); id++) {
Inverter<> *iv = mSys->getInverterByPos(id); Inverter<> *iv = mSys->getInverterByPos(id);
@ -387,7 +388,7 @@ class PubMqtt {
publish(topic, val, true); publish(topic, val, true);
snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/last_success", iv->config->name); snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/last_success", iv->config->name);
snprintf(val, 40, "%i", iv->getLastTs(rec) * 1000); snprintf(val, 40, "%d", iv->getLastTs(rec));
publish(topic, val, true); publish(topic, val, true);
} }

13
src/publisher/pubSerial.h

@ -21,12 +21,9 @@ class PubSerial {
mUtcTimestamp = utcTs; mUtcTimestamp = utcTs;
} }
void tickerMinute() { void tick(void) {
DPRINTLN(DBG_INFO, "tickerMinute");
if(++mTick >= mCfg->serial.interval) {
mTick = 0;
if (mCfg->serial.showIv) { if (mCfg->serial.showIv) {
char topic[30], val[10]; char topic[32 + MAX_NAME_LENGTH], val[40];
for (uint8_t id = 0; id < mSys->getNumInverters(); id++) { for (uint8_t id = 0; id < mSys->getNumInverters(); id++) {
Inverter<> *iv = mSys->getInverterByPos(id); Inverter<> *iv = mSys->getInverterByPos(id);
if (NULL != iv) { if (NULL != iv) {
@ -35,8 +32,8 @@ class PubSerial {
DPRINTLN(DBG_INFO, F("Inverter: ") + String(id)); DPRINTLN(DBG_INFO, F("Inverter: ") + String(id));
for (uint8_t i = 0; i < rec->length; i++) { for (uint8_t i = 0; i < rec->length; i++) {
if (0.0f != iv->getValue(i, rec)) { if (0.0f != iv->getValue(i, rec)) {
snprintf(topic, 30, "%s/ch%d/%s", iv->config->name, rec->assign[i].ch, iv->getFieldName(i, rec)); snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/ch%d/%s", iv->config->name, rec->assign[i].ch, iv->getFieldName(i, rec));
snprintf(val, 10, "%.3f %s", iv->getValue(i, rec), iv->getUnit(i, rec)); snprintf(val, 40, "%.3f %s", iv->getValue(i, rec), iv->getUnit(i, rec));
DPRINTLN(DBG_INFO, String(topic) + ": " + String(val)); DPRINTLN(DBG_INFO, String(topic) + ": " + String(val));
} }
yield(); yield();
@ -47,12 +44,10 @@ class PubSerial {
} }
} }
} }
}
private: private:
settings_t *mCfg; settings_t *mCfg;
HMSYSTEM *mSys; HMSYSTEM *mSys;
uint8_t mTick;
uint32_t *mUtcTimestamp; uint32_t *mUtcTimestamp;
}; };

108
src/utils/llist.h

@ -0,0 +1,108 @@
//-----------------------------------------------------------------------------
// 2022 Ahoy, https://ahoydtu.de
// Lukas Pusch, lukas@lpusch.de
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//-----------------------------------------------------------------------------
#ifndef __LIST_H__
#define __LIST_H__
template<class T, class... Args>
struct node_s {
typedef T dT;
node_s *pre;
node_s *nxt;
uint32_t id;
dT d;
node_s() : pre(NULL), nxt(NULL), d() {}
node_s(Args... args) : id(0), pre(NULL), nxt(NULL), d(args...) {}
};
template<int MAX_NUM, class T, class... Args>
class llist {
typedef node_s<T, Args...> elmType;
typedef T dataType;
public:
llist() : root(mPool) {
root = NULL;
elmType *p = mPool;
for(uint32_t i = 0; i < MAX_NUM; i++) {
p->id = i;
p++;
}
mFill = mMax = 0;
}
elmType *add(Args... args) {
elmType *p = root, *t;
if(NULL == (t = getFreeNode()))
return NULL;
if(++mFill > mMax)
mMax = mFill;
if(NULL == root) {
p = root = t;
p->pre = p;
p->nxt = p;
}
else {
p = root->pre;
t->pre = p;
p->nxt->pre = t;
t->nxt = p->nxt;
p->nxt = t;
}
t->d = dataType(args...);
return p;
}
elmType *getFront() {
return root;
}
elmType *get(elmType *p) {
p = p->nxt;
return (p == root) ? NULL : p;
}
elmType *rem(elmType *p) {
if(NULL == p)
return NULL;
elmType *t = p->nxt;
p->nxt->pre = p->pre;
p->pre->nxt = p->nxt;
if(root == p)
root = NULL;
p->nxt = NULL;
p->pre = NULL;
p = NULL;
mFill--;
return (NULL == root) ? NULL : ((t == root) ? NULL : t);
}
uint16_t getFill(void) {
return mFill;
}
uint16_t getMaxFill(void) {
return mMax;
}
protected:
elmType *root;
private:
elmType *getFreeNode(void) {
elmType *n = mPool;
for(uint32_t i = 0; i < MAX_NUM; i++) {
if(NULL == n->nxt)
return n;
n++;
}
return NULL;
}
elmType mPool[MAX_NUM];
uint16_t mFill, mMax;
};
#endif /*__LIST_H__*/

182
src/utils/scheduler.h

@ -7,96 +7,75 @@
#ifndef __SCHEDULER_H__ #ifndef __SCHEDULER_H__
#define __SCHEDULER_H__ #define __SCHEDULER_H__
#include <memory>
#include <functional> #include <functional>
#include <list> #include "llist.h"
#include "dbg.h"
enum {EVERY_SEC = 1, EVERY_MIN, EVERY_HR, EVERY_12H, EVERY_DAY}; namespace ah {
typedef std::function<void()> SchedulerCb; typedef std::function<void()> scdCb;
struct once_t { enum {SCD_SEC = 1, SCD_MIN = 60, SCD_HOUR = 3600, SCD_12H = 43200, SCD_DAY = 86400};
uint32_t n;
SchedulerCb f;
once_t(uint32_t a, SchedulerCb b) : n(a), f(b) {}
once_t() : n(0), f(NULL) {}
};
namespace ah { struct scdEvry_s {
class Scheduler { scdCb c;
uint32_t timeout;
uint32_t reload;
scdEvry_s() : c(NULL), timeout(0), reload(0) {}
scdEvry_s(scdCb a, uint32_t tmt, uint32_t rl) : c(a), timeout(tmt), reload(rl) {}
};
struct scdAt_s {
scdCb c;
uint32_t timestamp;
scdAt_s() : c(NULL), timestamp(0) {}
scdAt_s(scdCb a, uint32_t ts) : c(a), timestamp(ts) {}
};
typedef node_s<scdEvry_s, scdCb, uint32_t, uint32_t> sP;
typedef node_s<scdAt_s, scdCb, uint32_t> sPAt;
class Scheduler {
public: public:
Scheduler() {} Scheduler() {}
void setup() { void setup() {
mPrevMillis = 0;
mSeconds = 0;
mMinutes = 0;
mHours = 0;
mUptime = 0; mUptime = 0;
mTimestamp = 0; mTimestamp = 0;
mPrevMillis = millis();
} }
void loop() { void loop(void) {
if (millis() - mPrevMillis >= 1000) { mMillis = millis();
mPrevMillis += 1000; mDiff = mMillis - mPrevMillis;
mUptime++; if(mDiff >= 1000) {
if(0 != mTimestamp) if(mMillis < mPrevMillis) { // overflow
mTimestamp++; mPrevMillis = mMillis;
notify(&mListSecond); return;
onceFuncTick();
if(++mSeconds >= 60) {
mSeconds = 0;
notify(&mListMinute);
onceAtFuncTick();
if(++mMinutes >= 60) {
mMinutes = 0;
notify(&mListHour);
if(++mHours >= 24) {
mHours = 0;
notify(&mListDay);
notify(&mList12h);
}
else if(mHours == 12)
notify(&mList12h);
}
} }
mDiffSeconds = mDiff / 1000;
mPrevMillis += (mPrevMillis * 1000);
checkEvery();
checkAt();
mUptime += mDiffSeconds;
if(0 != mTimestamp)
mTimestamp += mDiffSeconds;
} }
} }
// checked every second void once(scdCb c, uint32_t timeout) { mStack.add(c, timeout, 0); }
void once(uint32_t sec, SchedulerCb cb, const char *info = NULL) { void every(scdCb c, uint32_t interval) { mStack.add(c, interval, interval); }
if(NULL != info) { void onceAt(scdCb c, uint32_t timestamp) { mStackAt.add(c, timestamp); }
DPRINT(DBG_INFO, F("once in [s]: ") + String(sec));
DBGPRINTLN(F(", ") + String(info));
}
mOnce.push_back(once_t(sec, cb));
}
// checked every minute void everySec(scdCb c) { mStack.add(c, SCD_SEC, SCD_SEC); }
void onceAt(uint32_t timestamp, SchedulerCb cb, const char *info = NULL) { void everyMin(scdCb c) { mStack.add(c, SCD_MIN, SCD_MIN); }
if(timestamp > mTimestamp) { void everyHour(scdCb c) { mStack.add(c, SCD_HOUR, SCD_HOUR); }
if(NULL != info) { void every12h(scdCb c) { mStack.add(c, SCD_12H, SCD_12H); }
DPRINT(DBG_INFO, F("onceAt (UTC): ") + getDateTimeStr(timestamp)); void everyDay(scdCb c) { mStack.add(c, SCD_DAY, SCD_DAY); }
DBGPRINTLN(F(", ") + String(info));
}
mOnceAt.push_back(once_t(timestamp, cb));
}
}
virtual void setTimestamp(uint32_t ts) { virtual void setTimestamp(uint32_t ts) {
mTimestamp = ts; mTimestamp = ts;
} }
void addListener(uint8_t every, SchedulerCb cb) {
switch(every) {
case EVERY_SEC: mListSecond.push_back(cb); break;
case EVERY_MIN: mListMinute.push_back(cb); break;
case EVERY_HR: mListHour.push_back(cb); break;
case EVERY_12H: mList12h.push_back(cb); break;
case EVERY_DAY: mListDay.push_back(cb); break;
default: break;
}
}
uint32_t getUptime(void) { uint32_t getUptime(void) {
return mUptime; return mUptime;
} }
@ -105,54 +84,55 @@ class Scheduler {
return mTimestamp; return mTimestamp;
} }
protected: void stat() {
virtual void notify(std::list<SchedulerCb> *lType) { DPRINTLN(DBG_INFO, "max fill every: " + String(mStack.getMaxFill()));
for(std::list<SchedulerCb>::iterator it = lType->begin(); it != lType->end(); ++it) { DPRINTLN(DBG_INFO, "max fill at: " + String(mStackAt.getMaxFill()));
(*it)();
}
} }
protected:
uint32_t mTimestamp; uint32_t mTimestamp;
private: private:
void onceFuncTick(void) { inline void checkEvery(void) {
if(mOnce.empty()) bool expired;
return; sP *p = mStack.getFront();
for(std::list<once_t>::iterator it = mOnce.begin(); it != mOnce.end();) { while(NULL != p) {
if(((*it).n)-- == 0) { if(mDiffSeconds >= p->d.timeout) expired = true;
((*it).f)(); else if((p->d.timeout--) == 0) expired = true;
it = mOnce.erase(it); else expired = false;
if(expired) {
(p->d.c)();
if(0 == p->d.reload)
p = mStack.rem(p);
else {
p->d.timeout = p->d.reload - 1;
p = mStack.get(p);
}
} }
else else
++it; p = mStack.get(p);
} }
} }
void onceAtFuncTick(void) { inline void checkAt(void) {
if(mOnceAt.empty()) sPAt *p = mStackAt.getFront();
return; while(NULL != p) {
for(std::list<once_t>::iterator it = mOnceAt.begin(); it != mOnceAt.end();) { if((p->d.timestamp) <= mTimestamp) {
if(((*it).n) < mTimestamp) { (p->d.c)();
((*it).f)(); p = mStackAt.rem(p);
it = mOnceAt.erase(it);
} }
else else
++it; p = mStackAt.get(p);
} }
} }
std::list<SchedulerCb> mListSecond; llist<25, scdEvry_s, scdCb, uint32_t, uint32_t> mStack;
std::list<SchedulerCb> mListMinute; llist<10, scdAt_s, scdCb, uint32_t> mStackAt;
std::list<SchedulerCb> mListHour; uint32_t mMillis, mPrevMillis, mDiff;
std::list<SchedulerCb> mList12h; uint32_t mUptime;
std::list<SchedulerCb> mListDay; uint8_t mDiffSeconds;
};
std::list<once_t> mOnce;
std::list<once_t> mOnceAt;
uint32_t mPrevMillis, mUptime;
uint8_t mSeconds, mMinutes, mHours;
};
} }
#endif /*__SCHEDULER_H__*/ #endif /*__SCHEDULER_H__*/

2
src/utils/sun.h

@ -10,7 +10,7 @@ namespace ah {
void calculateSunriseSunset(uint32_t utcTs, uint32_t offset, float lat, float lon, uint32_t *sunrise, uint32_t *sunset) { void calculateSunriseSunset(uint32_t utcTs, uint32_t offset, float lat, float lon, uint32_t *sunrise, uint32_t *sunset) {
// Source: https://en.wikipedia.org/wiki/Sunrise_equation#Complete_calculation_on_Earth // Source: https://en.wikipedia.org/wiki/Sunrise_equation#Complete_calculation_on_Earth
// Julian day since 1.1.2000 12:00 + correction 69.12s // Julian day since 1.1.2000 12:00
double n_JulianDay = (utcTs + offset) / 86400 - 10957.0; double n_JulianDay = (utcTs + offset) / 86400 - 10957.0;
// Mean solar time // Mean solar time
double J = n_JulianDay - lon / 360; double J = n_JulianDay - lon / 360;

10
src/web/html/index.html

@ -48,9 +48,6 @@
<div id="note"> <div id="note">
Discuss with us on <a href="https://discord.gg/WzhxEY62mB">Discord</a><br/> Discuss with us on <a href="https://discord.gg/WzhxEY62mB">Discord</a><br/>
<h3>Documentation</h3>
<a href="https://ahoydtu.de" target="_blank">ahoydtu.de</a>
<h3>Support this project:</h3> <h3>Support this project:</h3>
<ul> <ul>
<li>Report <a href="https://github.com/lumapu/ahoy/issues" target="_blank">issues</a></li> <li>Report <a href="https://github.com/lumapu/ahoy/issues" target="_blank">issues</a></li>
@ -118,8 +115,11 @@
var sec = up % 60; var sec = up % 60;
var sunrise = new Date(obj["ts_sunrise"] * 1000); var sunrise = new Date(obj["ts_sunrise"] * 1000);
var sunset = new Date(obj["ts_sunset"] * 1000); var sunset = new Date(obj["ts_sunset"] * 1000);
document.getElementById("uptime").innerHTML = days + " Days, " var e = document.getElementById("uptime");
+ ("0"+hrs).substr(-2) + ":" e.innerHTML = days + " Day";
if(1 != days)
e.innerHTML += "s";
e.innerHTML += ", " + ("0"+hrs).substr(-2) + ":"
+ ("0"+min).substr(-2) + ":" + ("0"+min).substr(-2) + ":"
+ ("0"+sec).substr(-2); + ("0"+sec).substr(-2);
var dSpan = document.getElementById("date"); var dSpan = document.getElementById("date");

7
src/wifi/ahoywifi.cpp

@ -8,8 +8,6 @@
#define F(sl) (sl) #define F(sl) (sl)
#endif #endif
#include "ahoywifi.h" #include "ahoywifi.h"
#include "../utils/ahoyTimer.h"
// NTP CONFIG // NTP CONFIG
#define NTP_PACKET_SIZE 48 #define NTP_PACKET_SIZE 48
@ -25,17 +23,16 @@ ahoywifi::ahoywifi() {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void ahoywifi::setup(settings_t *config, uint32_t *utcTimestamp) { void ahoywifi::setup(settings_t *config, uint32_t *utcTimestamp) {
char ipSta[16];
mConfig = config; mConfig = config;
mUtcTimestamp = utcTimestamp; mUtcTimestamp = utcTimestamp;
#if !defined(FB_WIFI_OVERRIDDEN) #if !defined(FB_WIFI_OVERRIDDEN)
if(strncmp(mConfig->sys.stationSsid, FB_WIFI_SSID, 14) != 0) if(strncmp(mConfig->sys.stationSsid, FB_WIFI_SSID, 14) == 0)
setupAp(); setupAp();
#endif #endif
#if !defined(AP_ONLY) #if !defined(AP_ONLY)
if(mConfig->valid)
setupStation(); setupStation();
ah::ip2Char(mConfig->sys.ip.ip, ipSta);
#endif #endif
#if defined(ESP8266) #if defined(ESP8266)

Loading…
Cancel
Save