/*
Zeile 24: hier kommen die Daten vom Zähler rein und die Anzahl Bytes
In Setup wird der key abgefragt. Key eingeben und Enter
*/


#define ESP

#include "esp8266_obis.h"


#include <Crypto.h>
#include <AES.h>
#include <GCM.h>

GCM<AES128> *gcmaes128 = 0;

float voltage_l1, voltage_l2, voltage_l3, current_l1, current_l2, current_l3, active_power_plus, active_power_minus, active_energy_plus, active_energy_minus, reactive_energy_plus, reactive_energy_minus;
uint16_t year;
uint8_t month, day, hour, minute, second;
char timestamp[21];  // 0000-00-00T00:00:00Z
char zaehlernummer[20], devicename[20];

//byte rxBuffer[376] = {0x68, 0xFA, 0xFA, 0x68, 0x53, 0xFF, 0x00, 0x01, 0x67, 0xDB, 0x08, 0x4B, 0x46, 0x4D, 0x10, 0x20, 0x04, 0x3B, 0xD7, 0x82, 0x01, 0x55, 0x21, 0x00, 0x00, 0x8F, 0xA2, 0xDF, 0x4E, 0x42, 0x85, 0x0F, 0x1A, 0x76, 0xEB, 0x5A, 0x93, 0x5E, 0x42, 0x36, 0xEA, 0x7D, 0x26, 0x10, 0x76, 0x44, 0xC7, 0xC1, 0x63, 0x22, 0x9F, 0x25, 0x38, 0xBE, 0xF9, 0xE3, 0x54, 0x01, 0x6C, 0x5D, 0xFC, 0x2D, 0xAC, 0x07, 0x1D, 0xD0, 0x83, 0xFD, 0xD2, 0xAB, 0x1E, 0xB4, 0xF3, 0x35, 0xF3, 0x3F, 0xBC, 0xE5, 0x4A, 0x88, 0xB5, 0xC9, 0x7A, 0x52, 0x9E, 0x97, 0x9B, 0x61, 0x42, 0x90, 0xFD, 0xB2, 0xB5, 0xDF, 0x76, 0x0A, 0x4E, 0x88, 0xD6, 0x98, 0x4E, 0xF2, 0x3D, 0x79, 0xC4, 0x83, 0x36, 0xD1, 0x6F, 0x29, 0x9F, 0xC3, 0xBF, 0x16, 0xC8, 0x07, 0x2F, 0xCC, 0xA2, 0x3E, 0xE6, 0x67, 0xC8, 0x03, 0x17, 0x57, 0xDB, 0xFC, 0x1F, 0xF2, 0x4E, 0xE6, 0x73, 0x84, 0x65, 0xC9, 0x0E, 0x67, 0x5D, 0x90, 0x61, 0x1B, 0xA7, 0x40, 0xCE, 0x91, 0x25, 0xA0, 0x30, 0x08, 0x53, 0xC0, 0xF0, 0xD9, 0x58, 0x28, 0xE0, 0x43, 0xB5, 0x01, 0x84, 0xAA, 0xA4, 0x3F, 0xF8, 0x33, 0x5B, 0xCE, 0x02, 0xD6, 0xE8, 0xA7, 0xB5, 0xF7, 0x81, 0x89, 0x23, 0xFB, 0xFE, 0x30, 0xAA, 0xC3, 0xF6, 0x19, 0xF8, 0xB4, 0xAF, 0x77, 0x8D, 0xAC, 0x38, 0xBF, 0xB3, 0xCC, 0xFF, 0xC5, 0x9C, 0x22, 0x31, 0xE9, 0x0E, 0x06, 0x7C, 0x59, 0x11, 0xDC, 0x93, 0x53, 0x20, 0xE6, 0x55, 0xC2, 0xCF, 0x1C, 0x04, 0xCA, 0x38, 0x05, 0xEB, 0xED, 0x05, 0x16, 0x39, 0x04, 0x13, 0x8F, 0x2B, 0xE3, 0xC7, 0xFD, 0xE8, 0x4F, 0x7F, 0xCE, 0xAC, 0x95, 0x19, 0xF9, 0x9D, 0x33, 0x43, 0xBD, 0x64, 0xFC, 0xB1, 0x10, 0xBA, 0xD6, 0x98, 0xBF, 0x67, 0xAE, 0x11, 0x2E, 0xB4, 0x16, 0x68, 0x72, 0x72, 0x68, 0x53, 0xFF, 0x11, 0x01, 0x67, 0x35, 0x51, 0x83, 0xCB, 0x78, 0xC9, 0xE3, 0xC4, 0x06, 0x70, 0xCC, 0x90, 0x6A, 0x45, 0xE6, 0x13, 0x38, 0x62, 0xA3, 0x84, 0x7E, 0x45, 0x3A, 0x5B, 0xC8, 0x11, 0xD3, 0x80, 0x3D, 0x98, 0xC1, 0xC4, 0x7E, 0x16, 0x1F, 0xB0, 0xF1, 0xC7, 0x47, 0x8E, 0x65, 0xAB, 0x29, 0xA5, 0x1F, 0x79, 0x0A, 0xC0, 0xAF, 0x68, 0x5B, 0x13, 0x6E, 0x37, 0xB4, 0xA9, 0xE7, 0xFA, 0x10, 0xC2, 0x7D, 0xB2, 0x9E, 0x4E, 0x29, 0xB8, 0x4A, 0x86, 0x03, 0x12, 0x0F, 0xEB, 0x4D, 0xFC, 0x4E, 0xF5, 0x87, 0xF6, 0xB3, 0xE8, 0x14, 0x8E, 0x45, 0x70, 0xFA, 0x99, 0xBA, 0x97, 0xB3, 0xC9, 0x41, 0xA0, 0x97, 0xA7, 0x9D, 0x35, 0x58, 0x10, 0x2C, 0x24, 0x9A, 0xCB, 0xEF, 0x12, 0x56, 0x1A, 0xA5, 0x8E, 0xF2, 0xE4, 0x16};
byte rxBuffer[376] = {0x68, 0xFA, 0xFA, 0x68, 0x53, 0xFF, 0x00, 0x01, 0x67, 0xDB, 0x08, 0x4B, 0x46, 0x4D, 0x10, 0x20, 0x04, 0x3B, 0xD7, 0x82, 0x01, 0x55, 0x21, 0x00, 0x01, 0x21, 0x7A, 0x29, 0xBE, 0x82, 0x12, 0x03, 0xEF, 0x1B, 0x19, 0xA8, 0xDE, 0x2F, 0x39, 0xEB, 0xE5, 0x08, 0xB8, 0x4A, 0x4B, 0x07, 0x64, 0x57, 0xD1, 0xC0, 0x85, 0x1F, 0x48, 0x83, 0x40, 0x8A, 0x70, 0x2D, 0x48, 0x78, 0x5E, 0x01, 0x84, 0xBE, 0xC2, 0xB4, 0xB8, 0x13, 0x32, 0x68, 0x18, 0x5A, 0xAE, 0x32, 0x22, 0x1D, 0x08, 0xCB, 0xF6, 0x89, 0x14, 0xD3, 0x9E, 0x5C, 0x5E, 0xB4, 0x67, 0x8A, 0x88, 0xF1, 0x83, 0xD2, 0xE3, 0x2E, 0xC2, 0x65, 0xE1, 0x55, 0xE6, 0xED, 0x1A, 0xFB, 0xEC, 0x7D, 0xB6, 0x96, 0x15, 0x3F, 0x06, 0xA0, 0x5A, 0xF9, 0x5C, 0x9D, 0x2D, 0x8E, 0xDE, 0x9F, 0xE1, 0x24, 0x9C, 0x96, 0x74, 0xCB, 0xB2, 0xFF, 0xD4, 0x3C, 0xB9, 0x82, 0x83, 0xC7, 0x55, 0x21, 0xE5, 0xA3, 0x94, 0x5F, 0x87, 0x4C, 0xD3, 0xC8, 0xC3, 0x2A, 0xBA, 0x5B, 0xA6, 0x6C, 0xB0, 0x81, 0x36, 0xC3, 0xA0, 0xFA, 0x0F, 0xF6, 0x31, 0x82, 0xF0, 0xCC, 0x42, 0xBC, 0x20, 0x60, 0x9C, 0xAB, 0x62, 0xD6, 0xA8, 0x97, 0x0E, 0xA4, 0xE6, 0xCE, 0x0E, 0xC7, 0x58, 0x35, 0x7E, 0x25, 0xE1, 0x94, 0x4D, 0x65, 0x5B, 0xD6, 0x6F, 0xB4, 0x74, 0x99, 0x1A, 0x8A, 0x56, 0xD1, 0xA1, 0x7D, 0xD6, 0x57, 0x6C, 0x37, 0x34, 0x1E, 0x56, 0xFB, 0x76, 0x2C, 0xC8, 0x82, 0x29, 0x25, 0x48, 0x92, 0x57, 0xB1, 0xB7, 0x83, 0xB8, 0x34, 0x94, 0x42, 0x3F, 0x29, 0xAB, 0xA1, 0x56, 0x6F, 0xE0, 0xEF, 0x35, 0xE7, 0x70, 0xC0, 0x51, 0x2E, 0xC6, 0xA0, 0xAD, 0x25, 0x1E, 0x61, 0x47, 0x0B, 0xB6, 0x79, 0x0C, 0x18, 0x5A, 0xAA, 0xBD, 0xAE, 0x31, 0x48, 0x52, 0xC8, 0x08, 0x16, 0x68, 0x72, 0x72, 0x68, 0x53, 0xFF, 0x11, 0x01, 0x67, 0xF4, 0xB6, 0x87, 0xFE, 0xC9, 0xC8, 0x9F, 0xC0, 0x31, 0xF7, 0x36, 0x7F, 0x69, 0x8E, 0x02, 0x68, 0x6B, 0xBF, 0x9E, 0xCC, 0xBD, 0x02, 0x18, 0x22, 0x7E, 0x58, 0x4D, 0x13, 0xA7, 0xD3, 0xEA, 0x8C, 0xE3, 0x30, 0xAD, 0x1B, 0xD4, 0xB8, 0x16, 0xD2, 0x70, 0x5D, 0x46, 0x46, 0x79, 0x42, 0x67, 0x37, 0xE2, 0x0F, 0xC5, 0x4C, 0x49, 0x19, 0xED, 0x28, 0x2C, 0x71, 0x55, 0xD1, 0x31, 0x3E, 0x61, 0xC5, 0xFB, 0x19, 0x8B, 0x65, 0x8E, 0x38, 0x4C, 0x0E, 0x83, 0x64, 0xC5, 0xDB, 0xB4, 0x3B, 0x85, 0x95, 0x8F, 0xBC, 0x70, 0xEE, 0xEF, 0xF4, 0x91, 0x99, 0xFF, 0x8E, 0x1D, 0x7C, 0xEE, 0xCF, 0xC2, 0x40, 0x5E, 0x40, 0xEE, 0xA6, 0xE3, 0x95, 0x3F, 0x8E, 0x0B, 0x5D, 0x1A, 0x1F, 0x64, 0x78, 0x16};

byte key[16];



byte systitle[8]; // von 11 bis 18
byte ic[4];       // von 23 bis 26
byte iv[12];      // systitle + ic


byte nachricht1[227];
byte nachricht2[227]; // angenommen Block 2 wird so gross wie Block 1


int nachricht1_startbyte = 0; // muss 0x68 sein
int nachricht1_endebyte = 0;  // muss 0xFA sein
int nachricht1_laenge = 0;    // muss 0x16 sein


int nachricht1_start_offset = 6;

int nachricht1_beginn_offset = 27;
int nachricht1_ende_offset = 0; // setzt sich zusammen aus nachricht1_laenge + nachricht1_start_offset


int nachricht2_laenge;

String Hex8_2_String(uint8_t *data, int length) { // prints 8-bit data in hex with leading zeroes
  String strg = "";
  char tmp2[16];
  for (int i = 0; i < length; i++) {
    sprintf(tmp2, "%.2X", data[i]);
    strg += tmp2;
  }
  return strg;
}


uint16_t swap_uint16(uint16_t val) {  return (val << 8) | (val >> 8); }
uint16_t swap_uint4(uint16_t val)  {  return (val << 4) ; }
uint32_t swap_uint32(uint32_t val) {
    val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF);
    return (val << 16) | (val >> 16);
}


void PrintHex8(uint8_t *data, int length) // prints 8-bit data in hex with leading zeroes
{
  char tmp[16];
  for (int i = 0; i < length; i++) {
    sprintf(tmp, "0x%.2X", data[i]);
    Serial.print(tmp); Serial.print(" ");
  }
}

char convertCharToHex(char ch) {
  char returnType;
  switch (ch) {
    case '0': returnType = 0; break;
    case '1': returnType = 1; break;
    case '2': returnType = 2; break;
    case '3': returnType = 3; break;
    case '4': returnType = 4; break;
    case '5': returnType = 5; break;
    case '6': returnType = 6; break;
    case '7': returnType = 7; break;
    case '8': returnType = 8; break;
    case '9': returnType = 9; break;
    case 'A':
    case 'a': returnType = 10; break;
    case 'B':
    case 'b': returnType = 11; break;
    case 'C':
    case 'c': returnType = 12; break;
    case 'D':
    case 'd': returnType = 13; break;
    case 'E':
    case 'e': returnType = 14; break;
    case 'F':
    case 'f': returnType = 15; break;
    default: returnType = -1; break;
  }
  return returnType;
}



void setup()
{
  Serial.begin(2400);
  gcmaes128 = new GCM<AES128>();

  Serial.println("32 stelligen key eingeben:");
  while (!Serial.available()); // Wait for input
  String keystring = Serial.readStringUntil('\n');


    for (char i = 0; i < 16; i++) {
      byte extract;
      char a = keystring[2 * i];
      char b = keystring[2 * i + 1];
      extract = convertCharToHex(a) << 4 | convertCharToHex(b);
      if (extract == -1) {
        Serial.print("ERROR - key Eingabefehler!");
      } else {
        key[i] = extract;
        //Serial.print("key von der Eingabe: ");Serial.print(key[i], HEX); Serial.print(" ");
      }
    }

 
}

void loop()
{
    
    nachricht1_startbyte = rxBuffer[0], DEC; // 104 ... 0x68 
    nachricht1_laenge = rxBuffer[1], DEC; // 250 ... 0xFA
    //Serial.print("nachricht1_laenge: "); Serial.println(nachricht1_laenge);   //
    nachricht1_endebyte = rxBuffer[(nachricht1_start_offset + nachricht1_laenge - 1)], DEC;
    //nachricht2_laenge = rxBuffer[27 + sizeof nachricht1 + 2 ], DEC;               // zeigt auf 0x68 und damit den Beginn von Nachricht2 68 26 26 68 53 FF 11 01 67 ....
    nachricht2_laenge = rxBuffer[27 + sizeof nachricht1 + 2 + 1], DEC;              // zeigt auf 0x26 (38 DEZ)
    nachricht2_laenge = nachricht2_laenge - 5;                                      // 33 Zeichen lang; 3 bytes cosem layer und 2 bytes am ende (checksum, endebyte) weglassen
    //Serial.print("nachricht2_laenge: "); Serial.println(nachricht2_laenge);   //
    
    //Serial.print("\nTelegramm_gesamtlaenge: "); Serial.println(nachricht1_laenge - 23 + nachricht2_laenge); 
    
    if (nachricht1_startbyte == 104 && nachricht1_endebyte == 22) {
      Serial.print("\n\nNachricht 1 gueltig");
      
      // memcpy um den IV zu bekommen
        memcpy(systitle, rxBuffer + 11, sizeof systitle);           // SysTitle holen
        memcpy(ic, rxBuffer + 23, sizeof ic);                       // IC holen
        memcpy(iv, systitle, sizeof systitle);                      // IV aus SysTitle und IC erstellen
        memcpy(iv + sizeof systitle, ic, sizeof ic);
      // IV wurde korrekt zusammengestellt

      memcpy(nachricht1, rxBuffer + 27, sizeof nachricht1 );    // liegt im Array von 0 bis 226                                   // Nachricht1 kopieren
      memcpy(nachricht2, rxBuffer + 27 + sizeof nachricht1 + 2 + 9, nachricht2_laenge );           // Nachricht2 kopieren; encrypted payload beginnt bei 9

      Serial.print("\nLänge nachricht1 = "); Serial.print(sizeof nachricht1);
      Serial.print("\nLänge nachricht2 = "); Serial.print(sizeof nachricht2);
      
      Serial.print("\n\nIV:  "); Serial.print(iv[0], HEX); Serial.print(" "); Serial.print(iv[1], HEX); Serial.print(" "); Serial.print(iv[2], HEX); Serial.print(" "); Serial.print(iv[3], HEX); Serial.print(" "); Serial.print(iv[4], HEX); Serial.print(" "); Serial.print(iv[5], HEX); Serial.print(" "); Serial.print(iv[6], HEX); Serial.print(" "); Serial.print(iv[7], HEX); Serial.print(" "); Serial.print(iv[8], HEX); Serial.print(" "); Serial.print(iv[9], HEX); Serial.print(" "); Serial.print(iv[10], HEX); Serial.print(" "); Serial.print(iv[11], HEX);
      Serial.print("\nkey: "); Serial.print(key[0], HEX); Serial.print(" "); Serial.print(key[1], HEX); Serial.print(" "); Serial.print(key[2], HEX); Serial.print(" "); Serial.print(key[3], HEX); Serial.print(" "); Serial.print(key[4], HEX); Serial.print(" "); Serial.print(key[5], HEX); Serial.print(" "); Serial.print(key[6], HEX); Serial.print(" "); Serial.print(key[7], HEX); Serial.print(" "); Serial.print(key[8], HEX); Serial.print(" "); Serial.print(key[9], HEX); Serial.print(" "); Serial.print(key[10], HEX); Serial.print(" ... "); Serial.print(key[15], HEX);

      Serial.print("\n\nNachricht1 Anfang Ende: "); Serial.print(nachricht1[0], HEX); Serial.print(" "); Serial.print(nachricht1[226], HEX);
      Serial.print("\nNachricht2 Anfang Ende: "); Serial.print(nachricht2[0], HEX); Serial.print(" "); Serial.print(nachricht2[nachricht2_laenge - 1], HEX);

      byte ciphertxt[nachricht1_laenge - 23 + nachricht2_laenge];
      memcpy(ciphertxt, nachricht1, sizeof nachricht1);
      memcpy(ciphertxt+nachricht1_laenge - 23, nachricht2, sizeof nachricht2);
      Serial.print("\n\nciphertxt: "); Serial.println(sizeof ciphertxt);
      
      Serial.print(Hex8_2_String(ciphertxt, sizeof ciphertxt));


      uint16_t payloadLength;
      memcpy(&payloadLength, &rxBuffer[20], 2); // die Payload Länge wird im Header mitgegeben !!! Copy payload length Byte[20] und Byte[21] ==> in payLoadLength steht 0x01 0x09
      payloadLength = swap_uint16(payloadLength) - 5;
      
      Serial.print("\n\npayloadLength: "); Serial.println(payloadLength);
      Serial.print("\nOBIS Code:\n");
      byte plaintext[payloadLength];

      gcmaes128->setKey(key, gcmaes128->keySize());
      gcmaes128->setIV(iv, 12);
      gcmaes128->decrypt(plaintext, ciphertxt, payloadLength);

      Serial.print(Hex8_2_String(plaintext, sizeof plaintext));

      // ********************************************************************************************************************************
      // ab hier domistyle Code => https://github.com/DomiStyle/esphome-dlms-meter/
      // ********************************************************************************************************************************

      if (plaintext[0] != 0x0F || plaintext[5] != 0x0C) {
        Serial.print("\n\nERROR - Obis Code fehlerhaft\n");
      }

      int currentPosition = DECODER_START_OFFSET;  // = 20


      do {
        if (plaintext[currentPosition + OBIS_TYPE_OFFSET] != DataType::OctetString)  
        {
          Serial.print("\n\ERROR - Unsupported OBIS header type");
        }
        byte obisCodeLength = plaintext[currentPosition + OBIS_LENGTH_OFFSET];  

        if (obisCodeLength != 0x06)  
        {
          Serial.print("\n\ERROR - Unsupported OBIS header length");
        }

        byte obisCode[obisCodeLength];  

        memcpy(&obisCode[0], &plaintext[currentPosition + OBIS_CODE_OFFSET], obisCodeLength);  

        currentPosition += obisCodeLength + 2;      
        byte dataType = plaintext[currentPosition];  

        currentPosition++;  // Advance past data type

        byte dataLength = 0x00;

        CodeType codeType = CodeType::Unknown;

        if (obisCode[OBIS_A] == Medium::Electricity) {
          // Compare C and D against code
          if (memcmp(&obisCode[OBIS_C], ESPDM_VOLTAGE_L1, 2) == 0) {
            codeType = CodeType::VoltageL1;
          } else if (memcmp(&obisCode[OBIS_C], ESPDM_VOLTAGE_L2, 2) == 0) {
            codeType = CodeType::VoltageL2;
          } else if (memcmp(&obisCode[OBIS_C], ESPDM_VOLTAGE_L3, 2) == 0) {
            codeType = CodeType::VoltageL3;
          } else if (memcmp(&obisCode[OBIS_C], ESPDM_CURRENT_L1, 2) == 0) {
            codeType = CodeType::CurrentL1;
          } else if (memcmp(&obisCode[OBIS_C], ESPDM_CURRENT_L2, 2) == 0) {
            codeType = CodeType::CurrentL2;
          } else if (memcmp(&obisCode[OBIS_C], ESPDM_CURRENT_L3, 2) == 0) {
            codeType = CodeType::CurrentL3;
          } else if (memcmp(&obisCode[OBIS_C], ESPDM_ACTIVE_POWER_PLUS, 2) == 0) {
            codeType = CodeType::ActivePowerPlus;
          } else if (memcmp(&obisCode[OBIS_C], ESPDM_ACTIVE_POWER_MINUS, 2) == 0) {
            codeType = CodeType::ActivePowerMinus;
          } else if (memcmp(&obisCode[OBIS_C], ESPDM_ACTIVE_ENERGY_PLUS, 2) == 0) {
            codeType = CodeType::ActiveEnergyPlus;
          } else if (memcmp(&obisCode[OBIS_C], ESPDM_ACTIVE_ENERGY_MINUS, 2) == 0) {
            codeType = CodeType::ActiveEnergyMinus;
          } else if (memcmp(&obisCode[OBIS_C], ESPDM_REACTIVE_ENERGY_PLUS, 2) == 0) {
            codeType = CodeType::ReactiveEnergyPlus;
          } else if (memcmp(&obisCode[OBIS_C], ESPDM_REACTIVE_ENERGY_MINUS, 2) == 0) {
            codeType = CodeType::ReactiveEnergyMinus;
          } else {
            Serial.print("\n\nERROR: Unsupported OBIS code");
          }
        } else if (obisCode[OBIS_A] == Medium::Abstract) {
          if (memcmp(&obisCode[OBIS_C], ESPDM_TIMESTAMP, 2) == 0) {
            codeType = CodeType::Timestamp;
          } else if (memcmp(&obisCode[OBIS_C], ESPDM_SERIAL_NUMBER, 2) == 0) {
            codeType = CodeType::SerialNumber;
          } else if (memcmp(&obisCode[OBIS_C], ESPDM_DEVICE_NAME, 2) == 0) {
            codeType = CodeType::DeviceName;
          } else {
            Serial.print("\n\nERROR: Unsupported OBIS code");
          }
        } else {
          Serial.print("\n\nERROR: Unsupported OBIS medium");
        }

        uint8_t uint8Value;
        uint16_t uint16Value;
        uint32_t uint32Value;
        float floatValue;

        active_power_plus = 0;
        active_power_minus = 0;
        active_energy_plus = 0;
        active_energy_minus = 0;
        reactive_energy_plus = 0;
        reactive_energy_minus = 0;
        voltage_l1 = 0;
        voltage_l2 = 0;
        voltage_l3 = 0;
        current_l1 = 0;
        current_l2 = 0;
        current_l3 = 0;


        switch (dataType) {
          case DataType::DoubleLongUnsigned:  // 0x06
            dataLength = 4;

            memcpy(&uint32Value, &plaintext[currentPosition], 4);  // Copy bytes to integer
            uint32Value = swap_uint32(uint32Value);                // Swap bytes

            floatValue = uint32Value;  // Ignore decimal digits for now

            if (codeType == CodeType::ActivePowerPlus) {
              active_power_plus = floatValue;
              Serial.print("\nWirkleistung Bezug +P (W):\t\t"); Serial.print(floatValue);
            }
            if (codeType == CodeType::ActivePowerMinus) {
              active_power_minus = floatValue;
              Serial.print("\nWirkleistung Lieferung -P (W):\t\t"); Serial.print(floatValue);
            }
            if (codeType == CodeType::ActiveEnergyPlus) {
              active_energy_plus = floatValue;
              Serial.print("\nWirkenergie Bezug +A (Wh):\t\t"); Serial.print(floatValue);
            }
            if (codeType == CodeType::ActiveEnergyMinus) {
              active_energy_minus = floatValue;
              Serial.print("\nWirkenergie Lieferung -A (Wh):\t\t"); Serial.print(floatValue);
            }
            if (codeType == CodeType::ReactiveEnergyPlus) {
              reactive_energy_plus = floatValue;
              Serial.print("\nBlindleistung Bezug +R (Wh):\t\t"); Serial.print(floatValue);
            }
            if (codeType == CodeType::ReactiveEnergyMinus) {
              reactive_energy_minus = floatValue;
              Serial.print("\nBlindleistung Lieferung -R (Wh):\t"); Serial.print(floatValue);
            }
            break;


          case DataType::LongUnsigned:  // 0x12
            dataLength = 2;

            memcpy(&uint16Value, &plaintext[currentPosition], 2);  // Copy bytes to integer
            uint16Value = swap_uint16(uint16Value);                // Swap bytes

            if (plaintext[currentPosition + 5] == Accuracy::SingleDigit)
              floatValue = uint16Value / 10.0;  // Divide by 10 to get decimal places
            else if (plaintext[currentPosition + 5] == Accuracy::DoubleDigit)
              floatValue = uint16Value / 100.0;  // Divide by 100 to get decimal places
            else
              floatValue = uint16Value;  // No decimal places

            if (codeType == CodeType::VoltageL1) {
              voltage_l1 = floatValue;
              Serial.print("\nSpannung L1 (V):\t\t\t"); Serial.print(floatValue);
            }
            if (codeType == CodeType::VoltageL2) {
              voltage_l2 = floatValue;
              Serial.print("\nSpannung L2 (V):\t\t\t"); Serial.print(floatValue);
            }
			if (codeType == CodeType::VoltageL3) {
              voltage_l3 = floatValue;
              Serial.print("\nSpannung L3 (V):\t\t\t"); Serial.print(floatValue);
            }
			if (codeType == CodeType::CurrentL1) {
              current_l1 = floatValue;
              Serial.print("\nStrom L1 (A):\t\t\t\t"); Serial.print(floatValue);
            }
            if (codeType == CodeType::CurrentL2) {
              current_l2 = floatValue;
              Serial.print("\nStrom L2 (A):\t\t\t\t"); Serial.print(floatValue);
            }
            if (codeType == CodeType::CurrentL3) {
              current_l3 = floatValue;
              Serial.print("\nStrom L3 (A):\t\t\t\t"); Serial.print(floatValue);
            }
            break;


          case DataType::OctetString:  // 0x09
            dataLength = plaintext[currentPosition];
            currentPosition++;  // Advance past string length

            if (codeType == CodeType::Timestamp)  // Handle timestamp generation
            {
              memcpy(&uint16Value, &plaintext[currentPosition], 2);
              year = swap_uint16(uint16Value);

              memcpy(&month, &plaintext[currentPosition + 2], 1);
              memcpy(&day, &plaintext[currentPosition + 3], 1);

              memcpy(&hour, &plaintext[currentPosition + 5], 1);
              memcpy(&minute, &plaintext[currentPosition + 6], 1);
              memcpy(&second, &plaintext[currentPosition + 7], 1);

              //sprintf(timestamp, "%04u-%02u-%02uT%02u:%02u:%02uZ", year, month, day, hour, minute, second);
              sprintf(timestamp, "%02u.%02u.%04u %02u:%02u:%02u", day, month, year, hour, minute, second);


              Serial.print("\n\nDatum/Zeit:\t\t\t"); Serial.print(timestamp);
            }

            if (codeType == CodeType::SerialNumber)  // Handle SerialNumber generation
            {
              dataLength = plaintext[currentPosition - 1];
              memcpy(&uint8Value, &plaintext[currentPosition - 1], 1);  // Copy byte to integer
              int zaehlernummerlen = uint8Value;
              uint8Value = swap_uint16(uint16Value);                                  // Swap bytes
              memcpy(&zaehlernummer, &plaintext[currentPosition], zaehlernummerlen);  // Copy byte to integer
              Serial.print("\nZaehlernummer:\t\t\t"); Serial.print(zaehlernummer);
            }
            if (codeType == CodeType::DeviceName)  // Handle DeviceName generation
            {
              dataLength = plaintext[currentPosition - 1];
              memcpy(&uint8Value, &plaintext[currentPosition - 1], 1);  // Copy byte to integer
              int devicenamelen = uint8Value;
              uint8Value = swap_uint16(uint16Value);                            // Swap bytes
              memcpy(&devicename, &plaintext[currentPosition], devicenamelen);  // Copy byte to integer
              Serial.print("\nDevicename:\t\t\t"); Serial.print(devicename);
            }
            break;
          default:
            Serial.print("\n\nERROR: Unsupported OBIS data type");
            break;
        } // ENDE switch (dataType)


        currentPosition += dataLength;  // Skip data length
        currentPosition += 2;           // Skip break after data

        if (plaintext[currentPosition] == 0x0F)  // There is still additional data for this type, skip it
          currentPosition += 6;                  // Skip additional data and additional break; this will jump out of bounds on last frame

      } while (currentPosition <= payloadLength);  // Loop until arrived at end


    } else {
      Serial.print("Nachricht 1 ungueltig - warte auf naechsten Block\n");
    }

delay(3000);


}