/** * DTZ EEG Logger * Version 3.99 open for ISKRA MT174 * 11.09.2022 P. Rebesky * author Creator P.Rebesky * Copyright (©): 2020-2022 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 use can exchange some parts of code or modify some code-lines. This code can change for private use only. * This software is basicly owned by Peter Rebesky and any comercial use is forbidden without approval of us (me). **/ /** * DTZ EEG Logger * Version 4.04 for ISKRA MT174 * 27.08.2022 P. Rebesky * author Creator P.Rebesky * Copyright (©): 2020-2022 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 use can exchange some parts of code or modify some code-lines. This code can change for private use only. * This software is basicly owned by Peter Rebesky and any comercial use is forbidden without approval of us (me). **/ #define OTA_on #include //ESP8266 Core WiFi Library #include //ESP8266 Core WiFi Library #include #include //Local WebServer #include "configDTZ.h" #include "strings.h" #include // LittleFS is declared #include #include #include // https://github.com/tzapu/WiFiManager #include #include #define BoardTaster 0 #define LED 14 #define ON 1 #define OFF 0 #define FLASHTIME 300 #define MESZ 0 // summer-time = 1 else winter-time = 0 #define MaxBuffer 512 // maximum buffer for received bytes #define polynom 0x1021 // x16 + x12 + x5 + 1 0x1021 #define TimeOf2021 1609459260 unsigned int year = 0; byte month = 0; int day = 0; int years=0; byte hour = 0; byte minute =0; byte second =0; int ShowProtocolTime = 0; int SecAdjust = 998; uint32_t timestamp = 0; uint32_t timeDummy = 0; uint32_t SecOfYear =0; uint32_t ProtocolCounter =0; byte oldsecond = 0; uint32_t previousMillis = 0; uint32_t secondMillis =0; String act_time; String act_date; String TransmitResult; char MeterType[] = " NOT_DETECT "; String MeterTime; String MeterDate; const long utcOffsetInSeconds = 3600; uint32_t FromGridS = 0; uint32_t IntoGrid = 0; uint32_t FromGridA = 0; uint32_t FromGridB = 0; int U_L1 =0; int U_L2 =0; int U_L3 =0; int I_L1 =0; int I_L2 =0; int I_L3 =0; int P_L1 =0; int P_L2 =0; int P_L3 =0; int Frequency = 0; float DispValue; int dtznumber = 0; int Consumption = 0; int consumptionFromGrid = 0; int consumptionIntoGrid = 0; uint32_t meterManufractur; uint32_t meterID0=0; uint32_t meterID1=0; uint32_t meterID2=0; uint16_t crc_ccitt = 0xffff; uint16_t crc = 0xffff; int x=0; int errorCounter = 0; char dataDTZ[MaxBuffer]; char receivedByte[8]; int pointer = 0; char extractValue[14]; uint32_t singleValue = 0; uint32_t value0 = 0; uint32_t value1 = 0; uint32_t value2 = 0; uint32_t valueD = 0; char dummy0 = 0; char dummy1 = 0; int flashCounter = 0; int zaehler =0; int serialSpeed = 0; bool sendingOK = false; int pointerEOT = MaxBuffer-2; int sendProtocols = 0; String bufferDTZ; WiFiManager wm; // global wm instance //WiFiManagerParameter custom_field; // global param ( for non blocking w params ) WiFiServer server(80); ESP8266WiFiMulti WiFiMulti; // Define NTP Client to get time WiFiUDP ntpUDP; NTPClient timeClient(ntpUDP, "pool.ntp.org", utcOffsetInSeconds); configDTZ cDTZ; //*********************** 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"); wm.setConfigPortalTimeout(300); // 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) { // Serial.println("Failed to connect or hit timeout"); return false; } else { //if you get here you have connected to the WiFi // Serial.println("Connected to saved ssid successfully"); 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() { timeClient.begin(); if(timeClient.update()) { timeDummy = timeClient.getEpochTime(); if(timeDummy !=0){ timeDummy += (MESZ * 3600); // 3600 means add 1 hour int correctur = timeDummy-timestamp; if(timestamp !=0){ if (correctur > 60 && SecAdjust > 980) SecAdjust--; if (correctur < -60 && SecAdjust < 1005) SecAdjust++; } timestamp = timeDummy; return true; } } return false; } //*************************** create a string from values for data and time ******************************** void getTimeDate2String(){ act_time = ""; act_date = ""; 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); 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 += "."; if(year < 10) act_date += "0"; act_date += String(year); } //**************************** intern clock ******************************* bool internClock(uint32_t currentMillis) { if (currentMillis < secondMillis) secondMillis = 0; // handle if currentMillis are overflows, max 32 bits if (currentMillis - secondMillis >=SecAdjust){ // intern clock adjust secondMillis = currentMillis; second++; timestamp++; if (second >=60){ second=0, minute++;} if (minute >=60){ minute=0, hour++;} if (hour >=24){ hour=0; day++;} 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 ? } // Match the request client.flush(); String s; if (argument == "/save") { cDTZ.savePostValues(PostValues); s = HTTP_ANSWER; s += HTTP_HEAD_DTZ; s += ""; s += cDTZ.getNameDTZ(); s += ""; s += HTTP_BODYSTYLE_DTZ; s += ""; s += HTTP_DIV_FIELD; s += "

Ihre Eingaben wurden gespeichert.

"; s += "

"; s += "\n"; } else if (argument == "/setup") { s = HTTP_ANSWER; s += HTTP_HEAD_DTZ; s += ""; s += cDTZ.getNameDTZ(); s += ""; s += HTTP_BODYSTYLE_DTZ; s += ""; s += HTTP_DIV_FIELD; s += "

Grundeinstellungen

"; s += "
"; s += "


"; s += "


"; s += "


"; s += "


"; s += "


"; s += "


"; s += "
Show Protokoll an LED:"; else s += ">"; s += "

"; s += "

"; s += "
StromLog-Version: "; s += LoggerVersion; #ifdef OTA_on s += "
Check for update firmware"; #endif s += "

Hilfe & Impressum

"; s += "\n"; } else if (argument == "/showsetup") { s = HTTP_ANSWER; s += "none"; } else if (argument == "/favicon.ico") { s = HTTP_ANSWER; s += "none"; } else if (argument.substring(0,13) == "/searchLogger") { s = HTTP_ANSWER; int argBegin = argument.indexOf("?"); if (argBegin != -1){ int idEx = argument.substring(argBegin+1,argument.length()).toInt(); if(idEx == cDTZ.getUserID() || cDTZ.getUserID() == 0){ s += "{\"DTZLOGGER\":\"" + LoggerVersion + "\"}"; } } } else if (argument == "/" || argument == "/back") { // Prepare the response s = HTTP_ANSWER; s += HTTP_HEAD_DTZ_REFRESH; s += ""; s += cDTZ.getNameDTZ(); s += ""; s += HTTP_BODYSTYLE_DTZ; s += ""; s += HTTP_DIV_FIELD; s += "

StromLog-Status

"; s += "

Zeit: 🔄
"; s += "
"; s += "Datum:

"; s += "StromLog-IP:

"; // s += "ESP-Voltage:
"; s += "

"; s += TransmitResult; s += "
"; s += "

"; s += cDTZ.getNameDTZ(); s += "

"; s += "Summen-Leistung:

"; s += "Bezug in kWh:

"; s += "Einspeisung in kWh:

"; s += "

Hersteller-ID:

"; s += "Zähler-ID:

"; s += "Received protocols:

"; if(sendingOK == false)s += "🔴"; // show a red bubble when StromLog not connect on server at the first time s += "Saved protocols:"; s += "

"; s += "Zeitkorrektur:

"; s += "

"; s += HTTP_SETUP_BUTTON; s += "

Hilfe & Impressum

"; s += "\n"; } else if (argument == "/memory") { s = HTTP_ANSWER; s += "Free space: "; s += cDTZ.FREESPACE; s += "
"; if(cDTZ.saveCounter > 0){ for(int i=1;i<=cDTZ.saveCounter;i++){ s += cDTZ.readSavedValues(i); s += "
"; } } else s += "No saved data.
"; s += "\n"; } else if (argument == "/sendProtocols"){ sendProtocols = 10; s = HTTP_ANSWER; s += "10 RAW Protocols will be send to server now.
"; s += HTTP_TEXT_END; } else if (argument == "/currentData"){ s = HTTP_ANSWERJSON; s += getValuesAsJSON(); } else if (argument == "/allData"){ s = HTTP_ANSWERJSON; s += prepareSendValuesAsJSON(); } else if (argument == "/consumption"){ s = HTTP_ANSWERJSON; s += sendConsumptionValue(); } else if(argument == "/setTime"){ s = HTTP_ANSWER; s += HTTP_BODYSTYLE_DTZ; s += ""; s += HTTP_DIV_FIELD; s += "

Die Uhr wird jetzt neu synchronisiert.

"; s += "

Zeit:

"; if (syncron_NTP_Time() == true){ Timestamp2DateTime(); 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); } } } //*********** prepare values for sending to the PHP-script ***************/ String prepareSendValues(){ String Data = "timestamp="; Data += timestamp; Data += "&usid="; Data += cDTZ.getUserID(); Data += "&bez1="; Data += FromGridS; Data += "&bez2="; Data += FromGridA; Data += "&eeg1="; Data += IntoGrid; Data += "&pwa="; Data += Consumption; Data += "&ul1="; Data += U_L1; Data += "&ul2="; Data += U_L2; Data += "&ul3="; Data += U_L3; Data += "&il1="; Data += I_L1; Data += "&il2="; Data += I_L2; Data += "&il3="; Data += I_L3; Data += "&frq="; Data += Frequency; // Data += "&frq="; Data += SecAdjust; Data += "&ip="; Data += WiFi.localIP().toString(); Data += "&dtzma="; Data += MeterType; Data += "&dtzid="; sprintf(extractValue,"%08X",meterID2); Data += extractValue; sprintf(extractValue,"%08X",meterID1); Data += extractValue; sprintf(extractValue,"%08X",meterID0); Data += extractValue; Data += "&dtzname="; Data += cDTZ.getNameDTZ(); Data += "&end"; return Data; } //*********** clear data after sending ********************************************/ void clearDtzData(){ meterManufractur=0; meterID0=meterID1=meterID2=0; FromGridS=IntoGrid=FromGridA=0; U_L1=U_L2=U_L3=I_L1=I_L2=I_L2=I_L3=Frequency=0; } //*********** prepare values for sending to influx-db *******************/ String prepareSendValuesInFlux(){ String Data = "meinzaehler"; Data += ",usid="; Data += cDTZ.getUserID(); Data += ",ip="; Data += WiFi.localIP().toString(); Data += ",dtzma="; Data += MeterType; Data += ",dtzid="; sprintf(extractValue,"%08X",meterID2); Data += extractValue; sprintf(extractValue,"%08X",meterID1); Data += extractValue; sprintf(extractValue,"%08X",meterID0); Data += extractValue; Data += ",dtzname="; Data += cDTZ.getNameDTZ(); Data += " pwa="; Data += Consumption/10; Data += ",bez1="; DispValue = FromGridS; Data += DispValue / 10000; Data += ",bez2="; DispValue = FromGridS; Data += DispValue / 10000; Data += ",eeg1="; DispValue = IntoGrid; Data += DispValue / 10000; Data += ",ul1="; DispValue = U_L1; Data += DispValue / 10; Data += ",ul2="; DispValue = U_L2; Data += DispValue / 10; Data += ",ul3="; DispValue = U_L3; Data += DispValue / 10; Data += ",il1="; DispValue = I_L1; Data += DispValue / 100; Data += ",il2="; DispValue = I_L2; Data += DispValue / 100; Data += ",il3="; DispValue = I_L3; Data += DispValue / 100; Data += ",frq="; DispValue = Frequency; Data += DispValue /10; Data += ",timestamp="; Data += timestamp; Data += ",end"; return Data; } //*********** build an JSON code of values and return it *****************/ String getValuesAsJSON(){ String json = "{\"timestamp\":"; json += timestamp; json += ",\"meterData\":"; json += "{\"consumption\":{\"value\":"; json += Consumption/10; json += ",\"unit\":\"W\"},"; json += "\"FromGridSum\":{\"value\":"; json+= FromGridS; json += ",\"unit\":\"dWh\"},"; json += "\"FromGridS\":{\"value\":"; json+= FromGridS; json += ",\"unit\":\"dWh\"},"; json += "\"FromGridB\":{\"value\":"; json+= FromGridB; json += ",\"unit\":\"dWh\"},"; json += "\"FeedIn\":{\"value\":"; json+= IntoGrid; json += ",\"unit\":\"dWh\"}"; json += "},\"electricsData\":{"; json += "\"voltageL1\":{\"value\":"; json+= U_L1; json += ",\"unit\":\"dV\"},"; json += "\"voltageL2\":{\"value\":"; json+= U_L2; json += ",\"unit\":\"dV\"},"; json += "\"voltageL3\":{\"value\":"; json+= U_L3; json += ",\"unit\":\"dV\"},"; json += "\"currentL1\":{\"value\":"; json+= I_L1; json += ",\"unit\":\"cI\"},"; json += "\"currentL2\":{\"value\":"; json+= I_L2; json += ",\"unit\":\"cI\"},"; json += "\"currentL3\":{\"value\":"; json+= I_L3; json += ",\"unit\":\"cI\"},"; json += "\"powerL1\":{\"value\":"; json+= P_L1; json += ",\"unit\":\"cW\"},"; json += "\"powerL2\":{\"value\":"; json+= P_L2; json += ",\"unit\":\"cW\"},"; json += "\"powerL3\":{\"value\":"; json+= P_L3; json += ",\"unit\":\"cW\"},"; json += "\"freq\":{\"value\":"; json+= Frequency; json += ",\"unit\":\"dHz\"}"; json += "}}"; return json; } //*********** send consumption only **************************************/ String sendConsumptionValue(){ String json = "{\"consumption\":{\"value\":"; json += Consumption/10; json += ",\"unit\":\"W\"}}"; return json; } //*********** prepare values for sending as JSON *************************/ String prepareSendValuesAsJSON(){ String Data = "{\"userid\":"; Data += cDTZ.getUserID(); Data += ",\"meter\":{"; Data += "\"localip\":\""+ WiFi.localIP().toString()+"\","; Data += "\"metername\":\""+cDTZ.getNameDTZ()+"\","; Data += "\"manufracturer\":\""; Data += MeterType; Data +="\","; Data += "\"meterid\":\""; sprintf(extractValue,"%08X",meterID2); Data += extractValue; sprintf(extractValue,"%08X",meterID1); Data += extractValue; sprintf(extractValue,"%08X",meterID0); Data += extractValue; Data += "\"},\"currentData\":"; Data += getValuesAsJSON(); Data += "}"; return Data; } //************ just for debug only ***************************************/ String sendProtokollDebug(){ String Data = "protDebug=1&usid="; Data += cDTZ.getUserID(); Data += "&buffer="; Data += bufferDTZ; Data += "&end"; return Data; } //*********** send DTZ-Datas to PHP script on server per POST *******************/ bool setContentAndSend(){ bool r = false; if (cDTZ.getTargetURL().indexOf(".php") > 0){ // sending data-values to a php-script r = send2dbasePOST(prepareSendValues(),true); } else {r = send2dbasePOST(prepareSendValuesInFlux(),true);} // send data-values into inFlux-db return r; } //*********** send DTZ-Datas into server per POST *******************/ bool send2dbasePOST(String content, bool saveIfError){ bool r=false; bool wrongAccess = false; if (WiFiMulti.run() == WL_CONNECTED) { WiFiClient client; HTTPClient http; String target = "http://"; target += cDTZ.getTargetURL(); if (http.begin(client,target)){ http.addHeader("Content-Type","application/x-www-form-urlencoded"); int httpCode = 0; httpCode = http.POST(content); if (httpCode > 100) { if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_NO_CONTENT){ TransmitResult = http.getString(); if (TransmitResult == "OK
" || httpCode == HTTP_CODE_NO_CONTENT){ r = true; // if data saved successful than result=true else false saveIfError = false; } else { TransmitResult = "Return: " + TransmitResult; r = false; saveIfError = false; wrongAccess = true; } } } else { TransmitResult ="[HTTP] Unable to connect!"; r = false; } } else { TransmitResult ="[HTTP] Server 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 > 15) {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(){ int dummy = 0; bool leapYear = false; // default: don't be a leap-year timeDummy = timestamp - 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 } year = years + 1972; // add start-year +2: now, we know the actual year and can figure out if is a leap-year // 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(year,0)==0 && bitRead(year,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 day = (timeDummy % SecOfYear) / 86400; // get the days of actual year month = 0; // set zero for (int i=0; day>=0; i++ ) { // do it until the day(s) are negativ, this loop has be exicute one time -> to set month at ones dummy = day; // store the value befor result is negativ for break, because the last one was the day of actual month day = day - DaysPerMonth[i]; // sub the days of month until it's negativ, according byte-array if (i == 1 && leapYear == true) {day -= 1;} // sub the 29. of february on leapyears ;-) month++; // add one month on every loop if (i > 11) return; // if this function wild ;-) then break (max. months are 0 - 11) } 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 ******/ hour = (timeDummy % 86400) / 3600; // the value of timeDummy contents the rest of hours, minutes and seconds only minute = (timeDummy % 3600) / 60; // it's shorter to take reduced timeDummy than the whole timestamp second = timeDummy % 60; } //****************************** 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 } } //****************************** compare search-string with buffer ***************************** int CheckOBIS(char *checkCode, int i, int searchLength){ int result = 0; for (int y=0;y<=searchLength;y++){ if(*(checkCode+y) != dataDTZ[i+y]) return -1; // check chars if identical } return result; } //****************************** search OBIS-code signation ***************************** int SearchOBIS(char *searchCode, int slength){ int x = 0; do { if (*searchCode == dataDTZ[x]){ if (CheckOBIS(searchCode, x, slength)==0)break; } x++; } while (x < MaxBuffer); if(x > MaxBuffer - 10)return -1; // nothing of OBIS founded else return x+slength+1; // pointer on the first value after OBIS-String } //************ reflected uint16_t for CRC calculation ******************** // this function turns the bits -> MSB will be LSB from 0111001 to 1001110 // it's needed for counting crc reflected input and reflected output uint16_t refUint(uint16_t reflector, int bits){ // bits = how many bits should be reflected uint16_t shift=0; int i=0; do { shift <<= 1; if(reflector & 0x0001) shift |= 1; reflector >>= 1; i++; } while (i < bits); return shift; } //******************* calculate CRC16-CCITT ****************************** uint16_t crc_one_byte(uint16_t crcR, uint16_t oneByte, uint16_t poly){ oneByte = refUint(oneByte,8); // reflected input oneByte <<= 8; // shift left eight times for(int i=0; i<8; i++){ if((crcR ^ oneByte) & 0x8000) crcR =(crcR << 1)^ poly; else crcR <<= 1; oneByte <<= 1; } return crcR; } //*********************** reset all metervalues ********************** void resetValues(){ FromGridS=FromGridA=IntoGrid=Consumption =0; U_L1 =0; U_L2 =0; U_L3 =0; I_L1 =0; I_L2 =0; I_L3 =0; Frequency =0; } //*********************** decode one value from meter ************ uint32_t getOneValueDIN(int startpoint, int endpoint, int valueType){ int32_t result= -1; //debug Serial.println(dataDTZ[startpoint]); // debug if(dataDTZ[startpoint] == 0x28){ // if begin by "(" -> then OK if(valueType == 0){ // meternumber String meterNumber = ""; for(int i=1; i < endpoint; i++){ if(dataDTZ[startpoint+i] == 0x29) endpoint = 1; // break if appeare ) meterNumber += dataDTZ[startpoint+i]; } int numberLength = meterNumber.length(); if (numberLength <= 10) meterID0 = meterNumber.toInt(); else { meterID0 = meterNumber.substring(8).toInt(); valueType = 1; endpoint = 9; } } if(valueType == 1){ //meter manufracture int oneChar = 0; meterID2 = 0x000000A0; for(int i=1; i < endpoint; i++){ oneChar = dataDTZ[startpoint+i]; if(oneChar == 0x29) endpoint = 1; // break if appeare ) if(oneChar >= 0x30 && oneChar <= 0x39){ oneChar &= 0x0f; if(i<4 && oneChar!=0){ meterID2 = meterID2 << 4; meterID2 += oneChar; } meterID1 = meterID1 << 4; meterID1 += oneChar; } else { meterID1 = meterID1 << 8; meterID1 += oneChar; } } } if(valueType == 0x50){ String valueSing = "0"; int i=0; int dot=0; for(i=1; i < endpoint; i++){ if(dataDTZ[startpoint+i] == 0x2a) endpoint = 1; // break if appeare * if(dataDTZ[startpoint+i] == 0x2e) dot=i; if(dataDTZ[startpoint+i] >= 0x30 && dataDTZ[startpoint+i] <= 0x39) valueSing += dataDTZ[startpoint+i]; } result = valueSing.toInt(); if (dot > 0){ // check if was a dot //*************************************************** if(dot == 3 && dataDTZ[startpoint+i] == 0x41) result = result/10; dot = i-dot-2; //calculate values after dot switch (dot){ case 1: result = result*1000; break; case 2: result = result*100; break; case 3: result = result*10; break; case 4: break; default: break; } } // Serial.println(result); // debug // Serial.println(dot); // debug } if(valueType == 0x10){ MeterTime = ""; for(int i=1; i < endpoint; i++){ MeterTime += dataDTZ[startpoint+i]; if(i == 2 || i == 4) MeterTime += ":"; } } if(valueType == 0x11){ MeterDate = ""; for(int i=1; i < endpoint; i++){ MeterDate += dataDTZ[startpoint+i]; if(i == 2 || i == 4) MeterDate += "-"; } } } return result; } //*********************** meter-typ identifications ***************** bool getMeterType(){ if (dataDTZ[pointer] == 0x0a && dataDTZ[pointer-1] == 0x0d){ // check of end-code ") + cr + lf" int poi=SearchOBIS("/",0); int i=0; if(poi>0){ if (dataDTZ[poi] != 0x3f && dataDTZ[poi+1] != 0x3f){ serialSpeed = dataDTZ[poi+3] & 0x0f; if (serialSpeed > 4 && serialSpeed < 9){ for(i=0;i<15 && dataDTZ[poi+i]!= 0x0d ;i++){ // get string until cr (0x0d) MeterType[i] = dataDTZ[poi+i]; } MeterType[i] = 0; // set end string by 0x00 hex } } } pointer = 0; dataDTZ[0] = 0; } if (dataDTZ[pointer-2] == 0x21){ pointer = 0; } pointer++; } //*********************** decode data from Meter EN65062 ************ void decodeDataDIN(){ if (dataDTZ[pointer] == 0x0a && dataDTZ[pointer-1] == 0x0d){ // check of end-code ") + cr + lf" int32_t result= -1; if (SearchOBIS(MeterType,4)> -1){ delay(200); Serial.print(SetBaud9600); // sent meter for comunication with baud 9600 ShowProtocolTime = 1; showProtocolOnLED(); delay(200); Serial.begin(9600,SERIAL_7E1); // set serial and switch to baud 9600 } // Serial.println("ID ok"); getOneValueDIN(SearchOBIS("0.0.0*255",8),26,0); // Zählernummer and getOneValueDIN(SearchOBIS("0.0.1*255",8),7,1); // and manufracture result = getOneValueDIN(SearchOBIS("1.8.0*255",8),14,0x50); // Bezug fromGrid0 if(result > 0)FromGridS=result; result = getOneValueDIN(SearchOBIS("1.8.1*255",8),14,0x50); // Bezug FromGridS if(result > 0)FromGridA=result; result = getOneValueDIN(SearchOBIS("1.8.2*255",8),14,0x50); // Bezug FromGridS if(result > 0)FromGridB=result; result = getOneValueDIN(SearchOBIS("2.8.0*255",8),14,0x50); // EEG intoGrid if(result > 0)IntoGrid=result; if(SearchOBIS("*kW",2)>0){ // is neseccary to identify consumtion! OBIS looks like 31.7.0 for ampere result = getOneValueDIN(SearchOBIS("1.7.0*255",8),14,0x50); // P in W if(result != -1)consumptionFromGrid=result; result = getOneValueDIN(SearchOBIS("2.7.0*255",8),14,0x50); // P in W if(result != -1)consumptionIntoGrid=result; // Consumption = consumptionFromGrid - consumptionIntoGrid; } if (dataDTZ[pointer-2] == 0x21){ Consumption = consumptionFromGrid - consumptionIntoGrid; ProtocolCounter ++; // count the protocols //************** protokoll debug only ****************/ if(sendProtocols > 0){ send2dbasePOST(sendProtokollDebug(),false); //debug sendProtocols --; bufferDTZ =""; } } if(sendProtocols > 0){ for(int i=1;i 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 (hour %3 == 0 && minute==5 && second==3) { // update timestamp if (syncron_NTP_Time() == true) Timestamp2DateTime(); else if(!WiFi.isConnected()) ESP.restart(); // check if connected yet } if (timestamp < TimeOf2021 && second == 50) { if (syncron_NTP_Time() == true) Timestamp2DateTime(); // get timestamp again if sync was failure, perhaps after power down } if (cDTZ.getUserID() !=0){ if (second == (cDTZ.getUserID()%40)+3) { // is called every minute at the n seconds if(cDTZ.saveCounter > 0)sendSavedData(); if (minute == 0 || minute == 15 || minute == 30 || minute == 45) { // and every quarter hour if (setContentAndSend()){ TransmitResult = "Letzter Datentransfer: "; sendingOK = true; } else{ flashCounter=300; // show an error n seconds TransmitResult += "🔴"; // show red dot when is an error } getTimeDate2String(); TransmitResult += "
"; TransmitResult += act_time; TransmitResult += " Uhr - "; TransmitResult += act_date; } } } if (second % 10 == 0){ Serial.begin(300,SERIAL_7E1); // 300 baud 7-E-1 // Serial.begin(9600,SERIAL_7E1); // 9600 baud 7-E-1 // debug pointer = 0; Serial.println("/?!"); } } if (Serial.available()>0) { // read the incoming byte from uart Serial.readBytes(receivedByte,1); dataDTZ[pointer] = receivedByte[0]; if (pointer > MaxBuffer-2) pointer =0; //zur sicherheit, dass pointer nicht überläuft if (serialSpeed == 0) getMeterType(); else decodeDataDIN(); } check4Client(); // thats it's if with the browser show data on port 80 by html }