Browse Source

Merge branch 'dev/littlefs' into development03

pull/425/head^2
lumapu 2 years ago
parent
commit
f72a2eb1ec
  1. 3
      src/.vscode/settings.json
  2. 218
      src/app.cpp
  3. 92
      src/app.h
  4. 18
      src/config/config.h
  5. 161
      src/config/eep.h
  6. 417
      src/config/settings.h
  7. 150
      src/defines.h
  8. 7
      src/hm/hmDefines.h
  9. 20
      src/hm/hmInverter.h
  10. 38
      src/hm/hmSystem.h
  11. 6
      src/platformio.ini
  12. 2
      src/utils/ahoyTimer.h
  13. 2
      src/utils/sun.h
  14. 2
      src/web/html/serial.html
  15. 8
      src/web/html/setup.html
  16. 44
      src/web/mqtt.h
  17. 101
      src/web/web.cpp
  18. 5
      src/web/web.h
  19. 69
      src/web/webApi.cpp
  20. 5
      src/web/webApi.h
  21. 36
      src/wifi/ahoywifi.cpp
  22. 14
      src/wifi/ahoywifi.h

3
src/.vscode/settings.json

@ -20,4 +20,7 @@
// https://clang.llvm.org/docs/ClangFormatStyleOptions.html // https://clang.llvm.org/docs/ClangFormatStyleOptions.html
"C_Cpp.clang_format_fallbackStyle": "{ BasedOnStyle: Google, IndentWidth: 4, ColumnLimit: 0}", "C_Cpp.clang_format_fallbackStyle": "{ BasedOnStyle: Google, IndentWidth: 4, ColumnLimit: 0}",
"files.associations": {
"typeinfo": "cpp"
},
} }

218
src/app.cpp

@ -13,33 +13,27 @@
#include "utils/sun.h" #include "utils/sun.h"
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
app::app() { void app::setup(uint32_t timeout) {
Serial.begin(115200); Serial.begin(115200);
DPRINTLN(DBG_VERBOSE, F("app::app")); while (!Serial)
mEep = new eep(); yield();
resetSystem(); resetSystem();
loadDefaultConfig(); mSettings.setup();
mSettings.getPtr(mConfig);
mWifi = new ahoywifi(mConfig);
mWifi = new ahoywifi(this, &mSysConfig, &mConfig);
mSys = new HmSystemType(); mSys = new HmSystemType();
mSys->enableDebug(); mSys->enableDebug();
mShouldReboot = false; mShouldReboot = false;
}
//-----------------------------------------------------------------------------
void app::setup(uint32_t timeout) {
DPRINTLN(DBG_VERBOSE, F("app::setup"));
mWifiSettingsValid = checkEEpCrc(ADDR_START, ADDR_WIFI_CRC, ADDR_WIFI_CRC); mWifi->setup(timeout, mSettings.getValid());
mSettingsValid = checkEEpCrc(ADDR_START_SETTINGS, ((ADDR_NEXT) - (ADDR_START_SETTINGS)), ADDR_SETTINGS_CRC);
loadEEpconfig();
mWifi->setup(timeout, mWifiSettingsValid); mSys->setup(mConfig->nrf.amplifierPower, mConfig->nrf.pinIrq, mConfig->nrf.pinCe, mConfig->nrf.pinCs);
mSys->addInverters(&mConfig->inst);
mSys->setup(mConfig.amplifierPower, mConfig.pinIrq, mConfig.pinCe, mConfig.pinCs);
mPayload.setup(mSys); mPayload.setup(mSys);
mPayload.enableSerialDebug(mConfig.serialDebug); mPayload.enableSerialDebug(mConfig->serial.debug);
#ifndef AP_ONLY #ifndef AP_ONLY
setupMqtt(); setupMqtt();
if(mMqttActive) if(mMqttActive)
@ -47,11 +41,12 @@ void app::setup(uint32_t timeout) {
#endif #endif
setupLed(); setupLed();
mWebInst = new web(this, &mSysConfig, &mConfig, &mStat, mVersion);
mWebInst = new web(this, mConfig, &mStat, mVersion);
mWebInst->setup(); mWebInst->setup();
mWebInst->setProtection(strlen(mConfig.password) != 0); mWebInst->setProtection(strlen(mConfig->sys.adminPwd) != 0);
DPRINTLN(DBG_INFO, F("Settings valid: ") + String((mSettingsValid) ? F("true") : F("false"))); DPRINTLN(DBG_INFO, F("Settings valid: ") + String((mSettings.getValid()) ? F("true") : F("false")));
DPRINTLN(DBG_INFO, F("EEprom storage size: 0x") + String(ADDR_SETTINGS_CRC, HEX));
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -88,7 +83,7 @@ void app::loop(void) {
if (mFlagSendDiscoveryConfig) { if (mFlagSendDiscoveryConfig) {
mFlagSendDiscoveryConfig = false; mFlagSendDiscoveryConfig = false;
mMqtt.sendMqttDiscoveryConfig(mConfig.mqtt.topic); mMqtt.sendMqttDiscoveryConfig(mConfig->mqtt.topic);
} }
mSys->Radio.loop(); mSys->Radio.loop();
@ -104,7 +99,7 @@ void app::loop(void) {
if (mSys->Radio.checkPaketCrc(p->packet, &len, p->rxCh)) { if (mSys->Radio.checkPaketCrc(p->packet, &len, p->rxCh)) {
// process buffer only on first occurrence // process buffer only on first occurrence
if (mConfig.serialDebug) { if (mConfig->serial.debug) {
DPRINT(DBG_INFO, "RX " + String(len) + "B Ch" + String(p->rxCh) + " | "); DPRINT(DBG_INFO, "RX " + String(len) + "B Ch" + String(p->rxCh) + " | ");
mSys->Radio.dumpBuf(NULL, p->packet, len); mSys->Radio.dumpBuf(NULL, p->packet, len);
} }
@ -119,23 +114,23 @@ void app::loop(void) {
yield(); yield();
if (rxRdy) if (rxRdy)
mPayload.process(true, mConfig.maxRetransPerPyld, &mStat); mPayload.process(true, mConfig->nrf.maxRetransPerPyld, &mStat);
} }
if (mMqttActive) if (mMqttActive)
mMqtt.loop(); mMqtt.loop();
if (ah::checkTicker(&mTicker, 1000)) { 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 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; mLatestSunTimestamp = mUtcTimestamp;
} }
if (mConfig.serialShowIv) { if (mConfig->serial.showIv) {
if (++mSerialTicker >= mConfig.serialInterval) { if (++mSerialTicker >= mConfig->serial.interval) {
mSerialTicker = 0; mSerialTicker = 0;
char topic[30], val[10]; char topic[30], val[10];
for (uint8_t id = 0; id < mSys->getNumInverters(); id++) { for (uint8_t id = 0; id < mSys->getNumInverters(); id++) {
@ -146,7 +141,7 @@ void app::loop(void) {
DPRINTLN(DBG_INFO, "Inverter: " + String(id)); DPRINTLN(DBG_INFO, "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->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)); snprintf(val, 10, "%.3f %s", iv->getValue(i, rec), iv->getUnit(i, rec));
DPRINTLN(DBG_INFO, String(topic) + ": " + String(val)); 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; 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 (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.serialDebug) if (mConfig->serial.debug)
DPRINTLN(DBG_DEBUG, F("Free heap: 0x") + String(ESP.getFreeHeap(), HEX)); DPRINTLN(DBG_DEBUG, F("Free heap: 0x") + String(ESP.getFreeHeap(), HEX));
if (!mSys->BufCtrl.empty()) { if (!mSys->BufCtrl.empty()) {
if (mConfig.serialDebug) 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()));
} }
@ -180,7 +175,7 @@ void app::loop(void) {
if (NULL != iv) { if (NULL != iv) {
if (!mPayload.isComplete(iv)) if (!mPayload.isComplete(iv))
mPayload.process(false, mConfig.maxRetransPerPyld, &mStat); mPayload.process(false, mConfig->nrf.maxRetransPerPyld, &mStat);
if (!mPayload.isComplete(iv)) { if (!mPayload.isComplete(iv)) {
if (0 == mPayload.getMaxPacketId(iv)) if (0 == mPayload.getMaxPacketId(iv))
@ -189,9 +184,9 @@ void app::loop(void) {
mStat.rxFail++; mStat.rxFail++;
iv->setQueuedCmdFinished(); // command failed iv->setQueuedCmdFinished(); // command failed
if (mConfig.serialDebug) if (mConfig->serial.debug)
DPRINTLN(DBG_INFO, F("enqueued cmd failed/timeout")); DPRINTLN(DBG_INFO, F("enqueued cmd failed/timeout"));
if (mConfig.serialDebug) { if (mConfig->serial.debug) {
DPRINT(DBG_INFO, F("(#") + String(iv->id) + ") "); DPRINT(DBG_INFO, F("(#") + String(iv->id) + ") ");
DPRINTLN(DBG_INFO, F("no Payload received! (retransmits: ") + String(mPayload.getRetransmits(iv)) + ")"); DPRINTLN(DBG_INFO, F("no Payload received! (retransmits: ") + String(mPayload.getRetransmits(iv)) + ")");
} }
@ -201,13 +196,13 @@ void app::loop(void) {
mPayload.request(iv); mPayload.request(iv);
yield(); yield();
if (mConfig.serialDebug) { if (mConfig->serial.debug) {
DPRINTLN(DBG_DEBUG, F("app:loop WiFi WiFi.status ") + String(WiFi.status())); 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 (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])); 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); mSys->Radio.sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit);
mPayload.setTxCmd(iv, iv->devControlCmd); mPayload.setTxCmd(iv, iv->devControlCmd);
@ -221,7 +216,7 @@ void app::loop(void) {
mRxTicker = 0; 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!")); DPRINTLN(DBG_WARN, F("Time not set or it is night time, therefore no communication to the inverter!"));
yield(); yield();
@ -254,6 +249,8 @@ void app::getAvailNetworks(JsonObject obj) {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void app::resetSystem(void) { void app::resetSystem(void) {
snprintf(mVersion, 12, "%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
mUptimeSecs = 0; mUptimeSecs = 0;
mPrevMillis = 0; mPrevMillis = 0;
mUpdateNtp = false; mUpdateNtp = false;
@ -284,130 +281,13 @@ void app::resetSystem(void) {
memset(&mStat, 0, sizeof(statistics_t)); 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) { void app::setupMqtt(void) {
if (mSettingsValid) { if (mConfig->mqtt.broker[0] > 0)
if (mConfig.mqtt.broker[0] > 0)
mMqttActive = true; mMqttActive = true;
if(mMqttActive) if(mMqttActive)
mMqtt.setup(&mConfig.mqtt, mSysConfig.deviceName, mVersion, mSys, &mUtcTimestamp); mMqtt.setup(&mConfig->mqtt, mConfig->sys.deviceName, mVersion, mSys, &mUtcTimestamp);
}
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -417,26 +297,26 @@ void app::setupLed(void) {
* PIN ---- |<----- 3.3V * PIN ---- |<----- 3.3V
* *
* */ * */
if(mConfig.led.led0 != 0xff) { if(mConfig->led.led0 != 0xff) {
pinMode(mConfig.led.led0, OUTPUT); pinMode(mConfig->led.led0, OUTPUT);
digitalWrite(mConfig.led.led0, HIGH); // LED off digitalWrite(mConfig->led.led0, HIGH); // LED off
} }
if(mConfig.led.led1 != 0xff) { if(mConfig->led.led1 != 0xff) {
pinMode(mConfig.led.led1, OUTPUT); pinMode(mConfig->led.led1, OUTPUT);
digitalWrite(mConfig.led.led1, HIGH); // LED off digitalWrite(mConfig->led.led1, HIGH); // LED off
} }
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void app::updateLed(void) { void app::updateLed(void) {
if(mConfig.led.led0 != 0xff) { if(mConfig->led.led0 != 0xff) {
Inverter<> *iv = mSys->getInverterByPos(0); Inverter<> *iv = mSys->getInverterByPos(0);
if (NULL != iv) { if (NULL != iv) {
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
if(iv->isProducing(mUtcTimestamp, rec)) if(iv->isProducing(mUtcTimestamp, rec))
digitalWrite(mConfig.led.led0, LOW); // LED on digitalWrite(mConfig->led.led0, LOW); // LED on
else else
digitalWrite(mConfig.led.led0, HIGH); // LED off digitalWrite(mConfig->led.led0, HIGH); // LED off
} }
} }
} }

92
src/app.h

@ -7,15 +7,12 @@
#define __APP_H__ #define __APP_H__
#include "utils/dbg.h" #include "utils/dbg.h"
#include "Arduino.h" #include <Arduino.h>
#include <queue>
#include <RF24.h> #include <RF24.h>
#include <RF24_config.h> #include <RF24_config.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include "config/eep.h" #include "config/settings.h"
#include "defines.h" #include "defines.h"
#include "utils/crc.h" #include "utils/crc.h"
#include "utils/ahoyTimer.h" #include "utils/ahoyTimer.h"
@ -42,7 +39,7 @@ class web;
class app { class app {
public: public:
app(); app() {}
~app() {} ~app() {}
void setup(uint32_t timeout); void setup(uint32_t timeout);
@ -55,9 +52,16 @@ class app {
void scanAvailNetworks(void); void scanAvailNetworks(void);
void getAvailNetworks(JsonObject obj); void getAvailNetworks(JsonObject obj);
void saveSettings(void) {
mSettings.saveSettings();
}
bool eraseSettings(bool eraseWifi = false) {
return mSettings.eraseSettings(eraseWifi);
}
uint8_t getIrqPin(void) { uint8_t getIrqPin(void) {
return mConfig.pinIrq; return mConfig->nrf.pinIrq;
} }
uint64_t Serial2u64(const char *val) { uint64_t Serial2u64(const char *val) {
@ -122,26 +126,8 @@ class app {
return mLatestSunTimestamp; 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 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 bool getRebootRequestState(void) { return mShowRebootRequest; }
inline uint32_t getMqttTxCnt(void) { return mMqtt.getTxCnt(); } inline uint32_t getMqttTxCnt(void) { return mMqtt.getTxCnt(); }
@ -151,57 +137,12 @@ class app {
private: private:
void resetSystem(void); void resetSystem(void);
void loadDefaultConfig(void);
void loadEEpconfig(void);
void setupMqtt(void); void setupMqtt(void);
void setupLed(void); void setupLed(void);
void updateLed(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) { void stats(void) {
DPRINTLN(DBG_VERBOSE, F("main.h:stats")); DPRINTLN(DBG_VERBOSE, F("main.h:stats"));
#ifdef ESP8266 #ifdef ESP8266
@ -228,11 +169,6 @@ class app {
uint32_t mNtpRefreshTicker; uint32_t mNtpRefreshTicker;
uint32_t mNtpRefreshInterval; uint32_t mNtpRefreshInterval;
bool mWifiSettingsValid;
bool mSettingsValid;
eep *mEep;
uint32_t mUtcTimestamp; uint32_t mUtcTimestamp;
bool mUpdateNtp; bool mUpdateNtp;
@ -240,10 +176,10 @@ class app {
ahoywifi *mWifi; ahoywifi *mWifi;
web *mWebInst; web *mWebInst;
sysConfig_t mSysConfig;
config_t mConfig;
char mVersion[12]; char mVersion[12];
PayloadType mPayload; PayloadType mPayload;
settings mSettings;
settings_t *mConfig;
uint16_t mSendTicker; uint16_t mSendTicker;
uint8_t mSendLastIvId; uint8_t mSendLastIvId;

18
src/config/config.h

@ -25,9 +25,6 @@
// If the next line is uncommented, Ahoy will stay in access point mode all the time // If the next line is uncommented, Ahoy will stay in access point mode all the time
//#define AP_ONLY //#define AP_ONLY
// protection of the GUI by password
#define GUI_DEF_PASSWORD ""
// timeout for automatic logoff (20 minutes) // timeout for automatic logoff (20 minutes)
#define LOGOUT_TIMEOUT (20 * 60 * 60) #define LOGOUT_TIMEOUT (20 * 60 * 60)
@ -47,13 +44,13 @@
// default pinout (GPIO Number) // default pinout (GPIO Number)
#if defined(ESP32) #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_CS_PIN 5
#define DEF_CE_PIN 4 #define DEF_CE_PIN 4
#define DEF_IRQ_PIN 16 #define DEF_IRQ_PIN 16
#else
#define DEF_CS_PIN 15
#define DEF_CE_PIN 2
#define DEF_IRQ_PIN 0
#endif #endif
#define DEF_LED0_PIN 255 // off #define DEF_LED0_PIN 255 // off
#define DEF_LED1_PIN 255 // off #define DEF_LED1_PIN 255 // off
@ -65,7 +62,7 @@
#define PACKET_BUFFER_SIZE 30 #define PACKET_BUFFER_SIZE 30
// number of configurable inverters // number of configurable inverters
#define MAX_NUM_INVERTERS 4 #define MAX_NUM_INVERTERS 10
// default serial interval // default serial interval
#define SERIAL_INTERVAL 5 #define SERIAL_INTERVAL 5
@ -121,6 +118,11 @@
// default MQTT topic // default MQTT topic
#define DEF_MQTT_TOPIC "inverter" #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") #if __has_include("config_override.h")
#include "config_override.h" #include "config_override.h"

161
src/config/eep.h

@ -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 <EEPROM.h>
#ifdef ESP32
#include <nvs_flash.h>
#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__*/

417
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 <Arduino.h>
#include <LittleFS.h>
#include <ArduinoJson.h>
#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<JsonObject>();
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<const char*>());
snprintf(mCfg.sys.stationPwd, PWD_LEN, "%s", obj[F("pwd")].as<const char*>());
snprintf(mCfg.sys.deviceName, DEVNAME_LEN, "%s", obj[F("dev")].as<const char*>());
snprintf(mCfg.sys.adminPwd, PWD_LEN, "%s", obj[F("adm")].as<const char*>());
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<const char*>());
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<const char*>());
snprintf(mCfg.mqtt.user, MQTT_USER_LEN, "%s", obj[F("user")].as<const char*>());
snprintf(mCfg.mqtt.pwd, MQTT_PWD_LEN, "%s", obj[F("pwd")].as<const char*>());
snprintf(mCfg.mqtt.topic, MQTT_TOPIC_LEN, "%s", obj[F("topic")].as<const char*>());
}
}
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<const char*>());
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<const char*>());
}
}
}
settings_t mCfg;
bool mValid;
};
#endif /*__SETTINGS_H__*/

150
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 35 #define VERSION_PATCH 36
//------------------------------------- //-------------------------------------
typedef struct { typedef struct {
@ -63,6 +63,11 @@ typedef enum {
RelativPersistent = 257UL // 0x0101 RelativPersistent = 257UL // 0x0101
} PowerLimitControlType; } PowerLimitControlType;
union serial_u {
uint64_t u64;
uint8_t b[8];
};
#define MIN_SERIAL_INTERVAL 5 #define MIN_SERIAL_INTERVAL 5
#define MIN_SEND_INTERVAL 15 #define MIN_SEND_INTERVAL 15
#define MIN_MQTT_INTERVAL 60 #define MIN_MQTT_INTERVAL 60
@ -78,116 +83,14 @@ typedef enum {
#define SSID_LEN 32 #define SSID_LEN 32
#define PWD_LEN 64 #define PWD_LEN 64
#define DEVNAME_LEN 16 #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 NTP_ADDR_LEN 32 // DNS Name
#define MQTT_ADDR_LEN 32 // DNS Name #define MQTT_ADDR_LEN 32 // DNS Name
#define MQTT_USER_LEN 16 #define MQTT_USER_LEN 16
#define MQTT_PWD_LEN 32 #define MQTT_PWD_LEN 32
#define MQTT_TOPIC_LEN 32 #define MQTT_TOPIC_LEN 64
#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_MAX_PACKET_SIZE 384
typedef struct { typedef struct {
uint32_t rxFail; uint32_t rxFail;
@ -196,41 +99,4 @@ typedef struct {
uint32_t frmCnt; uint32_t frmCnt;
} statistics_t; } 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__*/ #endif /*__DEFINES_H__*/

7
src/hm/hmDefines.h

@ -9,13 +9,6 @@
#include "../utils/dbg.h" #include "../utils/dbg.h"
#include <cstdint> #include <cstdint>
union serial_u {
uint64_t u64;
uint8_t b[8];
};
// units // units
enum {UNIT_V = 0, UNIT_A, UNIT_W, UNIT_WH, UNIT_KWH, UNIT_HZ, UNIT_C, UNIT_PCT, UNIT_VAR, UNIT_NONE}; 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", ""}; const char* const units[] = {"V", "A", "W", "Wh", "kWh", "Hz", "°C", "%", "var", ""};

20
src/hm/hmInverter.h

@ -14,6 +14,7 @@
#include "hmDefines.h" #include "hmDefines.h"
#include <memory> #include <memory>
#include <queue> #include <queue>
#include "../config/settings.h"
/** /**
* For values which are of interest and not transmitted by the inverter can be * For values which are of interest and not transmitted by the inverter can be
@ -104,8 +105,8 @@ const calcFunc_t<T> calcFunctions[] = {
template <class REC_TYP> template <class REC_TYP>
class Inverter { class Inverter {
public: public:
cfgIv_t *config; // stored settings
uint8_t id; // unique id 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 uint8_t type; // integer which refers to inverter type
uint16_t alarmMesIndex; // Last recorded Alarm Message Index uint16_t alarmMesIndex; // Last recorded Alarm Message Index
uint16_t fwVersion; // Firmware Version from Info Command Request uint16_t fwVersion; // Firmware Version from Info Command Request
@ -113,15 +114,12 @@ class Inverter {
float actPowerLimit; // actual power limit float actPowerLimit; // actual power limit
uint8_t devControlCmd; // carries the requested cmd uint8_t devControlCmd; // carries the requested cmd
bool devControlRequest; // true if change needed bool devControlRequest; // true if change needed
serial_u serial; // serial number as on barcode
serial_u radioId; // id converted to modbus serial_u radioId; // id converted to modbus
uint8_t channels; // number of PV channels (1-4) uint8_t channels; // number of PV channels (1-4)
record_t<REC_TYP> recordMeas; // structure for measured values record_t<REC_TYP> recordMeas; // structure for measured values
record_t<REC_TYP> recordInfo; // structure for info values record_t<REC_TYP> recordInfo; // structure for info values
record_t<REC_TYP> recordConfig; // structure for system config values record_t<REC_TYP> recordConfig; // structure for system config values
record_t<REC_TYP> recordAlarm; // structure for alarm values record_t<REC_TYP> 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; String lastAlarmMsg;
bool initialized; // needed to check if the inverter was correctly added (ESP32 specific - union types are never null) bool initialized; // needed to check if the inverter was correctly added (ESP32 specific - union types are never null)
@ -185,8 +183,6 @@ class Inverter {
initAssignment(&recordConfig, SystemConfigPara); initAssignment(&recordConfig, SystemConfigPara);
initAssignment(&recordAlarm, AlarmData); initAssignment(&recordAlarm, AlarmData);
toRadioId(); toRadioId();
memset(name, 0, MAX_NAME_LENGTH);
memset(chName, 0, MAX_NAME_LENGTH * 4);
initialized = true; initialized = true;
} }
@ -484,10 +480,10 @@ class Inverter {
void toRadioId(void) { void toRadioId(void) {
DPRINTLN(DBG_VERBOSE, F("hmInverter.h:toRadioId")); DPRINTLN(DBG_VERBOSE, F("hmInverter.h:toRadioId"));
radioId.u64 = 0ULL; radioId.u64 = 0ULL;
radioId.b[4] = serial.b[0]; radioId.b[4] = config->serial.b[0];
radioId.b[3] = serial.b[1]; radioId.b[3] = config->serial.b[1];
radioId.b[2] = serial.b[2]; radioId.b[2] = config->serial.b[2];
radioId.b[1] = serial.b[3]; radioId.b[1] = config->serial.b[3];
radioId.b[0] = 0x01; radioId.b[0] = 0x01;
} }
}; };
@ -583,8 +579,8 @@ static T calcIrradiation(Inverter<> *iv, uint8_t arg0) {
if(NULL != iv) { if(NULL != iv) {
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
uint8_t pos = iv->getPosByChFld(arg0, FLD_PDC, rec); uint8_t pos = iv->getPosByChFld(arg0, FLD_PDC, rec);
if(iv->chMaxPwr[arg0-1] > 0) if(iv->config->chMaxPwr[arg0-1] > 0)
return iv->getValue(pos, rec) / iv->chMaxPwr[arg0-1] * 100.0f; return iv->getValue(pos, rec) / iv->config->chMaxPwr[arg0-1] * 100.0f;
} }
return 0.0; return 0.0;
} }

38
src/hm/hmSystem.h

@ -37,7 +37,18 @@ class HmSystem {
Radio.setup(&BufCtrl, ampPwr, irqPin, cePin, csPin); 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")); DPRINTLN(DBG_VERBOSE, F("hmSystem.h:addInverter"));
if(MAX_INVERTER <= mNumInv) { if(MAX_INVERTER <= mNumInv) {
DPRINT(DBG_WARN, F("max number of inverters reached!")); DPRINT(DBG_WARN, F("max number of inverters reached!"));
@ -45,18 +56,17 @@ class HmSystem {
} }
INVERTERTYPE *p = &mInverter[mNumInv]; INVERTERTYPE *p = &mInverter[mNumInv];
p->id = mNumInv; p->id = mNumInv;
p->serial.u64 = serial; p->config = config;
memcpy(p->chMaxPwr, chMaxPwr, (4*2)); DPRINT(DBG_VERBOSE, "SERIAL: " + String(p->config->serial.b[5], HEX));
DPRINT(DBG_VERBOSE, "SERIAL: " + String(p->serial.b[5], HEX)); DPRINTLN(DBG_VERBOSE, " " + String(p->config->serial.b[4], HEX));
DPRINTLN(DBG_VERBOSE, " " + String(p->serial.b[4], HEX)); if(p->config->serial.b[5] == 0x11) {
if(p->serial.b[5] == 0x11) { switch(p->config->serial.b[4]) {
switch(p->serial.b[4]) {
case 0x21: p->type = INV_TYPE_1CH; break; case 0x21: p->type = INV_TYPE_1CH; break;
case 0x41: p->type = INV_TYPE_2CH; break; case 0x41: p->type = INV_TYPE_2CH; break;
case 0x61: p->type = INV_TYPE_4CH; break; case 0x61: p->type = INV_TYPE_4CH; break;
default: default:
DPRINT(DBG_ERROR, F("unknown inverter type: 11")); 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; break;
} }
} }
@ -64,8 +74,6 @@ class HmSystem {
DPRINTLN(DBG_ERROR, F("inverter type can't be detected!")); DPRINTLN(DBG_ERROR, F("inverter type can't be detected!"));
p->init(); p->init();
uint8_t len = (uint8_t)strlen(name);
strncpy(p->name, name, (len > MAX_NAME_LENGTH) ? MAX_NAME_LENGTH : len);
mNumInv ++; mNumInv ++;
return p; return p;
@ -76,10 +84,10 @@ class HmSystem {
INVERTERTYPE *p; INVERTERTYPE *p;
for(uint8_t i = 0; i < mNumInv; i++) { for(uint8_t i = 0; i < mNumInv; i++) {
p = &mInverter[i]; p = &mInverter[i];
if((p->serial.b[3] == buf[0]) if((p->config->serial.b[3] == buf[0])
&& (p->serial.b[2] == buf[1]) && (p->config->serial.b[2] == buf[1])
&& (p->serial.b[1] == buf[2]) && (p->config->serial.b[1] == buf[2])
&& (p->serial.b[0] == buf[3])) && (p->config->serial.b[0] == buf[3]))
return p; return p;
} }
return NULL; return NULL;
@ -89,7 +97,7 @@ class HmSystem {
DPRINTLN(DBG_VERBOSE, F("hmSystem.h:getInverterByPos")); DPRINTLN(DBG_VERBOSE, F("hmSystem.h:getInverterByPos"));
if(pos >= MAX_INVERTER) if(pos >= MAX_INVERTER)
return NULL; 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]; return &mInverter[pos];
else else
return NULL; return NULL;

6
src/platformio.ini

@ -10,12 +10,13 @@
[platformio] [platformio]
src_dir = . src_dir = .
include_dir = .
[env] [env]
framework = arduino framework = arduino
board_build.filesystem = littlefs
build_flags = ;build_flags =
-include "config.h"
; ;;;;; Possible Debug options ;;;;;; ; ;;;;; Possible Debug options ;;;;;;
; https://docs.platformio.org/en/latest/platforms/espressif8266.html#debug-level ; https://docs.platformio.org/en/latest/platforms/espressif8266.html#debug-level
;-DDEBUG_ESP_PORT=Serial ;-DDEBUG_ESP_PORT=Serial
@ -43,6 +44,7 @@ lib_deps =
;esp8266/SPI ;esp8266/SPI
;esp8266/Ticker ;esp8266/Ticker
[env:esp8266-release] [env:esp8266-release]
platform = espressif8266 platform = espressif8266
board = esp12e board = esp12e

2
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/ // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

2
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/ // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

2
src/web/html/serial.html

@ -128,7 +128,7 @@
}); });
document.getElementById("scroll").addEventListener("click", function() { document.getElementById("scroll").addEventListener("click", function() {
mAutoScroll = !mAutoScroll; mAutoScroll = !mAutoScroll;
this.value = (mAutoScroll) ? "autoscroll" : "manual scoll"; this.value = (mAutoScroll) ? "autoscroll" : "manual scroll";
}); });
if (!!window.EventSource) { if (!!window.EventSource) {

8
src/web/html/setup.html

@ -67,8 +67,10 @@
<input type="text" name="ipAddr" class="text" maxlength="15" /> <input type="text" name="ipAddr" class="text" maxlength="15" />
<label for="ipMask">Submask</label> <label for="ipMask">Submask</label>
<input type="text" name="ipMask" class="text" maxlength="15" /> <input type="text" name="ipMask" class="text" maxlength="15" />
<label for="ipDns">DNS</label> <label for="ipDns1">DNS 1</label>
<input type="text" name="ipDns" class="text" maxlength="15" /> <input type="text" name="ipDns1" class="text" maxlength="15" />
<label for="ipDns2">DNS 2</label>
<input type="text" name="ipDns2" class="text" maxlength="15" />
<label for="ipGateway">Gateway</label> <label for="ipGateway">Gateway</label>
<input type="text" name="ipGateway" class="text" maxlength="15" /> <input type="text" name="ipGateway" class="text" maxlength="15" />
</fieldset> </fieldset>
@ -336,7 +338,7 @@
} }
function parseStaticIp(obj) { 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]]) if(null != obj[i[1]])
document.getElementsByName(i[0])[0].value = obj[i[1]]; document.getElementsByName(i[0])[0].value = obj[i[1]];
} }

44
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/ // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -21,9 +21,9 @@
#include "../utils/ahoyTimer.h" #include "../utils/ahoyTimer.h"
#include "../config/config.h" #include "../config/config.h"
#include <PubSubClient.h> #include <PubSubClient.h>
#include <ArduinoJson.h>
#include "../defines.h" #include "../defines.h"
#include "../hm/hmSystem.h" #include "../hm/hmSystem.h"
#include <ArduinoJson.h>
template<class HMSYSTEM> template<class HMSYSTEM>
class mqtt { class mqtt {
@ -34,18 +34,16 @@ class mqtt {
mLastReconnect = 0; mLastReconnect = 0;
mTxCnt = 0; mTxCnt = 0;
memset(mDevName, 0, DEVNAME_LEN);
} }
~mqtt() { } ~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")); DPRINTLN(DBG_VERBOSE, F("mqtt.h:setup"));
mAddressSet = true; mAddressSet = true;
snprintf(mDevName, DEVNAME_LEN, "%s", devname);
mCfg = cfg; mCfg = cfg;
mDevName = devName;
mSys = sys; mSys = sys;
mUtcTimestamp = utcTs; mUtcTimestamp = utcTs;
@ -55,7 +53,7 @@ class mqtt {
setCallback(std::bind(&mqtt<HMSYSTEM>::cbMqtt, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); setCallback(std::bind(&mqtt<HMSYSTEM>::cbMqtt, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
sendMsg("version", version); sendMsg("version", version);
sendMsg("device", devname); sendMsg("device", devName);
sendMsg("uptime", "0"); sendMsg("uptime", "0");
} }
@ -76,8 +74,8 @@ class mqtt {
void sendMsg(const char *topic, const char *msg) { void sendMsg(const char *topic, const char *msg) {
//DPRINTLN(DBG_VERBOSE, F("mqtt.h:sendMsg")); //DPRINTLN(DBG_VERBOSE, F("mqtt.h:sendMsg"));
if(mAddressSet) { if(mAddressSet) {
char top[64]; char top[66];
snprintf(top, 64, "%s/%s", mCfg->topic, topic); snprintf(top, 66, "%s/%s", mCfg->topic, topic);
sendMsg2(top, msg, false); sendMsg2(top, msg, false);
} }
} }
@ -118,22 +116,22 @@ class mqtt {
if (NULL != iv) { if (NULL != iv) {
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
DynamicJsonDocument deviceDoc(128); DynamicJsonDocument deviceDoc(128);
deviceDoc["name"] = iv->name; deviceDoc["name"] = iv->config->name;
deviceDoc["ids"] = String(iv->serial.u64, HEX); deviceDoc["ids"] = String(iv->config->serial.u64, HEX);
deviceDoc["cu"] = F("http://") + String(WiFi.localIP().toString()); deviceDoc["cu"] = F("http://") + String(WiFi.localIP().toString());
deviceDoc["mf"] = "Hoymiles"; deviceDoc["mf"] = "Hoymiles";
deviceDoc["mdl"] = iv->name; deviceDoc["mdl"] = iv->config->name;
JsonObject deviceObj = deviceDoc.as<JsonObject>(); JsonObject deviceObj = deviceDoc.as<JsonObject>();
DynamicJsonDocument doc(384); DynamicJsonDocument doc(384);
for (uint8_t i = 0; i < rec->length; i++) { for (uint8_t i = 0; i < rec->length; i++) {
if (rec->assign[i].ch == CH0) { 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 { } 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(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->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)); 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 *devCls = getFieldDeviceClass(rec->assign[i].fieldId);
const char *stateCls = getFieldStateClass(rec->assign[i].fieldId); const char *stateCls = getFieldStateClass(rec->assign[i].fieldId);
@ -141,7 +139,7 @@ class mqtt {
doc["name"] = name; doc["name"] = name;
doc["stat_t"] = stateTopic; doc["stat_t"] = stateTopic;
doc["unit_of_meas"] = iv->getUnit(i, rec); 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["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!? 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) if (devCls != NULL)
@ -248,7 +246,7 @@ class mqtt {
if (MQTT_STATUS_AVAIL_PROD == status) if (MQTT_STATUS_AVAIL_PROD == status)
status = MQTT_STATUS_AVAIL_NOT_PROD; 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", snprintf(val, 40, "%s%s%s%s",
(status == MQTT_STATUS_NOT_AVAIL_NOT_PROD) ? "not yet " : "", (status == MQTT_STATUS_NOT_AVAIL_NOT_PROD) ? "not yet " : "",
"available and ", "available and ",
@ -257,11 +255,11 @@ class mqtt {
); );
sendMsg(topic, val); 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); snprintf(val, 40, "%d", status);
sendMsg(topic, val); 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); snprintf(val, 40, "%i", iv->getLastTs(rec) * 1000);
sendMsg(topic, val); sendMsg(topic, val);
} }
@ -269,7 +267,7 @@ class mqtt {
// data // data
if(iv->isAvailable(*mUtcTimestamp, rec)) { if(iv->isAvailable(*mUtcTimestamp, rec)) {
for (uint8_t i = 0; i < rec->length; i++) { 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)); snprintf(val, 40, "%.3f", iv->getValue(i, rec));
sendMsg(topic, val); sendMsg(topic, val);
@ -435,8 +433,8 @@ class mqtt {
uint32_t *mUtcTimestamp; uint32_t *mUtcTimestamp;
bool mAddressSet; bool mAddressSet;
mqttConfig_t *mCfg; cfgMqtt_t *mCfg;
char mDevName[DEVNAME_LEN]; const char *mDevName;
uint32_t mLastReconnect; uint32_t mLastReconnect;
uint32_t mTxCnt; uint32_t mTxCnt;
std::queue<uint8_t> mSendList; std::queue<uint8_t> mSendList;

101
src/web/web.cpp

@ -26,15 +26,14 @@
const char* const pinArgNames[] = {"pinCs", "pinCe", "pinIrq", "pinLed0", "pinLed1"}; 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; mMain = main;
mSysCfg = sysCfg;
mConfig = config; mConfig = config;
mStat = stat; mStat = stat;
mVersion = version; mVersion = version;
mWeb = new AsyncWebServer(80); mWeb = new AsyncWebServer(80);
mEvts = new AsyncEventSource("/events"); mEvts = new AsyncEventSource("/events");
mApi = new webApi(mWeb, main, sysCfg, config, stat, version); mApi = new webApi(mWeb, main, config, stat, version);
mProtected = true; mProtected = true;
mLogoutTimeout = 0; mLogoutTimeout = 0;
@ -151,7 +150,7 @@ void web::onLogin(AsyncWebServerRequest *request) {
DPRINTLN(DBG_VERBOSE, F("onLogin")); DPRINTLN(DBG_VERBOSE, F("onLogin"));
if(request->args() > 0) { if(request->args() > 0) {
if(String(request->arg("pwd")) == String(mConfig->password)) { if(String(request->arg("pwd")) == String(mConfig->sys.adminPwd)) {
mProtected = false; mProtected = false;
request->redirect("/"); request->redirect("/");
} }
@ -260,7 +259,7 @@ void web::showErase(AsyncWebServerRequest *request) {
} }
DPRINTLN(DBG_VERBOSE, F("showErase")); DPRINTLN(DBG_VERBOSE, F("showErase"));
mMain->eraseSettings(); mMain->eraseSettings(false);
onReboot(request); onReboot(request);
} }
@ -277,9 +276,11 @@ void web::showFactoryRst(AsyncWebServerRequest *request) {
int refresh = 3; int refresh = 3;
if(request->args() > 0) { if(request->args() > 0) {
if(request->arg("reset").toInt() == 1) { if(request->arg("reset").toInt() == 1) {
mMain->eraseSettings(true);
content = F("factory reset: success\n\nrebooting ... ");
refresh = 10; refresh = 10;
if(mMain->eraseSettings(true))
content = F("factory reset: success\n\nrebooting ... ");
else
content = F("factory reset: failed\n\nrebooting ... ");
} }
else { else {
content = F("factory reset: aborted"); content = F("factory reset: aborted");
@ -328,36 +329,40 @@ void web::showSave(AsyncWebServerRequest *request) {
// general // general
if(request->arg("ssid") != "") 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}") 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") != "") 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}") { if(request->arg("adminpwd") != "{PWD}") {
request->arg("adminpwd").toCharArray(mConfig->password, PWD_LEN); request->arg("adminpwd").toCharArray(mConfig->sys.adminPwd, PWD_LEN);
mProtected = (strlen(mConfig->password) > 0); mProtected = (strlen(mConfig->sys.adminPwd) > 0);
} }
// static ip // static ip
if(request->arg("ipAddr") != "") { if(request->arg("ipAddr") != "") {
request->arg("ipAddr").toCharArray(buf, SSID_LEN); request->arg("ipAddr").toCharArray(buf, SSID_LEN);
ip2Arr(mConfig->staticIp.ip, buf); ip2Arr(mConfig->sys.ip.ip, buf);
if(request->arg("ipMask") != "") { if(request->arg("ipMask") != "") {
request->arg("ipMask").toCharArray(buf, SSID_LEN); request->arg("ipMask").toCharArray(buf, SSID_LEN);
ip2Arr(mConfig->staticIp.mask, buf); ip2Arr(mConfig->sys.ip.mask, buf);
}
if(request->arg("ipDns1") != "") {
request->arg("ipDns1").toCharArray(buf, SSID_LEN);
ip2Arr(mConfig->sys.ip.dns1, buf);
} }
if(request->arg("ipDns") != "") { if(request->arg("ipDns2") != "") {
request->arg("ipDns").toCharArray(buf, SSID_LEN); request->arg("ipDns2").toCharArray(buf, SSID_LEN);
ip2Arr(mConfig->staticIp.dns, buf); ip2Arr(mConfig->sys.ip.dns2, buf);
} }
if(request->arg("ipGateway") != "") { if(request->arg("ipGateway") != "") {
request->arg("ipGateway").toCharArray(buf, SSID_LEN); request->arg("ipGateway").toCharArray(buf, SSID_LEN);
ip2Arr(mConfig->staticIp.gateway, buf); ip2Arr(mConfig->sys.ip.gateway, buf);
} }
} }
else else
memset(&mConfig->staticIp, 0, sizeof(staticIp_t)); memset(&mConfig->sys.ip.ip, 0, 4);
// inverter // inverter
@ -368,8 +373,8 @@ void web::showSave(AsyncWebServerRequest *request) {
request->arg("inv" + String(i) + "Addr").toCharArray(buf, 20); request->arg("inv" + String(i) + "Addr").toCharArray(buf, 20);
if(strlen(buf) == 0) if(strlen(buf) == 0)
memset(buf, 0, 20); memset(buf, 0, 20);
iv->serial.u64 = mMain->Serial2u64(buf); iv->config->serial.u64 = mMain->Serial2u64(buf);
switch(iv->serial.b[4]) { switch(iv->config->serial.b[4]) {
case 0x21: iv->type = INV_TYPE_1CH; iv->channels = 1; break; case 0x21: iv->type = INV_TYPE_1CH; iv->channels = 1; break;
case 0x41: iv->type = INV_TYPE_2CH; iv->channels = 2; break; case 0x41: iv->type = INV_TYPE_2CH; iv->channels = 2; break;
case 0x61: iv->type = INV_TYPE_4CH; iv->channels = 4; break; case 0x61: iv->type = INV_TYPE_4CH; iv->channels = 4; break;
@ -377,59 +382,54 @@ void web::showSave(AsyncWebServerRequest *request) {
} }
// name // 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 // max channel power / name
for(uint8_t j = 0; j < 4; j++) { for(uint8_t j = 0; j < 4; j++) {
iv->chMaxPwr[j] = request->arg("inv" + String(i) + "ModPwr" + String(j)).toInt() & 0xffff; iv->config->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); request->arg("inv" + String(i) + "ModName" + String(j)).toCharArray(iv->config->chName[j], MAX_NAME_LENGTH);
} }
iv->initialized = true; iv->initialized = true;
} }
if(request->arg("invInterval") != "") if(request->arg("invInterval") != "")
mConfig->sendInterval = request->arg("invInterval").toInt(); mConfig->nrf.sendInterval = request->arg("invInterval").toInt();
if(request->arg("invRetry") != "") if(request->arg("invRetry") != "")
mConfig->maxRetransPerPyld = request->arg("invRetry").toInt(); mConfig->nrf.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());
// pinout // pinout
uint8_t pin; uint8_t pin;
for(uint8_t i = 0; i < 5; i ++) { for(uint8_t i = 0; i < 5; i ++) {
pin = request->arg(String(pinArgNames[i])).toInt(); pin = request->arg(String(pinArgNames[i])).toInt();
switch(i) { switch(i) {
default: mConfig->pinCs = ((pin != 0xff) ? pin : DEF_CS_PIN); break; default: mConfig->nrf.pinCs = ((pin != 0xff) ? pin : DEF_CS_PIN); break;
case 1: mConfig->pinCe = ((pin != 0xff) ? pin : DEF_CE_PIN); break; case 1: mConfig->nrf.pinCe = ((pin != 0xff) ? pin : DEF_CE_PIN); break;
case 2: mConfig->pinIrq = ((pin != 0xff) ? pin : DEF_IRQ_PIN); break; case 2: mConfig->nrf.pinIrq = ((pin != 0xff) ? pin : DEF_IRQ_PIN); break;
case 3: mConfig->led.led0 = pin; break; case 3: mConfig->led.led0 = pin; break;
case 4: mConfig->led.led1 = pin; break; case 4: mConfig->led.led1 = pin; break;
} }
} }
// nrf24 amplifier power // nrf24 amplifier power
mConfig->amplifierPower = request->arg("rf24Power").toInt() & 0x03; mConfig->nrf.amplifierPower = request->arg("rf24Power").toInt() & 0x03;
// ntp // ntp
if(request->arg("ntpAddr") != "") { if(request->arg("ntpAddr") != "") {
request->arg("ntpAddr").toCharArray(mConfig->ntpAddr, NTP_ADDR_LEN); request->arg("ntpAddr").toCharArray(mConfig->ntp.addr, NTP_ADDR_LEN);
mConfig->ntpPort = request->arg("ntpPort").toInt() & 0xffff; mConfig->ntp.port = request->arg("ntpPort").toInt() & 0xffff;
} }
// sun // sun
if(request->arg("sunLat") == "" || (request->arg("sunLon") == "")) { if(request->arg("sunLat") == "" || (request->arg("sunLon") == "")) {
mConfig->sunLat = 0.0; mConfig->sun.lat = 0.0;
mConfig->sunLon = 0.0; mConfig->sun.lon = 0.0;
mConfig->sunDisNightCom = false; mConfig->sun.disNightCom = false;
} else { } else {
mConfig->sunLat = request->arg("sunLat").toFloat(); mConfig->sun.lat = request->arg("sunLat").toFloat();
mConfig->sunLon = request->arg("sunLon").toFloat(); mConfig->sun.lon = request->arg("sunLon").toFloat();
mConfig->sunDisNightCom = (request->arg("sunDisNightCom") == "on"); mConfig->sun.disNightCom = (request->arg("sunDisNightCom") == "on");
} }
// mqtt // mqtt
if(request->arg("mqttAddr") != "") { if(request->arg("mqttAddr") != "") {
String addr = request->arg("mqttAddr"); String addr = request->arg("mqttAddr");
@ -444,15 +444,14 @@ void web::showSave(AsyncWebServerRequest *request) {
// serial console // serial console
if(request->arg("serIntvl") != "") { 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->serial.debug = (request->arg("serDbg") == "on");
mConfig->serialShowIv = (request->arg("serEn") == "on"); mConfig->serial.showIv = (request->arg("serEn") == "on");
// Needed to log TX buffers to serial console // Needed to log TX buffers to serial console
mMain->mSys->Radio.mSerialDebug = mConfig->serialDebug; mMain->mSys->Radio.mSerialDebug = mConfig->serial.debug;
} }
mMain->saveSettings();
mMain->saveValues();
if(request->arg("reboot") == "on") if(request->arg("reboot") == "on")
onReboot(request); onReboot(request);
@ -701,7 +700,7 @@ void web::showMetrics(void) {
String metrics; String metrics;
char headline[80]; 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"; metrics += "# TYPE ahoy_solar_info gauge\n" + String(headline) + "\n";
for(uint8_t id = 0; id < mMain->mSys->getNumInverters(); id++) { for(uint8_t id = 0; id < mMain->mSys->getNumInverters(); id++) {

5
src/web/web.h

@ -24,7 +24,7 @@ class webApi;
class web { class web {
public: 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() {} ~web() {}
void setup(void); void setup(void);
@ -85,8 +85,7 @@ class web {
bool mProtected; bool mProtected;
uint32_t mLogoutTimeout; uint32_t mLogoutTimeout;
config_t *mConfig; settings_t *mConfig;
sysConfig_t *mSysCfg;
statistics_t *mStat; statistics_t *mStat;
char *mVersion; char *mVersion;
app *mMain; app *mMain;

69
src/web/webApi.cpp

@ -11,10 +11,9 @@
#include "webApi.h" #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; mSrv = srv;
mApp = app; mApp = app;
mSysCfg = sysCfg;
mConfig = config; mConfig = config;
mStat = stat; mStat = stat;
mVersion = version; mVersion = version;
@ -143,8 +142,8 @@ void webApi::onDwnldSetup(AsyncWebServerRequest *request) {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void webApi::getSysInfo(JsonObject obj) { void webApi::getSysInfo(JsonObject obj) {
obj[F("ssid")] = mSysCfg->stationSsid; obj[F("ssid")] = mConfig->sys.stationSsid;
obj[F("device_name")] = mSysCfg->deviceName; obj[F("device_name")] = mConfig->sys.deviceName;
obj[F("version")] = String(mVersion); obj[F("version")] = String(mVersion);
obj[F("build")] = String(AUTO_GIT_HASH); obj[F("build")] = String(AUTO_GIT_HASH);
obj[F("ts_uptime")] = mApp->getUptime(); obj[F("ts_uptime")] = mApp->getUptime();
@ -153,8 +152,7 @@ void webApi::getSysInfo(JsonObject obj) {
obj[F("ts_sunset")] = mApp->getSunset(); obj[F("ts_sunset")] = mApp->getSunset();
obj[F("ts_sun_upd")] = mApp->getLatestSunTimestamp(); obj[F("ts_sun_upd")] = mApp->getLatestSunTimestamp();
obj[F("wifi_rssi")] = WiFi.RSSI(); obj[F("wifi_rssi")] = WiFi.RSSI();
obj[F("disclaimer")] = mConfig->disclaimer; obj[F("pwd_set")] = (strlen(mConfig->sys.adminPwd) > 0);
obj[F("pwd_set")] = (strlen(mConfig->password) > 0);
#if defined(ESP32) #if defined(ESP32)
obj[F("esp_type")] = F("ESP32"); obj[F("esp_type")] = F("ESP32");
#else #else
@ -221,19 +219,19 @@ void webApi::getInverterList(JsonObject obj) {
if(NULL != iv) { if(NULL != iv) {
JsonObject obj2 = invArr.createNestedObject(); JsonObject obj2 = invArr.createNestedObject();
obj2[F("id")] = i; obj2[F("id")] = i;
obj2[F("name")] = String(iv->name); obj2[F("name")] = String(iv->config->name);
obj2[F("serial")] = String(iv->serial.u64, HEX); obj2[F("serial")] = String(iv->config->serial.u64, HEX);
obj2[F("channels")] = iv->channels; obj2[F("channels")] = iv->channels;
obj2[F("version")] = String(iv->fwVersion); obj2[F("version")] = String(iv->fwVersion);
for(uint8_t j = 0; j < iv->channels; j ++) { for(uint8_t j = 0; j < iv->channels; j ++) {
obj2[F("ch_max_power")][j] = iv->chMaxPwr[j]; obj2[F("ch_max_power")][j] = iv->config->chMaxPwr[j];
obj2[F("ch_name")][j] = iv->chName[j]; obj2[F("ch_name")][j] = iv->config->chName[j];
} }
} }
} }
obj[F("interval")] = String(mConfig->sendInterval); obj[F("interval")] = String(mConfig->nrf.sendInterval);
obj[F("retries")] = String(mConfig->maxRetransPerPyld); obj[F("retries")] = String(mConfig->nrf.maxRetransPerPyld);
obj[F("max_num_inverters")] = MAX_NUM_INVERTERS; obj[F("max_num_inverters")] = MAX_NUM_INVERTERS;
} }
@ -250,23 +248,23 @@ void webApi::getMqtt(JsonObject obj) {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void webApi::getNtp(JsonObject obj) { void webApi::getNtp(JsonObject obj) {
obj[F("addr")] = String(mConfig->ntpAddr); obj[F("addr")] = String(mConfig->ntp.addr);
obj[F("port")] = String(mConfig->ntpPort); obj[F("port")] = String(mConfig->ntp.port);
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void webApi::getSun(JsonObject obj) { void webApi::getSun(JsonObject obj) {
obj[F("lat")] = mConfig->sunLat ? String(mConfig->sunLat, 5) : ""; obj[F("lat")] = mConfig->sun.lat ? String(mConfig->sun.lat, 5) : "";
obj[F("lon")] = mConfig->sunLat ? String(mConfig->sunLon, 5) : ""; obj[F("lon")] = mConfig->sun.lat ? String(mConfig->sun.lon, 5) : "";
obj[F("disnightcom")] = mConfig->sunDisNightCom; obj[F("disnightcom")] = mConfig->sun.disNightCom;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void webApi::getPinout(JsonObject obj) { void webApi::getPinout(JsonObject obj) {
obj[F("cs")] = mConfig->pinCs; obj[F("cs")] = mConfig->nrf.pinCs;
obj[F("ce")] = mConfig->pinCe; obj[F("ce")] = mConfig->nrf.pinCe;
obj[F("irq")] = mConfig->pinIrq; obj[F("irq")] = mConfig->nrf.pinIrq;
obj[F("led0")] = mConfig->led.led0; obj[F("led0")] = mConfig->led.led0;
obj[F("led1")] = mConfig->led.led1; obj[F("led1")] = mConfig->led.led1;
} }
@ -274,25 +272,26 @@ void webApi::getPinout(JsonObject obj) {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void webApi::getRadio(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) { void webApi::getSerial(JsonObject obj) {
obj[F("interval")] = (uint16_t)mConfig->serialInterval; obj[F("interval")] = (uint16_t)mConfig->serial.interval;
obj[F("show_live_data")] = mConfig->serialShowIv; obj[F("show_live_data")] = mConfig->serial.showIv;
obj[F("debug")] = mConfig->serialDebug; obj[F("debug")] = mConfig->serial.debug;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void webApi::getStaticIp(JsonObject obj) { void webApi::getStaticIp(JsonObject obj) {
if(mConfig->staticIp.ip[0] != 0) { if(mConfig->sys.ip.ip[0] != 0) {
obj[F("ip")] = ip2String(mConfig->staticIp.ip); obj[F("ip")] = ip2String(mConfig->sys.ip.ip);
obj[F("mask")] = ip2String(mConfig->staticIp.mask); obj[F("mask")] = ip2String(mConfig->sys.ip.mask);
obj[F("dns")] = ip2String(mConfig->staticIp.dns); obj[F("dns1")] = ip2String(mConfig->sys.ip.dns1);
obj[F("gateway")] = ip2String(mConfig->staticIp.gateway); 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["link"][6] = "/update";
obj["name"][7] = "System"; obj["name"][7] = "System";
obj["link"][7] = "/system"; obj["link"][7] = "/system";
if(strlen(mConfig->password) > 0) { if(strlen(mConfig->sys.adminPwd) > 0) {
obj["name"][8] = "-"; obj["name"][8] = "-";
obj["name"][9] = "Logout"; obj["name"][9] = "Logout";
obj["link"][9] = "/logout"; obj["link"][9] = "/logout";
@ -327,7 +326,7 @@ void webApi::getIndex(JsonObject obj) {
getMenu(obj.createNestedObject(F("menu"))); getMenu(obj.createNestedObject(F("menu")));
getSysInfo(obj.createNestedObject(F("system"))); getSysInfo(obj.createNestedObject(F("system")));
getStatistics(obj.createNestedObject(F("statistics"))); getStatistics(obj.createNestedObject(F("statistics")));
obj["refresh_interval"] = mConfig->sendInterval; obj["refresh_interval"] = mConfig->nrf.sendInterval;
JsonArray inv = obj.createNestedArray(F("inverter")); JsonArray inv = obj.createNestedArray(F("inverter"));
Inverter<> *iv; Inverter<> *iv;
@ -337,7 +336,7 @@ void webApi::getIndex(JsonObject obj) {
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
JsonObject invObj = inv.createNestedObject(); JsonObject invObj = inv.createNestedObject();
invObj[F("id")] = i; 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("version")] = String(iv->fwVersion);
invObj[F("is_avail")] = iv->isAvailable(mApp->getTimestamp(), rec); invObj[F("is_avail")] = iv->isAvailable(mApp->getTimestamp(), rec);
invObj[F("is_producing")] = iv->isProducing(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"))); getMenu(obj.createNestedObject(F("menu")));
getSysInfo(obj.createNestedObject(F("system"))); getSysInfo(obj.createNestedObject(F("system")));
JsonArray invArr = obj.createNestedArray(F("inverter")); 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}; 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) { if(NULL != iv) {
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
JsonObject obj2 = invArr.createNestedObject(); JsonObject obj2 = invArr.createNestedObject();
obj2[F("name")] = String(iv->name); obj2[F("name")] = String(iv->config->name);
obj2[F("channels")] = iv->channels; obj2[F("channels")] = iv->channels;
obj2[F("power_limit_read")] = round3(iv->actPowerLimit); obj2[F("power_limit_read")] = round3(iv->actPowerLimit);
obj2[F("last_alarm")] = String(iv->lastAlarmMsg); 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 ++) { 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(); JsonArray cur = ch.createNestedArray();
for (uint8_t k = 0; k < 6; k++) { for (uint8_t k = 0; k < 6; k++) {
switch(k) { switch(k) {

5
src/web/webApi.h

@ -16,7 +16,7 @@ class app;
class webApi { class webApi {
public: 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 setup(void);
void loop(void); void loop(void);
@ -72,8 +72,7 @@ class webApi {
AsyncWebServer *mSrv; AsyncWebServer *mSrv;
app *mApp; app *mApp;
config_t *mConfig; settings_t *mConfig;
sysConfig_t *mSysCfg;
statistics_t *mStat; statistics_t *mStat;
char *mVersion; char *mVersion;

36
src/wifi/ahoywifi.cpp

@ -16,9 +16,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
ahoywifi::ahoywifi(app *main, sysConfig_t *sysCfg, config_t *config) { ahoywifi::ahoywifi(settings_t *config) {
mMain = main;
mSysCfg = sysCfg;
mConfig = config; mConfig = config;
mDns = new DNSServer(); 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) { void ahoywifi::setup(uint32_t timeout, bool settingValid) {
#ifdef FB_WIFI_OVERRIDDEN #ifdef FB_WIFI_OVERRIDDEN
mStationWifiIsDef = false; mStationWifiIsDef = false;
#else #else
mStationWifiIsDef = (strncmp(mSysCfg->stationSsid, FB_WIFI_SSID, 14) == 0); mStationWifiIsDef = (strncmp(mConfig->sys.stationSsid, FB_WIFI_SSID, 14) == 0);
#endif #endif
mWifiStationTimeout = timeout; mWifiStationTimeout = timeout;
#ifndef AP_ONLY #ifndef AP_ONLY
if(false == mApActive) if(false == mApActive)
mApActive = (mStationWifiIsDef) ? true : setupStation(mWifiStationTimeout); mApActive = (mStationWifiIsDef) ? true : setupStation(mWifiStationTimeout);
#endif #endif
if(!settingValid) { if(!settingValid) {
DPRINTLN(DBG_WARN, F("your settings are not valid! check [IP]/setup")); DPRINTLN(DBG_WARN, F("your settings are not valid! check [IP]/setup"));
mApActive = true; mApActive = true;
@ -58,7 +55,7 @@ void ahoywifi::setup(uint32_t timeout, bool settingValid) {
DPRINTLN(DBG_INFO, F("Welcome to AHOY!")); DPRINTLN(DBG_INFO, F("Welcome to AHOY!"));
DPRINT(DBG_INFO, F("\npoint your browser to http://")); DPRINT(DBG_INFO, F("\npoint your browser to http://"));
if(mApActive) if(mApActive)
DBGPRINTLN(F("192.168.1.1")); DBGPRINTLN(F("192.168.4.1"));
else else
DBGPRINTLN(WiFi.localIP().toString()); DBGPRINTLN(WiFi.localIP().toString());
DPRINTLN(DBG_INFO, F("to configure your device")); DPRINTLN(DBG_INFO, F("to configure your device"));
@ -142,20 +139,21 @@ bool ahoywifi::setupStation(uint32_t timeout) {
} }
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
if(mConfig->staticIp.ip[0] != 0) { if(mConfig->sys.ip.ip[0] != 0) {
IPAddress ip(mConfig->staticIp.ip); IPAddress ip(mConfig->sys.ip.ip);
IPAddress mask(mConfig->staticIp.mask); IPAddress mask(mConfig->sys.ip.mask);
IPAddress dns(mConfig->staticIp.dns); IPAddress dns1(mConfig->sys.ip.dns1);
IPAddress gateway(mConfig->staticIp.gateway); IPAddress dns2(mConfig->sys.ip.dns2);
if(!WiFi.config(ip, gateway, mask, dns)) IPAddress gateway(mConfig->sys.ip.gateway);
if(!WiFi.config(ip, gateway, mask, dns1, dns2))
DPRINTLN(DBG_ERROR, F("failed to set static IP!")); DPRINTLN(DBG_ERROR, F("failed to set static IP!"));
} }
WiFi.begin(mSysCfg->stationSsid, mSysCfg->stationPwd); WiFi.begin(mConfig->sys.stationSsid, mConfig->sys.stationPwd);
if(String(mSysCfg->deviceName) != "") if(String(mConfig->sys.deviceName) != "")
WiFi.hostname(mSysCfg->deviceName); WiFi.hostname(mConfig->sys.deviceName);
delay(2000); 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) { while (WiFi.status() != WL_CONNECTED) {
delay(100); delay(100);
if(cnt % 40 == 0) if(cnt % 40 == 0)
@ -197,8 +195,8 @@ time_t ahoywifi::getNtpTime(void) {
uint8_t buf[NTP_PACKET_SIZE]; uint8_t buf[NTP_PACKET_SIZE];
uint8_t retry = 0; uint8_t retry = 0;
WiFi.hostByName(mConfig->ntpAddr, timeServer); WiFi.hostByName(mConfig->ntp.addr, timeServer);
mUdp->begin(mConfig->ntpPort); mUdp->begin(mConfig->ntp.port);
sendNTPpacket(timeServer); sendNTPpacket(timeServer);

14
src/wifi/ahoywifi.h

@ -7,21 +7,19 @@
#define __AHOYWIFI_H__ #define __AHOYWIFI_H__
#include "../utils/dbg.h" #include "../utils/dbg.h"
#include <Arduino.h>
// NTP
#include <WiFiUdp.h> #include <WiFiUdp.h>
#include <TimeLib.h> #include <TimeLib.h>
#include <DNSServer.h> #include <DNSServer.h>
#include "ESPAsyncWebServer.h"
#include "../defines.h" #include "../config/settings.h"
#include "../app.h"
class app; class app;
class ahoywifi { class ahoywifi {
public: public:
ahoywifi(app *main, sysConfig_t *sysCfg, config_t *config); ahoywifi(settings_t *config);
~ahoywifi() {} ~ahoywifi() {}
void setup(uint32_t timeout, bool settingValid); void setup(uint32_t timeout, bool settingValid);
@ -36,9 +34,7 @@ class ahoywifi {
private: private:
void sendNTPpacket(IPAddress& address); void sendNTPpacket(IPAddress& address);
config_t *mConfig; settings_t *mConfig;
sysConfig_t *mSysCfg;
app *mMain;
DNSServer *mDns; DNSServer *mDns;
WiFiUDP *mUdp; // for time server WiFiUDP *mUdp; // for time server

Loading…
Cancel
Save