/** * L&G E320 VDE-DIN decode 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). **/ #ifndef DECODE_METER_H_ #define DECODE_METER_H_ //#define VALUE_SERIAL_DEBUG //#define UNIT_TEST // debug for test by delivery data from pc into serial 7E1 9600 baud #include "global.h" extern bool send2dbasePOST(String content, bool saveIfError); extern String sendProtokollDebug(); extern int getWandlerFactor(); /***** class for data of meter *****************************/ class meterData{ public: uint32_t MeterID[3]; //[_MID0][_MID1][_MID2] uint32_t MeterKWH[4]; //[_FROMGRID_0][_FROMGRID_1][_FROMGRID_2][_FEEDIN] int Electric_data[11]; //[_U_L1][_U_L2][_U_L3][_I_L1][_I_L2][_I_L3][_P_L1][_P_L2][_P_L3][_CONSUMPTION][_FREQ] String getMeterID(); String getMeter_ID(); private: }; String meterData::getMeterID(){ String ret; char dummy[10]; sprintf(dummy,"%08X",MeterID[_MID2]); ret += dummy; sprintf(dummy,"%08X",MeterID[_MID1]); ret += dummy; sprintf(dummy,"%08X",MeterID[_MID0]); ret += dummy; return ret; } String meterData::getMeter_ID(){ String ret; char dummy[10]; sprintf(dummy,"%X",MeterID[_MID2]); ret += dummy; ret +="-"; sprintf(dummy,"%08X",MeterID[_MID1]); ret += dummy; ret +="-"; sprintf(dummy,"%08X",MeterID[_MID0]); ret += dummy; return ret; } /***** end of meterData ************************************/ /***** class for decode data from meter - SML protocol *****/ class decodeMeter : public meterData { private: int PointerEOT; int BufferLength; int ValueScale; int SingleValue; uint32_t value0; uint32_t value1; uint32_t value2; uint16_t CRC_ccitt; uint16_t CRC_meter; uint32_t TimeMarker=0; bool Bezug1OK; bool Bezug2OK; bool Bezug3OK; bool FeedInOK; char MeterManu[6]; char ExtractValue[14]; int SearchOBIS(char *searchCode, int slength); int CheckOBIS(char *checkCode, int i, int searchLength); uint32_t getOneValueDIN(int actPointer, int length, int valueType, int unitScale); void HandleDataStream(); public: uint32_t ErrorCounter=0; uint32_t ProtocolCounter=0; int SendProtocols=0; int Prot_Type; int SerialSpeed; String MeterDate; String MeterTime; String MeterType = NOT_DETECT; String Buffer4Send; String DecodeOneValueDIN(int actPointer, int length, int type); void Begin(int maxBuffer); String getProtocolType(); int mainEntryDecode(); int decodeDataDIN(int pointer); void clearValues(); int getErrorsPercent(); void sendRequest2Meter(); void setSerialSpeed(); void crc_add(uint16_t dummy); // it is just for compatiblity; meter doesnt delivered crc check byte }; void decodeMeter::Begin(int maxBuffer){ PointerEOT=maxBuffer; BufferLength=maxBuffer; CRC_ccitt = 0xffff; CRC_meter = 0xffff; Prot_Type = _DETECT_prot; SerialSpeed=0; } String decodeMeter::getProtocolType(){ // it's in user for build-versions-info return "LG-E320 "; } //**** set standard speed 300 ******/ void decodeMeter::setSerialSpeed(){ Serial.begin(9600,SERIAL_8N1); // 9600 baud 8-N-1 } int decodeMeter::getErrorsPercent(){ if(ProtocolCounter > 0){ return ErrorCounter*100/ProtocolCounter; } else return 0; } void decodeMeter::clearValues(){ for (int i=0;i<10;i++){ Electric_data[i]=0; } } //*** end *** //*** only for compatiblity *** void decodeMeter::crc_add(uint16_t dummy){ } //**** check time differece between two protocols to recognise rest ** void decodeMeter::HandleDataStream(){ if(TimeMarker 0){ send2dbasePOST(sendProtokollDebug(),false); //debug SendProtocols --; } _dataPointer=0; // setze speicher an anfang _ShowProtocolTime = 400; } TimeMarker=millis(); // aktuallisiere den zeitmerker } //****************************** search OBIS-code signation ***************************** int decodeMeter::SearchOBIS(char *searchCode, int slength){ int x = 0; do { if (*searchCode == _dataDTZ[x]){ if (CheckOBIS(searchCode, x, slength)==0)break; } x++; } while (x < BufferLength-1); if(x > BufferLength - 10)return 0; // nothing of OBIS founded else return x+slength+1; // pointer on the first value after OBIS-String } //****************************** compare search-string with buffer ***************************** int decodeMeter::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; } //************** decode one value from data ********************************************/ uint32_t decodeMeter::getOneValueDIN(int actPointer, int length, int valueType, int unitScale){ //uint32_t getOneValueSML(int startpoint, int endpoint, int valueType, int target-scaler) int32_t result= -1; if(_dataDTZ[actPointer] == 0x28){ // if begin by "(" -> then OK if(valueType == 0){ // meternumber String meterNumber = ""; for(int i=1; i < length; i++){ if(_dataDTZ[actPointer+i] == 0x29) break; //length = 0; // break if appear else meterNumber += _dataDTZ[actPointer+i]; } int numberLength = meterNumber.length(); if (numberLength <= 10) MeterID[_MID0] = meterNumber.toInt(); else { length = numberLength-7; MeterID[_MID0] = meterNumber.substring(length-1).toInt(); valueType = 1; } // Serial.println(MeterID[_MID0]); // debug result=MeterID[_MID0]; } if(valueType == 1){ //meter manufracture int oneChar = 0; MeterID[_MID2] = 0x000000A0; for(int i=1; i < length; i++){ oneChar = _dataDTZ[actPointer+i]; if(oneChar == 0x29) break; //length = 1; // break if appear if(oneChar >= 0x30 && oneChar <= 0x39){ oneChar &= 0x0f; if(i<4 && oneChar!=0){ MeterID[_MID2] = MeterID[_MID2] << 4; MeterID[_MID2] += oneChar; } MeterID[_MID1] = MeterID[_MID1] << 4; MeterID[_MID1] += oneChar; } else { MeterID[_MID1] = MeterID[_MID1] << 8; MeterID[_MID1] += oneChar; } } result=0; } if(valueType == 0x50){ String valueSing = "0"; int i; int dot=0; bool negative = false; for(i=1; i < length; i++){ if(_dataDTZ[actPointer+i] == 0x2a) length = 1; // break if appear * if(dot != 0 && i-dot > 4){length = 1; i++; break;} // break if numbers after dot larger then 4, because just 32 bit value if(_dataDTZ[actPointer+i] == 0x2e) dot=i; if(_dataDTZ[actPointer+i] == 0x2d) negative = true; // check if negativ value if(_dataDTZ[actPointer+i] >= 0x30 && _dataDTZ[actPointer+i] <= 0x39) valueSing += _dataDTZ[actPointer+i]; } result = valueSing.toInt(); if(negative) result *= -1; int dotx=i-dot-2; if (dot > 0){ switch (dotx){ case 1: if(unitScale>=10) result *= (unitScale/10); else result /= (unitScale*10); break; case 2: if(unitScale>=100) result *= (unitScale/100); else result /= (unitScale*100); break; case 3: if(unitScale>=1000) result *= (unitScale/1000); else result /= (unitScale*1000); break; case 4: if(unitScale>=10000) result *= (unitScale/10000); else result /= (unitScale*10000); break; default: result *= unitScale; break; } } else result *= unitScale; // default value } if(valueType == 0x10){ MeterTime = ""; int c=1; for(int i=1; i < length; i++){ if(_dataDTZ[actPointer+i] == 0x29) break; // break if appeared ')' if(_dataDTZ[actPointer+i]>= 0x30 && _dataDTZ[actPointer+i]<= 0x39){ //let out '-' and ':' MeterTime += _dataDTZ[actPointer+i]; if(c == 2 || c == 4) MeterTime += ":"; c++; } } } if(valueType == 0x11){ MeterDate = ""; int c=1; for(int i=1; i < length; i++){ if(_dataDTZ[actPointer+i] == 0x29) break; // break if appeared ')' if(_dataDTZ[actPointer+i]>= 0x30 && _dataDTZ[actPointer+i]<= 0x39){ //let out '-' and ':' MeterDate += _dataDTZ[actPointer+i]; if(c == 2 || c == 4) MeterDate += "-"; c++; } } } } // Serial.println(result); //debug return result; } //*** end *** //**** main entry for find meter-type and selcet protocol-type ******* int decodeMeter::mainEntryDecode(){ HandleDataStream(); _dataPointer=decodeDataDIN(_dataPointer); return 0; } //**** send a request to meter -> only for old types like AS1440 or else types ****/ void decodeMeter::sendRequest2Meter(){ // Serial.begin(9600,SERIAL_8N1); // 9600 baud 7-N-1 } //*********************** decode data from Logarex LX13BE ************ int decodeMeter::decodeDataDIN(int pointer){ if (_dataDTZ[pointer] == 0x0a && _dataDTZ[pointer-1] == 0x0d){ // check of end-code ") + cr + lf" int32_t result= -1; getOneValueDIN(SearchOBIS("C.1.0",4),17,0,1); // Zählernummer and getOneValueDIN(SearchOBIS("C.1.1",4),7,1,1); result = getOneValueDIN(SearchOBIS("1.8.0",4),12,0x50,10000); // Bezug fromGridS if(result > 0 && Bezug1OK == false){MeterKWH[_FROMGRID_0]=result*getWandlerFactor(); Bezug1OK = true;} result = getOneValueDIN(SearchOBIS("1.8.1",4),12,0x50,10000); // Bezug fromGridA if(result > 0 && Bezug2OK == false){MeterKWH[_FROMGRID_1]=result*getWandlerFactor(); Bezug2OK = true;} result = getOneValueDIN(SearchOBIS("1.8.2",4),12,0x50,10000); // Bezug fromGridA if(result > 0 && Bezug3OK == false){MeterKWH[_FROMGRID_2]=result*getWandlerFactor(); Bezug3OK = true;} result = getOneValueDIN(SearchOBIS("2.8.0",4),12,0x50,10000); // EEG intoGrid if(result > 0 && FeedInOK == false){MeterKWH[_FEEDIN]=result*getWandlerFactor(); FeedInOK = true;} if(SearchOBIS("*kW",2)>0){ // is neseccary to identify consumtion! OBIS looks like 31.7.0 for ampere result = getOneValueDIN(SearchOBIS("16.7",3),10,0x50,1000); // P in W for L&G E650 if(result != -1)Electric_data[_CONSUMPTION]=result*getWandlerFactor(); } if(SearchOBIS("*V",1)>0){ // is neseccary to identify result = getOneValueDIN(SearchOBIS("32.7",3),10,0x50,10); if(result != -1)Electric_data[_U_L1]=result; result = getOneValueDIN(SearchOBIS("52.7",3),10,0x50,10); if(result != -1)Electric_data[_U_L2]=result; result = getOneValueDIN(SearchOBIS("72.7",3),10,0x50,10); if(result != -1)Electric_data[_U_L3]=result; } if(SearchOBIS("*A",1)>0){ // is neseccary to identify result = getOneValueDIN(SearchOBIS("31.7",3),10,0x50,1000); if(result != -1)Electric_data[_I_L1]=result*getWandlerFactor()/10; result = getOneValueDIN(SearchOBIS("51.7",3),10,0x50,1000); if(result != -1)Electric_data[_I_L2]=result*getWandlerFactor()/10; result = getOneValueDIN(SearchOBIS("71.7",3),10,0x50,1000); if(result != -1)Electric_data[_I_L3]=result*getWandlerFactor()/10; } if (_dataDTZ[pointer-2] == 0x21){ Bezug1OK = false; Bezug2OK = false; Bezug3OK = false; FeedInOK = false; ProtocolCounter ++; // count the protocols // _ShowProtocolTime = 400; //************** protokoll debug only ****************/ // if(SendProtocols > 0){ // send2dbasePOST(sendProtokollDebugDIN(Buffer4Send),false); //debug // SendProtocols --; // Buffer4Send =""; // } // } // if(SendProtocols > 0){ // for(int i=1;i