diff --git a/.github/workflows/compile_development.yml b/.github/workflows/compile_development.yml
index 8d8ab45e..203e23a7 100644
--- a/.github/workflows/compile_development.yml
+++ b/.github/workflows/compile_development.yml
@@ -47,7 +47,7 @@ jobs:
       run: python convert.py
 
     - name: Run PlatformIO
-      run: pio run -d src --environment esp8266-release --environment esp8285-release --environment esp8266-nokia5110 --environment esp8266-ssd1306 --environment esp32-wroom32-release --environment esp32-wroom32-nokia5110 --environment esp32-wroom32-ssd1306
+      run: pio run -d src --environment esp8266-release --environment esp8285-release --environment esp8266-nokia5110 --environment esp8266-ssd1306 --environment esp8266-sh1106 --environment esp32-wroom32-release --environment esp32-wroom32-nokia5110 --environment esp32-wroom32-ssd1306 --environment esp32-wroom32-sh1106
 
     - name: Rename Binary files
       id: rename-binary-files
diff --git a/.github/workflows/compile_release.yml b/.github/workflows/compile_release.yml
index 20fcef87..91c4c8e3 100644
--- a/.github/workflows/compile_release.yml
+++ b/.github/workflows/compile_release.yml
@@ -51,7 +51,7 @@ jobs:
       run: python convert.py
 
     - name: Run PlatformIO
-      run: pio run -d src --environment esp8266-release --environment esp8285-release --environment esp8266-nokia5110 --environment esp8266-ssd1306 --environment esp32-wroom32-release --environment esp32-wroom32-nokia5110 --environment esp32-wroom32-ssd1306
+      run: pio run -d src --environment esp8266-release --environment esp8285-release --environment esp8266-nokia5110 --environment esp8266-ssd1306 --environment esp8266-sh1106 --environment esp32-wroom32-release --environment esp32-wroom32-nokia5110 --environment esp32-wroom32-ssd1306 --environment esp32-wroom32-sh1106
 
     - name: Rename Binary files
       id: rename-binary-files
diff --git a/User_Manual.md b/User_Manual.md
index 529125a0..be7519ec 100644
--- a/User_Manual.md
+++ b/User_Manual.md
@@ -29,6 +29,7 @@ The AhoyDTU will publish on the following topics
 | `uptime` | 73630 | uptime in seconds | false |
 | `version` | 0.5.61 | current installed verison of AhoyDTU | true |
 | `wifi_rssi` | -75 | WiFi signal strength | false |
+| `ip_addr` | 192.168.178.25 | WiFi Station IP Address | true |
 
 | status code | Remarks |
 |---|---|
@@ -43,6 +44,7 @@ The AhoyDTU will publish on the following topics
 |---|---|---|---|
 | `available` | 2 | see table below | true |
 | `last_success` | 1672155690 | UTC Timestamp | true |
+| `ack_pwr_limit` | true | fast information if inverter has accepted power limit | false |
 
 | status code | Remarks |
 |---|---|
diff --git a/scripts/getVersion.py b/scripts/getVersion.py
index f7c825ce..5f96f37f 100644
--- a/scripts/getVersion.py
+++ b/scripts/getVersion.py
@@ -52,42 +52,52 @@ def readVersion(path, infile):
     os.mkdir(path + "firmware/")
     sha = os.getenv("SHA",default="sha")
 
-    versionout = version[:-1] + "_esp8266_" + sha + ".bin"
+    versionout = version[:-1] + "_" + sha + "_esp8266.bin"
     src = path + ".pio/build/esp8266-release/firmware.bin"
     dst = path + "firmware/" + versionout
     os.rename(src, dst)
 
-    versionout = version[:-1] + "_esp8266_nokia5110_" + sha + ".bin"
+    versionout = version[:-1] + "_" + sha + "_esp8266_nokia5110.bin"
     src = path + ".pio/build/esp8266-nokia5110/firmware.bin"
     dst = path + "firmware/" + versionout
     os.rename(src, dst)
 
-    versionout = version[:-1] + "_esp8266_ssd1306_" + sha + ".bin"
+    versionout = version[:-1] + "_" + sha + "_esp8266_ssd1306.bin"
     src = path + ".pio/build/esp8266-ssd1306/firmware.bin"
     dst = path + "firmware/" + versionout
     os.rename(src, dst)
+
+    versionout = version[:-1] + "_" + sha + "_esp8266_sh1106.bin"
+    src = path + ".pio/build/esp8266-sh1106/firmware.bin"
+    dst = path + "firmware/" + versionout
+    os.rename(src, dst)
     
-    versionout = version[:-1] + "_esp8285_" + sha + ".bin"
+    versionout = version[:-1] + "_" + sha + "_esp8285.bin"
     src = path + ".pio/build/esp8285-release/firmware.bin"
     dst = path + "firmware/" + versionout
     os.rename(src, dst)
     gzip_bin(dst, dst + ".gz")
 
-    versionout = version[:-1] + "_esp32_" + sha + ".bin"
+    versionout = version[:-1] + "_" + sha + "_esp32.bin"
     src = path + ".pio/build/esp32-wroom32-release/firmware.bin"
     dst = path + "firmware/" + versionout
     os.rename(src, dst)
 
-    versionout = version[:-1] + "_esp32_nokia5110_" + sha + ".bin"
+    versionout = version[:-1] + "_" + sha + "_esp32_nokia5110.bin"
     src = path + ".pio/build/esp32-wroom32-nokia5110/firmware.bin"
     dst = path + "firmware/" + versionout
     os.rename(src, dst)
 
-    versionout = version[:-1] + "_esp32_ssd1306_" + sha + ".bin"
+    versionout = version[:-1] + "_" + sha + "_esp32_ssd1306.bin"
     src = path + ".pio/build/esp32-wroom32-ssd1306/firmware.bin"
     dst = path + "firmware/" + versionout
     os.rename(src, dst)
 
+    versionout = version[:-1] + "_" + sha + "_esp32_sh1106.bin"
+    src = path + ".pio/build/esp32-wroom32-sh1106/firmware.bin"
+    dst = path + "firmware/" + versionout
+    os.rename(src, dst)
+
     # other ESP32 bin files
     src = path + ".pio/build/esp32-wroom32-release/"
     dst = path + "firmware/"
diff --git a/src/CHANGES.md b/src/CHANGES.md
index 4a0a2f94..ce304e4b 100644
--- a/src/CHANGES.md
+++ b/src/CHANGES.md
@@ -2,6 +2,29 @@
 
 (starting from release version `0.5.66`)
 
+## 0.5.70
+* corrected MQTT `comm_disabled` #529
+* fix Prometheus and JSON endpoints (`config_override.h`) #561
+* publish MQTT with fixed interval even if inverter is not available #542
+* added JSON settings upload. NOTE: settings JSON download changed, so only settings should be uploaded starting from version `0.5.70` #551
+* MQTT topic and inverter name have more allowed characters: `[A-Za-z0-9./#$%&=+_-]+`, thx: @Mo Demman
+* improved potential issue with `checkTicker`, thx @cbscpe
+* MQTT option for reset values on midnight / not avail / communication stop #539
+* small fix in `tickIVCommunication` #534
+* add `YieldTotal` correction, eg. to have the option to zero at year start #512
+
+## 0.5.69
+* merged SH1106 1.3" Display, thx @dAjaY85
+* added SH1106 to automatic build
+* added IP address to MQTT (version, device and IP are retained and only transmitted once after boot) #556
+* added `set_power_limit` acknowledge MQTT publish #553
+* changed: version, device name are only published via MQTT once after boot
+* added `Login` to menu if admin password is set #554
+* added `development` to second changelog link in `index.html` #543
+* added interval for MQTT (as option). With this settings MQTT live data is published in a fixed timing (only if inverter is available) #542, #523
+* added MQTT `comm_disabled` #529
+* changed name of binaries, moved GIT-Sha to the front #538
+
 ## 0.5.68
 * repaired receive payload
 * Powerlimit is transfered immediately to inverter
diff --git a/src/app.cpp b/src/app.cpp
index 83be5a36..e42b8c2b 100644
--- a/src/app.cpp
+++ b/src/app.cpp
@@ -51,7 +51,7 @@ void app::setup() {
     #endif
 
     mSys->addInverters(&mConfig->inst);
-    mPayload.setup(mSys, &mStat, mConfig->nrf.maxRetransPerPyld, &mTimestamp);
+    mPayload.setup(this, mSys, &mStat, mConfig->nrf.maxRetransPerPyld, &mTimestamp);
     mPayload.enableSerialDebug(mConfig->serial.debug);
 
     if(!mSys->Radio.isChipConnected())
@@ -62,6 +62,9 @@ void app::setup() {
     if (mConfig->mqtt.broker[0] > 0) {
         everySec(std::bind(&PubMqttType::tickerSecond, &mMqtt));
         everyMin(std::bind(&PubMqttType::tickerMinute, &mMqtt));
+        uint32_t nxtTrig = mTimestamp - ((mTimestamp - 1) % 86400) + 86400; // next midnight
+        if(mConfig->mqtt.rstYieldMidNight)
+            onceAt(std::bind(&app::tickMidnight, this), nxtTrig);
         mMqtt.setSubscriptionCb(std::bind(&app::mqttSubRxCb, this, std::placeholders::_1));
     }
     #endif
@@ -161,15 +164,18 @@ void app::tickIVCommunication(void) {
         if (mTimestamp < (mSunrise - mConfig->sun.offsetSec)) { // current time is before communication start, set next trigger to communication start
             nxtTrig = mSunrise - mConfig->sun.offsetSec;
         } else {
-            if (mTimestamp > (mSunset + mConfig->sun.offsetSec)) { // current time is past communication stop, nothing to do. Next update will be done at midnight by tickCalcSunrise
-                return;
+            if (mTimestamp >= (mSunset + mConfig->sun.offsetSec)) { // current time is past communication stop, nothing to do. Next update will be done at midnight by tickCalcSunrise
+                nxtTrig = 0;
             } else { // current time lies within communication start/stop time, set next trigger to communication stop
                 mIVCommunicationOn = true;
                 nxtTrig = mSunset + mConfig->sun.offsetSec;
             }
         }
-        onceAt(std::bind(&app::tickIVCommunication, this), nxtTrig);
+        if (nxtTrig != 0)
+            onceAt(std::bind(&app::tickIVCommunication, this), nxtTrig);
     }
+    if (mConfig->mqtt.broker[0] > 0)
+        mMqtt.tickerComm(!mIVCommunicationOn);
 }
 
 //-----------------------------------------------------------------------------
@@ -204,6 +210,15 @@ void app::tickSend(void) {
     updateLed();
 }
 
+//-----------------------------------------------------------------------------
+void app::tickMidnight(void) {
+    // only used and enabled by MQTT (see setup())
+    uint32_t nxtTrig = mTimestamp - ((mTimestamp - 1) % 86400) + 86400; // next midnight
+    onceAt(std::bind(&app::tickMidnight, this), nxtTrig);
+
+    mMqtt.tickerMidnight();
+}
+
 //-----------------------------------------------------------------------------
 void app::handleIntr(void) {
     DPRINTLN(DBG_VERBOSE, F("app::handleIntr"));
diff --git a/src/app.h b/src/app.h
index 5a4bb494..fdbf9616 100644
--- a/src/app.h
+++ b/src/app.h
@@ -78,6 +78,10 @@ class app : public IApp, public ah::Scheduler {
             return mSettings.saveSettings();
         }
 
+        bool readSettings(const char *path) {
+            return mSettings.readSettings(path);
+        }
+
         bool eraseSettings(bool eraseWifi = false) {
             return mSettings.eraseSettings(eraseWifi);
         }
@@ -95,7 +99,7 @@ class app : public IApp, public ah::Scheduler {
         }
 
         void setRebootFlag() {
-            once(std::bind(&app::tickReboot, this), 1);
+            once(std::bind(&app::tickReboot, this), 3);
         }
 
         const char *getVersion() {
@@ -122,6 +126,10 @@ class app : public IApp, public ah::Scheduler {
             once(std::bind(&PubMqttType::sendDiscoveryConfig, &mMqtt), 1);
         }
 
+        void setMqttPowerLimitAck(Inverter<> *iv) {
+            mMqtt.setPowerLimitAck(iv);
+        }
+
         void ivSendHighPrio(Inverter<> *iv) {
             mPayload.ivSendHighPrio(iv);
         }
@@ -199,6 +207,7 @@ class app : public IApp, public ah::Scheduler {
         void tickCalcSunrise(void);
         void tickIVCommunication(void);
         void tickSend(void);
+        void tickMidnight(void);
         /*void tickSerial(void) {
             if(Serial.available() == 0)
                 return;
diff --git a/src/appInterface.h b/src/appInterface.h
index 64acab6b..c2d191b2 100644
--- a/src/appInterface.h
+++ b/src/appInterface.h
@@ -15,6 +15,7 @@ class IApp {
     public:
         virtual ~IApp() {}
         virtual bool saveSettings() = 0;
+        virtual bool readSettings(const char *path) = 0;
         virtual bool eraseSettings(bool eraseWifi) = 0;
         virtual void setRebootFlag() = 0;
         virtual const char *getVersion() = 0;
@@ -34,6 +35,7 @@ class IApp {
         virtual bool getRebootRequestState() = 0;
         virtual bool getSettingsValid() = 0;
         virtual void setMqttDiscoveryFlag() = 0;
+        virtual void setMqttPowerLimitAck(Inverter<> *iv) = 0;
 
         virtual void ivSendHighPrio(Inverter<> *iv) = 0;
 
diff --git a/src/config/settings.h b/src/config/settings.h
index 47c59a8c..50fbe01d 100644
--- a/src/config/settings.h
+++ b/src/config/settings.h
@@ -96,6 +96,10 @@ typedef struct {
     char user[MQTT_USER_LEN];
     char pwd[MQTT_PWD_LEN];
     char topic[MQTT_TOPIC_LEN];
+    uint16_t interval;
+    bool rstYieldMidNight;
+    bool rstValsNotAvail;
+    bool rstValsCommStop;
 } cfgMqtt_t;
 
 typedef struct {
@@ -104,6 +108,7 @@ typedef struct {
     serial_u serial;
     uint16_t chMaxPwr[4];
     char chName[4][MAX_NAME_LENGTH];
+    uint32_t yieldCor; // YieldTotal correction value
 } cfgIv_t;
 
 typedef struct {
@@ -154,7 +159,7 @@ class settings {
             else
                 DPRINTLN(DBG_INFO, F(" .. done"));
 
-            readSettings();
+            readSettings("/settings.json");
         }
 
         // should be used before OTA
@@ -185,9 +190,10 @@ class settings {
             #endif
         }
 
-        void readSettings(void) {
+        bool readSettings(const char* path) {
+            bool success = false;
             loadDefaults();
-            File fp = LittleFS.open("/settings.json", "r");
+            File fp = LittleFS.open(path, "r");
             if(!fp)
                 DPRINTLN(DBG_WARN, F("failed to load json, using default config"));
             else {
@@ -205,6 +211,7 @@ class settings {
                     jsonMqtt(root["mqtt"]);
                     jsonLed(root["led"]);
                     jsonInst(root["inst"]);
+                    success = true;
                 }
                 else {
                     Serial.println(F("failed to parse json, using default config"));
@@ -212,6 +219,7 @@ class settings {
 
                 fp.close();
             }
+            return success;
         }
 
         bool saveSettings(void) {
@@ -297,6 +305,10 @@ class settings {
             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.mqtt.interval = 0; // off
+            mCfg.mqtt.rstYieldMidNight  = false;
+            mCfg.mqtt.rstValsNotAvail = false;
+            mCfg.mqtt.rstValsCommStop   = false;
 
             mCfg.led.led0 = DEF_LED0_PIN;
             mCfg.led.led1 = DEF_LED1_PIN;
@@ -396,8 +408,17 @@ class settings {
                 obj[F("user")]   = mCfg.mqtt.user;
                 obj[F("pwd")]    = mCfg.mqtt.pwd;
                 obj[F("topic")]  = mCfg.mqtt.topic;
+                obj[F("intvl")]  = mCfg.mqtt.interval;
+                obj[F("rstMidNight")] = (bool)mCfg.mqtt.rstYieldMidNight;
+                obj[F("rstNotAvail")] = (bool)mCfg.mqtt.rstValsNotAvail;
+                obj[F("rstComStop")]  = (bool)mCfg.mqtt.rstValsCommStop;
+
             } else {
-                mCfg.mqtt.port = obj[F("port")];
+                mCfg.mqtt.port     = obj[F("port")];
+                mCfg.mqtt.interval = obj[F("intvl")];
+                mCfg.mqtt.rstYieldMidNight = (bool)obj["rstMidNight"];
+                mCfg.mqtt.rstValsNotAvail  = (bool)obj["rstNotAvail"];
+                mCfg.mqtt.rstValsCommStop  = (bool)obj["rstComStop"];
                 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*>());
@@ -434,9 +455,10 @@ class settings {
 
         void jsonIv(JsonObject obj, cfgIv_t *cfg, bool set = false) {
             if(set) {
-                obj[F("en")]   = (bool)cfg->enabled;
-                obj[F("name")] = cfg->name;
-                obj[F("sn")]   = cfg->serial.u64;
+                obj[F("en")]    = (bool)cfg->enabled;
+                obj[F("name")]  = cfg->name;
+                obj[F("sn")]    = cfg->serial.u64;
+                obj[F("yield")] = cfg->yieldCor;
                 for(uint8_t i = 0; i < 4; i++) {
                     obj[F("pwr")][i]  = cfg->chMaxPwr[i];
                     obj[F("chName")][i] = cfg->chName[i];
@@ -445,6 +467,7 @@ class settings {
                 cfg->enabled = (bool)obj[F("en")];
                 snprintf(cfg->name, MAX_NAME_LENGTH, "%s", obj[F("name")].as<const char*>());
                 cfg->serial.u64 = obj[F("sn")];
+                cfg->yieldCor   = obj[F("yield")];
                 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*>());
diff --git a/src/defines.h b/src/defines.h
index 616caa0d..8ab9ccda 100644
--- a/src/defines.h
+++ b/src/defines.h
@@ -13,7 +13,7 @@
 //-------------------------------------
 #define VERSION_MAJOR       0
 #define VERSION_MINOR       5
-#define VERSION_PATCH       68
+#define VERSION_PATCH       70
 
 //-------------------------------------
 typedef struct {
diff --git a/src/hm/hmInverter.h b/src/hm/hmInverter.h
index 8c1fccc8..f02c6b9b 100644
--- a/src/hm/hmInverter.h
+++ b/src/hm/hmInverter.h
@@ -233,11 +233,13 @@ class Inverter {
                             val <<= 8;
                             val |= buf[ptr];
                         } while(++ptr != end);
-                        if(FLD_T == rec->assign[pos].fieldId) {
+                        if (FLD_T == rec->assign[pos].fieldId) {
                             // temperature is a signed value!
                             rec->record[pos] = (REC_TYP)((int16_t)val) / (REC_TYP)(div);
-                        }
-                        else {
+                        } else if ((FLD_YT == rec->assign[pos].fieldId)
+                                    && (config->yieldCor != 0)) {
+                            rec->record[pos] = (REC_TYP)(val) / (REC_TYP)(div) - (REC_TYP)config->yieldCor;
+                        } else {
                             if ((REC_TYP)(div) > 1)
                                 rec->record[pos] = (REC_TYP)(val) / (REC_TYP)(div);
                             else
@@ -286,6 +288,16 @@ class Inverter {
                 DPRINTLN(DBG_ERROR, F("addValue: assignment not found with cmd 0x"));
         }
 
+        bool setValue(uint8_t pos, record_t<> *rec, REC_TYP val) {
+            DPRINTLN(DBG_VERBOSE, F("hmInverter.h:setValue"));
+            if(NULL == rec)
+                return false;
+            if(pos > rec->length)
+                return false;
+            rec->record[pos] = val;
+            return true;
+        }
+
         REC_TYP getValue(uint8_t pos, record_t<> *rec) {
             DPRINTLN(DBG_VERBOSE, F("hmInverter.h:getValue"));
             if(NULL == rec)
diff --git a/src/hm/payload.h b/src/hm/payload.h
index b0ea7464..1f112844 100644
--- a/src/hm/payload.h
+++ b/src/hm/payload.h
@@ -35,7 +35,8 @@ class Payload : public Handler<payloadListenerType> {
     public:
         Payload() : Handler() {}
 
-        void setup(HMSYSTEM *sys, statistics_t *stat, uint8_t maxRetransmits, uint32_t *timestamp) {
+        void setup(IApp *app, HMSYSTEM *sys, statistics_t *stat, uint8_t maxRetransmits, uint32_t *timestamp) {
+            mApp        = app;
             mSys        = sys;
             mStat       = stat;
             mMaxRetrans = maxRetransmits;
@@ -141,7 +142,11 @@ class Payload : public Handler<payloadListenerType> {
                 iv->devControlRequest = false;
 
                 if ((p->packet[12] == ActivePowerContr) && (p->packet[13] == 0x00)) {
-                    String msg = (p->packet[10] == 0x00 && p->packet[11] == 0x00) ? "" : "NOT ";
+                    String msg = "";
+                    if((p->packet[10] == 0x00) && (p->packet[11] == 0x00)) {
+                        msg = "NOT ";
+                        mApp->setMqttPowerLimitAck(iv);
+                    }
                     DPRINTLN(DBG_INFO, F("Inverter ") + String(iv->id) + F(" has ") + msg + F("accepted power limit set point ") + String(iv->powerLimit[0]) + F(" with PowerLimitControl ") + String(iv->powerLimit[1]));
                 }
                 iv->devControlCmd = Init;
@@ -272,6 +277,7 @@ class Payload : public Handler<payloadListenerType> {
         }
 
     private:
+        IApp *mApp;
         HMSYSTEM *mSys;
         statistics_t *mStat;
         uint8_t mMaxRetrans;
diff --git a/src/publisher/pubMqtt.h b/src/publisher/pubMqtt.h
index bd7a709f..85742bd5 100644
--- a/src/publisher/pubMqtt.h
+++ b/src/publisher/pubMqtt.h
@@ -41,11 +41,13 @@ class PubMqtt {
         ~PubMqtt() { }
 
         void setup(cfgMqtt_t *cfg_mqtt, const char *devName, const char *version, HMSYSTEM *sys, uint32_t *utcTs) {
-            mCfgMqtt        = cfg_mqtt;
-            mDevName        = devName;
-            mVersion        = version;
-            mSys            = sys;
-            mUtcTimestamp   = utcTs;
+            mCfgMqtt         = cfg_mqtt;
+            mDevName         = devName;
+            mVersion         = version;
+            mSys             = sys;
+            mUtcTimestamp    = utcTs;
+            mExeOnce         = true;
+            mIntervalTimeout = 1;
 
             snprintf(mLwtTopic, MQTT_TOPIC_LEN + 5, "%s/mqtt", mCfgMqtt->topic);
 
@@ -73,7 +75,16 @@ class PubMqtt {
         }
 
         void tickerSecond() {
-            sendIvData();
+            if(0 == mCfgMqtt->interval) // no fixed interval, publish once new data were received (from inverter)
+                sendIvData();
+            else { // send mqtt data in a fixed interval
+                if(--mIntervalTimeout == 0) {
+                    mIntervalTimeout = mCfgMqtt->interval;
+                    mSendList.push(RealTimeRunData_Debug);
+                    sendIvData();
+                }
+            }
+
         }
 
         void tickerMinute() {
@@ -98,9 +109,37 @@ class PubMqtt {
             publish("dis_night_comm", ((disNightCom) ? "true" : "false"), true);
         }
 
+        void tickerComm(bool disabled) {
+            publish("comm_disabled", ((disabled) ? "true" : "false"), true);
+            publish("comm_dis_ts", String(*mUtcTimestamp).c_str(), true);
+
+            if(disabled && (mCfgMqtt->rstValsCommStop))
+                zeroAllInverters();
+        }
+
+        void tickerMidnight() {
+            Inverter<> *iv;
+            record_t<> *rec;
+
+            // set YieldDay to zero
+            for (uint8_t id = 0; id < mSys->getNumInverters(); id++) {
+                iv = mSys->getInverterByPos(id);
+                if (NULL == iv)
+                    continue; // skip to next inverter
+                rec = iv->getRecordStruct(RealTimeRunData_Debug);
+                uint8_t pos = iv->getPosByChFld(CH0, FLD_YD, rec);
+                iv->setValue(pos, rec, 0.0f);
+            }
+
+            mSendList.push(RealTimeRunData_Debug);
+            sendIvData();
+        }
+
         void payloadEventListener(uint8_t cmd) {
-            if(mClient.connected()) // prevent overflow if MQTT broker is not reachable but set
-                mSendList.push(cmd);
+            if(mClient.connected()) { // prevent overflow if MQTT broker is not reachable but set
+                if((0 == mCfgMqtt->interval) || (RealTimeRunData_Debug != cmd)) // no interval or no live data
+                    mSendList.push(cmd);
+            }
         }
 
         void publish(const char *subTopic, const char *payload, bool retained = false, bool addTopic = true) {
@@ -188,6 +227,15 @@ class PubMqtt {
             }
         }
 
+        void setPowerLimitAck(Inverter<> *iv) {
+            if (NULL != iv) {
+                char topic[7 + MQTT_TOPIC_LEN];
+
+                snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/ack_pwr_limit", iv->config->name);
+                publish(topic, "true", true);
+            }
+        }
+
     private:
         #if defined(ESP8266)
         void onWifiConnect(const WiFiEventStationModeGotIP& event) {
@@ -223,8 +271,12 @@ class PubMqtt {
             DPRINTLN(DBG_INFO, F("MQTT connected"));
             mEnReconnect = true;
 
-            publish("version", mVersion, true);
-            publish("device", mDevName, true);
+            if(mExeOnce) {
+                publish("version", mVersion, true);
+                publish("device", mDevName, true);
+                publish("ip_addr", WiFi.localIP().toString().c_str(), true);
+                mExeOnce = false;
+            }
             tickerMinute();
             publish(mLwtTopic, mLwtOnline, true, false);
 
@@ -363,18 +415,21 @@ class PubMqtt {
                         allAvail = false;
                     }
                 }
-                else if (!iv->isProducing(*mUtcTimestamp, rec)) {
+                else {
                     mIvAvail = true;
-                    if (MQTT_STATUS_AVAIL_PROD == status)
-                        status = MQTT_STATUS_AVAIL_NOT_PROD;
+                    if (!iv->isProducing(*mUtcTimestamp, rec)) {
+                        if (MQTT_STATUS_AVAIL_PROD == status)
+                            status = MQTT_STATUS_AVAIL_NOT_PROD;
+                    }
                 }
-                else
-                    mIvAvail = true;
 
                 if(mLastIvState[id] != status) {
                     mLastIvState[id] = status;
                     changed = true;
 
+                    if(mCfgMqtt->rstValsNotAvail)
+                        zeroValues(iv);
+
                     snprintf(topic, 32 + MAX_NAME_LENGTH, "%s/available", iv->config->name);
                     snprintf(val, 40, "%d", status);
                     publish(topic, val, true);
@@ -388,12 +443,13 @@ class PubMqtt {
             if(changed) {
                 snprintf(val, 32, "%d", ((allAvail) ? MQTT_STATUS_ONLINE : ((mIvAvail) ? MQTT_STATUS_PARTIAL : MQTT_STATUS_OFFLINE)));
                 publish("status", val, true);
+                sendIvData(false); // false prevents loop of same function
             }
 
             return totalComplete;
         }
 
-        void sendIvData(void) {
+        void sendIvData(bool sendTotals = true) {
             if(mSendList.empty())
                 return;
 
@@ -411,49 +467,52 @@ class PubMqtt {
                     record_t<> *rec = iv->getRecordStruct(mSendList.front());
 
                     // data
-                    if(iv->isAvailable(*mUtcTimestamp, rec)) {
-                        for (uint8_t i = 0; i < rec->length; i++) {
-                            bool retained = false;
-                            if (mSendList.front() == RealTimeRunData_Debug) {
+                    //if(iv->isAvailable(*mUtcTimestamp, rec) || (0 != mCfgMqtt->interval)) { // is avail or fixed pulish interval was set
+                    for (uint8_t i = 0; i < rec->length; i++) {
+                        bool retained = false;
+                        if (mSendList.front() == RealTimeRunData_Debug) {
+                            switch (rec->assign[i].fieldId) {
+                                case FLD_YT:
+                                case FLD_YD:
+                                    retained = true;
+                                    break;
+                            }
+                        }
+
+                        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, "%g", ah::round3(iv->getValue(i, rec)));
+                        publish(topic, val, retained);
+
+                        // calculate total values for RealTimeRunData_Debug
+                        if (mSendList.front() == RealTimeRunData_Debug) {
+                            if (CH0 == rec->assign[i].ch) {
                                 switch (rec->assign[i].fieldId) {
+                                    case FLD_PAC:
+                                        total[0] += iv->getValue(i, rec);
+                                        break;
                                     case FLD_YT:
+                                        total[1] += iv->getValue(i, rec);
+                                        break;
                                     case FLD_YD:
-                                        retained = true;
+                                        total[2] += iv->getValue(i, rec);
+                                        break;
+                                    case FLD_PDC:
+                                        total[3] += iv->getValue(i, rec);
                                         break;
                                 }
                             }
-
-                            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, "%g", ah::round3(iv->getValue(i, rec)));
-                            publish(topic, val, retained);
-
-                            // calculate total values for RealTimeRunData_Debug
-                            if (mSendList.front() == RealTimeRunData_Debug) {
-                                if (CH0 == rec->assign[i].ch) {
-                                    switch (rec->assign[i].fieldId) {
-                                        case FLD_PAC:
-                                            total[0] += iv->getValue(i, rec);
-                                            break;
-                                        case FLD_YT:
-                                            total[1] += iv->getValue(i, rec);
-                                            break;
-                                        case FLD_YD:
-                                            total[2] += iv->getValue(i, rec);
-                                            break;
-                                        case FLD_PDC:
-                                            total[3] += iv->getValue(i, rec);
-                                            break;
-                                    }
-                                }
-                                sendTotal = true;
-                            }
-                            yield();
+                            sendTotal = true;
                         }
+                        yield();
                     }
+                    //}
                 }
 
                 mSendList.pop(); // remove from list once all inverters were processed
 
+                if(!sendTotals) // skip total value calculation
+                    continue;
+
                 if ((true == sendTotal) && processIvStatus()) {
                     uint8_t fieldId;
                     for (uint8_t i = 0; i < 4; i++) {
@@ -480,6 +539,47 @@ class PubMqtt {
             }
         }
 
+        void zeroAllInverters() {
+            Inverter<> *iv;
+
+            // set values to zero, exept yields
+            for (uint8_t id = 0; id < mSys->getNumInverters(); id++) {
+                iv = mSys->getInverterByPos(id);
+                if (NULL == iv)
+                    continue; // skip to next inverter
+
+                zeroValues(iv);
+            }
+            sendIvData();
+        }
+
+        void zeroValues(Inverter<> *iv) {
+            record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
+            for(uint8_t ch = 0; ch <= iv->channels; ch++) {
+                uint8_t pos = 0;
+                uint8_t fld = 0;
+                while(0xff != pos) {
+                    switch(fld) {
+                        case FLD_YD:
+                        case FLD_YT:
+                        case FLD_FW_VERSION:
+                        case FLD_FW_BUILD_YEAR:
+                        case FLD_FW_BUILD_MONTH_DAY:
+                        case FLD_FW_BUILD_HOUR_MINUTE:
+                        case FLD_HW_ID:
+                        case FLD_ACT_ACTIVE_PWR_LIMIT:
+                            continue;
+                            break;
+                    }
+                    pos = iv->getPosByChFld(ch, fld, rec);
+                    iv->setValue(pos, rec, 0.0f);
+                    fld++;
+                }
+            }
+
+            mSendList.push(RealTimeRunData_Debug);
+        }
+
         espMqttClient mClient;
         cfgMqtt_t *mCfgMqtt;
         #if defined(ESP8266)
@@ -494,6 +594,8 @@ class PubMqtt {
         subscriptionCb mSubscriptionCb;
         bool mIvAvail; // shows if at least one inverter is available
         uint8_t mLastIvState[MAX_NUM_INVERTERS];
+        bool mExeOnce;
+        uint16_t mIntervalTimeout;
 
         // last will topic and payload must be available trough lifetime of 'espMqttClient'
         char mLwtTopic[MQTT_TOPIC_LEN+5];
diff --git a/src/utils/ahoyTimer.h b/src/utils/ahoyTimer.h
index 5c960a34..08c09016 100644
--- a/src/utils/ahoyTimer.h
+++ b/src/utils/ahoyTimer.h
@@ -15,7 +15,7 @@ namespace ah {
             *ticker = mil + interval;
             return true;
         }
-        else if(mil < (*ticker - interval)) {
+        else if((mil + interval) < (*ticker)) {
             *ticker = mil + interval;
             return true;
         }
diff --git a/src/utils/llist.h b/src/utils/llist.h
deleted file mode 100644
index 69750f19..00000000
--- a/src/utils/llist.h
+++ /dev/null
@@ -1,110 +0,0 @@
-//-----------------------------------------------------------------------------
-// 2022 Ahoy, https://ahoydtu.de
-// Lukas Pusch, lukas@lpusch.de
-// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
-//-----------------------------------------------------------------------------
-#ifndef __LIST_H__
-#define __LIST_H__
-
-template<class T, class... Args>
-struct node_s {
-    typedef T dT;
-    node_s *pre;
-    node_s *nxt;
-    uint8_t id;
-    dT d;
-    node_s() : pre(NULL), nxt(NULL), d() {}
-    node_s(Args... args) : id(0), pre(NULL), nxt(NULL), d(args...) {}
-};
-
-template<int MAX_NUM, class T, class... Args>
-class llist {
-    typedef node_s<T, Args...> elmType;
-    typedef T dataType;
-    public:
-        llist() : root(mPool) {
-            root = NULL;
-            elmType *p = mPool;
-            for(uint32_t i = 0; i < MAX_NUM; i++) {
-                p->id = i;
-                p++;
-            }
-            mFill = mMax = 0;
-        }
-
-        elmType *add(Args... args) {
-            elmType *p = root, *t;
-            if(NULL == (t = getFreeNode()))
-                return NULL;
-            if(++mFill > mMax)
-                mMax = mFill;
-
-            if(NULL == root) {
-                p = root = t;
-                p->pre = p;
-                p->nxt = p;
-            }
-            else {
-                p = root->pre;
-                t->pre = p;
-                p->nxt->pre = t;
-                t->nxt = p->nxt;
-                p->nxt = t;
-            }
-            t->d = dataType(args...);
-            return p;
-        }
-
-        elmType *getFront() {
-            return root;
-        }
-
-        elmType *get(elmType *p) {
-            p = p->nxt;
-            return (p == root) ? NULL : p;
-        }
-
-        elmType *rem(elmType *p) {
-            if(NULL == p)
-                return NULL;
-            elmType *t = p->nxt;
-            p->nxt->pre = p->pre;
-            p->pre->nxt = p->nxt;
-            if((root == p) && (p->nxt == p))
-                root = NULL;
-            else
-                root = p->nxt;
-            p->nxt = NULL;
-            p->pre = NULL;
-            p = NULL;
-            mFill--;
-            return (NULL == root) ? NULL : ((t == root) ? NULL : t);
-        }
-
-        uint16_t getFill(void) {
-            return mFill;
-        }
-
-        uint16_t getMaxFill(void) {
-            return mMax;
-        }
-
-    protected:
-        elmType *root;
-
-    private:
-        elmType *getFreeNode(void) {
-            elmType *n = mPool;
-            for(uint32_t i = 0; i < MAX_NUM; i++) {
-                if(NULL == n->nxt)
-                    return n;
-                n++;
-            }
-            return NULL;
-        }
-
-        elmType mPool[MAX_NUM];
-        uint16_t mFill, mMax;
-};
-
-#endif /*__LIST_H__*/
diff --git a/src/utils/scheduler.h b/src/utils/scheduler.h
index 36dcdaae..330ab080 100644
--- a/src/utils/scheduler.h
+++ b/src/utils/scheduler.h
@@ -129,6 +129,7 @@ namespace ah {
                                 mTickerInUse[i] = false;
                             else
                                 mTicker[i].timeout = mTicker[i].reload;
+                            //DPRINTLN(DBG_INFO, "checkTick " + String(i) + " reload: " + String(mTicker[i].reload) + ", timeout: " + String(mTicker[i].timeout));
                             (mTicker[i].c)();
                             yield();
                         }
diff --git a/src/web/RestApi.h b/src/web/RestApi.h
index 4246a866..07c12994 100644
--- a/src/web/RestApi.h
+++ b/src/web/RestApi.h
@@ -134,17 +134,34 @@ class RestApi {
             ep[F("record/config")] = url + F("record/config");
             ep[F("record/live")]   = url + F("record/live");
         }
+
         void onDwnldSetup(AsyncWebServerRequest *request) {
-            AsyncJsonResponse* response = new AsyncJsonResponse(false, 8192);
-            JsonObject root = response->getRoot();
+            AsyncWebServerResponse *response;
 
-            getSetup(root);
+            File fp = LittleFS.open("/settings.json", "r");
+            if(!fp) {
+                DPRINTLN(DBG_ERROR, F("failed to load settings"));
+                response = request->beginResponse(200, F("application/json"), "{}");
+            }
+            else {
+                String tmp = fp.readString();
+                int i = 0;
+                // remove all passwords
+                while (i != -1) {
+                    i = tmp.indexOf("\"pwd\":", i);
+                    if(-1 != i) {
+                        i+=7;
+                        tmp.remove(i, tmp.indexOf("\"", i)-i);
+                    }
+                }
+                response = request->beginResponse(200, F("application/json"), tmp);
+            }
 
-            response->setLength();
             response->addHeader("Content-Type", "application/octet-stream");
             response->addHeader("Content-Description", "File Transfer");
             response->addHeader("Content-Disposition", "attachment; filename=ahoy_setup.json");
             request->send(response);
+            fp.close();
         }
 
         void getGeneric(JsonObject obj) {
@@ -165,7 +182,7 @@ class RestApi {
             obj[F("device_name")]  = mConfig->sys.deviceName;
 
             obj[F("mac")]          = WiFi.macAddress();
-            obj[F("hostname")]     = WiFi.getHostname();
+            obj[F("hostname")]     = mConfig->sys.deviceName;
             obj[F("pwd_set")]      = (strlen(mConfig->sys.adminPwd) > 0);
             obj[F("prot_mask")]    = mConfig->sys.protectionMask;
 
@@ -263,6 +280,7 @@ class RestApi {
                     obj2[F("serial")]   = String(iv->config->serial.u64, HEX);
                     obj2[F("channels")] = iv->channels;
                     obj2[F("version")]  = String(iv->getFwVersion());
+                    obj2[F("yieldCor")] = iv->config->yieldCor;
 
                     for(uint8_t j = 0; j < iv->channels; j ++) {
                         obj2[F("ch_max_power")][j] = iv->config->chMaxPwr[j];
@@ -276,11 +294,15 @@ class RestApi {
         }
 
         void getMqtt(JsonObject obj) {
-            obj[F("broker")] = String(mConfig->mqtt.broker);
-            obj[F("port")]   = String(mConfig->mqtt.port);
-            obj[F("user")]   = String(mConfig->mqtt.user);
-            obj[F("pwd")]    = (strlen(mConfig->mqtt.pwd) > 0) ? F("{PWD}") : String("");
-            obj[F("topic")]  = String(mConfig->mqtt.topic);
+            obj[F("broker")]     = String(mConfig->mqtt.broker);
+            obj[F("port")]       = String(mConfig->mqtt.port);
+            obj[F("user")]       = String(mConfig->mqtt.user);
+            obj[F("pwd")]        = (strlen(mConfig->mqtt.pwd) > 0) ? F("{PWD}") : String("");
+            obj[F("topic")]      = String(mConfig->mqtt.topic);
+            obj[F("interval")]   = String(mConfig->mqtt.interval);
+            obj[F("rstMid")]     = (bool)mConfig->mqtt.rstYieldMidNight;
+            obj[F("rstNAvail")]  = (bool)mConfig->mqtt.rstValsNotAvail;
+            obj[F("rstComStop")] = (bool)mConfig->mqtt.rstValsCommStop;
         }
 
         void getNtp(JsonObject obj) {
@@ -357,10 +379,15 @@ class RestApi {
             obj[F("name")][i] = "Documentation";
             obj[F("link")][i] = "https://ahoydtu.de";
             obj[F("trgt")][i++] = "_blank";
-            if((strlen(mConfig->sys.adminPwd) > 0) && !mApp->getProtection()) {
+            if(strlen(mConfig->sys.adminPwd) > 0) {
                 obj[F("name")][i++] = "-";
-                obj[F("name")][i] = "Logout";
-                obj[F("link")][i++] = "/logout";
+                if(mApp->getProtection()) {
+                    obj[F("name")][i] = "Login";
+                    obj[F("link")][i++] = "/login";
+                } else {
+                    obj[F("name")][i] = "Logout";
+                    obj[F("link")][i++] = "/logout";
+                }
             }
         }
 
@@ -411,6 +438,8 @@ class RestApi {
             JsonArray info = obj.createNestedArray(F("infos"));
             if(mApp->getMqttIsConnected())
                 info.add(F("MQTT is connected, ") + String(mApp->getMqttTxCnt()) + F(" packets sent, ") + String(mApp->getMqttRxCnt()) + F(" packets received"));
+            if(mConfig->mqtt.interval > 0)
+                info.add(F("MQTT publishes in a fixed interval of ") + String(mConfig->mqtt.interval) + F(" seconds"));
         }
 
         void getSetup(JsonObject obj) {
diff --git a/src/web/html/index.html b/src/web/html/index.html
index 0cd7f430..6634197e 100644
--- a/src/web/html/index.html
+++ b/src/web/html/index.html
@@ -51,7 +51,7 @@
                         <li>Discuss with us on <a href="https://discord.gg/WzhxEY62mB">Discord</a></li>
                         <li>Report <a href="https://github.com/lumapu/ahoy/issues" target="_blank">issues</a></li>
                         <li>Contribute to <a href="https://github.com/lumapu/ahoy/blob/main/User_Manual.md"  target="_blank">documentation</a></li>
-                        <li><a href="https://nightly.link/lumapu/ahoy/workflows/compile_development/development03/ahoydtu_dev.zip" target="_blank">Download</a> & Test development firmware, <a href="https://github.com/lumapu/ahoy/blob/development03/src/CHANGES.md" target="_blank">Changelog</a></li>
+                        <li><a href="https://nightly.link/lumapu/ahoy/workflows/compile_development/development03/ahoydtu_dev.zip" target="_blank">Download</a> & Test development firmware, <a href="https://github.com/lumapu/ahoy/blob/development03/src/CHANGES.md" target="_blank">Development Changelog</a></li>
                         <li>make a <a href="https://paypal.me/lupusch"  target="_blank">donation</a></li>
                     </ul>
                     <p class="lic">
diff --git a/src/web/html/setup.html b/src/web/html/setup.html
index cf57f12b..aad46232 100644
--- a/src/web/html/setup.html
+++ b/src/web/html/setup.html
@@ -31,7 +31,13 @@
         <div id="wrapper">
             <div id="content">
                 <a class="btn" href="/erase">ERASE SETTINGS (not WiFi)</a>
-
+                <fieldset>
+                    <legend class="des">Upload JSON Settings</legend>
+                    <form id="form" method="POST" action="/upload" enctype="multipart/form-data" accept-charset="utf-8">
+                        <input type="file" name="upload">
+                        <input type="button" class="btn" value="Upload" onclick="hide()">
+                    </form>
+                </fieldset>
                 <form method="post" action="/save">
                     <fieldset>
                         <legend class="des">Device Host Name</legend>
@@ -94,7 +100,7 @@
                         <input type="button" id="btnAdd" class="btn" value="Add Inverter"/>
                         <p class="subdes">General</p>
                         <label for="invInterval">Interval [s]</label>
-                        <input type="text" class="text" name="invInterval"/>
+                        <input type="text" class="text" name="invInterval" pattern="[0-9]+" title="Invalid input"/>
                         <label for="invRetry">Max retries per Payload</label>
                         <input type="text" class="text" name="invRetry"/>
                     </fieldset>
@@ -147,7 +153,16 @@
                         <label for="mqttPwd">Password (optional)</label>
                         <input type="password" class="text" name="mqttPwd"/>
                         <label for="mqttTopic">Topic</label>
-                        <input type="text" class="text" name="mqttTopic" pattern="[A-Za-z0-9.\-_\+\/]+" title="Invalid input" />
+                        <input type="text" class="text" name="mqttTopic" pattern="[A-Za-z0-9./#$%&=+_-]+" title="Invalid input" />
+                        <label for="mqttRstMid">Reset YieldDay at Midnight</label>
+                        <input type="checkbox" class="cb" name="mqttRstMid"/><br/>
+                        <label for="mqttRstComStop">Reset Values at Communication stop</label>
+                        <input type="checkbox" class="cb" name="mqttRstComStop"/><br/>
+                        <label for="mqttRstNotAvail">Reset Values on 'not available'</label>
+                        <input type="checkbox" class="cb" name="mqttRstNotAvail"/><br/>
+                        <p class="des">Send Inverter data in a fixed interval, even if there is no change. A value of '0' disables the fixed interval. The data is published once it was successfully received from inverter. (default: 0)</p>
+                        <label for="mqttIntvl">Interval [s]</label>
+                        <input type="text" class="text" name="mqttInterval" pattern="[0-9]+" title="Invalid input" />
                         <label for="mqttBtn">Discovery Config (homeassistant)</label>
                         <input type="button" name="mqttDiscovery" id="mqttDiscovery" class="btn" value="send" onclick="sendDiscoveryConfig()"/>
                         <span id="apiResultMqtt"></span>
@@ -170,7 +185,7 @@
                         <label for="serDbg">Serial Debug</label>
                         <input type="checkbox" class="cb" name="serDbg"/><br/>
                         <label for="serIntvl">Interval [s]</label>
-                        <input type="text" class="text" name="serIntvl"/>
+                        <input type="text" class="text" name="serIntvl" pattern="[0-9]+" title="Invalid input"/>
                     </fieldset>
                     </div>
 
@@ -181,7 +196,7 @@
                     </div>
                     <div class="hr mb-3 mt-3"></div>
                     <div class="mb-4">
-                        <a href="/get_setup" target="_blank">Download your settings (JSON file)</a> (only saved values)
+                        <a href="/get_setup" target="_blank">Download your settings (JSON file)</a> (only saved values, passwords will be removed!)
                     </div>
                 </form>
             </div>
@@ -209,8 +224,9 @@
             const re = /11[2,4,6]1.*/;
 
             document.getElementById("btnAdd").addEventListener("click", function() {
-                if(highestId <= (maxInv-1))
-                    ivHtml(JSON.parse('{"enabled":true,"name":"","serial":"","channels":4,"ch_max_power":[0,0,0,0],"ch_name":["","","",""]}'), highestId + 1);
+                if(highestId <= (maxInv-1)) {
+                    ivHtml(JSON.parse('{"enabled":true,"name":"","serial":"","channels":4,"ch_max_power":[0,0,0,0],"ch_name":["","","",""]}'), highestId);
+                }
             });
 
             function apiCbWifi(obj) {
@@ -265,6 +281,12 @@
                 getAjax("/api/setup", apiCbMqtt, "POST", JSON.stringify(obj));
             }
 
+            function hide() {
+                document.getElementById("form").submit();
+                var e = document.getElementById("content");
+                e.replaceChildren(span("upload started"));
+            }
+
             function delIv() {
                 var id = this.id.substring(0,4);
                 var e = document.getElementsByName(id + "Addr")[0];
@@ -275,8 +297,8 @@
             }
 
             function ivHtml(obj, id) {
-                highestId = id;
-                if(highestId == (maxInv - 1))
+                highestId = id + 1;
+                if(highestId == maxInv)
                     setHide("btnAdd", true);
                 iv = document.getElementById("inverter");
                 iv.appendChild(des("Inverter " + id));
@@ -289,7 +311,7 @@
                 iv.appendChild(br());
 
                 iv.appendChild(lbl(id + "Addr", "Serial Number (12 digits)*"));
-                var addr = inp(id + "Addr", obj["serial"], 12);
+                var addr = inp(id + "Addr", obj["serial"], 12, ["text"], null, "text", "[0-9]+", "Invalid input");
                 iv.appendChild(addr);
                 ['keyup', 'change'].forEach(function(evt) {
                     addr.addEventListener(evt, (e) => {
@@ -320,7 +342,7 @@
 
                 iv.append(
                     lbl(id + "Name", "Name*"),
-                    inp(id + "Name", obj["name"], 32, ["text"], null, "text", "[A-Za-z0-9.\\-_\\+\\/]+", "Invalid input")
+                    inp(id + "Name", obj["name"], 32, ["text"], null, "text", "[A-Za-z0-9./#$%&=+_-]+", "Invalid input")
                 );
 
                 for(var j of [["ModPwr", "ch_max_power", "Max Module Power (Wp)", 4, "[0-9]+"], ["ModName", "ch_name", "Module Name", 16, null]]) {
@@ -336,10 +358,15 @@
                     iv.appendChild(d);
                 }
 
+                iv.append(
+                    br(),
+                    lbl(id + "YieldCor", "Yield Total Correction (will be subtracted) [kWh]"),
+                    inp(id + "YieldCor", obj["yieldCor"], 32, ["text"], null, "text", "[0-9]+", "Invalid input")
+                );
+
                 var del = inp(id+"del", "X", 0, ["btn", "btnDel"], id+"del", "button");
                 del.addEventListener("click", delIv);
                 iv.append(
-                    br(),
                     lbl(id + "lbldel", "Delete"),
                     del
                 );
@@ -389,8 +416,11 @@
             }
 
             function parseMqtt(obj) {
-                for(var i of [["Addr", "broker"], ["Port", "port"], ["User", "user"], ["Pwd", "pwd"], ["Topic", "topic"]])
+                for(var i of [["Addr", "broker"], ["Port", "port"], ["User", "user"], ["Pwd", "pwd"], ["Topic", "topic"], ["Interval", "interval"]])
                     document.getElementsByName("mqtt"+i[0])[0].value = obj[i[1]];
+
+                for(var i of [["Mid", "rstMid"], ["ComStop", "rstNAvail"], ["NotAvail", "rstComStop"]])
+                    document.getElementsByName("mqttRst"+i[0])[0].checked = obj[i[1]];
             }
 
             function parseNtp(obj) {
diff --git a/src/web/html/update.html b/src/web/html/update.html
index 215188db..e9bcde87 100644
--- a/src/web/html/update.html
+++ b/src/web/html/update.html
@@ -23,7 +23,6 @@
                     <input type="file" name="update">
                     <input type="button" class="btn" value="Update" onclick="hide()">
                 </form>
-
             </div>
         </div>
         <div id="footer">
diff --git a/src/web/web.h b/src/web/web.h
index d8922747..db67f8e5 100644
--- a/src/web/web.h
+++ b/src/web/web.h
@@ -82,7 +82,9 @@ class Web {
 
             mWeb.on("/update",         HTTP_GET,  std::bind(&Web::onUpdate,       this, std::placeholders::_1));
             mWeb.on("/update",         HTTP_POST, std::bind(&Web::showUpdate,     this, std::placeholders::_1),
-                                                   std::bind(&Web::showUpdate2,    this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6));
+                                                  std::bind(&Web::showUpdate2,    this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6));
+            mWeb.on("/upload",         HTTP_POST, std::bind(&Web::onUpload,       this, std::placeholders::_1),
+                                                  std::bind(&Web::onUpload2,      this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6));
             mWeb.on("/serial",         HTTP_GET,  std::bind(&Web::onSerial,       this, std::placeholders::_1));
 
 
@@ -92,6 +94,8 @@ class Web {
             mWeb.begin();
 
             registerDebugCb(std::bind(&Web::serialCb, this, std::placeholders::_1)); // dbg.h
+
+            mUploadFail = false;
         }
 
         void tickSecond() {
@@ -150,6 +154,34 @@ class Web {
             }
         }
 
+        void onUpload2(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
+            if(!index) {
+                mUploadFail = false;
+                mUploadFp = LittleFS.open("/tmp.json", "w");
+                if(!mUploadFp) {
+                    DPRINTLN(DBG_ERROR, F("can't open file!"));
+                    mUploadFail = true;
+                    mUploadFp.close();
+                }
+            }
+            mUploadFp.write(data, len);
+            if(final) {
+                mUploadFp.close();
+                File fp = LittleFS.open("/tmp.json", "r");
+                if(!fp)
+                    mUploadFail = true;
+                else {
+                    if(!mApp->readSettings("tmp.json")) {
+                        mUploadFail = true;
+                        DPRINTLN(DBG_ERROR, F("upload JSON error!"));
+                    }
+                    else
+                        mApp->saveSettings();
+                }
+                DPRINTLN(DBG_INFO, F("upload finished!"));
+            }
+        }
+
         void serialCb(String msg) {
             if(!mSerialClientConnnected)
                 return;
@@ -214,6 +246,23 @@ class Web {
                 mApp->setRebootFlag();
         }
 
+        void onUpload(AsyncWebServerRequest *request) {
+            bool reboot = !mUploadFail;
+
+            String html = F("<!doctype html><html><head><title>Upload</title><meta http-equiv=\"refresh\" content=\"20; URL=/\"></head><body>Upload: ");
+            if(reboot)
+                html += "success";
+            else
+                html += "failed";
+            html += F("<br/><br/>rebooting ... auto reload after 20s</body></html>");
+
+            AsyncWebServerResponse *response = request->beginResponse(200, F("text/html"), html);
+            response->addHeader("Connection", "close");
+            request->send(response);
+            if(reboot)
+                mApp->setRebootFlag();
+        }
+
         void onConnect(AsyncEventSourceClient *client) {
             DPRINTLN(DBG_VERBOSE, "onConnect");
 
@@ -429,6 +478,7 @@ class Web {
                     case 0x61: iv->type = INV_TYPE_4CH; iv->channels = 4; break;
                     default:  break;
                 }
+                iv->config->yieldCor = request->arg("inv" + String(i) + "YieldCor").toInt();
 
                 // name
                 request->arg("inv" + String(i) + "Name").toCharArray(iv->config->name, MAX_NAME_LENGTH);
@@ -494,6 +544,10 @@ class Web {
                 request->arg("mqttPwd").toCharArray(mConfig->mqtt.pwd, MQTT_PWD_LEN);
             request->arg("mqttTopic").toCharArray(mConfig->mqtt.topic, MQTT_TOPIC_LEN);
             mConfig->mqtt.port = request->arg("mqttPort").toInt();
+            mConfig->mqtt.interval = request->arg("mqttInterval").toInt();
+            mConfig->mqtt.rstYieldMidNight = (request->arg("mqttRstMid") == "on");
+            mConfig->mqtt.rstValsNotAvail  = (request->arg("mqttRstComStop") == "on");
+            mConfig->mqtt.rstValsCommStop  = (request->arg("mqttRstNotAvail") == "on");
 
             // serial console
             if(request->arg("serIntvl") != "") {
@@ -626,59 +680,68 @@ class Web {
         }
 
 #ifdef ENABLE_JSON_EP
-        void showJson(void) {
+        void showJson(AsyncWebServerRequest *request) {
             DPRINTLN(DBG_VERBOSE, F("web::showJson"));
             String modJson;
+            Inverter<> *iv;
+            record_t<> *rec;
+                char topic[40], val[25];
 
             modJson = F("{\n");
             for(uint8_t id = 0; id < mSys->getNumInverters(); id++) {
-                Inverter<> *iv = mSys->getInverterByPos(id);
-                if(NULL != iv) {
-                    char topic[40], val[25];
-                    snprintf(topic, 30, "\"%s\": {\n", iv->name);
-                    modJson += String(topic);
-                    for(uint8_t i = 0; i < iv->listLen; i++) {
-                        snprintf(topic, 40, "\t\"ch%d/%s\"", iv->assign[i].ch, iv->getFieldName(i));
-                        snprintf(val, 25, "[%.3f, \"%s\"]", iv->getValue(i), iv->getUnit(i));
-                        modJson += String(topic) + ": " + String(val) + F(",\n");
-                    }
-                    modJson += F("\t\"last_msg\": \"") + ah::getDateTimeStr(iv->ts) + F("\"\n\t},\n");
+                iv = mSys->getInverterByPos(id);
+                if(NULL == iv)
+                    continue;
+
+                rec = iv->getRecordStruct(RealTimeRunData_Debug);
+                snprintf(topic, 30, "\"%s\": {\n", iv->config->name);
+                modJson += String(topic);
+                for(uint8_t i = 0; i < rec->length; i++) {
+                    snprintf(topic, 40, "\t\"ch%d/%s\"", rec->assign[i].ch, iv->getFieldName(i, rec));
+                    snprintf(val, 25, "[%.3f, \"%s\"]", iv->getValue(i, rec), iv->getUnit(i, rec));
+                    modJson += String(topic) + ": " + String(val) + F(",\n");
                 }
+                modJson += F("\t\"last_msg\": \"") + ah::getDateTimeStr(rec->ts) + F("\"\n\t},\n");
             }
-            modJson += F("\"json_ts\": \"") + String(ah::getDateTimeStr(mMain->mTimestamp)) + F("\"\n}\n");
+            modJson += F("\"json_ts\": \"") + String(ah::getDateTimeStr(mApp->getTimestamp())) + F("\"\n}\n");
 
-            mWeb.send(200, F("application/json"), modJson);
+            AsyncWebServerResponse *response = request->beginResponse(200, F("application/json"), modJson);
+            request->send(response);
         }
 #endif
 
 #ifdef ENABLE_PROMETHEUS_EP
-        void showMetrics(void) {
+        void showMetrics(AsyncWebServerRequest *request) {
             DPRINTLN(DBG_VERBOSE, F("web::showMetrics"));
             String metrics;
             char headline[80];
 
-            snprintf(headline, 80, "ahoy_solar_info{version=\"%s\",image=\"\",devicename=\"%s\"} 1", mApp->getVersion(), mconfig->sys.deviceName);
+            snprintf(headline, 80, "ahoy_solar_info{version=\"%s\",image=\"\",devicename=\"%s\"} 1", mApp->getVersion(), mConfig->sys.deviceName);
             metrics += "# TYPE ahoy_solar_info gauge\n" + String(headline) + "\n";
-
+            Inverter<> *iv;
+            record_t<> *rec;
+            char type[60], topic[60], val[25];
             for(uint8_t id = 0; id < mSys->getNumInverters(); id++) {
-                Inverter<> *iv = mSys->getInverterByPos(id);
-                if(NULL != iv) {
-                    char type[60], topic[60], val[25];
-                    for(uint8_t i = 0; i < iv->listLen; i++) {
-                        uint8_t channel = iv->assign[i].ch;
-                        if(channel == 0) {
-                            String promUnit, promType;
-                            std::tie(promUnit, promType) = convertToPromUnits( iv->getUnit(i) );
-                            snprintf(type, 60, "# TYPE ahoy_solar_%s_%s %s", iv->getFieldName(i), promUnit.c_str(), promType.c_str());
-                            snprintf(topic, 60, "ahoy_solar_%s_%s{inverter=\"%s\"}", iv->getFieldName(i), promUnit.c_str(), iv->name);
-                            snprintf(val, 25, "%.3f", iv->getValue(i));
-                            metrics += String(type) + "\n" + String(topic) + " " + String(val) + "\n";
-                        }
+                iv = mSys->getInverterByPos(id);
+                if(NULL == iv)
+                    continue;
+
+                rec = iv->getRecordStruct(RealTimeRunData_Debug);
+                for(uint8_t i = 0; i < rec->length; i++) {
+                    uint8_t channel = rec->assign[i].ch;
+                    if(channel == 0) {
+                        String promUnit, promType;
+                        std::tie(promUnit, promType) = convertToPromUnits(iv->getUnit(i, rec));
+                        snprintf(type, 60, "# TYPE ahoy_solar_%s_%s %s", iv->getFieldName(i, rec), promUnit.c_str(), promType.c_str());
+                        snprintf(topic, 60, "ahoy_solar_%s_%s{inverter=\"%s\"}", iv->getFieldName(i, rec), promUnit.c_str(), iv->config->name);
+                        snprintf(val, 25, "%.3f", iv->getValue(i, rec));
+                        metrics += String(type) + "\n" + String(topic) + " " + String(val) + "\n";
                     }
                 }
             }
 
-            mWeb.send(200, F("text/plain"), metrics);
+            AsyncWebServerResponse *response = request->beginResponse(200, F("text/html"), metrics);
+            request->send(response);
         }
 
         std::pair<String, String> convertToPromUnits(String shortUnit) {
@@ -707,6 +770,9 @@ class Web {
         char mSerialBuf[WEB_SERIAL_BUF_SIZE];
         uint16_t mSerialBufFill;
         bool mSerialClientConnnected;
+
+        File mUploadFp;
+        bool mUploadFail;
 };
 
 #endif /*__WEB_H__*/
diff --git a/src/wifi/ahoywifi.h b/src/wifi/ahoywifi.h
index 6eca48e2..a04f0520 100644
--- a/src/wifi/ahoywifi.h
+++ b/src/wifi/ahoywifi.h
@@ -27,8 +27,7 @@ class ahoywifi {
         void getAvailNetworks(JsonObject obj);
 
     private:
-        typedef enum WiFiStatus
-        {
+        typedef enum WiFiStatus {
             DISCONNECTED = 0,
             CONNECTING,
             CONNECTED,