diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json index a04cb695..841ff790 100644 --- a/src/.vscode/settings.json +++ b/src/.vscode/settings.json @@ -20,4 +20,7 @@ // https://clang.llvm.org/docs/ClangFormatStyleOptions.html "C_Cpp.clang_format_fallbackStyle": "{ BasedOnStyle: Google, IndentWidth: 4, ColumnLimit: 0}", + "files.associations": { + "typeinfo": "cpp" + }, } \ No newline at end of file diff --git a/src/app.cpp b/src/app.cpp index c032c178..443e7fcb 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -13,33 +13,27 @@ #include "utils/sun.h" //----------------------------------------------------------------------------- -app::app() { +void app::setup(uint32_t timeout) { Serial.begin(115200); - DPRINTLN(DBG_VERBOSE, F("app::app")); - mEep = new eep(); + while (!Serial) + yield(); resetSystem(); - loadDefaultConfig(); + mSettings.setup(); + mSettings.getPtr(mConfig); + + mWifi = new ahoywifi(mConfig); - mWifi = new ahoywifi(this, &mSysConfig, &mConfig); mSys = new HmSystemType(); mSys->enableDebug(); mShouldReboot = false; -} -//----------------------------------------------------------------------------- -void app::setup(uint32_t timeout) { - DPRINTLN(DBG_VERBOSE, F("app::setup")); - - mWifiSettingsValid = checkEEpCrc(ADDR_START, ADDR_WIFI_CRC, ADDR_WIFI_CRC); - mSettingsValid = checkEEpCrc(ADDR_START_SETTINGS, ((ADDR_NEXT) - (ADDR_START_SETTINGS)), ADDR_SETTINGS_CRC); - loadEEpconfig(); + mWifi->setup(timeout, mSettings.getValid()); - mWifi->setup(timeout, mWifiSettingsValid); - - mSys->setup(mConfig.amplifierPower, mConfig.pinIrq, mConfig.pinCe, mConfig.pinCs); + mSys->setup(mConfig->nrf.amplifierPower, mConfig->nrf.pinIrq, mConfig->nrf.pinCe, mConfig->nrf.pinCs); + mSys->addInverters(&mConfig->inst); mPayload.setup(mSys); - mPayload.enableSerialDebug(mConfig.serialDebug); + mPayload.enableSerialDebug(mConfig->serial.debug); #ifndef AP_ONLY setupMqtt(); if(mMqttActive) @@ -47,11 +41,12 @@ void app::setup(uint32_t timeout) { #endif setupLed(); - mWebInst = new web(this, &mSysConfig, &mConfig, &mStat, mVersion); + + mWebInst = new web(this, mConfig, &mStat, mVersion); mWebInst->setup(); - mWebInst->setProtection(strlen(mConfig.password) != 0); - DPRINTLN(DBG_INFO, F("Settings valid: ") + String((mSettingsValid) ? F("true") : F("false"))); - DPRINTLN(DBG_INFO, F("EEprom storage size: 0x") + String(ADDR_SETTINGS_CRC, HEX)); + mWebInst->setProtection(strlen(mConfig->sys.adminPwd) != 0); + DPRINTLN(DBG_INFO, F("Settings valid: ") + String((mSettings.getValid()) ? F("true") : F("false"))); + } //----------------------------------------------------------------------------- @@ -88,7 +83,7 @@ void app::loop(void) { if (mFlagSendDiscoveryConfig) { mFlagSendDiscoveryConfig = false; - mMqtt.sendMqttDiscoveryConfig(mConfig.mqtt.topic); + mMqtt.sendMqttDiscoveryConfig(mConfig->mqtt.topic); } mSys->Radio.loop(); @@ -104,7 +99,7 @@ void app::loop(void) { if (mSys->Radio.checkPaketCrc(p->packet, &len, p->rxCh)) { // process buffer only on first occurrence - if (mConfig.serialDebug) { + if (mConfig->serial.debug) { DPRINT(DBG_INFO, "RX " + String(len) + "B Ch" + String(p->rxCh) + " | "); mSys->Radio.dumpBuf(NULL, p->packet, len); } @@ -119,23 +114,23 @@ void app::loop(void) { yield(); if (rxRdy) - mPayload.process(true, mConfig.maxRetransPerPyld, &mStat); + mPayload.process(true, mConfig->nrf.maxRetransPerPyld, &mStat); } if (mMqttActive) mMqtt.loop(); if (ah::checkTicker(&mTicker, 1000)) { - if (mUtcTimestamp > 946684800 && mConfig.sunLat && mConfig.sunLon && (mUtcTimestamp + mCalculatedTimezoneOffset) / 86400 != (mLatestSunTimestamp + mCalculatedTimezoneOffset) / 86400) { // update on reboot or midnight + 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.sunLon >= 0 ? mConfig.sunLon + 7.5 : mConfig.sunLon - 7.5) / 15) * 3600; + mCalculatedTimezoneOffset = (int8_t)((mConfig->sun.lon >= 0 ? mConfig->sun.lon + 7.5 : mConfig->sun.lon - 7.5) / 15) * 3600; } - ah::calculateSunriseSunset(mUtcTimestamp, mCalculatedTimezoneOffset, mConfig.sunLat, mConfig.sunLon, &mSunrise, &mSunset); + ah::calculateSunriseSunset(mUtcTimestamp, mCalculatedTimezoneOffset, mConfig->sun.lat, mConfig->sun.lon, &mSunrise, &mSunset); mLatestSunTimestamp = mUtcTimestamp; } - if (mConfig.serialShowIv) { - if (++mSerialTicker >= mConfig.serialInterval) { + if (mConfig->serial.showIv) { + if (++mSerialTicker >= mConfig->serial.interval) { mSerialTicker = 0; char topic[30], val[10]; for (uint8_t id = 0; id < mSys->getNumInverters(); id++) { @@ -146,7 +141,7 @@ void app::loop(void) { DPRINTLN(DBG_INFO, "Inverter: " + String(id)); for (uint8_t i = 0; i < rec->length; i++) { if (0.0f != iv->getValue(i, rec)) { - snprintf(topic, 30, "%s/ch%d/%s", iv->name, rec->assign[i].ch, iv->getFieldName(i, rec)); + snprintf(topic, 30, "%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)); DPRINTLN(DBG_INFO, String(topic) + ": " + String(val)); } @@ -159,15 +154,15 @@ void app::loop(void) { } } - if (++mSendTicker >= mConfig.sendInterval) { + if (++mSendTicker >= mConfig->nrf.sendInterval) { mSendTicker = 0; - if (mUtcTimestamp > 946684800 && (!mConfig.sunDisNightCom || !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.serialDebug) + 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.serialDebug) + if (mConfig->serial.debug) DPRINTLN(DBG_DEBUG, F("recbuf not empty! #") + String(mSys->BufCtrl.getFill())); } @@ -180,7 +175,7 @@ void app::loop(void) { if (NULL != iv) { if (!mPayload.isComplete(iv)) - mPayload.process(false, mConfig.maxRetransPerPyld, &mStat); + mPayload.process(false, mConfig->nrf.maxRetransPerPyld, &mStat); if (!mPayload.isComplete(iv)) { if (0 == mPayload.getMaxPacketId(iv)) @@ -189,9 +184,9 @@ void app::loop(void) { mStat.rxFail++; iv->setQueuedCmdFinished(); // command failed - if (mConfig.serialDebug) + if (mConfig->serial.debug) DPRINTLN(DBG_INFO, F("enqueued cmd failed/timeout")); - if (mConfig.serialDebug) { + if (mConfig->serial.debug) { DPRINT(DBG_INFO, F("(#") + String(iv->id) + ") "); DPRINTLN(DBG_INFO, F("no Payload received! (retransmits: ") + String(mPayload.getRetransmits(iv)) + ")"); } @@ -201,13 +196,13 @@ void app::loop(void) { mPayload.request(iv); yield(); - if (mConfig.serialDebug) { + 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->serial.u64, HEX)); + DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") Requesting Inv SN ") + String(iv->config->serial.u64, HEX)); } if (iv->devControlRequest) { - if (mConfig.serialDebug) + 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); @@ -221,7 +216,7 @@ void app::loop(void) { mRxTicker = 0; } } - } else if (mConfig.serialDebug) + } else if (mConfig->serial.debug) DPRINTLN(DBG_WARN, F("Time not set or it is night time, therefore no communication to the inverter!")); yield(); @@ -254,6 +249,8 @@ void app::getAvailNetworks(JsonObject obj) { //----------------------------------------------------------------------------- void app::resetSystem(void) { + snprintf(mVersion, 12, "%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); + mUptimeSecs = 0; mPrevMillis = 0; mUpdateNtp = false; @@ -284,130 +281,13 @@ void app::resetSystem(void) { memset(&mStat, 0, sizeof(statistics_t)); } -//----------------------------------------------------------------------------- -void app::loadDefaultConfig(void) { - memset(&mSysConfig, 0, sizeof(sysConfig_t)); - memset(&mConfig, 0, sizeof(config_t)); - snprintf(mVersion, 12, "%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); - - snprintf(mSysConfig.deviceName, DEVNAME_LEN, "%s", DEF_DEVICE_NAME); - - // wifi - snprintf(mSysConfig.stationSsid, SSID_LEN, "%s", FB_WIFI_SSID); - snprintf(mSysConfig.stationPwd, PWD_LEN, "%s", FB_WIFI_PWD); - - // password - snprintf(mConfig.password, PWD_LEN, "%s", GUI_DEF_PASSWORD); - - // nrf24 - mConfig.sendInterval = SEND_INTERVAL; - mConfig.maxRetransPerPyld = DEF_MAX_RETRANS_PER_PYLD; - mConfig.pinCs = DEF_CS_PIN; - mConfig.pinCe = DEF_CE_PIN; - mConfig.pinIrq = DEF_IRQ_PIN; - mConfig.amplifierPower = DEF_AMPLIFIERPOWER & 0x03; - - // status LED - mConfig.led.led0 = DEF_LED0_PIN; - mConfig.led.led1 = DEF_LED1_PIN; - - // ntp - snprintf(mConfig.ntpAddr, NTP_ADDR_LEN, "%s", DEF_NTP_SERVER_NAME); - mConfig.ntpPort = DEF_NTP_PORT; - - // Latitude + Longitude - mConfig.sunLat = 0.0; - mConfig.sunLon = 0.0; - mConfig.sunDisNightCom = false; - - // mqtt - snprintf(mConfig.mqtt.broker, MQTT_ADDR_LEN, "%s", DEF_MQTT_BROKER); - mConfig.mqtt.port = DEF_MQTT_PORT; - snprintf(mConfig.mqtt.user, MQTT_USER_LEN, "%s", DEF_MQTT_USER); - snprintf(mConfig.mqtt.pwd, MQTT_PWD_LEN, "%s", DEF_MQTT_PWD); - snprintf(mConfig.mqtt.topic, MQTT_TOPIC_LEN, "%s", DEF_MQTT_TOPIC); - - // serial - mConfig.serialInterval = SERIAL_INTERVAL; - mConfig.serialShowIv = false; - mConfig.serialDebug = false; - - // Disclaimer - mConfig.disclaimer = false; -} - -//----------------------------------------------------------------------------- -void app::loadEEpconfig(void) { - DPRINTLN(DBG_INFO, F("loadEEpconfig")); - - if (mWifiSettingsValid) - mEep->read(ADDR_CFG_SYS, (uint8_t *)&mSysConfig, CFG_SYS_LEN); - if (mSettingsValid) { - mEep->read(ADDR_CFG, (uint8_t *)&mConfig, CFG_LEN); - - mSendTicker = mConfig.sendInterval; - mSerialTicker = 0; - - // inverter - uint64_t invSerial; - char name[MAX_NAME_LENGTH + 1] = {0}; - uint16_t modPwr[4]; - Inverter<> *iv; - for (uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { - mEep->read(ADDR_INV_ADDR + (i * 8), &invSerial); - mEep->read(ADDR_INV_NAME + (i * MAX_NAME_LENGTH), name, MAX_NAME_LENGTH); - mEep->read(ADDR_INV_CH_PWR + (i * 2 * 4), modPwr, 4); - if (0ULL != invSerial) { - iv = mSys->addInverter(name, invSerial, modPwr); - if (NULL != iv) { // will run once on every dtu boot - for (uint8_t j = 0; j < 4; j++) { - mEep->read(ADDR_INV_CH_NAME + (i * 4 * MAX_NAME_LENGTH) + j * MAX_NAME_LENGTH, iv->chName[j], MAX_NAME_LENGTH); - } - } - } - } - - for (uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { - iv = mSys->getInverterByPos(i, false); - if (NULL != iv) - mPayload.reset(iv, mUtcTimestamp); - } - } -} - -//----------------------------------------------------------------------------- -void app::saveValues(void) { - DPRINTLN(DBG_VERBOSE, F("app::saveValues")); - - mEep->write(ADDR_CFG_SYS, (uint8_t *)&mSysConfig, CFG_SYS_LEN); - mEep->write(ADDR_CFG, (uint8_t *)&mConfig, CFG_LEN); - Inverter<> *iv; - for (uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { - iv = mSys->getInverterByPos(i, false); - mEep->write(ADDR_INV_ADDR + (i * 8), iv->serial.u64); - mEep->write(ADDR_INV_NAME + (i * MAX_NAME_LENGTH), iv->name, MAX_NAME_LENGTH); - // max channel power / name - for (uint8_t j = 0; j < 4; j++) { - mEep->write(ADDR_INV_CH_PWR + (i * 2 * 4) + (j * 2), iv->chMaxPwr[j]); - mEep->write(ADDR_INV_CH_NAME + (i * 4 * MAX_NAME_LENGTH) + j * MAX_NAME_LENGTH, iv->chName[j], MAX_NAME_LENGTH); - } - } - - updateCrc(); - - // update sun - mLatestSunTimestamp = 0; -} - //----------------------------------------------------------------------------- void app::setupMqtt(void) { - if (mSettingsValid) { - if (mConfig.mqtt.broker[0] > 0) - mMqttActive = true; + if (mConfig->mqtt.broker[0] > 0) + mMqttActive = true; - if(mMqttActive) - mMqtt.setup(&mConfig.mqtt, mSysConfig.deviceName, mVersion, mSys, &mUtcTimestamp); - } + if(mMqttActive) + mMqtt.setup(&mConfig->mqtt, mConfig->sys.deviceName, mVersion, mSys, &mUtcTimestamp); } //----------------------------------------------------------------------------- @@ -417,26 +297,26 @@ void app::setupLed(void) { * PIN ---- |<----- 3.3V * * */ - if(mConfig.led.led0 != 0xff) { - pinMode(mConfig.led.led0, OUTPUT); - digitalWrite(mConfig.led.led0, HIGH); // LED off + if(mConfig->led.led0 != 0xff) { + pinMode(mConfig->led.led0, OUTPUT); + digitalWrite(mConfig->led.led0, HIGH); // LED off } - if(mConfig.led.led1 != 0xff) { - pinMode(mConfig.led.led1, OUTPUT); - digitalWrite(mConfig.led.led1, HIGH); // LED off + if(mConfig->led.led1 != 0xff) { + pinMode(mConfig->led.led1, OUTPUT); + digitalWrite(mConfig->led.led1, HIGH); // LED off } } //----------------------------------------------------------------------------- void app::updateLed(void) { - if(mConfig.led.led0 != 0xff) { + if(mConfig->led.led0 != 0xff) { Inverter<> *iv = mSys->getInverterByPos(0); if (NULL != iv) { record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); if(iv->isProducing(mUtcTimestamp, rec)) - digitalWrite(mConfig.led.led0, LOW); // LED on + digitalWrite(mConfig->led.led0, LOW); // LED on else - digitalWrite(mConfig.led.led0, HIGH); // LED off + digitalWrite(mConfig->led.led0, HIGH); // LED off } } } diff --git a/src/app.h b/src/app.h index 21031dbc..640c94d5 100644 --- a/src/app.h +++ b/src/app.h @@ -7,15 +7,12 @@ #define __APP_H__ #include "utils/dbg.h" -#include "Arduino.h" - - -#include +#include #include #include #include -#include "config/eep.h" +#include "config/settings.h" #include "defines.h" #include "utils/crc.h" #include "utils/ahoyTimer.h" @@ -42,7 +39,7 @@ class web; class app { public: - app(); + app() {} ~app() {} void setup(uint32_t timeout); @@ -55,9 +52,16 @@ class app { void scanAvailNetworks(void); void getAvailNetworks(JsonObject obj); + void saveSettings(void) { + mSettings.saveSettings(); + } + + bool eraseSettings(bool eraseWifi = false) { + return mSettings.eraseSettings(eraseWifi); + } uint8_t getIrqPin(void) { - return mConfig.pinIrq; + return mConfig->nrf.pinIrq; } uint64_t Serial2u64(const char *val) { @@ -122,26 +126,8 @@ class app { return mLatestSunTimestamp; } - void eraseSettings(bool all = false) { - //DPRINTLN(DBG_VERBOSE, F("main.h:eraseSettings")); - uint8_t buf[64]; - uint16_t addr = (all) ? ADDR_START : ADDR_START_SETTINGS; - uint16_t end; - - memset(buf, 0xff, 64); - do { - end = addr + 64; - if(end > (ADDR_SETTINGS_CRC + 2)) - end = (ADDR_SETTINGS_CRC + 2); - DPRINTLN(DBG_DEBUG, F("erase: 0x") + String(addr, HEX) + " - 0x" + String(end, HEX)); - mEep->write(addr, buf, (end-addr)); - addr = end; - } while(addr < (ADDR_SETTINGS_CRC + 2)); - mEep->commit(); - } - inline bool mqttIsConnected(void) { return mMqtt.isConnected(); } - inline bool getSettingsValid(void) { return mSettingsValid; } + inline bool getSettingsValid(void) { return mSettings.getValid(); } inline bool getRebootRequestState(void) { return mShowRebootRequest; } inline uint32_t getMqttTxCnt(void) { return mMqtt.getTxCnt(); } @@ -151,57 +137,12 @@ class app { private: void resetSystem(void); - void loadDefaultConfig(void); - void loadEEpconfig(void); void setupMqtt(void); void setupLed(void); void updateLed(void); - void processPayload(bool retransmit); - - inline uint16_t buildEEpCrc(uint32_t start, uint32_t length) { - DPRINTLN(DBG_VERBOSE, F("main.h:buildEEpCrc")); - uint8_t buf[32]; - uint16_t crc = 0xffff; - uint8_t len; - - while(length > 0) { - len = (length < 32) ? length : 32; - mEep->read(start, buf, len); - crc = ah::crc16(buf, len, crc); - start += len; - length -= len; - } - return crc; - } - - void updateCrc(void) { - DPRINTLN(DBG_VERBOSE, F("app::updateCrc")); - uint16_t crc; - - crc = buildEEpCrc(ADDR_START, ADDR_WIFI_CRC); - DPRINTLN(DBG_DEBUG, F("new Wifi CRC: ") + String(crc, HEX)); - mEep->write(ADDR_WIFI_CRC, crc); - - crc = buildEEpCrc(ADDR_START_SETTINGS, ((ADDR_NEXT) - (ADDR_START_SETTINGS))); - DPRINTLN(DBG_DEBUG, F("new Settings CRC: ") + String(crc, HEX)); - mEep->write(ADDR_SETTINGS_CRC, crc); - - mEep->commit(); - } - - bool checkEEpCrc(uint32_t start, uint32_t length, uint32_t crcPos) { - DPRINTLN(DBG_VERBOSE, F("main.h:checkEEpCrc")); - DPRINTLN(DBG_DEBUG, F("start: ") + String(start) + F(", length: ") + String(length)); - uint16_t crcRd, crcCheck; - crcCheck = buildEEpCrc(start, length); - mEep->read(crcPos, &crcRd); - DPRINTLN(DBG_DEBUG, "CRC RD: " + String(crcRd, HEX) + " CRC CALC: " + String(crcCheck, HEX)); - return (crcCheck == crcRd); - } - void stats(void) { DPRINTLN(DBG_VERBOSE, F("main.h:stats")); #ifdef ESP8266 @@ -228,11 +169,6 @@ class app { uint32_t mNtpRefreshTicker; uint32_t mNtpRefreshInterval; - - bool mWifiSettingsValid; - bool mSettingsValid; - - eep *mEep; uint32_t mUtcTimestamp; bool mUpdateNtp; @@ -240,10 +176,10 @@ class app { ahoywifi *mWifi; web *mWebInst; - sysConfig_t mSysConfig; - config_t mConfig; char mVersion[12]; PayloadType mPayload; + settings mSettings; + settings_t *mConfig; uint16_t mSendTicker; uint8_t mSendLastIvId; diff --git a/src/config/config.h b/src/config/config.h index 66dc22f9..70a36f58 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -25,9 +25,6 @@ // If the next line is uncommented, Ahoy will stay in access point mode all the time //#define AP_ONLY -// protection of the GUI by password -#define GUI_DEF_PASSWORD "" - // timeout for automatic logoff (20 minutes) #define LOGOUT_TIMEOUT (20 * 60 * 60) @@ -47,13 +44,13 @@ // default pinout (GPIO Number) #if defined(ESP32) - #define DEF_CS_PIN 15 - #define DEF_CE_PIN 2 - #define DEF_IRQ_PIN 0 -#else #define DEF_CS_PIN 5 #define DEF_CE_PIN 4 #define DEF_IRQ_PIN 16 +#else + #define DEF_CS_PIN 15 + #define DEF_CE_PIN 2 + #define DEF_IRQ_PIN 0 #endif #define DEF_LED0_PIN 255 // off #define DEF_LED1_PIN 255 // off @@ -65,7 +62,7 @@ #define PACKET_BUFFER_SIZE 30 // number of configurable inverters -#define MAX_NUM_INVERTERS 4 +#define MAX_NUM_INVERTERS 10 // default serial interval #define SERIAL_INTERVAL 5 @@ -121,6 +118,11 @@ // default MQTT topic #define DEF_MQTT_TOPIC "inverter" +// discovery prefix +#define MQTT_DISCOVERY_PREFIX "homeassistant" + +// reconnect delay +#define MQTT_RECONNECT_DELAY 5000 #if __has_include("config_override.h") #include "config_override.h" diff --git a/src/config/eep.h b/src/config/eep.h deleted file mode 100644 index 69aff7f5..00000000 --- a/src/config/eep.h +++ /dev/null @@ -1,161 +0,0 @@ -//----------------------------------------------------------------------------- -// 2022 Ahoy, https://www.mikrocontroller.net/topic/525778 -// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ -//----------------------------------------------------------------------------- - -#ifndef __EEP_H__ -#define __EEP_H__ - -#include "Arduino.h" -#include -#ifdef ESP32 - #include -#endif - -class eep { - public: - eep() { - - #ifdef ESP32 - if(!EEPROM.begin(4096)) { - nvs_flash_init(); - EEPROM.begin(4096); - } - #else - EEPROM.begin(4096); - #endif - - } - ~eep() { - EEPROM.end(); - } - - void read(uint32_t addr, char *str, uint8_t length) { - for(uint8_t i = 0; i < length; i ++) { - *(str++) = (char)EEPROM.read(addr++); - } - } - - void read(uint32_t addr, float *value) { - uint8_t *p = (uint8_t*)value; - for(uint8_t i = 0; i < 4; i ++) { - *(p++) = (uint8_t)EEPROM.read(addr++); - } - } - - void read(uint32_t addr, bool *value) { - uint8_t intVal = 0x00; - intVal = EEPROM.read(addr++); - *value = (intVal == 0x01); - } - - void read(uint32_t addr, uint8_t *value) { - *value = (EEPROM.read(addr++)); - } - - void read(uint32_t addr, uint8_t data[], uint16_t length) { - for(uint16_t i = 0; i < length; i ++) { - *(data++) = EEPROM.read(addr++); - } - } - - void read(uint32_t addr, uint16_t *value) { - *value = (EEPROM.read(addr++) << 8); - *value |= (EEPROM.read(addr++)); - } - - void read(uint32_t addr, uint16_t data[], uint16_t length) { - for(uint16_t i = 0; i < length; i ++) { - *(data) = (EEPROM.read(addr++) << 8); - *(data++) |= (EEPROM.read(addr++)); - } - } - - void read(uint32_t addr, uint32_t *value) { - *value = (EEPROM.read(addr++) << 24); - *value |= (EEPROM.read(addr++) << 16); - *value |= (EEPROM.read(addr++) << 8); - *value |= (EEPROM.read(addr++)); - } - - void read(uint32_t addr, uint64_t *value) { - read(addr, (uint32_t *)value); - *value <<= 32; - uint32_t tmp; - read(addr+4, &tmp); - *value |= tmp; - /**value = (EEPROM.read(addr++) << 56); - *value |= (EEPROM.read(addr++) << 48); - *value |= (EEPROM.read(addr++) << 40); - *value |= (EEPROM.read(addr++) << 32); - *value |= (EEPROM.read(addr++) << 24); - *value |= (EEPROM.read(addr++) << 16); - *value |= (EEPROM.read(addr++) << 8); - *value |= (EEPROM.read(addr++));*/ - } - - void write(uint32_t addr, const char *str, uint8_t length) { - for(uint8_t i = 0; i < length; i ++) { - EEPROM.write(addr++, str[i]); - } - } - - void write(uint32_t addr, uint8_t data[], uint16_t length) { - for(uint16_t i = 0; i < length; i ++) { - EEPROM.write(addr++, data[i]); - } - } - - void write(uint32_t addr, float value) { - uint8_t *p = (uint8_t*)&value; - for(uint8_t i = 0; i < 4; i ++) { - EEPROM.write(addr++, p[i]); - } - } - - void write(uint32_t addr, bool value) { - uint8_t intVal = (value) ? 0x01 : 0x00; - EEPROM.write(addr++, intVal); - } - - void write(uint32_t addr, uint8_t value) { - EEPROM.write(addr++, value); - } - - void write(uint32_t addr, uint16_t value) { - EEPROM.write(addr++, (value >> 8) & 0xff); - EEPROM.write(addr++, (value ) & 0xff); - } - - - void write(uint32_t addr, uint16_t data[], uint16_t length) { - for(uint16_t i = 0; i < length; i ++) { - EEPROM.write(addr++, (data[i] >> 8) & 0xff); - EEPROM.write(addr++, (data[i] ) & 0xff); - } - } - - void write(uint32_t addr, uint32_t value) { - EEPROM.write(addr++, (value >> 24) & 0xff); - EEPROM.write(addr++, (value >> 16) & 0xff); - EEPROM.write(addr++, (value >> 8) & 0xff); - EEPROM.write(addr++, (value ) & 0xff); - } - - void write(uint32_t addr, uint64_t value) { - EEPROM.write(addr++, (value >> 56) & 0xff); - EEPROM.write(addr++, (value >> 48) & 0xff); - EEPROM.write(addr++, (value >> 40) & 0xff); - EEPROM.write(addr++, (value >> 32) & 0xff); - EEPROM.write(addr++, (value >> 24) & 0xff); - EEPROM.write(addr++, (value >> 16) & 0xff); - EEPROM.write(addr++, (value >> 8) & 0xff); - EEPROM.write(addr++, (value ) & 0xff); - } - - void commit(void) { - EEPROM.commit(); - } -}; - -#endif /*__EEP_H__*/ diff --git a/src/config/settings.h b/src/config/settings.h new file mode 100644 index 00000000..3b0791d6 --- /dev/null +++ b/src/config/settings.h @@ -0,0 +1,417 @@ +//----------------------------------------------------------------------------- +// 2022 Ahoy, https://ahoydtu.de +// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ +//----------------------------------------------------------------------------- + +#ifndef __SETTINGS_H__ +#define __SETTINGS_H__ + +#include +#include +#include +#include "../utils/dbg.h" +#include "../defines.h" + +/** + * More info: + * https://arduino-esp8266.readthedocs.io/en/latest/filesystem.html#flash-layout + * */ + +typedef struct { + uint8_t ip[4]; // ip address + uint8_t mask[4]; // sub mask + uint8_t dns1[4]; // dns 1 + uint8_t dns2[4]; // dns 2 + uint8_t gateway[4]; // standard gateway +} cfgIp_t; + +typedef struct { + char deviceName[DEVNAME_LEN]; + char adminPwd[PWD_LEN]; + + // wifi + char stationSsid[SSID_LEN]; + char stationPwd[PWD_LEN]; + + cfgIp_t ip; +} cfgSys_t; + +typedef struct { + uint16_t sendInterval; + uint8_t maxRetransPerPyld; + uint8_t pinCs; + uint8_t pinCe; + uint8_t pinIrq; + uint8_t amplifierPower; +} cfgNrf24_t; + +typedef struct { + char addr[NTP_ADDR_LEN]; + uint16_t port; +} cfgNtp_t; + +typedef struct { + float lat; + float lon; + bool disNightCom; // disable night communication +} cfgSun_t; + +typedef struct { + uint16_t interval; + bool showIv; + bool debug; +} cfgSerial_t; + +typedef struct { + uint8_t led0; // first LED pin + uint8_t led1; // second LED pin +} cfgLed_t; + +typedef struct { + char broker[MQTT_ADDR_LEN]; + uint16_t port; + char user[MQTT_USER_LEN]; + char pwd[MQTT_PWD_LEN]; + char topic[MQTT_TOPIC_LEN]; +} cfgMqtt_t; + +typedef struct { + bool enabled; + char name[MAX_NAME_LENGTH]; + serial_u serial; + uint16_t chMaxPwr[4]; + char chName[4][MAX_NAME_LENGTH]; +} cfgIv_t; + +typedef struct { + bool enabled; + cfgIv_t iv[MAX_NUM_INVERTERS]; +} cfgInst_t; + +typedef struct { + cfgSys_t sys; + cfgNrf24_t nrf; + cfgNtp_t ntp; + cfgSun_t sun; + cfgSerial_t serial; + cfgMqtt_t mqtt; + cfgLed_t led; + cfgInst_t inst; +} settings_t; + +class settings { + public: + settings() {} + + void setup() { + DPRINTLN(DBG_INFO, F("Initializing FS ..")); + + mValid = false; + + LittleFSConfig cfg; + cfg.setAutoFormat(false); + LittleFS.setConfig(cfg); + + if(!LittleFS.begin()) { + DPRINTLN(DBG_INFO, F(".. format ..")); + LittleFS.format(); + if(LittleFS.begin()) + DPRINTLN(DBG_INFO, F(".. success")); + else + DPRINTLN(DBG_INFO, F(".. failed")); + + } + else + DPRINTLN(DBG_INFO, F(" .. done")); + + readSettings(); + } + + // should be used before OTA + void stop() { + LittleFS.end(); + DPRINTLN(DBG_INFO, F("FS stopped")); + } + + void getPtr(settings_t *&cfg) { + cfg = &mCfg; + } + + bool getValid(void) { + return mValid; + } + + void getInfo(uint32_t *used, uint32_t *size) { + FSInfo info; + LittleFS.info(info); + *used = info.usedBytes; + *size = info.totalBytes; + + DPRINTLN(DBG_INFO, F("-- FILESYSTEM INFO --")); + DPRINTLN(DBG_INFO, String(info.usedBytes) + F(" of ") + String(info.totalBytes) + F(" used")); + } + + void readSettings(void) { + loadDefaults(); + File fp = LittleFS.open("/settings.json", "r"); + if(!fp) + DPRINTLN(DBG_WARN, F("failed to load json, using default config")); + else { + DPRINTLN(DBG_INFO, fp.readString()); + fp.seek(0, SeekSet); + DynamicJsonDocument root(4096); + DeserializationError err = deserializeJson(root, fp); + if(!err) { + mValid = true; + jsonWifi(root["wifi"]); + jsonNrf(root["nrf"]); + jsonNtp(root["ntp"]); + jsonSun(root["sun"]); + jsonSerial(root["serial"]); + jsonMqtt(root["mqtt"]); + jsonLed(root["led"]); + jsonInst(root["inst"]); + } + else { + Serial.println(F("failed to parse json, using default config")); + } + + fp.close(); + } + } + + bool saveSettings(void) { + DPRINTLN(DBG_INFO, F("save settings")); + File fp = LittleFS.open("/settings.json", "w"); + if(!fp) { + DPRINTLN(DBG_ERROR, F("can't open settings file!")); + return false; + } + + DynamicJsonDocument json(4096); + JsonObject root = json.to(); + jsonWifi(root.createNestedObject(F("wifi")), true); + jsonNrf(root.createNestedObject(F("nrf")), true); + jsonNtp(root.createNestedObject(F("ntp")), true); + jsonSun(root.createNestedObject(F("sun")), true); + jsonSerial(root.createNestedObject(F("serial")), true); + jsonMqtt(root.createNestedObject(F("mqtt")), true); + jsonLed(root.createNestedObject(F("led")), true); + jsonInst(root.createNestedObject(F("inst")), true); + + if(0 == serializeJson(root, fp)) { + DPRINTLN(DBG_ERROR, F("can't write settings file!")); + return false; + } + fp.close(); + + return true; + } + + bool eraseSettings(bool eraseWifi = false) { + loadDefaults(eraseWifi); + return saveSettings(); + } + + String ip2Str(uint8_t ip[]) { + return String(ip[0]) + F(".") + + String(ip[1]) + F(".") + + String(ip[2]) + F(".") + + String(ip[3]); + } + + void ip2Arr(uint8_t ip[], const char *ipStr) { + char *tmp = new char[strlen(ipStr)]; + strncpy(tmp, ipStr, strlen(ipStr)); + char *p = strtok(tmp, "."); + uint8_t i = 0; + while(NULL != p) { + ip[i++] = atoi(p); + p = strtok(NULL, "."); + } + delete[] tmp; + } + + private: + void loadDefaults(bool wifi = true) { + DPRINTLN(DBG_INFO, F("loadDefaults")); + + memset(&mCfg, 0, sizeof(settings_t)); + if(wifi) { + snprintf(mCfg.sys.stationSsid, SSID_LEN, FB_WIFI_SSID); + snprintf(mCfg.sys.stationPwd, PWD_LEN, FB_WIFI_PWD); + snprintf(mCfg.sys.deviceName, DEVNAME_LEN, DEF_DEVICE_NAME); + } + + mCfg.nrf.sendInterval = SEND_INTERVAL; + mCfg.nrf.maxRetransPerPyld = DEF_MAX_RETRANS_PER_PYLD; + mCfg.nrf.pinCs = DEF_CS_PIN; + mCfg.nrf.pinCe = DEF_CE_PIN; + mCfg.nrf.pinIrq = DEF_IRQ_PIN; + mCfg.nrf.amplifierPower = DEF_AMPLIFIERPOWER & 0x03; + + snprintf(mCfg.ntp.addr, NTP_ADDR_LEN, "%s", DEF_NTP_SERVER_NAME); + mCfg.ntp.port = DEF_NTP_PORT; + + mCfg.sun.lat = 0.0; + mCfg.sun.lon = 0.0; + mCfg.sun.disNightCom = false; + + mCfg.serial.interval = SERIAL_INTERVAL; + mCfg.serial.showIv = false; + mCfg.serial.debug = false; + + mCfg.mqtt.port = DEF_MQTT_PORT; + snprintf(mCfg.mqtt.broker, MQTT_ADDR_LEN, "%s", DEF_MQTT_BROKER); + snprintf(mCfg.mqtt.user, MQTT_USER_LEN, "%s", DEF_MQTT_USER); + snprintf(mCfg.mqtt.pwd, MQTT_PWD_LEN, "%s", DEF_MQTT_PWD); + snprintf(mCfg.mqtt.topic, MQTT_TOPIC_LEN, "%s", DEF_MQTT_TOPIC); + + mCfg.led.led0 = DEF_LED0_PIN; + mCfg.led.led1 = DEF_LED1_PIN; + } + + void jsonWifi(JsonObject obj, bool set = false) { + if(set) { + obj[F("ssid")] = mCfg.sys.stationSsid; + obj[F("pwd")] = mCfg.sys.stationPwd; + obj[F("dev")] = mCfg.sys.deviceName; + obj[F("adm")] = mCfg.sys.adminPwd; + obj[F("ip")] = ip2Str(mCfg.sys.ip.ip); + obj[F("mask")] = ip2Str(mCfg.sys.ip.mask); + obj[F("dns1")] = ip2Str(mCfg.sys.ip.dns1); + obj[F("dns2")] = ip2Str(mCfg.sys.ip.dns2); + obj[F("gtwy")] = ip2Str(mCfg.sys.ip.gateway); + } else { + snprintf(mCfg.sys.stationSsid, SSID_LEN, "%s", obj[F("ssid")].as()); + snprintf(mCfg.sys.stationPwd, PWD_LEN, "%s", obj[F("pwd")].as()); + snprintf(mCfg.sys.deviceName, DEVNAME_LEN, "%s", obj[F("dev")].as()); + snprintf(mCfg.sys.adminPwd, PWD_LEN, "%s", obj[F("adm")].as()); + ip2Arr(mCfg.sys.ip.ip, obj[F("ip")]); + ip2Arr(mCfg.sys.ip.mask, obj[F("mask")]); + ip2Arr(mCfg.sys.ip.dns1, obj[F("dns1")]); + ip2Arr(mCfg.sys.ip.dns2, obj[F("dns2")]); + ip2Arr(mCfg.sys.ip.gateway, obj[F("gtwy")]); + } + } + + void jsonNrf(JsonObject obj, bool set = false) { + if(set) { + obj[F("intvl")] = mCfg.nrf.sendInterval; + obj[F("maxRetry")] = mCfg.nrf.maxRetransPerPyld; + obj[F("cs")] = mCfg.nrf.pinCs; + obj[F("ce")] = mCfg.nrf.pinCe; + obj[F("irq")] = mCfg.nrf.pinIrq; + obj[F("pwr")] = mCfg.nrf.amplifierPower; + } else { + mCfg.nrf.sendInterval = obj[F("intvl")]; + mCfg.nrf.maxRetransPerPyld = obj[F("maxRetry")]; + mCfg.nrf.pinCs = obj[F("cs")]; + mCfg.nrf.pinCe = obj[F("ce")]; + mCfg.nrf.pinIrq = obj[F("irq")]; + mCfg.nrf.amplifierPower = obj[F("pwr")]; + } + } + + void jsonNtp(JsonObject obj, bool set = false) { + if(set) { + obj[F("addr")] = mCfg.ntp.addr; + obj[F("port")] = mCfg.ntp.port; + } else { + snprintf(mCfg.ntp.addr, NTP_ADDR_LEN, "%s", obj[F("addr")].as()); + mCfg.ntp.port = obj[F("port")]; + } + } + + void jsonSun(JsonObject obj, bool set = false) { + if(set) { + obj[F("lat")] = mCfg.sun.lat; + obj[F("lon")] = mCfg.sun.lon; + obj[F("dis")] = mCfg.sun.disNightCom; + } else { + mCfg.sun.lat = obj[F("lat")]; + mCfg.sun.lon = obj[F("lon")]; + mCfg.sun.disNightCom = obj[F("dis")]; + } + } + + void jsonSerial(JsonObject obj, bool set = false) { + if(set) { + obj[F("intvl")] = mCfg.serial.interval; + obj[F("show")] = mCfg.serial.showIv; + obj[F("debug")] = mCfg.serial.debug; + } else { + mCfg.serial.interval = obj[F("intvl")]; + mCfg.serial.showIv = obj[F("show")]; + mCfg.serial.debug = obj[F("debug")]; + } + } + + void jsonMqtt(JsonObject obj, bool set = false) { + if(set) { + obj[F("broker")] = mCfg.mqtt.broker; + obj[F("port")] = mCfg.mqtt.port; + obj[F("user")] = mCfg.mqtt.user; + obj[F("pwd")] = mCfg.mqtt.pwd; + obj[F("topic")] = mCfg.mqtt.topic; + } else { + mCfg.mqtt.port = obj[F("port")]; + snprintf(mCfg.mqtt.broker, MQTT_ADDR_LEN, "%s", obj[F("broker")].as()); + snprintf(mCfg.mqtt.user, MQTT_USER_LEN, "%s", obj[F("user")].as()); + snprintf(mCfg.mqtt.pwd, MQTT_PWD_LEN, "%s", obj[F("pwd")].as()); + snprintf(mCfg.mqtt.topic, MQTT_TOPIC_LEN, "%s", obj[F("topic")].as()); + } + } + + void jsonLed(JsonObject obj, bool set = false) { + if(set) { + obj[F("0")] = mCfg.led.led0; + obj[F("1")] = mCfg.led.led1; + } else { + mCfg.led.led0 = obj[F("0")]; + mCfg.led.led1 = obj[F("1")]; + } + } + + void jsonInst(JsonObject obj, bool set = false) { + if(set) + obj[F("en")] = mCfg.inst.enabled; + else + mCfg.inst.enabled = obj[F("en")]; + + JsonArray ivArr; + if(set) + ivArr = obj.createNestedArray(F("iv")); + for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { + if(set) + jsonIv(ivArr.createNestedObject(), &mCfg.inst.iv[i], true); + else + jsonIv(obj[F("iv")][i], &mCfg.inst.iv[i]); + } + } + + void jsonIv(JsonObject obj, cfgIv_t *cfg, bool set = false) { + if(set) { + obj[F("en")] = cfg->enabled; + obj[F("name")] = cfg->name; + obj[F("sn")] = cfg->serial.u64; + for(uint8_t i = 0; i < 4; i++) { + obj[F("pwr")][i] = cfg->chMaxPwr[i]; + obj[F("chName")][i] = cfg->chName[i]; + } + } else { + cfg->enabled = obj[F("en")]; + snprintf(cfg->name, MAX_NAME_LENGTH, "%s", obj[F("name")].as()); + cfg->serial.u64 = obj[F("sn")]; + for(uint8_t i = 0; i < 4; i++) { + cfg->chMaxPwr[i] = obj[F("pwr")][i]; + snprintf(cfg->chName[i], MAX_NAME_LENGTH, "%s", obj[F("chName")][i].as()); + } + } + } + + settings_t mCfg; + bool mValid; +}; + +#endif /*__SETTINGS_H__*/ diff --git a/src/defines.h b/src/defines.h index 0e76eb8a..0bf9c491 100644 --- a/src/defines.h +++ b/src/defines.h @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 5 -#define VERSION_PATCH 35 +#define VERSION_PATCH 36 //------------------------------------- typedef struct { @@ -63,6 +63,11 @@ typedef enum { RelativPersistent = 257UL // 0x0101 } PowerLimitControlType; +union serial_u { + uint64_t u64; + uint8_t b[8]; +}; + #define MIN_SERIAL_INTERVAL 5 #define MIN_SEND_INTERVAL 15 #define MIN_MQTT_INTERVAL 60 @@ -78,116 +83,14 @@ typedef enum { #define SSID_LEN 32 #define PWD_LEN 64 #define DEVNAME_LEN 16 -#define CRC_LEN 2 // uint16_t -#define DISCLAIMER_LEN 1 -#define STATIC_IP_LEN 16 // 4x uint32_t -#define STATUS_LED_LEN 2 // 2x uint8_t - -#define INV_ADDR_LEN MAX_NUM_INVERTERS * 8 // uint64_t -#define INV_NAME_LEN MAX_NUM_INVERTERS * MAX_NAME_LENGTH // char[] -#define INV_CH_CH_PWR_LEN MAX_NUM_INVERTERS * 2 * 4 // uint16_t (4 channels) -#define INV_CH_CH_NAME_LEN MAX_NUM_INVERTERS * MAX_NAME_LENGTH * 4 // (4 channels) -#define INV_INTERVAL_LEN 2 // uint16_t -#define INV_MAX_RTRY_LEN 1 // uint8_t -#define INV_ENABLED_LEN 1 // uint8_t - -#define CFG_SUN_LEN 9 // 2x float(4+4) + bool(1) - #define NTP_ADDR_LEN 32 // DNS Name #define MQTT_ADDR_LEN 32 // DNS Name #define MQTT_USER_LEN 16 #define MQTT_PWD_LEN 32 -#define MQTT_TOPIC_LEN 32 -#define MQTT_DISCOVERY_PREFIX "homeassistant" -#define MQTT_MAX_PACKET_SIZE 384 -#define MQTT_RECONNECT_DELAY 5000 - -#pragma pack(push) // push current alignment to stack -#pragma pack(1) // set alignment to 1 byte boundary -typedef struct { - char broker[MQTT_ADDR_LEN]; - uint16_t port; - char user[MQTT_USER_LEN]; - char pwd[MQTT_PWD_LEN]; - char topic[MQTT_TOPIC_LEN]; -} mqttConfig_t; -#pragma pack(pop) // restore original alignment from stack - - -#pragma pack(push) -#pragma pack(1) -typedef struct { - char deviceName[DEVNAME_LEN]; - - // wifi - char stationSsid[SSID_LEN]; - char stationPwd[PWD_LEN]; -} sysConfig_t; -#pragma pack(pop) - - -#pragma pack(push) -#pragma pack(1) -typedef struct { - uint8_t ip[4]; // ip address - uint8_t mask[4]; // sub mask - uint8_t dns[4]; // dns - uint8_t gateway[4]; // standard gateway -} staticIp_t; -#pragma pack(pop) - -#pragma pack(push) -#pragma pack(1) -typedef struct { - uint8_t led0; // first led pin - uint8_t led1; // second led pin -} statusLed_t; -#pragma pack(pop) - - -#pragma pack(push) -#pragma pack(1) -typedef struct { - // protection - char password[PWD_LEN]; - - // nrf24 - uint16_t sendInterval; - uint8_t maxRetransPerPyld; - uint8_t pinCs; - uint8_t pinCe; - uint8_t pinIrq; - uint8_t amplifierPower; - - // Disclaimer - bool disclaimer; - - // ntp - char ntpAddr[NTP_ADDR_LEN]; - uint16_t ntpPort; - - // mqtt - mqttConfig_t mqtt; - - // sun - float sunLat; - float sunLon; - bool sunDisNightCom; // disable night communication - - // serial - uint16_t serialInterval; - bool serialShowIv; - bool serialDebug; - - // static ip - staticIp_t staticIp; - - // status LED(s) - statusLed_t led; -} config_t; -#pragma pack(pop) +#define MQTT_TOPIC_LEN 64 +#define MQTT_MAX_PACKET_SIZE 384 typedef struct { uint32_t rxFail; @@ -196,41 +99,4 @@ typedef struct { uint32_t frmCnt; } statistics_t; - -#define CFG_MQTT_LEN MQTT_ADDR_LEN + 2 + MQTT_USER_LEN + MQTT_PWD_LEN +MQTT_TOPIC_LEN -#define CFG_SYS_LEN DEVNAME_LEN + SSID_LEN + PWD_LEN -#define CFG_LEN PWD_LEN + 7 + DISCLAIMER_LEN + NTP_ADDR_LEN + 2 + CFG_MQTT_LEN + CFG_SUN_LEN + 4 + STATIC_IP_LEN + STATUS_LED_LEN - -#define ADDR_START 0 -#define ADDR_CFG_SYS ADDR_START -#define ADDR_WIFI_CRC ADDR_CFG_SYS + CFG_SYS_LEN -#define ADDR_START_SETTINGS ADDR_WIFI_CRC + CRC_LEN - -#define ADDR_CFG ADDR_START_SETTINGS -#define ADDR_CFG_INVERTER ADDR_CFG + CFG_LEN - -#define ADDR_INV_ADDR ADDR_CFG_INVERTER -#define ADDR_INV_NAME ADDR_INV_ADDR + INV_ADDR_LEN -#define ADDR_INV_CH_PWR ADDR_INV_NAME + INV_NAME_LEN -#define ADDR_INV_CH_NAME ADDR_INV_CH_PWR + INV_CH_CH_PWR_LEN -#define ADDR_INV_INTERVAL ADDR_INV_CH_NAME + INV_CH_CH_NAME_LEN -#define ADDR_INV_MAX_RTRY ADDR_INV_INTERVAL + INV_INTERVAL_LEN -#define ADDR_INV_ENABLED ADDR_INV_MAX_RTRY + INV_MAX_RTRY_LEN - -#define ADDR_NEXT ADDR_INV_MAX_RTRY + INV_ENABLED_LEN - - -#define ADDR_SETTINGS_CRC ADDR_NEXT + 2 - -#if(ADDR_SETTINGS_CRC <= ADDR_NEXT) -#pragma error "address overlap! (ADDR_SETTINGS_CRC="+ ADDR_SETTINGS_CRC +", ADDR_NEXT="+ ADDR_NEXT +")" -#endif - -#if(ADDR_SETTINGS_CRC >= 4096 - CRC_LEN) -#pragma error "EEPROM size exceeded! (ADDR_SETTINGS_CRC="+ ADDR_SETTINGS_CRC +", CRC_LEN="+ CRC_LEN +")" -#pragma error "Configure less inverters? (MAX_NUM_INVERTERS=" + MAX_NUM_INVERTERS +")" -#endif - - - #endif /*__DEFINES_H__*/ diff --git a/src/hm/hmDefines.h b/src/hm/hmDefines.h index 6cf6f4b7..21af98f0 100644 --- a/src/hm/hmDefines.h +++ b/src/hm/hmDefines.h @@ -9,13 +9,6 @@ #include "../utils/dbg.h" #include - -union serial_u { - uint64_t u64; - uint8_t b[8]; -}; - - // units enum {UNIT_V = 0, UNIT_A, UNIT_W, UNIT_WH, UNIT_KWH, UNIT_HZ, UNIT_C, UNIT_PCT, UNIT_VAR, UNIT_NONE}; const char* const units[] = {"V", "A", "W", "Wh", "kWh", "Hz", "°C", "%", "var", ""}; diff --git a/src/hm/hmInverter.h b/src/hm/hmInverter.h index 1587335a..f38a36cb 100644 --- a/src/hm/hmInverter.h +++ b/src/hm/hmInverter.h @@ -14,6 +14,7 @@ #include "hmDefines.h" #include #include +#include "../config/settings.h" /** * For values which are of interest and not transmitted by the inverter can be @@ -104,8 +105,8 @@ const calcFunc_t calcFunctions[] = { template class Inverter { public: + cfgIv_t *config; // stored settings uint8_t id; // unique id - char name[MAX_NAME_LENGTH]; // human readable name, eg. "HM-600.1" uint8_t type; // integer which refers to inverter type uint16_t alarmMesIndex; // Last recorded Alarm Message Index uint16_t fwVersion; // Firmware Version from Info Command Request @@ -113,15 +114,12 @@ class Inverter { float actPowerLimit; // actual power limit uint8_t devControlCmd; // carries the requested cmd bool devControlRequest; // true if change needed - serial_u serial; // serial number as on barcode serial_u radioId; // id converted to modbus uint8_t channels; // number of PV channels (1-4) record_t recordMeas; // structure for measured values record_t recordInfo; // structure for info values record_t recordConfig; // structure for system config values record_t recordAlarm; // structure for alarm values - uint16_t chMaxPwr[4]; // maximum power of the modules (Wp) - char chName[4][MAX_NAME_LENGTH]; // human readable name for channels String lastAlarmMsg; bool initialized; // needed to check if the inverter was correctly added (ESP32 specific - union types are never null) @@ -150,14 +148,14 @@ class Inverter { void setQueuedCmdFinished() { if (!_commandQueue.empty()) { // Will destroy CommandAbstract Class Object (?) - _commandQueue.pop(); + _commandQueue.pop(); } } void clearCmdQueue() { while (!_commandQueue.empty()) { // Will destroy CommandAbstract Class Object (?) - _commandQueue.pop(); + _commandQueue.pop(); } } @@ -185,8 +183,6 @@ class Inverter { initAssignment(&recordConfig, SystemConfigPara); initAssignment(&recordAlarm, AlarmData); toRadioId(); - memset(name, 0, MAX_NAME_LENGTH); - memset(chName, 0, MAX_NAME_LENGTH * 4); initialized = true; } @@ -484,10 +480,10 @@ class Inverter { void toRadioId(void) { DPRINTLN(DBG_VERBOSE, F("hmInverter.h:toRadioId")); radioId.u64 = 0ULL; - radioId.b[4] = serial.b[0]; - radioId.b[3] = serial.b[1]; - radioId.b[2] = serial.b[2]; - radioId.b[1] = serial.b[3]; + radioId.b[4] = config->serial.b[0]; + radioId.b[3] = config->serial.b[1]; + radioId.b[2] = config->serial.b[2]; + radioId.b[1] = config->serial.b[3]; radioId.b[0] = 0x01; } }; @@ -583,8 +579,8 @@ static T calcIrradiation(Inverter<> *iv, uint8_t arg0) { if(NULL != iv) { record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); uint8_t pos = iv->getPosByChFld(arg0, FLD_PDC, rec); - if(iv->chMaxPwr[arg0-1] > 0) - return iv->getValue(pos, rec) / iv->chMaxPwr[arg0-1] * 100.0f; + if(iv->config->chMaxPwr[arg0-1] > 0) + return iv->getValue(pos, rec) / iv->config->chMaxPwr[arg0-1] * 100.0f; } return 0.0; } diff --git a/src/hm/hmSystem.h b/src/hm/hmSystem.h index ee7c180c..53b6e85d 100644 --- a/src/hm/hmSystem.h +++ b/src/hm/hmSystem.h @@ -20,7 +20,7 @@ class HmSystem { RadioType Radio; typedef BUFFER BufferType; BufferType BufCtrl; - //DevControlCmdType DevControlCmd; + //DevControlCmdType DevControlCmd; HmSystem() { mNumInv = 0; @@ -37,7 +37,18 @@ class HmSystem { Radio.setup(&BufCtrl, ampPwr, irqPin, cePin, csPin); } - INVERTERTYPE *addInverter(const char *name, uint64_t serial, uint16_t chMaxPwr[]) { + void addInverters(cfgInst_t *config) { + Inverter<> *iv; + for (uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) { + iv = addInverter(&config->iv[i]); + if (0ULL != config->iv[i].serial.u64) { + if (NULL != iv) + DPRINTLN(DBG_INFO, "added inverter " + String(iv->config->serial.u64, HEX)); + } + } + } + + INVERTERTYPE *addInverter(cfgIv_t *config) { DPRINTLN(DBG_VERBOSE, F("hmSystem.h:addInverter")); if(MAX_INVERTER <= mNumInv) { DPRINT(DBG_WARN, F("max number of inverters reached!")); @@ -45,18 +56,17 @@ class HmSystem { } INVERTERTYPE *p = &mInverter[mNumInv]; p->id = mNumInv; - p->serial.u64 = serial; - memcpy(p->chMaxPwr, chMaxPwr, (4*2)); - DPRINT(DBG_VERBOSE, "SERIAL: " + String(p->serial.b[5], HEX)); - DPRINTLN(DBG_VERBOSE, " " + String(p->serial.b[4], HEX)); - if(p->serial.b[5] == 0x11) { - switch(p->serial.b[4]) { + p->config = config; + DPRINT(DBG_VERBOSE, "SERIAL: " + String(p->config->serial.b[5], HEX)); + DPRINTLN(DBG_VERBOSE, " " + String(p->config->serial.b[4], HEX)); + if(p->config->serial.b[5] == 0x11) { + switch(p->config->serial.b[4]) { case 0x21: p->type = INV_TYPE_1CH; break; case 0x41: p->type = INV_TYPE_2CH; break; case 0x61: p->type = INV_TYPE_4CH; break; - default: + default: DPRINT(DBG_ERROR, F("unknown inverter type: 11")); - DPRINTLN(DBG_ERROR, String(p->serial.b[4], HEX)); + DPRINTLN(DBG_ERROR, String(p->config->serial.b[4], HEX)); break; } } @@ -64,8 +74,6 @@ class HmSystem { DPRINTLN(DBG_ERROR, F("inverter type can't be detected!")); p->init(); - uint8_t len = (uint8_t)strlen(name); - strncpy(p->name, name, (len > MAX_NAME_LENGTH) ? MAX_NAME_LENGTH : len); mNumInv ++; return p; @@ -76,10 +84,10 @@ class HmSystem { INVERTERTYPE *p; for(uint8_t i = 0; i < mNumInv; i++) { p = &mInverter[i]; - if((p->serial.b[3] == buf[0]) - && (p->serial.b[2] == buf[1]) - && (p->serial.b[1] == buf[2]) - && (p->serial.b[0] == buf[3])) + if((p->config->serial.b[3] == buf[0]) + && (p->config->serial.b[2] == buf[1]) + && (p->config->serial.b[1] == buf[2]) + && (p->config->serial.b[0] == buf[3])) return p; } return NULL; @@ -89,7 +97,7 @@ class HmSystem { DPRINTLN(DBG_VERBOSE, F("hmSystem.h:getInverterByPos")); if(pos >= MAX_INVERTER) return NULL; - else if((mInverter[pos].initialized && mInverter[pos].serial.u64 != 0ULL) || false == check) + else if((mInverter[pos].initialized && mInverter[pos].config->serial.u64 != 0ULL) || false == check) return &mInverter[pos]; else return NULL; diff --git a/src/platformio.ini b/src/platformio.ini index 73c42da7..ac38b9e8 100644 --- a/src/platformio.ini +++ b/src/platformio.ini @@ -10,12 +10,13 @@ [platformio] src_dir = . +include_dir = . [env] framework = arduino +board_build.filesystem = littlefs -build_flags = - -include "config.h" +;build_flags = ; ;;;;; Possible Debug options ;;;;;; ; https://docs.platformio.org/en/latest/platforms/espressif8266.html#debug-level ;-DDEBUG_ESP_PORT=Serial @@ -43,6 +44,7 @@ lib_deps = ;esp8266/SPI ;esp8266/Ticker + [env:esp8266-release] platform = espressif8266 board = esp12e diff --git a/src/utils/ahoyTimer.h b/src/utils/ahoyTimer.h index 41a4b09a..5c960a34 100644 --- a/src/utils/ahoyTimer.h +++ b/src/utils/ahoyTimer.h @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// 2022 Ahoy, https://www.mikrocontroller.net/topic/525778 +// 2022 Ahoy, https://ahoydtu.de // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ //----------------------------------------------------------------------------- diff --git a/src/utils/sun.h b/src/utils/sun.h index 7c6d035b..476ad0ff 100644 --- a/src/utils/sun.h +++ b/src/utils/sun.h @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// 2022 Ahoy, https://www.mikrocontroller.net/topic/525778 +// 2022 Ahoy, https://ahoydtu.de // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ //----------------------------------------------------------------------------- diff --git a/src/web/html/serial.html b/src/web/html/serial.html index 7581a69e..ada7657d 100644 --- a/src/web/html/serial.html +++ b/src/web/html/serial.html @@ -128,7 +128,7 @@ }); document.getElementById("scroll").addEventListener("click", function() { mAutoScroll = !mAutoScroll; - this.value = (mAutoScroll) ? "autoscroll" : "manual scoll"; + this.value = (mAutoScroll) ? "autoscroll" : "manual scroll"; }); if (!!window.EventSource) { diff --git a/src/web/html/setup.html b/src/web/html/setup.html index 2f9febd9..c86b7b4b 100644 --- a/src/web/html/setup.html +++ b/src/web/html/setup.html @@ -67,8 +67,10 @@ - - + + + + @@ -336,7 +338,7 @@ } function parseStaticIp(obj) { - for(var i of [["ipAddr", "ip"], ["ipMask", "mask"], ["ipDns", "dns"], ["ipGateway", "gateway"]]) + for(var i of [["ipAddr", "ip"], ["ipMask", "mask"], ["ipDns1", "dns1"], ["ipDns2", "dns2"], ["ipGateway", "gateway"]]) if(null != obj[i[1]]) document.getElementsByName(i[0])[0].value = obj[i[1]]; } diff --git a/src/web/mqtt.h b/src/web/mqtt.h index cc3de390..8402599b 100644 --- a/src/web/mqtt.h +++ b/src/web/mqtt.h @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// 2022 Ahoy, https://www.mikrocontroller.net/topic/525778 +// 2022 Ahoy, https://ahoydtu.de // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/ //----------------------------------------------------------------------------- @@ -21,9 +21,9 @@ #include "../utils/ahoyTimer.h" #include "../config/config.h" #include +#include #include "../defines.h" #include "../hm/hmSystem.h" -#include template class mqtt { @@ -34,18 +34,16 @@ class mqtt { mLastReconnect = 0; mTxCnt = 0; - - memset(mDevName, 0, DEVNAME_LEN); } ~mqtt() { } - void setup(mqttConfig_t *cfg, const char *devname, const char *version, HMSYSTEM *sys, uint32_t *utcTs) { + void setup(cfgMqtt_t *cfg, const char *devName, const char *version, HMSYSTEM *sys, uint32_t *utcTs) { DPRINTLN(DBG_VERBOSE, F("mqtt.h:setup")); mAddressSet = true; - snprintf(mDevName, DEVNAME_LEN, "%s", devname); mCfg = cfg; + mDevName = devName; mSys = sys; mUtcTimestamp = utcTs; @@ -55,7 +53,7 @@ class mqtt { setCallback(std::bind(&mqtt::cbMqtt, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); sendMsg("version", version); - sendMsg("device", devname); + sendMsg("device", devName); sendMsg("uptime", "0"); } @@ -76,8 +74,8 @@ class mqtt { void sendMsg(const char *topic, const char *msg) { //DPRINTLN(DBG_VERBOSE, F("mqtt.h:sendMsg")); if(mAddressSet) { - char top[64]; - snprintf(top, 64, "%s/%s", mCfg->topic, topic); + char top[66]; + snprintf(top, 66, "%s/%s", mCfg->topic, topic); sendMsg2(top, msg, false); } } @@ -118,22 +116,22 @@ class mqtt { if (NULL != iv) { record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); DynamicJsonDocument deviceDoc(128); - deviceDoc["name"] = iv->name; - deviceDoc["ids"] = String(iv->serial.u64, HEX); + deviceDoc["name"] = iv->config->name; + deviceDoc["ids"] = String(iv->config->serial.u64, HEX); deviceDoc["cu"] = F("http://") + String(WiFi.localIP().toString()); deviceDoc["mf"] = "Hoymiles"; - deviceDoc["mdl"] = iv->name; + deviceDoc["mdl"] = iv->config->name; JsonObject deviceObj = deviceDoc.as(); DynamicJsonDocument doc(384); for (uint8_t i = 0; i < rec->length; i++) { if (rec->assign[i].ch == CH0) { - snprintf(name, 32, "%s %s", iv->name, iv->getFieldName(i, rec)); + snprintf(name, 32, "%s %s", iv->config->name, iv->getFieldName(i, rec)); } else { - snprintf(name, 32, "%s CH%d %s", iv->name, rec->assign[i].ch, iv->getFieldName(i, rec)); + snprintf(name, 32, "%s CH%d %s", iv->config->name, rec->assign[i].ch, iv->getFieldName(i, rec)); } - snprintf(stateTopic, 64, "%s/%s/ch%d/%s", topic, iv->name, rec->assign[i].ch, iv->getFieldName(i, rec)); - snprintf(discoveryTopic, 64, "%s/sensor/%s/ch%d_%s/config", MQTT_DISCOVERY_PREFIX, iv->name, rec->assign[i].ch, iv->getFieldName(i, rec)); + snprintf(stateTopic, 64, "%s/%s/ch%d/%s", topic, iv->config->name, rec->assign[i].ch, iv->getFieldName(i, rec)); + snprintf(discoveryTopic, 64, "%s/sensor/%s/ch%d_%s/config", MQTT_DISCOVERY_PREFIX, iv->config->name, rec->assign[i].ch, iv->getFieldName(i, rec)); snprintf(uniq_id, 32, "ch%d_%s", rec->assign[i].ch, iv->getFieldName(i, rec)); const char *devCls = getFieldDeviceClass(rec->assign[i].fieldId); const char *stateCls = getFieldStateClass(rec->assign[i].fieldId); @@ -141,7 +139,7 @@ class mqtt { doc["name"] = name; doc["stat_t"] = stateTopic; doc["unit_of_meas"] = iv->getUnit(i, rec); - doc["uniq_id"] = String(iv->serial.u64, HEX) + "_" + uniq_id; + doc["uniq_id"] = String(iv->config->serial.u64, HEX) + "_" + uniq_id; doc["dev"] = deviceObj; doc["exp_aft"] = MQTT_INTERVAL + 5; // add 5 sec if connection is bad or ESP too slow @TODO: stimmt das wirklich als expire!? if (devCls != NULL) @@ -248,7 +246,7 @@ class mqtt { if (MQTT_STATUS_AVAIL_PROD == status) status = MQTT_STATUS_AVAIL_NOT_PROD; } - snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/available_text", iv->name); + snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/available_text", iv->config->name); snprintf(val, 40, "%s%s%s%s", (status == MQTT_STATUS_NOT_AVAIL_NOT_PROD) ? "not yet " : "", "available and ", @@ -257,11 +255,11 @@ class mqtt { ); sendMsg(topic, val); - snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/available", iv->name); + snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/available", iv->config->name); snprintf(val, 40, "%d", status); sendMsg(topic, val); - snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/last_success", iv->name); + snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/last_success", iv->config->name); snprintf(val, 40, "%i", iv->getLastTs(rec) * 1000); sendMsg(topic, val); } @@ -269,7 +267,7 @@ class mqtt { // data if(iv->isAvailable(*mUtcTimestamp, rec)) { for (uint8_t i = 0; i < rec->length; i++) { - snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/ch%d/%s", iv->name, rec->assign[i].ch, fields[rec->assign[i].fieldId]); + snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/ch%d/%s", iv->config->name, rec->assign[i].ch, fields[rec->assign[i].fieldId]); snprintf(val, 40, "%.3f", iv->getValue(i, rec)); sendMsg(topic, val); @@ -435,8 +433,8 @@ class mqtt { uint32_t *mUtcTimestamp; bool mAddressSet; - mqttConfig_t *mCfg; - char mDevName[DEVNAME_LEN]; + cfgMqtt_t *mCfg; + const char *mDevName; uint32_t mLastReconnect; uint32_t mTxCnt; std::queue mSendList; diff --git a/src/web/web.cpp b/src/web/web.cpp index 50e6fcb0..d762751b 100644 --- a/src/web/web.cpp +++ b/src/web/web.cpp @@ -26,15 +26,14 @@ const char* const pinArgNames[] = {"pinCs", "pinCe", "pinIrq", "pinLed0", "pinLed1"}; //----------------------------------------------------------------------------- -web::web(app *main, sysConfig_t *sysCfg, config_t *config, statistics_t *stat, char version[]) { +web::web(app *main, settings_t *config, statistics_t *stat, char version[]) { mMain = main; - mSysCfg = sysCfg; mConfig = config; mStat = stat; mVersion = version; mWeb = new AsyncWebServer(80); mEvts = new AsyncEventSource("/events"); - mApi = new webApi(mWeb, main, sysCfg, config, stat, version); + mApi = new webApi(mWeb, main, config, stat, version); mProtected = true; mLogoutTimeout = 0; @@ -151,7 +150,7 @@ void web::onLogin(AsyncWebServerRequest *request) { DPRINTLN(DBG_VERBOSE, F("onLogin")); if(request->args() > 0) { - if(String(request->arg("pwd")) == String(mConfig->password)) { + if(String(request->arg("pwd")) == String(mConfig->sys.adminPwd)) { mProtected = false; request->redirect("/"); } @@ -260,7 +259,7 @@ void web::showErase(AsyncWebServerRequest *request) { } DPRINTLN(DBG_VERBOSE, F("showErase")); - mMain->eraseSettings(); + mMain->eraseSettings(false); onReboot(request); } @@ -277,9 +276,11 @@ void web::showFactoryRst(AsyncWebServerRequest *request) { int refresh = 3; if(request->args() > 0) { if(request->arg("reset").toInt() == 1) { - mMain->eraseSettings(true); - content = F("factory reset: success\n\nrebooting ... "); refresh = 10; + if(mMain->eraseSettings(true)) + content = F("factory reset: success\n\nrebooting ... "); + else + content = F("factory reset: failed\n\nrebooting ... "); } else { content = F("factory reset: aborted"); @@ -328,36 +329,40 @@ void web::showSave(AsyncWebServerRequest *request) { // general if(request->arg("ssid") != "") - request->arg("ssid").toCharArray(mSysCfg->stationSsid, SSID_LEN); + request->arg("ssid").toCharArray(mConfig->sys.stationSsid, SSID_LEN); if(request->arg("pwd") != "{PWD}") - request->arg("pwd").toCharArray(mSysCfg->stationPwd, PWD_LEN); + request->arg("pwd").toCharArray(mConfig->sys.stationPwd, PWD_LEN); if(request->arg("device") != "") - request->arg("device").toCharArray(mSysCfg->deviceName, DEVNAME_LEN); + request->arg("device").toCharArray(mConfig->sys.deviceName, DEVNAME_LEN); if(request->arg("adminpwd") != "{PWD}") { - request->arg("adminpwd").toCharArray(mConfig->password, PWD_LEN); - mProtected = (strlen(mConfig->password) > 0); + request->arg("adminpwd").toCharArray(mConfig->sys.adminPwd, PWD_LEN); + mProtected = (strlen(mConfig->sys.adminPwd) > 0); } // static ip if(request->arg("ipAddr") != "") { request->arg("ipAddr").toCharArray(buf, SSID_LEN); - ip2Arr(mConfig->staticIp.ip, buf); + ip2Arr(mConfig->sys.ip.ip, buf); if(request->arg("ipMask") != "") { request->arg("ipMask").toCharArray(buf, SSID_LEN); - ip2Arr(mConfig->staticIp.mask, buf); + ip2Arr(mConfig->sys.ip.mask, buf); } - if(request->arg("ipDns") != "") { - request->arg("ipDns").toCharArray(buf, SSID_LEN); - ip2Arr(mConfig->staticIp.dns, buf); + if(request->arg("ipDns1") != "") { + request->arg("ipDns1").toCharArray(buf, SSID_LEN); + ip2Arr(mConfig->sys.ip.dns1, buf); + } + if(request->arg("ipDns2") != "") { + request->arg("ipDns2").toCharArray(buf, SSID_LEN); + ip2Arr(mConfig->sys.ip.dns2, buf); } if(request->arg("ipGateway") != "") { request->arg("ipGateway").toCharArray(buf, SSID_LEN); - ip2Arr(mConfig->staticIp.gateway, buf); + ip2Arr(mConfig->sys.ip.gateway, buf); } } else - memset(&mConfig->staticIp, 0, sizeof(staticIp_t)); + memset(&mConfig->sys.ip.ip, 0, 4); // inverter @@ -368,8 +373,8 @@ void web::showSave(AsyncWebServerRequest *request) { request->arg("inv" + String(i) + "Addr").toCharArray(buf, 20); if(strlen(buf) == 0) memset(buf, 0, 20); - iv->serial.u64 = mMain->Serial2u64(buf); - switch(iv->serial.b[4]) { + iv->config->serial.u64 = mMain->Serial2u64(buf); + switch(iv->config->serial.b[4]) { case 0x21: iv->type = INV_TYPE_1CH; iv->channels = 1; break; case 0x41: iv->type = INV_TYPE_2CH; iv->channels = 2; break; case 0x61: iv->type = INV_TYPE_4CH; iv->channels = 4; break; @@ -377,59 +382,54 @@ void web::showSave(AsyncWebServerRequest *request) { } // name - request->arg("inv" + String(i) + "Name").toCharArray(iv->name, MAX_NAME_LENGTH); + request->arg("inv" + String(i) + "Name").toCharArray(iv->config->name, MAX_NAME_LENGTH); // max channel power / name for(uint8_t j = 0; j < 4; j++) { - iv->chMaxPwr[j] = request->arg("inv" + String(i) + "ModPwr" + String(j)).toInt() & 0xffff; - request->arg("inv" + String(i) + "ModName" + String(j)).toCharArray(iv->chName[j], MAX_NAME_LENGTH); + iv->config->chMaxPwr[j] = request->arg("inv" + String(i) + "ModPwr" + String(j)).toInt() & 0xffff; + request->arg("inv" + String(i) + "ModName" + String(j)).toCharArray(iv->config->chName[j], MAX_NAME_LENGTH); } iv->initialized = true; } + if(request->arg("invInterval") != "") - mConfig->sendInterval = request->arg("invInterval").toInt(); + mConfig->nrf.sendInterval = request->arg("invInterval").toInt(); if(request->arg("invRetry") != "") - mConfig->maxRetransPerPyld = request->arg("invRetry").toInt(); - - // Disclaimer - if(request->arg("disclaimer") != "") - mConfig->disclaimer = strcmp("true", request->arg("disclaimer").c_str()) == 0 ? true : false; - DPRINTLN(DBG_INFO, request->arg("disclaimer").c_str()); + mConfig->nrf.maxRetransPerPyld = request->arg("invRetry").toInt(); // pinout uint8_t pin; for(uint8_t i = 0; i < 5; i ++) { pin = request->arg(String(pinArgNames[i])).toInt(); switch(i) { - default: mConfig->pinCs = ((pin != 0xff) ? pin : DEF_CS_PIN); break; - case 1: mConfig->pinCe = ((pin != 0xff) ? pin : DEF_CE_PIN); break; - case 2: mConfig->pinIrq = ((pin != 0xff) ? pin : DEF_IRQ_PIN); break; + default: mConfig->nrf.pinCs = ((pin != 0xff) ? pin : DEF_CS_PIN); break; + case 1: mConfig->nrf.pinCe = ((pin != 0xff) ? pin : DEF_CE_PIN); break; + case 2: mConfig->nrf.pinIrq = ((pin != 0xff) ? pin : DEF_IRQ_PIN); break; case 3: mConfig->led.led0 = pin; break; case 4: mConfig->led.led1 = pin; break; } } // nrf24 amplifier power - mConfig->amplifierPower = request->arg("rf24Power").toInt() & 0x03; + mConfig->nrf.amplifierPower = request->arg("rf24Power").toInt() & 0x03; // ntp if(request->arg("ntpAddr") != "") { - request->arg("ntpAddr").toCharArray(mConfig->ntpAddr, NTP_ADDR_LEN); - mConfig->ntpPort = request->arg("ntpPort").toInt() & 0xffff; + request->arg("ntpAddr").toCharArray(mConfig->ntp.addr, NTP_ADDR_LEN); + mConfig->ntp.port = request->arg("ntpPort").toInt() & 0xffff; } // sun if(request->arg("sunLat") == "" || (request->arg("sunLon") == "")) { - mConfig->sunLat = 0.0; - mConfig->sunLon = 0.0; - mConfig->sunDisNightCom = false; + mConfig->sun.lat = 0.0; + mConfig->sun.lon = 0.0; + mConfig->sun.disNightCom = false; } else { - mConfig->sunLat = request->arg("sunLat").toFloat(); - mConfig->sunLon = request->arg("sunLon").toFloat(); - mConfig->sunDisNightCom = (request->arg("sunDisNightCom") == "on"); + mConfig->sun.lat = request->arg("sunLat").toFloat(); + mConfig->sun.lon = request->arg("sunLon").toFloat(); + mConfig->sun.disNightCom = (request->arg("sunDisNightCom") == "on"); } - // mqtt if(request->arg("mqttAddr") != "") { String addr = request->arg("mqttAddr"); @@ -444,15 +444,14 @@ void web::showSave(AsyncWebServerRequest *request) { // serial console if(request->arg("serIntvl") != "") { - mConfig->serialInterval = request->arg("serIntvl").toInt() & 0xffff; + mConfig->serial.interval = request->arg("serIntvl").toInt() & 0xffff; - mConfig->serialDebug = (request->arg("serDbg") == "on"); - mConfig->serialShowIv = (request->arg("serEn") == "on"); + mConfig->serial.debug = (request->arg("serDbg") == "on"); + mConfig->serial.showIv = (request->arg("serEn") == "on"); // Needed to log TX buffers to serial console - mMain->mSys->Radio.mSerialDebug = mConfig->serialDebug; + mMain->mSys->Radio.mSerialDebug = mConfig->serial.debug; } - - mMain->saveValues(); + mMain->saveSettings(); if(request->arg("reboot") == "on") onReboot(request); @@ -701,7 +700,7 @@ void web::showMetrics(void) { String metrics; char headline[80]; - snprintf(headline, 80, "ahoy_solar_info{version=\"%s\",image=\"\",devicename=\"%s\"} 1", mVersion, mSysCfg->deviceName); + snprintf(headline, 80, "ahoy_solar_info{version=\"%s\",image=\"\",devicename=\"%s\"} 1", mVersion, mconfig->sys.deviceName); metrics += "# TYPE ahoy_solar_info gauge\n" + String(headline) + "\n"; for(uint8_t id = 0; id < mMain->mSys->getNumInverters(); id++) { diff --git a/src/web/web.h b/src/web/web.h index 6e32f8b6..582b5421 100644 --- a/src/web/web.h +++ b/src/web/web.h @@ -24,7 +24,7 @@ class webApi; class web { public: - web(app *main, sysConfig_t *sysCfg, config_t *config, statistics_t *stat, char version[]); + web(app *main, settings_t *config, statistics_t *stat, char version[]); ~web() {} void setup(void); @@ -85,8 +85,7 @@ class web { bool mProtected; uint32_t mLogoutTimeout; - config_t *mConfig; - sysConfig_t *mSysCfg; + settings_t *mConfig; statistics_t *mStat; char *mVersion; app *mMain; diff --git a/src/web/webApi.cpp b/src/web/webApi.cpp index 9e094386..f445f176 100644 --- a/src/web/webApi.cpp +++ b/src/web/webApi.cpp @@ -11,10 +11,9 @@ #include "webApi.h" //----------------------------------------------------------------------------- -webApi::webApi(AsyncWebServer *srv, app *app, sysConfig_t *sysCfg, config_t *config, statistics_t *stat, char version[]) { +webApi::webApi(AsyncWebServer *srv, app *app, settings_t *config, statistics_t *stat, char version[]) { mSrv = srv; mApp = app; - mSysCfg = sysCfg; mConfig = config; mStat = stat; mVersion = version; @@ -143,8 +142,8 @@ void webApi::onDwnldSetup(AsyncWebServerRequest *request) { //----------------------------------------------------------------------------- void webApi::getSysInfo(JsonObject obj) { - obj[F("ssid")] = mSysCfg->stationSsid; - obj[F("device_name")] = mSysCfg->deviceName; + obj[F("ssid")] = mConfig->sys.stationSsid; + obj[F("device_name")] = mConfig->sys.deviceName; obj[F("version")] = String(mVersion); obj[F("build")] = String(AUTO_GIT_HASH); obj[F("ts_uptime")] = mApp->getUptime(); @@ -153,8 +152,7 @@ void webApi::getSysInfo(JsonObject obj) { obj[F("ts_sunset")] = mApp->getSunset(); obj[F("ts_sun_upd")] = mApp->getLatestSunTimestamp(); obj[F("wifi_rssi")] = WiFi.RSSI(); - obj[F("disclaimer")] = mConfig->disclaimer; - obj[F("pwd_set")] = (strlen(mConfig->password) > 0); + obj[F("pwd_set")] = (strlen(mConfig->sys.adminPwd) > 0); #if defined(ESP32) obj[F("esp_type")] = F("ESP32"); #else @@ -221,19 +219,19 @@ void webApi::getInverterList(JsonObject obj) { if(NULL != iv) { JsonObject obj2 = invArr.createNestedObject(); obj2[F("id")] = i; - obj2[F("name")] = String(iv->name); - obj2[F("serial")] = String(iv->serial.u64, HEX); + obj2[F("name")] = String(iv->config->name); + obj2[F("serial")] = String(iv->config->serial.u64, HEX); obj2[F("channels")] = iv->channels; obj2[F("version")] = String(iv->fwVersion); for(uint8_t j = 0; j < iv->channels; j ++) { - obj2[F("ch_max_power")][j] = iv->chMaxPwr[j]; - obj2[F("ch_name")][j] = iv->chName[j]; + obj2[F("ch_max_power")][j] = iv->config->chMaxPwr[j]; + obj2[F("ch_name")][j] = iv->config->chName[j]; } } } - obj[F("interval")] = String(mConfig->sendInterval); - obj[F("retries")] = String(mConfig->maxRetransPerPyld); + obj[F("interval")] = String(mConfig->nrf.sendInterval); + obj[F("retries")] = String(mConfig->nrf.maxRetransPerPyld); obj[F("max_num_inverters")] = MAX_NUM_INVERTERS; } @@ -250,23 +248,23 @@ void webApi::getMqtt(JsonObject obj) { //----------------------------------------------------------------------------- void webApi::getNtp(JsonObject obj) { - obj[F("addr")] = String(mConfig->ntpAddr); - obj[F("port")] = String(mConfig->ntpPort); + obj[F("addr")] = String(mConfig->ntp.addr); + obj[F("port")] = String(mConfig->ntp.port); } //----------------------------------------------------------------------------- void webApi::getSun(JsonObject obj) { - obj[F("lat")] = mConfig->sunLat ? String(mConfig->sunLat, 5) : ""; - obj[F("lon")] = mConfig->sunLat ? String(mConfig->sunLon, 5) : ""; - obj[F("disnightcom")] = mConfig->sunDisNightCom; + obj[F("lat")] = mConfig->sun.lat ? String(mConfig->sun.lat, 5) : ""; + obj[F("lon")] = mConfig->sun.lat ? String(mConfig->sun.lon, 5) : ""; + obj[F("disnightcom")] = mConfig->sun.disNightCom; } //----------------------------------------------------------------------------- void webApi::getPinout(JsonObject obj) { - obj[F("cs")] = mConfig->pinCs; - obj[F("ce")] = mConfig->pinCe; - obj[F("irq")] = mConfig->pinIrq; + obj[F("cs")] = mConfig->nrf.pinCs; + obj[F("ce")] = mConfig->nrf.pinCe; + obj[F("irq")] = mConfig->nrf.pinIrq; obj[F("led0")] = mConfig->led.led0; obj[F("led1")] = mConfig->led.led1; } @@ -274,25 +272,26 @@ void webApi::getPinout(JsonObject obj) { //----------------------------------------------------------------------------- void webApi::getRadio(JsonObject obj) { - obj[F("power_level")] = mConfig->amplifierPower; + obj[F("power_level")] = mConfig->nrf.amplifierPower; } //----------------------------------------------------------------------------- void webApi::getSerial(JsonObject obj) { - obj[F("interval")] = (uint16_t)mConfig->serialInterval; - obj[F("show_live_data")] = mConfig->serialShowIv; - obj[F("debug")] = mConfig->serialDebug; + obj[F("interval")] = (uint16_t)mConfig->serial.interval; + obj[F("show_live_data")] = mConfig->serial.showIv; + obj[F("debug")] = mConfig->serial.debug; } //----------------------------------------------------------------------------- void webApi::getStaticIp(JsonObject obj) { - if(mConfig->staticIp.ip[0] != 0) { - obj[F("ip")] = ip2String(mConfig->staticIp.ip); - obj[F("mask")] = ip2String(mConfig->staticIp.mask); - obj[F("dns")] = ip2String(mConfig->staticIp.dns); - obj[F("gateway")] = ip2String(mConfig->staticIp.gateway); + if(mConfig->sys.ip.ip[0] != 0) { + obj[F("ip")] = ip2String(mConfig->sys.ip.ip); + obj[F("mask")] = ip2String(mConfig->sys.ip.mask); + obj[F("dns1")] = ip2String(mConfig->sys.ip.dns1); + obj[F("dns2")] = ip2String(mConfig->sys.ip.dns2); + obj[F("gateway")] = ip2String(mConfig->sys.ip.gateway); } } @@ -314,7 +313,7 @@ void webApi::getMenu(JsonObject obj) { obj["link"][6] = "/update"; obj["name"][7] = "System"; obj["link"][7] = "/system"; - if(strlen(mConfig->password) > 0) { + if(strlen(mConfig->sys.adminPwd) > 0) { obj["name"][8] = "-"; obj["name"][9] = "Logout"; obj["link"][9] = "/logout"; @@ -327,7 +326,7 @@ void webApi::getIndex(JsonObject obj) { getMenu(obj.createNestedObject(F("menu"))); getSysInfo(obj.createNestedObject(F("system"))); getStatistics(obj.createNestedObject(F("statistics"))); - obj["refresh_interval"] = mConfig->sendInterval; + obj["refresh_interval"] = mConfig->nrf.sendInterval; JsonArray inv = obj.createNestedArray(F("inverter")); Inverter<> *iv; @@ -337,7 +336,7 @@ void webApi::getIndex(JsonObject obj) { record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); JsonObject invObj = inv.createNestedObject(); invObj[F("id")] = i; - invObj[F("name")] = String(iv->name); + invObj[F("name")] = String(iv->config->name); invObj[F("version")] = String(iv->fwVersion); invObj[F("is_avail")] = iv->isAvailable(mApp->getTimestamp(), rec); invObj[F("is_producing")] = iv->isProducing(mApp->getTimestamp(), rec); @@ -387,7 +386,7 @@ void webApi::getLive(JsonObject obj) { getMenu(obj.createNestedObject(F("menu"))); getSysInfo(obj.createNestedObject(F("system"))); JsonArray invArr = obj.createNestedArray(F("inverter")); - obj["refresh_interval"] = mConfig->sendInterval; + obj["refresh_interval"] = mConfig->nrf.sendInterval; uint8_t list[] = {FLD_UAC, FLD_IAC, FLD_PAC, FLD_F, FLD_PF, FLD_T, FLD_YT, FLD_YD, FLD_PDC, FLD_EFF, FLD_Q}; @@ -398,7 +397,7 @@ void webApi::getLive(JsonObject obj) { if(NULL != iv) { record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); JsonObject obj2 = invArr.createNestedObject(); - obj2[F("name")] = String(iv->name); + obj2[F("name")] = String(iv->config->name); obj2[F("channels")] = iv->channels; obj2[F("power_limit_read")] = round3(iv->actPowerLimit); obj2[F("last_alarm")] = String(iv->lastAlarmMsg); @@ -415,7 +414,7 @@ void webApi::getLive(JsonObject obj) { } for(uint8_t j = 1; j <= iv->channels; j ++) { - obj2[F("ch_names")][j] = String(iv->chName[j-1]); + obj2[F("ch_names")][j] = String(iv->config->chName[j-1]); JsonArray cur = ch.createNestedArray(); for (uint8_t k = 0; k < 6; k++) { switch(k) { diff --git a/src/web/webApi.h b/src/web/webApi.h index 519f2162..873080d7 100644 --- a/src/web/webApi.h +++ b/src/web/webApi.h @@ -16,7 +16,7 @@ class app; class webApi { public: - webApi(AsyncWebServer *srv, app *app, sysConfig_t *sysCfg, config_t *config, statistics_t *stat, char version[]); + webApi(AsyncWebServer *srv, app *app, settings_t *config, statistics_t *stat, char version[]); void setup(void); void loop(void); @@ -72,8 +72,7 @@ class webApi { AsyncWebServer *mSrv; app *mApp; - config_t *mConfig; - sysConfig_t *mSysCfg; + settings_t *mConfig; statistics_t *mStat; char *mVersion; diff --git a/src/wifi/ahoywifi.cpp b/src/wifi/ahoywifi.cpp index 05d2720d..caf2007e 100644 --- a/src/wifi/ahoywifi.cpp +++ b/src/wifi/ahoywifi.cpp @@ -16,9 +16,7 @@ //----------------------------------------------------------------------------- -ahoywifi::ahoywifi(app *main, sysConfig_t *sysCfg, config_t *config) { - mMain = main; - mSysCfg = sysCfg; +ahoywifi::ahoywifi(settings_t *config) { mConfig = config; mDns = new DNSServer(); @@ -34,18 +32,17 @@ ahoywifi::ahoywifi(app *main, sysConfig_t *sysCfg, config_t *config) { //----------------------------------------------------------------------------- void ahoywifi::setup(uint32_t timeout, bool settingValid) { + #ifdef FB_WIFI_OVERRIDDEN mStationWifiIsDef = false; #else - mStationWifiIsDef = (strncmp(mSysCfg->stationSsid, FB_WIFI_SSID, 14) == 0); + mStationWifiIsDef = (strncmp(mConfig->sys.stationSsid, FB_WIFI_SSID, 14) == 0); #endif - mWifiStationTimeout = timeout; #ifndef AP_ONLY if(false == mApActive) mApActive = (mStationWifiIsDef) ? true : setupStation(mWifiStationTimeout); #endif - if(!settingValid) { DPRINTLN(DBG_WARN, F("your settings are not valid! check [IP]/setup")); mApActive = true; @@ -58,7 +55,7 @@ void ahoywifi::setup(uint32_t timeout, bool settingValid) { DPRINTLN(DBG_INFO, F("Welcome to AHOY!")); DPRINT(DBG_INFO, F("\npoint your browser to http://")); if(mApActive) - DBGPRINTLN(F("192.168.1.1")); + DBGPRINTLN(F("192.168.4.1")); else DBGPRINTLN(WiFi.localIP().toString()); DPRINTLN(DBG_INFO, F("to configure your device")); @@ -142,20 +139,21 @@ bool ahoywifi::setupStation(uint32_t timeout) { } WiFi.mode(WIFI_STA); - if(mConfig->staticIp.ip[0] != 0) { - IPAddress ip(mConfig->staticIp.ip); - IPAddress mask(mConfig->staticIp.mask); - IPAddress dns(mConfig->staticIp.dns); - IPAddress gateway(mConfig->staticIp.gateway); - if(!WiFi.config(ip, gateway, mask, dns)) + if(mConfig->sys.ip.ip[0] != 0) { + IPAddress ip(mConfig->sys.ip.ip); + IPAddress mask(mConfig->sys.ip.mask); + IPAddress dns1(mConfig->sys.ip.dns1); + IPAddress dns2(mConfig->sys.ip.dns2); + IPAddress gateway(mConfig->sys.ip.gateway); + if(!WiFi.config(ip, gateway, mask, dns1, dns2)) DPRINTLN(DBG_ERROR, F("failed to set static IP!")); } - WiFi.begin(mSysCfg->stationSsid, mSysCfg->stationPwd); - if(String(mSysCfg->deviceName) != "") - WiFi.hostname(mSysCfg->deviceName); + WiFi.begin(mConfig->sys.stationSsid, mConfig->sys.stationPwd); + if(String(mConfig->sys.deviceName) != "") + WiFi.hostname(mConfig->sys.deviceName); delay(2000); - DPRINTLN(DBG_INFO, F("connect to network '") + String(mSysCfg->stationSsid) + F("' ...")); + DPRINTLN(DBG_INFO, F("connect to network '") + String(mConfig->sys.stationSsid) + F("' ...")); while (WiFi.status() != WL_CONNECTED) { delay(100); if(cnt % 40 == 0) @@ -197,8 +195,8 @@ time_t ahoywifi::getNtpTime(void) { uint8_t buf[NTP_PACKET_SIZE]; uint8_t retry = 0; - WiFi.hostByName(mConfig->ntpAddr, timeServer); - mUdp->begin(mConfig->ntpPort); + WiFi.hostByName(mConfig->ntp.addr, timeServer); + mUdp->begin(mConfig->ntp.port); sendNTPpacket(timeServer); diff --git a/src/wifi/ahoywifi.h b/src/wifi/ahoywifi.h index f164571e..c2add85b 100644 --- a/src/wifi/ahoywifi.h +++ b/src/wifi/ahoywifi.h @@ -7,21 +7,19 @@ #define __AHOYWIFI_H__ #include "../utils/dbg.h" - -// NTP +#include #include #include #include +#include "ESPAsyncWebServer.h" -#include "../defines.h" - -#include "../app.h" +#include "../config/settings.h" class app; class ahoywifi { public: - ahoywifi(app *main, sysConfig_t *sysCfg, config_t *config); + ahoywifi(settings_t *config); ~ahoywifi() {} void setup(uint32_t timeout, bool settingValid); @@ -36,9 +34,7 @@ class ahoywifi { private: void sendNTPpacket(IPAddress& address); - config_t *mConfig; - sysConfig_t *mSysCfg; - app *mMain; + settings_t *mConfig; DNSServer *mDns; WiFiUDP *mUdp; // for time server