/** * Main StromLog * Version 7.04 * 28.06.2024 P. Rebesky * author Creator P.Rebesky * Copyright (©): 2022-2032 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, 2MB (FS:256kB OTA:896kB) **/ #include //ESP8266 Core WiFi Library //#include //ESP8266 Core WiFi Library #include #include //Local WebServer #include #include // LittleFS is declared #include #include // https://github.com/tzapu/WiFiManager #include #include #include #include "global.h" #include "WebClient.h" #include "OTAhandle.h" #include "configDTZ.h" #include "encryption.h" #include "diagram.h" #ifdef SML_DIN #include "decodeMeterSML.h" #endif #ifdef ISKRA_MT174 #include "decodeMeterMT174.h" #endif #ifdef VDE_DIN #include "decodeMeterDIN.h" #endif #ifdef ZE311_DR #include "decodeMeterZE311.h" #endif #ifdef LG_E350 #include "decodeMeterE350.h" #endif #ifdef MBUS_DLMS #include "decodeMeterDLMS.h" #endif #ifdef PAF_EC3 #include "dMeterPAFec3.h" #endif #ifdef LG_E320 #include "decodeMeterE320.h" #endif #ifdef DIN_EBZ #include "decodeDIN_EBZ.h" #endif //#define WM_DEBUG_LEVEL WiFiManager wm; // global wm instance //WiFiManagerParameter custom_field; // global param ( for non blocking w params ) WiFiServer server(80); WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, "de.pool.ntp.org", _UTCOFFSET); configDTZ cDTZ; decodeMeter dMETER; encryptData eDATA; WiFiClient espClient; PubSubClient client(espClient); //callback notifying us of the need to save config //flag for saving data //bool shouldSaveConfig = false; //void saveConfigCallback () { // Serial.println("Should save config StromLog"); // shouldSaveConfig = true; //} //*********************** 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(_LED, OUTPUT); // set as output digitalWrite(_LED,_ON); // 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.setSaveConfigCallback(saveConfigCallback); 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("StromLog_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(_LED,_OFF); // LED OFF delay(5000); // reset delay hold if( digitalRead(_BOARDTASTER) == LOW ){ digitalWrite(_LED,_ON); // show button pressed on LED wm.resetSettings(); delay(100); wm.resetSettings(); for(int i=0;i<50;i++){ if(i%2)digitalWrite(_LED,_ON); else digitalWrite(_LED,_OFF); delay(50); } 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(){ Serial.println("[CALLBACK] saveParamCallback fired"); Serial.println("PARAM customfieldid = " + getParam("customfieldid")); } //*************************** function for get time & date as timestamp ******************************** bool syncron_NTP_Time(bool correctOn) { uint32_t timestamp=0; timeClient.begin(); if(timeClient.update()) { timestamp = timeClient.getEpochTime(); // get as GMT if(timestamp > _TIMEOF2021){ timestamp += (_MESZ * _UTCOFFSET); // means add 1 hour if(correctOn == true) { _secAdjust +=clockAdjust(timestamp-_timestamp); // negative difference clock is running too fast / positive difference clock is running too slow } _timestamp = timestamp; // syncron timestamp return true; } } return false; } //********* adjust intern clock if time is runnung false ****************// int clockAdjust(int timeDifference){ int adjust=0; if(timeDifference > 60 && _secAdjust > 940){ // positive difference clock is running too slow // if(timeDifference <= 60) adjust--; adjust = timeDifference / -60; } else if(timeDifference < -60 && _secAdjust < 999){ // negative difference clock is running too fast // if(timeDifference >= -60) adjust++; adjust = timeDifference / -60; } if(_secAdjust > 1000 || _secAdjust < 900){ // if time-correctur get wrong then check if time higher than 1000ms (1s) _secAdjust = 1000; adjust = 0; } return adjust; } //*************************** create a string from values for data and time ******************************** String getTime2String(int hour,int minute,int second){ String act_time; if(hour < 10) act_time += "0"; // if value < 10 then show a zero before act_time += String(hour); //combine string for time act_time += ":"; if(minute < 10) act_time += "0"; act_time += String(minute); act_time += ":"; if(second < 10) act_time += "0"; act_time += String(second); return act_time; } String getDate2String(int year,int month,int day){ String act_date; if (day < 10) act_date += "0"; // if value < 10 then show a zero before act_date += String(day); //combine string for time act_date += "."; if(month < 10) act_date += "0"; act_date += String(month); act_date += "."; act_date += String(year); return act_date; } //**************************** intern clock ******************************* bool internClock(uint32_t currentMillis) { if (currentMillis < _secondMillis) _secondMillis = 0; // handle if currentMillis are overflows, max 32 bits if (currentMillis - _secondMillis >=_secAdjust){ _secondMillis = currentMillis; _act_date_time[_SECOND]++; _timestamp++; if (_act_date_time[_SECOND] >=60){ _act_date_time[_SECOND]=0, _act_date_time[_MINUTE]++;} if (_act_date_time[_MINUTE] >=60){ _act_date_time[_MINUTE]=0, _act_date_time[_HOUR]++;} // if (_act_date_time[_HOUR] >=24){ _act_date_time[_HOUR]=0; _act_date_time[_DAY]++;} if (_act_date_time[_HOUR] >=24){ getTime2Value(); } return true; } else return false; } //***************************** show DTZ-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 print it to Serial 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 ? } #ifdef SHOW_ARGUMENTS_DEBUG Serial.println(argument); // debug Serial.println(PostValues); // debug #endif // Match the request client.flush(); String s; if (argument == "/consumption"){ s+=webConsumption(); } else if(argument =="/vJSON"){ s = getDiagramData(); } else if(argument =="/diagram"){ s = showDiagram(); } else if (argument == "/json.txt"){ s+=ioBrokerData(); } else if (argument == "/currentData"){ s+=webCurrentData(); } else if (argument == "/allData"){ s+=webAllData(); } else if (argument == "/refresh"){ s+=getRefreshJSON(); } else if (argument == "/" || argument == "/back") { s+=webMain(_transmitResult); } else if (argument == "/setup") { s+=webSetup(); } else if (argument == "/save") { s+=webSave(line); setMQTTserver(); // set new server if it changed on setup } else if (argument == "/showsetup") { s+=webShowSetup(); } else if (argument == "/favicon.ico") { s+=webFavicon(); } else if (argument == "/searchLogger") { s+=webSearchStromLog(line); } else if (argument == "/memory") { s+=webMemory(); } else if (argument == "/sendProtocols"){ s+=webSendProtocols(); } else if(argument == "/setTime"){ s+=webSetTime(); } else if(argument == "/firmware"){ s+=getUpdateFiles(WiFi.localIP()); } else if(argument == "/upDate"){ s+=prepareUpdate(line); } else if(argument == "/CRC_ON"){ //setCRC_Check(bool onOff) s+=setCheckCRC(false); } else if(argument == "/CRC_OFF"){ s+=setCheckCRC(true); } else if(argument == "/help"){ s = webShowHelp(); } else if(argument == "/setIntervall"){ int valueBegin = line.indexOf("?"); if(valueBegin > 0){ int valueEnd = line.indexOf("HTTP"); argument = line.substring(valueBegin+1,valueEnd-1); int intervall=argument.toInt(); s+=setIntervall(intervall); } else s+="No value entered! Process cancelled."; } 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(10); if(argument == "/upDate"){ 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,cDTZ.getTargetServer()); // start update now } } } } } //*********** prepare values for sending to the PHP-script ***************/ String prepareSendValues(){ String Data = "timestamp="; Data += _timestamp; Data += "&usid="; Data += cDTZ.getUserID(); Data += "&bez1="; Data += dMETER.MeterKWH[_FROMGRID_0]; Data += "&bez2="; Data += dMETER.MeterKWH[_FROMGRID_1]; Data += "&bez3="; Data += dMETER.MeterKWH[_FROMGRID_2]; Data += "&eeg1="; Data += dMETER.MeterKWH[_FEEDIN]; Data += "&pwa="; Data += dMETER.Electric_data[_CONSUMPTION]; Data += "&ul1="; Data += dMETER.Electric_data[_U_L1]; Data += "&ul2="; Data += dMETER.Electric_data[_U_L2]; Data += "&ul3="; Data += dMETER.Electric_data[_U_L3]; Data += "&il1="; Data += dMETER.Electric_data[_I_L1]; Data += "&il2="; Data += dMETER.Electric_data[_I_L2]; Data += "&il3="; Data += dMETER.Electric_data[_I_L3]; Data += "&frq="; Data += dMETER.Electric_data[_FREQ]; Data += "&ip="; Data += WiFi.localIP().toString(); Data += "&dtzid="; Data += dMETER.getMeterID(); Data += "&dtzname="; Data += cDTZ.getNameDTZ(); Data += "&dtzma="; Data += dMETER.MeterType; Data += ">ime=getTime"; // "getTime" identical string for returned the timestamp of server Data += "&end"; return Data; } //*********** prepare values for sending to influx-db *******************/ String prepareSendValuesInFlux(){ float DispValue; String Data = "StromLog"; Data += ",usid="; Data += cDTZ.getUserID(); Data += ",ip="; Data += WiFi.localIP().toString(); Data += ",dtzid="; Data += dMETER.getMeterID(); Data += ",dtzname="; Data += cDTZ.getNameDTZ(); Data += " pwa="; Data += dMETER.Electric_data[_CONSUMPTION]; // here is the space as an separator between immutable and changeable values Data += ",bez1="; DispValue = dMETER.MeterKWH[_FROMGRID_0]; Data += DispValue / 10000; Data += ",bez2="; DispValue = dMETER.MeterKWH[_FROMGRID_1]; Data += DispValue / 10000; Data += ",bez3="; DispValue = dMETER.MeterKWH[_FROMGRID_1]; Data += DispValue / 10000; Data += ",eeg1="; DispValue = dMETER.MeterKWH[_FEEDIN]; Data += DispValue / 10000; Data += ",ul1="; DispValue = dMETER.Electric_data[_U_L1]; Data += DispValue / 10; Data += ",ul2="; DispValue = dMETER.Electric_data[_U_L2]; Data += DispValue / 10; Data += ",ul3="; DispValue = dMETER.Electric_data[_U_L3]; Data += DispValue / 10; Data += ",il1="; DispValue = dMETER.Electric_data[_I_L1]; Data += DispValue / 100; Data += ",il2="; DispValue = dMETER.Electric_data[_I_L2]; Data += DispValue / 100; Data += ",il3="; DispValue = dMETER.Electric_data[_I_L3]; Data += DispValue / 100; Data += ",frq="; DispValue = dMETER.Electric_data[_FREQ]; Data += DispValue /10; Data += ",timestamp="; Data += _timestamp; // -3600; // i dont know why, but db shows an hour more, also sub an hour return Data; } //************ just for debug only ***************************************/ String sendProtokollDebug(){ String Data = "protDebug=1&usid="; Data += cDTZ.getUserID(); String bufferDTZ; for(int i=0;i<_MAXBUFFER;i++ ){ bufferDTZ += _dataDTZ[i]; } Data += "&buffer="; Data += base64::encode(bufferDTZ,_MAXBUFFER); Data += "&end"; return Data; } String sendProtokollDebugDIN(String protData){ String Data = "protDebug=1&usid="; Data += cDTZ.getUserID(); Data += "&buffer="; Data += protData; Data += "&end"; return Data; } //*********** clear data after sending ********************************************/ void clearValues(){ dMETER.clearValues(); for(int i=0;i<_MAXBUFFER;i++){ // clear buffer of old data _dataDTZ[i]=0; } } //*********** send DTZ-Datas to PHP script on server per POST *******************/ bool setContentAndSend(){ bool r = false; if(cDTZ.getUser() != "sendJSON"){ if (cDTZ.getTargetURL().indexOf(".php") > 0){ // sending data-values to a php-script if (cDTZ.getTargetURL().indexOf("crypt") > 0){ _crypt="AES128 CTR"; // r = send2dbasePOST(eDATA.encryptByHashKey(prepareSendValues(),cDTZ.getUserID(),cDTZ.getPasswEncrypt()),true); r = send2dbasePOST(eDATA.encryptDataByAES(prepareSendValues(),cDTZ.getUserID()),true); } else{ _crypt="none"; r = send2dbasePOST(prepareSendValues(),true); } } else {r = send2dbasePOST(prepareSendValuesInFlux(),true);} // send data-values into inFlux-db } else {r = send2dbasePOST(prepareSendValuesAsJSON(),true);} // send data-values as JSON return r; } //*********** parts for send data over mqtt message to broker *******************************/ String getElectricData(){ String message="{\"U1\":"; message += String(dMETER.Electric_data[_U_L1]); message += ",\"U2\":"; message += String(dMETER.Electric_data[_U_L2]); message += ",\"U3\":"; message += String(dMETER.Electric_data[_U_L3]); message += ",\"I1\":"; message += String(dMETER.Electric_data[_I_L1]); message += ",\"I2\":"; message += String(dMETER.Electric_data[_I_L2]); message += ",\"I3\":"; message += String(dMETER.Electric_data[_I_L3]); message += ",\"FQ\":"; message += String(dMETER.Electric_data[_FREQ]); message += "}"; return message; } String getKWHs(){ String message="{\"A0\":"; message += String(dMETER.MeterKWH[_FROMGRID_0]); message += ",\"A1\":"; message += String(dMETER.MeterKWH[_FROMGRID_1]); message += ",\"A2\":"; message += String(dMETER.MeterKWH[_FROMGRID_2]); message += ",\"B0\":"; message += String(dMETER.MeterKWH[_FEEDIN]); message += "}"; return message; } String getConsumptions(){ String message="{\"PS\":"; message += String(dMETER.Electric_data[_CONSUMPTION]); message += ",\"P1\":"; message += String(dMETER.Electric_data[_P_L1]); message += ",\"P2\":"; message += String(dMETER.Electric_data[_P_L2]); message += ",\"P3\":"; message += String(dMETER.Electric_data[_P_L3]); message += "}"; return message; } String getMQTTpath(){ String path="StromLog/"; path += cDTZ.getNameDTZ(); return path; } bool sendKWH2mqtt(){ String message=getKWHs(); String path=getMQTTpath(); path += "/kwh"; client.publish(path.c_str(),message.c_str()); } bool sendElectricsData2mqtt(){ String message=getConsumptions(); String path=getMQTTpath(); path += "/watt"; client.publish(path.c_str(),message.c_str()); path=getMQTTpath(); path += "/electrics"; message=getElectricData(); client.publish(path.c_str(),message.c_str()); } //*** check and reconnect if it nesseccary *****// void reconnect() { // Loop until we're reconnected or timeout #define maxLoop 5 uint maximalLoop=0; while (!client.connected() && maximalLoop <= maxLoop) { String clientId = "StromLog-"; clientId += cDTZ.getNameDTZ(); // Create client ID if (client.connect(clientId.c_str(),cDTZ.getIoBrokerUser().c_str(),cDTZ.getIoBrokerPW().c_str())) { // Attempt to connect // client.subscribe("StromLog"); // Once connected, publish an announcement... yield(); } delay(100); _mqttWaitTime2Connect = 0; maximalLoop++; } if(maximalLoop >= maxLoop){ _mqttBrokerOK=0; // turn off, because broker is not available _mqttWaitTime2Connect = 120; } else _mqttBrokerOK=1; } //*********************** set mqtt server address ************************ void setMQTTserver(){ IPAddress broker; client.disconnect(); broker.fromString(cDTZ.getIoBroker()); if(broker)client.setServer(broker, cDTZ.getIoBrokerPort()); else cDTZ.SendMQTT=0; client.setSocketTimeout(2); //socket timeout interval in Seconds // Serial.println(broker.toString()); } //*********** mqtt parts end ****************************************************/ //*********** send DTZ-Datas to PHP script on server per POST *******************/ bool send2dbasePOST(String content, bool saveIfError){ bool r=false; bool wrongAccess = false; if (WiFi.status() == WL_CONNECTED) { WiFiClientSecure clientSSL; WiFiClient client; HTTPClient http; String getTarget = cDTZ.getTargetURLclean(); bool connect; String target; if(cDTZ.SendSSL>0){ target = "https://"; target += getTarget; //target +="?"; target +=cDTZ.getUserID(); int httpsPort=443; clientSSL.setInsecure(); // magic line without check certificate clientSSL.connect(target,httpsPort); // set connection by https connect = http.begin(clientSSL,target); }else{ target= "http://"; target += getTarget; //target +="?"; target +=cDTZ.getUserID(); connect = http.begin(client,target); } if(connect){ // http.setTimeout(10000); // 10 seconds? if(cDTZ.getUser() == "sendJSON") http.addHeader("Content-Type","application/json;charset=utf-8"); else http.addHeader("Content-Type","application/x-www-form-urlencoded"); int httpCode = 0; _transmitResult = ""; httpCode = http.POST(content); _httpMarker = httpCode; //debug if (httpCode >= 100) { if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_NO_CONTENT){ _transmitResult = http.getString(); if (_transmitResult == "OK
"){ r = true; // if data saved successful and push time != 15 will set 15 minutes again cDTZ.PushIntervall=15; saveIfError = false; } else if (_transmitResult.substring(0,7) == "OKtime="){ r = true; // if data saved successful and push time != 15 will set 15 minutes again cDTZ.PushIntervall=15; saveIfError = false; _timestamp = _transmitResult.substring(7,17).toInt(); //change new timestamp delivered from server. example: 1640998800 _secAdjust = 1000; //set normal-time of seconds getTime2Value(); // set the new time } else if(_transmitResult == "saved"){ r = true; // if data saved successful for (user) own server saveIfError = false; } else if(httpCode == HTTP_CODE_NO_CONTENT){ r = true; // if data saved successful for (user) own server saveIfError = false; } else if(_transmitResult == "RestartESP"){ ESP.restart(); // restart StromLog and delete all saved protocols } else { r = false; saveIfError = false; wrongAccess = true; } } else {_transmitResult = "[HTTP] Unable to connect! "; _transmitResult +=httpCode; _transmitResult +="
Ret: "; _transmitResult += http.getString();} } else {_transmitResult ="[http] missing return-code"; r = false;} } else {_transmitResult ="[HTTP] Service not found!"; r = false;} http.end(); } if(r == false && saveIfError == true && wrongAccess == false && _sendingOK == true) cDTZ.saveValues(content); // save values data into littleFS to send later again return r; } //****************** read saved data and send 2 server **************************** void sendSavedData(){ if(cDTZ.saveCounter > 0){ if(send2dbasePOST(cDTZ.readSavedValues(1),false) == true){ if(cDTZ.saveCounter > 1){ for(int i=2;i<=cDTZ.saveCounter;i++){ delay(500); send2dbasePOST(cDTZ.readSavedValues(i),false); yield(); cDTZ.delFile(i); if(i > _SAVE_PROT_MAX) {cDTZ.delFile(0); return;} // error handling } cDTZ.delFile(0); _flashCounter=0; } else { cDTZ.delFile(1); cDTZ.saveCounter = 0; _flashCounter=0; } } if(_transmitResult == "Wrong MeterID")cDTZ.delFile(0); if(_transmitResult == "Wrong user or password")cDTZ.delFile(0); } } /*********************************************************** * convert a unix-timestamp into single bytes as value * function works up to 1972 until 19.01.2038 ***********************************************************/ void 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; // 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]" } void writeTime(){ _act_date_time[_YEAR] = _date_time[_YEAR]; _act_date_time[_MONTH] = _date_time[_MONTH]; _act_date_time[_DAY] = _date_time[_DAY]; _act_date_time[_HOUR] = _date_time[_HOUR]; _act_date_time[_MINUTE] = _date_time[_MINUTE]; _act_date_time[_SECOND] = _date_time[_SECOND]; } void getTime2Value(){ Timestamp2DateTime(_timestamp); writeTime(); } //****************************** show received a protocol on red LED *************************** void showProtocolOnLED(){ if (cDTZ.SHOWPROTOCOL==1 && _flashCounter <= 0){ if (_ShowProtocolTime > 0){ // Flash LED when protocol received digitalWrite(_LED,_ON); _ShowProtocolTime --; } else digitalWrite(_LED,_OFF); // if time over turn off LED } } //*** get and deliver the wandler-factor for calculation / just for AS1440 and L&G E650 **** int getWandlerFactor(){ return cDTZ.getFactor(); } //*********************** protocol check if meter is connected without error *************** void checkConnection(uint32_t actProtCount){ if(actProtCount == _protocolCheck){ _protocolError=true; clearValues(); // clear data } else _protocolError=false; _protocolCheck = actProtCount; } //*********************** setup esp on start ***************************** void setup() { pinMode(_BOARDTASTER, INPUT_PULLUP); // set as input pinMode(_LED, OUTPUT); // set as output digitalWrite(_LED,_OFF); pinMode(_RECEIVE485, OUTPUT); digitalWrite(_RECEIVE485, _OFF); // set pin on low-level for chip MAX485 to receive _flashCounter = 30; // time to show flash on LED for infos dMETER.Begin(_MAXBUFFER); dMETER.setSerialSpeed(); // Serial.begin(9600); // DTZ liefert 9600 baud 8-N-1 _serialType=0; delay(200); wifi_station_set_hostname("StromLog"); // set hostname into WLAN network bool result = setupWIFI(); if (result) { cDTZ.begin(); // start filesystem and properties of user server.begin(); syncron_NTP_Time(false); getTime2Value(); delay(200); } else { for (int i=0 ; i<20;i++){ if (digitalRead(_LED) == HIGH)digitalWrite(_LED,_OFF); else digitalWrite(_LED,_ON); delay(200); } ESP.restart(); } if(cDTZ.SendSSL==0) _secAdjust=_ADJUST_HTTP; else _secAdjust=_ADJUST_HTTPS; setMQTTserver(); } //************************* esp loop function *************************** void loop() { yield(); checkButton(); // check if user pressed button for set new SSID uint32_t currentMillis = millis(); if(internClock(currentMillis)== true) { // every second will run this component if(cDTZ.SendMQTT && _mqttBrokerOK && _newProtocol){ // check if mqtt is enabled if (!client.connected()) { reconnect(); } sendElectricsData2mqtt(); // send consumption to mqtt every second #if (defined VDE_DIN || defined ISKRA_M174 || defined ZE311_DR || defined PAF_EC3 || defined LG_350) _newProtocol=0; #endif } else if(cDTZ.SendMQTT && !_mqttBrokerOK) _mqttWaitTime2Connect--; if(_act_date_time[_MINUTE] % 2 && _act_date_time[_SECOND]==0 && _mqttBrokerOK==0){ // try reconnect to mqtt broker every two minutes _mqttBrokerOK=1; } // Serial.println(eDATA.encryptDataByAES("Verschlüsselter Text",cDTZ.getUserID())); // for debug if (_flashCounter > 0){ // Flash LED when errors occurs or for information if (digitalRead(_LED) == HIGH)digitalWrite(_LED,_OFF); else digitalWrite(_LED,_ON); _flashCounter --; } else digitalWrite(_LED,_OFF); // if flashtime over turn off LED if (_act_date_time[_HOUR] == 21 && _act_date_time[_MINUTE]==(cDTZ.getUserID()%50) && _act_date_time[_SECOND]==1){ // update timestamp if (syncron_NTP_Time(true) == true) getTime2Value(); // convert timestamp to date-time-string else if(!WiFi.isConnected()) ESP.restart(); // check if connected yet } 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(_act_date_time[_MINUTE] % 15 ==0 && _act_date_time[_SECOND] == 45){ if (syncron_NTP_Time(false) == true) getTime2Value(); // get timestamp again if sync was failure, perhaps after power down } } if(cDTZ.SendMQTT==1 && _mqttBrokerOK && _act_date_time[_SECOND] ==1 && (_act_date_time[_MINUTE]%2)){ sendKWH2mqtt(); // sending per mqtt } if (cDTZ.getUserID() !=0){ if (_act_date_time[_SECOND] == (cDTZ.getUserID()%40)+3) { // is called every minute at the n seconds if(cDTZ.saveCounter > 0){ sendSavedData(); } if (_act_date_time[_MINUTE] % cDTZ.PushIntervall == 0) { // and every n-th minute if (setContentAndSend()){ _transmitResult = "Letzter Datentransfer: "; // clearValues(); // clear data _sendingOK = true; } else{ _flashCounter=300; // show an error n seconds _transmitResult += "🔴"; } _transmitResult += "
"; _transmitResult += getTime2String(_act_date_time[_HOUR],_act_date_time[_MINUTE],_act_date_time[_SECOND]); _transmitResult += " Uhr - "; _transmitResult += getDate2String(_act_date_time[_YEAR],_act_date_time[_MONTH],_act_date_time[_DAY]); } } } if (_act_date_time[_SECOND] % 15 == 0){ // it's in use for old meter-types which dont send data automaticly if(_act_date_time[_MINUTE]%2 == 0)checkConnection(dMETER.ProtocolCounter); if((_act_date_time[_HOUR]==0) || (cDTZ.getRestDay() != _act_date_time[_DAY])){ // dont call meter at one day of month dMETER.sendRequest2Meter(); // if the meter need an push for send data back -> dependent on decodeMeterXY.h } } if(_act_date_time[_SECOND] == 0){ saveDiagramWatt(_timestamp,dMETER.Electric_data[_CONSUMPTION]); } } if (Serial.available()>0) { // read the incoming byte from uart Serial.readBytes(_receivedByte,1); _dataDTZ[_dataPointer] = _receivedByte[0]; _millis4Reciever=millis(); //save millis for every income byte for control check of errors dMETER.crc_add(_dataDTZ[_dataPointer]); // calculate crc if (_dataPointer > _MAXBUFFER-2){ //zur sicherheit, dass pointer nicht überläuft _dataPointer =0; } dMETER.mainEntryDecode(); // main entry into object to decode data received from meter -> dependent on decodeMeterXY.h } showProtocolOnLED(); check4Client(); // thats it's if with the browser show data on port 80 by html }