/** * Power PWM (PID) Regler for StromLog * Version 0.09 * 11.04.2024 P. Rebesky * author Creator P.Rebesky * Copyright (©): 2024-2028 by Peter Rebesky * This code can use in private cases only. Every business or companies using of this codes or codes parts is required an approval of us (me) * Every private using can exchange some parts of code or modify some code-lines. This code is allowed change for private use only. * This software is basicly owned by Peter Rebesky and any comercial using is forbidden without approval of us (me). * * Arduino setup: Generic ESP8266 Module, 4MB (FS:2MB OTA:1019kB) **/ #include #include #include #include #include //ESP8266 Core WiFi Library #include #include //Local WebServer #include "configPWR.h" #include "global.h" #include "WebClient.h" #include "controller.h" #include "diagram.h" #include // LittleFS is declared #include #include #include // https://github.com/tzapu/WiFiManager #include #include #include #include "mqtt.h" #include "calibrate.h" #ifdef OTA_on #include #include #endif WiFiManager wm; // global wm instance //WiFiManagerParameter custom_field; // global param ( for non blocking w params ) WiFiServer server(80); WiFiUDP ntpUDP; configPWR cPWR; OneWire oneWire(_oneWireBus); DallasTemperature sensors(&oneWire); NTPClient timeClient(ntpUDP, "pool.ntp.org", _utcOffsetInSeconds); //*********************** use WIFI manager by Tzapu https://github.com/tzapu/WiFiManager ************************************ bool setupWIFI() { WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP pinMode(blueLED, OUTPUT); // set as output digitalWrite(blueLED,LEDON); // show setup-mode on LED delay(1000); // Serial.println("\n Starting"); std::vector menu = {"wifi","info","sep","restart","exit"}; wm.setMenu(menu); // set dark theme wm.setClass("invert"); //set static ip // wm.setSTAStaticIPConfig(IPAddress(192,168,0,10),IPAddress(192,168,0,1),IPAddress(255,255,255,0),IPAddress(8,8,8,8)); // set static ip,gw,sn,dns // wm.setShowStaticFields(true); // force show static ip fields // wm.setShowDnsFields(true); // force show dns field always wm.setConfigPortalTimeout(500); // auto close configportal after n seconds // checkButton(); // if pressed then reset WIFI config to start new change ssid bool res; res = wm.autoConnect("PWR_Setup"); // anonymous ap, first parameter is name of access point if(!res) { return false; } else { return true; } } //************************* press button for set a new SSID ********************************** void checkButton(){ // check for button press if ( digitalRead(BoardTaster) == LOW ) { // poor mans debounce/press-hold delay(50); if( digitalRead(BoardTaster) == LOW ){ digitalWrite(blueLED,LEDOFF); // LED OFF delay(5000); // reset delay hold if( digitalRead(BoardTaster) == LOW ){ digitalWrite(blueLED,LEDON); // show button pressed on LED wm.resetSettings(); delay(100); wm.resetSettings(); for(int i=0;i<50;i++){ if(i%2)digitalWrite(blueLED,LEDON); else digitalWrite(blueLED,LEDOFF); delay(50); } // saveHistoryStatus(NEWSSID); // cPWR.saveHistory(getRawHistory()); delay(100); ESP.restart(); } } } } //*************************** function by WIFI manager ******************************** String getParam(String name){ //read parameter from server, for customhmtl input String value; if(wm.server->hasArg(name)) { value = wm.server->arg(name); } return value; } //*************************** function by WIFI manager ******************************** void saveParamCallback(){ #ifdef DEBUG_VALUE Serial.println("[CALLBACK] saveParamCallback fired"); Serial.println("PARAM customfieldid = " + getParam("customfieldid")); #endif } //*************************** function for get time & date as timestamp ******************************** bool syncron_NTP_Time() { timeClient.begin(); if(timeClient.update()) { uint32_t timeNTP = timeClient.getEpochTime(); if(timeNTP !=0){ _timestamp = timeNTP + (MESZ * 3600); // 3600 means add 1 hour return true; } } return false; } //*************************** create a string from values for data and time ******************************** //*** time *** String getTime2String(int hour,int minute,int second){ String acttime; if(hour < 10) acttime += "0"; // if value < 10 then show a zero before acttime += String(hour); //combine string for time acttime += ":"; if(minute < 10) acttime += "0"; acttime += String(minute); acttime += ":"; if(second < 10) acttime += "0"; acttime += String(second); return acttime; } //*** date *** String getDate2String(int year,int month,int day){ String actdate; if (day < 10) actdate += "0"; // if value < 10 then show a zero before actdate += String(day); //combine string for time actdate += "."; if(month < 10) actdate += "0"; actdate += String(month); actdate += "."; if(year < 10) actdate += "0"; actdate += String(year); return actdate; } //**************************** intern clock ******************************* bool internClock(uint32_t currentMillis) { if (currentMillis < _secondMillis) _secondMillis = 0; // handle if currentMillis are overflows, max 32 bits if (currentMillis - _secondMillis >=1000){ _secondMillis = currentMillis; _second++; _timestamp++; if (_second >=60){ _second=0, _minute++;} if (_minute >=60){ _minute=0, _hour++;} // if (_hour >=24){ _hour=0; _day++;} if (_hour >=24){ getTime2Value(_timestamp); } return true; } else return false; } //***************************** show Data on WEB-Browser ****************************** void check4Client() { // Check if a client has connected String argument; String PostValues; if (WiFi.status() == WL_CONNECTED) { WiFiClient client = server.available(); if (!client) { return; } // Wait until the client sends some data unsigned long timeout = millis() + 3000; while (!client.available() && millis() < timeout) { delay(1); } if (millis() > timeout) { // Serial.println("timeout"); client.flush(); client.stop(); return; } // Read the first line of the reply from server and copy it into variable "argument" if(client.available()){ String methode; String line; methode = client.readStringUntil(' '); if (methode == "GET") line = client.readStringUntil('\n'); else { line = client.readString(); int postBegin = line.indexOf("\r\n\r\n"); PostValues = line.substring(postBegin+4, line.length()); } int argEnd = line.indexOf("HTTP"); argument = line.substring(0, argEnd-1); argEnd = line.indexOf("?"); if(argEnd>0 && methode == "GET"){ argument = line.substring(0, argEnd); // cut the values after ? } // Serial.println(argument); // debug // Serial.println(PostValues); // debug // Match the request client.flush(); String s; if (argument == "/status"){ //deprecated s = WebGetStatus(); } else if(argument == "/refresh"){ s = WebGetRefresh(); } else if(argument == "/recalib"){ s = getCalibrationValues(); } else if(argument =="/vJSON"){ s = getDiagramData(); } else if(argument =="/diagram"){ s = showDiagram(); } else if(argument == "/reOn"){ digitalWrite(SecureRELAIS, ON); // set relais on s = HTTP_ANSWERJSON; s += WebGetRefresh(); } else if(argument == "/reOff"){ turnAllOff(); digitalWrite(SecureRELAIS, OFF); // set relais off s = HTTP_ANSWERJSON; s += WebGetRefresh(); } else if(argument == "/getKWH"){ s = HTTP_ANSWER; s += _sumConsumption/(float)cPWR.FactorKWH; s += HTTP_TEXT_END; } else if(argument == "/pumpOff"){ turnOffCirculationPump(); s = WebGetMainScreen(); } else if(argument == "/pumpOn"){ if(!turnOnCirculationPump()){ s = HTTP_ANSWER; s+="Wartezeit fuer Pumpe ist aktiv!"; s+=HTTP_TEXT_END; } else { s = WebGetMainScreen(); } } else if (argument == "/save") { s = WebSaveConfig(PostValues); } else if (argument == "/setup") { s = WebSetSetup(); } else if (argument == "/advance"){ s = WebSetSetupAdvance(); } else if (argument == "/saveAdvance"){ s = WebSaveConfigAdvance(PostValues);; setMQTTserver(); } else if (argument == "/favicon.ico") { s = HTTP_ANSWER; s += "none"; } else if (argument == "/searchLogger") { s = HTTP_ANSWER; int argBegin = line.indexOf("?"); s += "{\"DTZLOGGER\":\"" + PWRVersion + "\"}"; } else if (argument == "/" || argument == "/back") { // Prepare the response s = WebGetMainScreen(); } else if (argument == "/memory") { s = WebGetMemory(); } else if(argument == "/shutdown"){ s = WebShutDown(); } else if(argument == "/autoON"){ cPWR.setAutomatic(true); s = HTTP_ANSWER; s += "Automatic is ON"; s += HTTP_TEXT_END; } else if(argument == "/autoOFF"){ cPWR.setAutomatic(false); if(turnOffCirculationPump()){ s = HTTP_ANSWER; s += "Automatic is OFF"; s += HTTP_TEXT_END; } } else if(argument == "/sendON"){ cPWR.setSend2Server(true); s = HTTP_ANSWER; s += "Send 2 Server is ON"; s += HTTP_TEXT_END; } else if(argument == "/sendOFF"){ cPWR.setSend2Server(false); s = HTTP_ANSWER; s += "Send 2 Server is OFF"; s += HTTP_TEXT_END; } else if(argument == "/autoToggle"){ if(cPWR.getAutomatic()){ cPWR.setAutomatic(false); turnOffCirculationPump(); }else{ cPWR.setAutomatic(true); } s = HTTP_ANSWER; s += "Automatic is "; if(cPWR.getAutomatic()) s+="ON"; else s+="OFF"; s += HTTP_TEXT_END; } else if(argument == "/help"){ s = WebHelpRestEndpoints(); } else if(argument == "/calibrate"){ s += calibrationWeb(); //startCalibration(cPWR.getScanTime()); } else if(argument == "/mstart"){ startCalibration(cPWR.getScanTime()); } else if(argument == "/getScan"){ s += readScanConsumption(); } else if(argument == "/setTime"){ s += webSetTime(); } else if(argument == "/setWIFI"){ s =""; wm.resetSettings(); delay(100); ESP.restart(); } #ifdef OTA_on else if (argument == "/firmware"){ s = HTTP_ANSWER; s += "Your PWRegler versions number: "+Version+"
"; s += "Check for new firmware now.
"; s += getFilesFromServer(WiFi.localIP()); s += HTTP_TEXT_END; } else if (argument == "/upDate"){ int valueBegin = line.indexOf("?"); s = HTTP_ANSWER; if(valueBegin > 0){ int valueEnd = line.indexOf("HTTP"); String argumentValue = line.substring(valueBegin+1,valueEnd-1); if(argumentValue.indexOf(ESPTYP)>=0){ // check if the right firmware and memory of esp (1M,2M,4M....) s += "Update will be start now.
"; s += "Update-file: "+argumentValue+"
"; s += "Don't turn off power during update!

"; s += "After update, the reconnection only via IP, without arguments behind it!

"; s += "Click here for reconnect after update
"; } else{ s += "Wrong firmware for this IoT.
Update canceled.
"; argument =""; } } else { s += "Error: Missing name of update file!
"; s += "Update canceled.
"; argument =""; } s += HTTP_TEXT_END; } #endif else if(argument == "/setTime"){ s = HTTP_ANSWER; s += HTTP_BODYSTYLE_PWR; s += ""; s += HTTP_DIV_FIELD; s += "

Die Uhr wird jetzt neu synchronisiert.

"; s += "

Zeit:

"; if (syncron_NTP_Time() == true){ getTime2Value(_timestamp); s += "Neue Zeit:

"; } else { s += "Serverfehler! Aktuallisierung nicht erfolgt.
Bitte eine Wartezeit von 2 Minuten,
bis zur wiederholten, manuellen Aktuallisierung einhalten.
"; } s += "


"; s += HTTP_TEXT_END; } else s = "HTTP/1.1 403 OK\r\nContent-Type: text/html\r\n\r\n"; // Send the response to the client client.print(s); delay(1); #ifdef OTA_on if(argument == "/upDate"){ turnAllOff(); int valueBegin = line.indexOf("?"); if(valueBegin > 0){ int valueEnd = line.indexOf("HTTP"); argument = line.substring(valueBegin+1,valueEnd-1); delay(1000); //wait one second updateStromLog(argument); // start update now } } #endif } } } //*********** update new firmware ****************************************/ //*********** get available files ****************************************/ #ifdef OTA_on String getFilesFromServer(IPAddress ip){ String files = ""; if (WiFi.status() == WL_CONNECTED) { WiFiClient client; HTTPClient http; String target = "http://"; target+=cPWR.getTargetServer(); target+="/fwpwr/getFiles.php"; if (http.begin(client,target)){ http.addHeader("Content-Type","application/json;charset=utf-8"); int httpCode = http.POST("{\"userIP\":\""+ip.toString()+"\",\"build\":\""+Version+"\"}"); if(httpCode >= 100){ files = http.getString(); } else{ files = "Error: no connection. Return: "; files+=httpCode; files+="
"; files+= target; } } http.end(); } return files; } //********** update, input file-name ************************************/ String updateStromLog(String file){ delay(500); String result = "ERROR: Update failed!"; ESPhttpUpdate.rebootOnUpdate(false); // remove automatic update digitalWrite(blueLED,LEDON); WiFiClient client; t_httpUpdate_return ret = ESPhttpUpdate.update(client, cPWR.getTargetServer(), 80, "/fwpwr/"+file); switch (ret) { case HTTP_UPDATE_FAILED: break; case HTTP_UPDATE_NO_UPDATES: break; case HTTP_UPDATE_OK: String result = "Update successful."; delay(500); // Wait a second and restart ESP.restart(); break; } return result; } #endif //*********** update end *************************************************/ //*********** prepare URL sending parameters *************/ //*********** send values to server **********************/ //*********************************************************** bool send2dbase(String restPoint, String content){ // Serial.println(t1); Serial.println(hum); Serial.println(hPa); Serial.println(heaterOnOff); bool r=false; if (WiFi.status() == WL_CONNECTED) { WiFiClientSecure clientSSL; WiFiClient client; HTTPClient http; bool connect; // String getTarget = cPWR.getTargetURLclean(); String target; if(cPWR.SendSSL>0){ target = "https://"; target += restPoint; int httpsPort=443; clientSSL.setInsecure(); // magic line without check certificate because memory insufficient clientSSL.connect(target,httpsPort); // set connection by https connect = http.begin(clientSSL,target); }else{ target= "http://"; target += restPoint; connect = http.begin(client,target); } if(connect){ // http.setTimeout(10000); // 10 seconds? http.addHeader("Content-Type","application/x-www-form-urlencoded"); int httpCode = 0; _SendResult = ""; httpCode = http.POST(content); _httpMarker = httpCode; //debug if (httpCode >= 100) { if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_NO_CONTENT){ _SendResult = http.getString(); if (_SendResult == "OK
" || httpCode == HTTP_CODE_NO_CONTENT){ _SendResult = "Temperatur wurde gesendet: "+getTime2String(_hour,_minute,_second); r = true; // if data saved successful than result=true else false } else { r = false; } } else {_SendResult ="[HTTP] Unable to connect! "; r = false; _SendResult +=httpCode;} } else {_SendResult ="[HTTP] missing return-code!"; r = false;} } else {_SendResult ="[HTTP] Service not connect!"; r = false;} http.end(); } if(r == false) _flashCounter = 300; // time to show flash on LED for infos return r; } //*********** prepaire send string ***********************/ String prepaireValues2Send(){ String ret ="&time="; ret += _timestamp; ret+="&name="; ret += cPWR.getPName(); ret+="&usip="; ret += WiFi.localIP().toString(); ret+="&name1=KesselTemperatur&temp1="; ret += int(_temperature0*1000); ret+="&name2=HeizstabTemperatur&temp2="; ret += int(_temperature1*1000); return ret; } //*********** get json data from meter *******************/ bool getConsumptionMeter(String meterIP){ bool r=false; if (WiFi.status() == WL_CONNECTED) { WiFiClient client; HTTPClient http; #ifdef PC_EMULATOR // String target = "http://192.168.10.46/consumption.txt"; String target = "http://"; target += meterIP; target+="/consumption.txt"; #else String target = "http://"; target += meterIP; target+="/consumption"; #endif int httpCode = 0; if (http.begin(client,target)){ httpCode = http.GET(); if (httpCode == HTTP_CODE_OK){ _TransmitResult = http.getString(); r=true; #ifdef DEBUG_PAYLOAD Serial.println(httpCode); Serial.println(_TransmitResult); #endif } else {_TransmitResult ="Missing answer! "; _TransmitResult +=httpCode;} } else _TransmitResult ="Target not found!"; http.end(); } else _TransmitResult ="WiFi not connect to meter!"; return r; } /*********************************************************** * convert a unix-timestamp into single bytes as value * function works up to 1972 until 19.01.2038 ***********************************************************/ uint32_t Timestamp2DateTime(uint32_t unixTime){ int dummy = 0; int years=0; uint32_t SecOfYear; uint32_t timeDummy; bool leapYear = false; // default: don't be a leap-year timeDummy = unixTime - 31536000 *2; // sub two years (1970 and 1971) add the years later years= (timeDummy / 126230400) *4; // get the periodes of four years cycle; 3Y+leapY = 126230400 seconds timeDummy = timeDummy % 126230400; // get the rest of timestamp without the cycles like above this line if (timeDummy >= 31622400) { timeDummy -= 31622400; // sub the first leap-year if it's over years++; // add the leapyear years += timeDummy / 31536000; // get the rest of years between 0 until 2 years } years = years + 1972; // add start-year +2: now, we know the actual year and can figure out if is a leap-year _date_time[YEAR]=years; // if (year % 4 == 0) leapYear = true; // if year divison by four without some rest, then is a leap-year...yeeeh -> it works until 2036 only if ( bitRead(years,0)==0 && bitRead(years,1)==0) { // it's very faster than %-arithmetic leapYear = true; SecOfYear = 31622400; // set the counts of seconds leap-year // Serial.print("LeapYear "); } else SecOfYear = 31536000; // or don't be a leap-year -> regulary seconds _date_time[DAY] = (timeDummy % SecOfYear) / 86400; // get the days of actual year _date_time[MONTH] = 0; // set zero for (int i=0; _date_time[DAY]>=0; i++ ) { // do it until the day(s) are negativ, this loop has be exicute one time -> to set month at ones dummy = _date_time[DAY]; // store the value befor result is negativ for break, because the last one was the day of actual month _date_time[DAY] = _date_time[DAY] - DaysPerMonth[i]; // sub the days of month until it's negativ, according byte-array if (i == 1 && leapYear == true) {_date_time[DAY] -= 1;} // sub the 29. of february on leapyears ;-) _date_time[MONTH]++; // add one month on every loop if (i > 11) return -1; // if this function wild ;-) then break (max. months are 0 - 11) } _date_time[DAY] = dummy+1; // add one, because the start was on 1. january (start of timstamp = 01.01.1970 and we have to add it) /****** get time ******/ _date_time[HOUR] = (timeDummy % 86400) / 3600; // the value of _timeDummy contents the rest of hours, minutes and seconds only _date_time[MINUTE] = (timeDummy % 3600) / 60; // it's shorter to take reduced _timeDummy than the whole timestamp _date_time[SECOND] = timeDummy % 60; // return values are in array "unsigned int _date_time[6]; //[YEAR][MONTH][DAY][HOUR][MINUTE][SECOND]" return timeDummy; // return just the time without year, month and date. Its in use for daily events at the same times } void writeTime(){ _year = _date_time[YEAR]; _month = _date_time[MONTH]; _day = _date_time[DAY]; _hour = _date_time[HOUR]; _minute = _date_time[MINUTE]; _second = _date_time[SECOND]; } void getTime2Value(uint32_t unixTime){ Timestamp2DateTime(unixTime); writeTime(); } //****************************** show received a protocol on red LED *************************** void showProtocolOnLED(){ } //**** get temperature from sensor DS18B20 ***************// void getValuesTemperature() { float tempDummy=0; int readError=0; oneWire.reset(); sensors.requestTemperatures(); tempDummy=sensors.getTempCByIndex(0); if (tempDummy > -20 || _tempRequestError >=10) { if(_exchangeTemp) _temperature1 = tempDummy; else _temperature0 = tempDummy; } else readError++; tempDummy=sensors.getTempCByIndex(1); if(tempDummy > -20 || _tempRequestError >=10){ if(_exchangeTemp) _temperature0 = tempDummy; else _temperature1 = tempDummy; } else readError++; if(readError>0)_tempRequestError++; if(_tempRequestError > 10 || readError == 0){ _tempRequestError=0; } if(_temp0Max < int(_temperature0-1000))_temp0Max = int(_temperature0-1000); if(_temp1Max < int(_temperature1-1000))_temp1Max = int(_temperature1-1000); if(_temp0Min > int(_temperature0-1000))_temp0Min = int(_temperature0-1000); if(_temp1Min > int(_temperature1-1000))_temp1Min = int(_temperature1-1000); } //*********************** decode value from JSON ********************************* int decodeValue(){ _receivedOK=true; int consumptionValue=0; int valueBegin = _TransmitResult.indexOf("value"); if(valueBegin >= 0){ int valueEnd = _TransmitResult.indexOf(",",valueBegin); if (valueEnd != -1){ String value = _TransmitResult.substring(valueBegin+7, valueEnd); consumptionValue=value.toInt(); #ifdef DEBUG_VALUE Serial.println(value); #endif } } else { consumptionValue=0; _receivedOK=false; } #ifdef DEBUG_VALUE Serial.println(consumptionValue); #endif return consumptionValue; } //*********************** setup esp on start ***************************** void setup() { pinMode(blueLED, OUTPUT); // set as output digitalWrite(blueLED,LEDOFF); SetupRelais(); _flashCounter = 30; // time to show flash on LED for infos #ifdef SERIAL_ON Serial.begin(115200); #else pinMode(BoardTaster, INPUT_PULLUP); // set as input // pinMode(LEDboard, OUTPUT); // set as output (blue LED) #endif delay(200); wifi_station_set_hostname("PWM_Regler"); // set hostname into WLAN network bool result = setupWIFI(); if (result) { cPWR.begin(); // start filesystem and properties of user server.begin(); syncron_NTP_Time(); getTime2Value(_timestamp); delay(200); } else { for (int i=0 ; i<20;i++){ if (digitalRead(blueLED) == LEDOFF)digitalWrite(blueLED,LEDON); else digitalWrite(blueLED,LEDOFF); delay(200); } ESP.restart(); } sensors.getDeviceCount(); _temperature0 = sensors.getTempCByIndex(0); setMQTTserver(); } //************************* esp loop function *************************** void loop() { yield(); // disabled checkButton(); // check if user pressed button for set new SSID uint32_t currentMillis = millis(); if(internClock(currentMillis)== true) { // every second will run this component //*** mqtt part ***// if(cPWR.SendMQTT){handleMQTTclient();}// check if mqtt is enabled //*** mqtt part end ***// if (_flashCounter > 0){ // Flash LED when errors occurs or for information if (digitalRead(blueLED) == LEDOFF)digitalWrite(blueLED,LEDON); else digitalWrite(blueLED,LEDOFF); _flashCounter --; } else digitalWrite(blueLED,LEDOFF); // if flashtime over turn off LED if (_hour == 23 && _minute==1 && _second==3){ // update _timestamp if (syncron_NTP_Time() == true){ getTime2Value(_timestamp); _sumConsumption=0; // set zero the sum of consumption every day } _errorCount=0; _temp0Max=0; _temp0Min=100000; _temp1Max=0; _temp1Min=100000; } if (_timestamp < TimeOf2021) { // change function that not every minute is calling the NTP-Server() Because there have to be a wait-time of min. 2 minute! if(_minute % 5 ==0 && _second == 50){ if (syncron_NTP_Time() == true) getTime2Value(_timestamp); // get timestamp again if sync was failure, perhaps after power down } } if(cPWR.getSend2Server()){ if(_minute % 15==0 && _second == 1) send2dbase(cPWR.getTargetURLclean(),prepaireValues2Send()); // send temperatur to server } if(_second % cPWR.getScanTime() == 0){ // handle get data from meter and switch relais if(cPWR.getAutomatic()){ // is automatic on if(getConsumptionMeter(cPWR.getTargetMeter())){ // get value of consumption from meter _actualConsumption = decodeValue(); // decode value of consumption #ifdef PC_EMULATOR _TransmitResult="PC als Mock verbunden
IP: "; _TransmitResult += cPWR.getTargetMeter(); #else _TransmitResult="Verbunden mit Strom-Zähler"; #endif _errorWait=0; // set error wait of zero if(_calibrationON == false) handleRegulation(_actualConsumption); // handle turn on off PWM control, circulation pump and secure relais, that can using as two point regulation else handleCalibrationEntry(_actualConsumption,cPWR.getScanTime()); // handle calibration tool saveDiagramWatt(_timestamp, relais.PWM_Power); } else { _errorWait++; // count error wait errorConnect(); // if connection lost turn off relais after errorWait value #ifdef PC_EMULATOR _TransmitResult="PC als Mock failed!"; #else _TransmitResult="Zähler nicht gefunden (Timeout)"; #endif if(!WiFi.isConnected()){ _flashCounter=20; if(_errorWait > 20){ delay(500); ESP.restart(); // connect lost do restart } } } #ifdef DEBUG_CONNECT Serial.print("Status: "); Serial.println(WiFi.status()); Serial.print("RRSI: "); Serial.println(WiFi.RSSI()); Serial.print("ErroWait: "); Serial.println(_errorWait); #endif } else { _TransmitResult="Automatic is off"; turnOffCirculationPump(); turnAllOff(); } }// else _TransmitResult="Error: No meter scan!"; if(_second % 10 ==0){ getValuesTemperature(); // get temperature of sensor DS18B20 } } check4Client(); // thats it's if with the browser show data on port 80 by html }