MasterBus Mastervolt Combimaster Can

Hi all there,
Is someone out there who tried to connect to the can bus from Mastervolt?
I have a inverter/charger combination, Type is CombiMaster.
You can visualize and Control it via masterbus.
Masterbus is based on can.
I have developed an interface, most of it is already working.
Only a few commands are not working.
One command I need is to switch the charger part off.
But until now I did not found out this command.
Maybe someone can help me.
If someone is interested, please send me a Message.

2 Likes

How is this related to Arduino. If it is Arduino post your schematic and code following forum instructions.

@gilshultz
...yes, you are right, but the sketch was not ready.
Here it is now. If someone has also done investigations in the bus, please tell me.

#include <stdint.h>                          // Standardisierte Datentypen
// Vorzeichenbehaftete int-Typen
// Typname   Bit-Breite  Wertebereich
// int8_t             8  −128 ⋯ 127
// int16_t           16  −32768 ⋯ 32767
// int32_t           32  −2147483648 ⋯ 2147483647                      int beginnen immer mit kleinem Buchstaben
// int64_t           64  −9223372036854775808 ⋯ 9223372036854775807
// Vorzeichenlose int-Typen
// uint8_t            8   0 ⋯ 255                                      uint beginnen immer mit großem Buchstaben
// uint16_t          16   0 ⋯ 65535
// uint32_t          32   0 ⋯ 4294967295                                   
// uint64_t          64   0 ⋯ 18446744073709551615

// float             32   auf ca. 6 Kommastellen genau                  float enden immer mit _f
// double            64   auf ca. 12 Kommastellen genau                 double enden immer mit _d



// Diverse Librarys --------------------------------------------------------------------------------------------------------------------

#include <Arduino.h>
#include <math.h> 
#include <CAN.h>
#include <Preferences.h>   // EEprom; Infos: https://unsinnsbasis.de/esp32-preferences/
Preferences prefs;


// WiFi --------------------------------------------------------------------------------------------------------------------

// Das ESP32 arbeiter hier im Station (STA) Mode. Das heißt, das ESP32 verbindet sich mit einem vorhandenen Router und ist selbst ein WiFi Station.
// Das ESP32 erhält seine IP Adresse vom Router. Das ESP32 kann aber auch beim Router eine statische IP Adresse anfragen. (wird weiter unten im SetUp gemacht)
// Auf dem ESP32 läuft ein HTTP Server.

#include <WiFi.h>         // is required for doing all WiFi related functionalities such as connection, AP, etc.

                          // Wenn nur die Daten vom Cortex AIS empfangen werden sollen, ist außer WiFi.h nichts erforderlich.
                          // Das Cortex AIS sendet die Daten continuirlich über TCP, eine HTTP Anfrage ist nicht erforderlich.
                          // Das ESP32 ist einfacher Client. Das ESP32 kann entweder direkt an den Access Point des Cortex AIS eingeloggt werden.
                          // Oder das Cortex AIS wird in das Huawei Netzwerk eingeloggt und kann dann über das Huawei angebunden werden.
                          // Das reine empfangen wird aber nicht mehr genacht, da die Daten über NMEA2000 empfangen werden.                 

#include <WebServer.h>    // it handles all HTTP protocols 
WebServer server(80);     // Erforderlich zusammen mit WebSerfer.h
 


// **********************************************    Adresse und Passwort Netgear
const char* ssid     = "sid";               // Wlan SSID   
const char* password = "password";           // Wlan Passwort
                                                    // Folgende Daten sind erforderlich, um beim Router eine bestimmte IP Adresse anzufragen.
                                                    // Die Festlegung geschieht im SetUp, mit der Schleife --- Configures static IP address ---  
                                                    // Wenn die Gateway, DNS ect. Daten nicht bekannt sind, müssen diese im SetUp erst ermittelt werden.
                                                    // Siehe näheres im SetUp.
IPAddress local_IP(192, 168, 178, 98);              // Set your Static IP address 
IPAddress gateway(192, 168, 178, 1);                // Set your Gateway IP address                                             
IPAddress subnet(255, 255, 255, 0);                 // Set Subnet Maske

IPAddress primaryDNS(192, 168, 178, 1);             // optional; Fritzbox gibt dies aus, wenn keine Static IP festgelegt wird;
                                                    // Ist in der Fritzbox unter Einstellungen so als lokaler DNS Server eingetragen!!!
IPAddress secondaryDNS(0, 0, 0, 0);                 // optional; Fritzbox gibt dies aus, wenn keine Static IP festgelegt wird



// OTA ---------------------------------------------------------------------------------------------------------------------------------

#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>


// Zur Anzeige Filname im Programm:---------------------------------------------------------------------------------------------------------------

#include <string.h>
#define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)



// Definition Uhrzeit register  -------------------------------------------------------------------------------------------------------------------------------

#include "time.h"                                 // Standard C Library

const char*     ntpServer = "pool.ntp.org";   
int32_t         gmtOffset_sec = 3600;             // Offset in Sekunden zu UTC
const int32_t   daylightOffset_sec = 3600;        // Zusätzlicher Offset in Sekunden zu UTC während Sommerzeit

char            TimeDateComplete_char20[21];
String          Zeitzone;



// Variablen ---------------------------------------------------------------------------------------------------------

uint32_t zyklusTime, zyklusTimeLast, zyklusTimeHigh, zyklusTimeHighWeb, LEDgruenTime, PreviousCheck, TimeSZ, PollTime, Hardbeat;
uint32_t Length;
uint32_t deviceUniqueId;
uint32_t deviceKindId;
uint32_t Time1s;

uint8_t  BlockMon, BlockErr, BlockConfig, BlockHistory;
uint8_t  MbDeviceStateIst, MbDeviceStateSoll, MbChargerStateIst;
uint8_t  MbInverterOnOffIst, MbInverterPreOffIst, MbChargerOnOffIst, MbChargerPreOffIst;
uint8_t  CfLandstromIst, CfACinputSupportModeIst, CfBatterieTypeIst, CfInverterEnergySaveIst, CfInverterEnergySaveSoll, 
uint8_t  AcFuseSettingIst, CfMaxChargeCurrentIst;

unsigned char RxBuf[8];

int32_t zyklusCount, espRestartCounter, packet, watchDogSchalter;

float inValue_f, wert_f;

String FilenameShort;

String canString[21];
int32_t num;
int32_t numMax = 19;


boolean  bFirstCycle, bLEDgruen;
boolean  bIRequest;
boolean  bDS, bDataOK, bProtocol = true;


#define MonitorDeviceID       0x5506C1A

#define CombiMasterID         0xBC5D    

#define PageMonitoring        0x818
#define PageMonitoringRequest 0x1818
#define PageMonRequestID      0x1818BC5D

#define MBDeviceState         0x00
#define MBChargerState        0x01
#define MBAcInputVoltage      0x02
#define MBAcInputCurrent      0x03
#define MBAcInputFrequency    0x04
#define MBAcOutVoltage        0x05
#define MBAcOutFrequency      0x06
#define MBAcOutPowerW         0x07
#define MBAcOutPower2         0x08
#define MBAuslastung          0x09
#define MBInverterOnOff       0x13
#define MBInverterPreOff      0x14
#define MBChargerOnOff        0x15
#define MBChargerPreOff       0x16
#define MBAcNC3               0x0A
#define MBBatterieVoltage     0x0B
#define MBBatterieCurrent     0x0C
#define MBFuseSettingACinput  0x17


float MBDeviceState_f;
float MBChargerState_f;
float MBAcInputVoltage_f;
float MBAcInputCurrent_f;
float MBAcInputFrequency_f;
float MBAcOutVoltage_f;
float MBAcOutFrequency_f;
float MBAcOutPowerW_f;
float MBAcOutPower2_f;
float MBAuslastung_f;
float MBAcNC3_f;
float MBBatterieVoltage_f;
float MBBatterieCurrent_f;
float MBFuseSettingACinput_f;

#define PageAlarm             0x218
#define PageAlarmRequest      0x0328
#define PageAlarmRequestID    0x0318BC5D

#define ALTemperatureHigh     0x00
#define ALFanError            0x01
#define ALDChighVoltage       0x02
#define ALDClowVoltage        0x03
#define ALOverload            0x04
#define ALDeviceError         0x05
#define ALACinError           0x06

float ALTemperatureHigh_f;
float ALFanError_f;
float ALDChighVoltage_f;
float ALDClowVoltage_f;
float ALOverload_f;
float ALDeviceError_f;
float ALACinError_f;

#define PageHistory           0xD98
#define PageHistoryRequest    0x1D98
#define PageHistoryRequestID  0x1D98BC5D

#define HIinverterRunTime     0x00
#define HIchargerRunTime      0x01

float HIinverterRunTime_f;
float HIchargerRunTime_f;

#define PageConfig            0xB98
#define PageConfigRequest     0x1B98
#define PageConfigRequestID   0x1B98BC5D

#define CFInverterEnergySave  0x09
#define CFLandstrom           0x0A
#define CFACinputSupportMode  0x0B
#define CFmaxChargeCurrent    0x0F
#define CFBatterieType        0x10
#define MLI                   4
#define ConstVoltage          5
#define CFAValueConstVoltage  0x11
#define CFInverterLowVoltON   0x15
#define CFInverterLowVoltOFF  0x16

#define CFmaxValueAC_IN_Limit 0x33

float CFmaxChargeCurrent_f;
float CFInverterLowVoltON_f;
float CFInverterLowVoltOFF_f;

// Definition E/A -------------------------------------------------------------------------------------------------------------------------------

// Ausgänge:
#define  LEDrot         14           // nur auf Testboard
#define  LEDgruen       27
// can TX   33
// can RX   32

// Eingänge:
// #define  INA_ALE        17           // Board 1, Hauptstrang
// #define  LiFePoAlarm     2           // LiFePo Batterie Unterspannung Voralarm



//-----------------------------------------------------------------------------------------------------------------
//--SetUp
//-----------------------------------------------------------------------------------------------------------------


void setup()
{
   
   // Filname bearbeiten ---------------------------------------------------------------------------------------------------------

   // Wichtig bei indexOf:
   // String beispiel "01234,567890"
   // Die              0               steht auf Poition 0
   // Das                   ,          steht auf Poition 5
   // Um einen bestimmten Bereich auszufiltern, zum Beispiel die erste Zifferngruppe, ist die Startposition die entsprechend erste Position, also 0,
   // die Endposition aber die entsprechende Position plus 1, also 5!!!!!! 

   String Filename = __FILENAME__;

   int slashPos = Filename.lastIndexOf('/');         //  "/" im String von hinten suchen. ...44-073/AnkerAppEmail-V2-44-073.ino
                                                     // Nur der Teil nach dem "/" soll genutzt werden. In slashPos wird die Position des "/" abgelegt
   FilenameShort = Filename.substring(slashPos+1);   // Filname inklusive ".ino"    
   
   Length = FilenameShort.length();                 // Gesamtlänge String

   FilenameShort = FilenameShort.substring(0, (Length-4));  // letzten 4 Zeichen (.ino) entfernen
   


   // Hardware ---------------------------------------------------------------------------------------------------------

   // Ausgänge:
   pinMode(LEDgruen, OUTPUT);
   digitalWrite(LEDgruen, LOW);  
   
   // Eingänge:    
   // pinMode(LiFePoAlarm, INPUT_PULLUP);       // 
 
   // Serial ---------------------------------------------------------------------------------------------------------
     
   if (1 == 1) { 
      delay(3000);
      Serial.begin(115200);       // Kommunikation Serial Terminal
      bDS = true;
   }

   // CAN ---------------------------------------------------------------------------------------------------------

   if (bDS == true) Serial.println("CAN Receiver");

   CAN.setPins(32, 33);                        // (rx, tx)
  
   if (!CAN.begin(250E3)) {                    // start the CAN bus at 250 kbps
      if (bDS == true) Serial.println("Starting CAN failed!");
      while (1);
   }



   // EEprom auslesen -------------------------------------------------------------------------------------------------------------------
    
   if (!prefs.begin("Loop")) {    // Sehr gute Infos: https://unsinnsbasis.de/esp32-preferences/
                                  // Open Preferences with "Loop" namespace. Each application module, library, etc has to use a 
                                  // namespace name to prevent key name collisions. We will open storage in RW-mode 
                                  // (second parameter has to be false). Note: Namespace name is limited to 15 chars.

      if (bDS == true) Serial.println("Fehler beim Öffnen des NVS-Namespace");
      for (;;);                                                // leere Dauerschleife -> Ende
   }
   if (bDS == true) Serial.print("Anzahl freier Einträge: ");
   if (bDS == true) Serial.println (prefs.freeEntries());

  
  
   // zum erstmaligen laden EEprom --------------------------------------------------------------------------------------------------------------
/*
   prefs.clear();
   if (bDS == true) Serial.print("Anzahl freier Einträge nach dem Init./Löschen: ");
   if (bDS == true) Serial.println (prefs.freeEntries());
         
   if (prefs.putInt("gmtOffset", 3600) == 0)
   if (bDS == true) Serial.println("error gmtOffset");


   if (bDS == true) Serial.printf("Anzahl freier Einträge nach dem Speichern: %d\n", prefs.freeEntries());
*/
   gmtOffset_sec = prefs.getInt("gmtOffset", 0);

   

   // start by connecting to a WiFi network----------------------------------------------------------------------------------------------

   if (bDS == true) Serial.println();
   if (bDS == true) Serial.print("Connecting to ");
   if (bDS == true) Serial.println(ssid);


   // Normalerweise wird die IP Adresse durch den Router zugewiesen. (DHCP)
   // Da wir aber immer die gleiche IP Adresse nutzen wollen, configurieren wir nachfolgend eine feste IP Adresse innerhalb des privaten Netzwerks.
   // Die entsprechenden Daten sind oben in den Definitionen bereits hinterlegt.
   // Bei Benutzung eines anderen Routers, müssen die Daten local_IP, gateway, subnet, primaryDNS, secondaryDNS erst ermittelt werden.
   // Dazu die folgenden drei Zeilen auskommentieren und die Daten bei aktivem Serial.print anzeigen lassen.
   // (Siehe nachfolgende Serial.print Zeilen)
   
 
   WiFi.mode(WIFI_STA);                               // Station Mode: ESP32 verbindet sich mit einem Router

   if (!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {
      if (bDS == true) Serial.println("STA Failed to configure");
   }

   // WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE, INADDR_NONE);  // define hostname (Beide Zeilen erforderlich) Hat niccht funktioniert!!!
   // WiFi.setHostname(hostname.c_str());              

   
   WiFi.begin(ssid, password);

   uint32_t TimeWaitMax = millis();
   while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      if (bDS == true) Serial.print(".");
      if (millis() - TimeWaitMax > 3000)   {          // Wenn nach 10 Sekunden keine Verbindung, dann abbrechen
         if (bDS == true) Serial.println("Keine Verbindung zum Router möglich, ESP Restart follows");
         espRestartCounter = prefs.getInt("espRestartCount", 0);
         if (bDS == true) Serial.print("Counter aus EEprom: ");
         if (bDS == true) Serial.println(espRestartCounter);
         espRestartCounter++;
         if (espRestartCounter < 3)  {
            prefs.putInt("espRestartCount", espRestartCounter);    
            if (bDS == true) Serial.print("Counter gesichert:**********************************:  ");
            if (bDS == true) Serial.println(espRestartCounter);
              
            ESP.restart();                                // Wenn der ESP noch im Router eingetragn ist, ist eine sofortige, erneute Verbindung 
                                                          // nicht möglich. Nach dem Restart klappts problemlos.          
         }
         else {
            if (bDS == true) Serial.print("Counter 2: ");
            if (bDS == true) Serial.println(espRestartCounter);
             
            break;                                      // Nach drittem Versuch break aus der while Schleife und fortfahren ohne Wifi
         }
      }
   }
   prefs.putInt("espRestartCount", 0);                  // Restart Counter ggf. löschen


   if (bDS == true) Serial.print("RRSI: ");
   if (bDS == true) Serial.println(WiFi.RSSI());


   //if (bDS == true) Serial.println("");   
   //if (bDS == true) Serial.print("Local IP address: ");         // Das ist die IP Adresse, welche der Access Point (Huawei Modul) uns als Teilnehmer im 
   //if (bDS == true) Serial.println(WiFi.localIP());             // Intranet zugeteilt hat.
   //if (bDS == true) Serial.print("Subnet Mask: ");              // To obtain the subnet mask, we simply need to call the subnetMask method.
   //if (bDS == true) Serial.println(WiFi.subnetMask());
   //if (bDS == true) Serial.print("Gateway IP: ");               // For the gateway IP address, we can call the gatewayIP method.
   //if (bDS == true) Serial.println(WiFi.gatewayIP());
   //if (bDS == true) Serial.print("DNS 1: ");                    // To get the main and backup DNS server IP addresses, we can call the dnsIP method, passing as input the values 0 and 1, respectively.
   //if (bDS == true) Serial.println(WiFi.dnsIP(0));
   //if (bDS == true) Serial.print("DNS 2: ");
   //if (bDS == true) Serial.println(WiFi.dnsIP(1));



   // Init OTA ------------------------------------------------------------------------------------------------------------------
   
   // Port defaults to 3232
   // ArduinoOTA.setPort(3232);

   // Hostname defaults to esp3232-[MAC]
   ArduinoOTA.setHostname("Esp32-MasterBus");

   // No authentication by default
   // ArduinoOTA.setPassword("admin");

   // Password can be set with it's md5 value as well
   // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
   // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");


/*
   ArduinoOTA.onStart([]() {
      String type;
      if (ArduinoOTA.getCommand() == U_FLASH)
        type = "sketch";
      else // U_SPIFFS
        type = "filesystem";

      // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
      if (bDS == true) Serial.println("Start updating " + type);
    });
      ArduinoOTA.onEnd([]() {
      if (bDS == true) Serial.println("\nEnd");
    });
      ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
      if (bDS == true) Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
    });
      ArduinoOTA.onError([](ota_error_t error) {
      if (bDS == true) Serial.printf("Error[%u]: ", error);
      if (error == OTA_AUTH_ERROR) {
         if (bDS == true) Serial.println("Auth Failed");
      }
      else if (error == OTA_BEGIN_ERROR) {
         if (bDS == true) Serial.println("Begin Failed");
      }
      else if (error == OTA_CONNECT_ERROR) {
         if (bDS == true) Serial.println("Connect Failed");
      }
      else if (error == OTA_RECEIVE_ERROR) {
         if (bDS == true) Serial.println("Receive Failed");
      }
      else if (error == OTA_END_ERROR) {
         if (bDS == true) Serial.println("End Failed");
      }
    });
*/
   ArduinoOTA.begin();

   //if (bDS == true) Serial.println("2. Test");
   //if (bDS == true) Serial.println(WiFi.localIP());


 
   // Zeit vom Zeitserver holen -----------------------------------------------------------------------------------------------------------------
 
   configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
   delay(3000);              // 3 Sekunden warten, sonst klappt das noch nicht über WiFi
   getTime();

   // if (bDS == true) Serial.println(TimeDateComplete_char20);

 
   // Init Webserver   -------------------------------------------------------------------------------------------------------------------

   // siehe:
   // https://lastminuteengineers.com/creating-esp32-web-server-arduino-ide/
   
   // In order to handle incoming HTTP requests, we need to specify which code to execute when a particular URL is hit. 
   // To do so, we use on method. This method takes two parameters. First one is a URL path and second one is the name of function which 
   // we want to execute when that URL is hit.
   // For example, the first line of below code snippet indicates that when a server receives an HTTP request on the root (/) path, 
   // it will trigger the handle_OnConnect() function. Note that the URL specified is a relative path.
   
   server.on("/", handle_OnConnect);
   server.on("/ref", handle_ref);
   server.on("/Charger", handle_Charger);
   server.on("/BatteryTypeMLI", handle_BatteryTypeMLI);
   server.on("/BatteryTypeConstant", handle_BatteryTypeConstant);  
   server.on("/MaxChargeCurrent0A", handle_MaxChargeCurrent0A);   
   server.on("/MaxChargeCurrent5A", handle_MaxChargeCurrent5A);  
   server.on("/MaxChargeCurrent25A", handle_MaxChargeCurrent25A);  
   server.on("/MaxChargeCurrent60A", handle_MaxChargeCurrent60A);  
   server.on("/Inverter", handle_Inverter);     
   server.on("/InvEnergySave", handle_InverterEnergySaveState);  
   server.on("/SiLand4A", handle_SiLand4A);  
   server.on("/SiLand10A", handle_SiLand10A);  
   server.on("/SiLand16A", handle_SiLand16A);  
   server.on("/ACinputSupportMode", handle_ACinputSupportMode); 
   server.on("/UTC1", handle_UTC1);
   server.on("/UTC2", handle_UTC2);

   
   // We haven’t specified what the server should do if the client requests any URL other than specified with server.on() . 
   // It should respond with an HTTP status 404 (Not Found) and a message for the user. We put this in a function as well, 
   // and use server.onNotFound() to tell it that it should execute it when it receives a request for a URI that wasn’t specified with server.on
   
   server.onNotFound(handle_NotFound);

   server.begin();                              // Startet den HTTP Server (web Server) im ESP32, so dass andere clients im gleichen Netz 


   // Init sonstiges -------------------------------------------------------------------------------------------------------------------
       
   bFirstCycle = true;      
   PreviousCheck = millis();
   zyklusTime = millis();
}


//-----------------------------------------------------------------------------------------------------------------
//--Main Program
//-----------------------------------------------------------------------------------------------------------------



void loop()  {


   ArduinoOTA.handle();              // Muss regelmäßig aufgerufen werden!!!!! Kann öfter aufgerufen werden, z.B. in länger laufenden while Schleifen
 
   server.handleClient();            // To handle the actual incoming HTTP requests, we need to call the handleClient() method on the server object.
 
   ReceiveMasterBus();               // can bus auf neue Daten prüfen

   
   zyklusTime = millis() - zyklusTimeLast;
   zyklusTimeLast = millis();
   if (zyklusTime > zyklusTimeHigh) {
      zyklusTimeHigh = zyklusTime;
   }


   if (millis() > Time1s == true) {
      Time1s += 10000;                       // 10 Sekunden
      //if (bDS == true) Serial.print("zykl Time ms: ");
      //if (bDS == true) Serial.println(zyklusTimeHigh);
      zyklusTimeHighWeb = zyklusTimeHigh;
      zyklusTimeHigh = 0;
   }

   ++zyklusCount;
   if (zyklusCount >= 1000)  {
      digitalWrite(LEDgruen, HIGH);     // LED Grün, zur Anzeige, dass Programm läuft
      if (bLEDgruen == false) {
         bLEDgruen = true;
         LEDgruenTime = millis();
      }
      if (millis() - LEDgruenTime >= 15)  {
         digitalWrite(LEDgruen, LOW);
         bLEDgruen = false;
         zyklusCount = 0;
      }
   }
   uint32_t Minuten = millis() / 60000;
   if ((Minuten > PreviousCheck) || (bFirstCycle == true))   {   // Prüfen, ob Wlan Verbindung noch aktiv
      if (WiFi.status() != WL_CONNECTED)    {
         WiFi.disconnect();
         WiFi.reconnect();
         // if (bDS == true) Serial.println("WiFi nach Verbindungsabbruch neu gestartet...");
      }
      else {
         // if (bDS == true) Serial.println("WiFi bei Prüfung noch aktiv...");
      }
      PreviousCheck = Minuten + 10;
   }



   //------------------------------------------------------------------------------------------------------------------------------------------------
   //--Masterbus Daten abfragen
   //------------------------------------------------------------------------------------------------------------------------------------------------

   if (millis() - PollTime >= 50)  {
      PollTime = millis();
      if (BlockMon <= 17) {
         transmitMonitoring(BlockMon);
         BlockMon++;
      }
      else if (BlockErr <= 6) {
         transmitError(BlockErr);
         BlockErr++;
      }

      else if (BlockConfig <= 8) {
         transmitConfig(BlockConfig);
         BlockConfig++;
      }
      else if (BlockHistory <= 1) {
         transmitHistory(BlockHistory);
         BlockHistory++;
         if (BlockHistory > 1) {
            BlockMon = 0;
            BlockErr = 0;
            BlockConfig = 0;
            BlockHistory = 0;
         }
      }
   }


   //if(millis() - Hardbeat > 1300)
   // {
   //   transmit6C1B();
   //   Hardbeat = millis();                             // 1300ms Zeitscheibe zurücksetzen
   //   delay(1);
   // }
}


//-----------------------------------------------------------------------------------------------------------------
//--Funktionen für Webserver
//-----------------------------------------------------------------------------------------------------------------


// Next, we need to create a function we attached to root (/) URL with server.on. Remember? At the start of this function, 
// we set the status of both the LEDs to LOW (Initial state of LEDs) and print it on serial monitor. 
// In order to respond to the HTTP request, we use the send method. Although the method can be called with a different set of arguments, 
// its simplest form consists of the HTTP response code, the content type and the content.
// In our case, we are sending the code 200 (one of the HTTP status codes), which corresponds to the OK response. 
// Then, we are specifying the content type as “text/html“, and finally we are calling SendHTML() custom function which creates a 
// dynamic HTML page containing status of LEDs.
// Likewise, we need to create four functions to handle LED ON/OFF requests and 404 Error page.


void handle_OnConnect() {
   server.send(200, "text/html", SendHTML()); 
}

void handle_ref() {
   server.send(200, "text/html", SendHTML()); 
}

void handle_NotFound(){
  server.send(404, "text/plain", "Not found");
}

void handle_Charger() {
   bProtocol = true;
   num = 0;

   if (MbChargerOnOffIst == 0) {
      canPageCommand(PageMonRequestID, MBChargerOnOff, 0x3F, 0x80, 0x00);
      delay(5);
      ReceiveMasterBus(); 
      canPageCommand(PageMonRequestID, MBChargerPreOff, 0x00, 0x00, 0x00); 
      delay(5);
      ReceiveMasterBus(); 
      canPageRequest(PageMonRequestID,MBChargerOnOff, 0x00);
      delay(5);
      ReceiveMasterBus(); 
           
      while ((MbChargerOnOffIst != 1) && (MbChargerPreOffIst != 0)) {    // Warten, bis die Umschaltung erfolgt ist und die Antwort 
                                                           // über MasterBus empfangen wurde
         watchDogSchalter++;                               // Watchdog, zur Absicherung, falls die Umschaltung nicht funtioniert
         ArduinoOTA.handle();                              // Muss regelmäßig aufgerufen werden!!!!! Kann öfter aufgerufen werden.
         canPageRequest(PageMonRequestID,MBChargerPreOff, 0x00);
         delay(2);
         ReceiveMasterBus();                               // MasterBus Daten empfangen
         canPageRequest(PageMonRequestID,MBChargerOnOff, 0x00);
         delay(2);
         ReceiveMasterBus();                                      
         if (watchDogSchalter > 1000) {                    // nach 1000 Zyklen (wenige Sekunden) abbrechen
            break;
         }
      }   
   }
  
   else if (MbChargerOnOffIst == 1) {
      canPageCommand(PageMonRequestID, MBChargerOnOff, 0x00, 0x00, 0x00); 
      delay(5);
      ReceiveMasterBus();  
      canPageCommand(PageMonRequestID, MBChargerPreOff, 0x3F, 0x80, 0x00);
      delay(5);
      ReceiveMasterBus();  
      canPageRequest(PageMonRequestID,MBChargerOnOff, 0x00);
      delay(5);
      ReceiveMasterBus();  
      
      while ((MbChargerOnOffIst != 0) && (MbChargerPreOffIst != 1))  {  // Warten, bis die Umschaltung erfolgt ist und die Antwort 
                                                           // über MasterBus empfangen wurde
         watchDogSchalter++;                               // Watchdog, zur Absicherung, falls die Umschaltung nicht funtioniert
         ArduinoOTA.handle();                              // Muss regelmäßig aufgerufen werden!!!!! Kann öfter aufgerufen werden.
         canPageRequest(PageMonRequestID,MBChargerOnOff, 0x00); 
         delay(2); 
         ReceiveMasterBus();                               // MasterBus Daten empfangen
         canPageRequest(PageMonRequestID,MBChargerPreOff, 0x00);
         delay(2);
         ReceiveMasterBus();  
         if (watchDogSchalter > 1000) {                    // nach 1000 Zyklen (wenige Sekunden) abbrechen
            break;
         }
      }
   }

   watchDogSchalter = 0;
   server.send(200, "text/html", SendHTML()); 
}


void handle_BatteryTypeMLI() {
   bProtocol = true;
   num = 0;
   canPageCommand(PageConfigRequestID, CFBatterieType, 0x40, 0x80, 0x00);
   while (CfBatterieTypeIst != 4) {    // Warten, bis die Umschaltung erfolgt ist und die Antwort 
                                     // über MasterBus empfangen wurde
      watchDogSchalter++;            // Watchdog, zur Absicherung, falls die Umschaltung nicht funtioniert
      ArduinoOTA.handle();           // Muss regelmäßig aufgerufen werden!!!!! Kann öfter aufgerufen werden.
      ReceiveMasterBus();            // MasterBus Daten empfangen
      if (watchDogSchalter > 100) {       // nach 100 Zyklen abbrechen
         break;
      }
   }
   watchDogSchalter = 0;


   server.send(200, "text/html", SendHTML()); 
}

void handle_BatteryTypeConstant() {
   bProtocol = true;
   num = 0;
   canPageCommand(PageConfigRequestID, CFBatterieType, 0x40, 0xA0, 0x00);
   while (CfBatterieTypeIst != 5) {    // Warten, bis die Umschaltung erfolgt ist und die Antwort 
                                     // über MasterBus empfangen wurde
      watchDogSchalter++;            // Watchdog, zur Absicherung, falls die Umschaltung nicht funtioniert
      ArduinoOTA.handle();           // Muss regelmäßig aufgerufen werden!!!!! Kann öfter aufgerufen werden.
      ReceiveMasterBus();            // MasterBus Daten empfangen
      if (watchDogSchalter > 100) {       // nach 100 Zyklen abbrechen
         break;
      }
   }
   watchDogSchalter = 0;

   //if (bDS == true) Serial.println("Batterie Type Soll = Constant voltage");
   server.send(200, "text/html", SendHTML()); 
}


void handle_MaxChargeCurrent0A()  {
   bProtocol = true;
   num = 0;
   canPageCommand(PageConfigRequestID, CFmaxChargeCurrent, 0x00, 0x00, 0x00);
   while (CfMaxChargeCurrentIst != 0) {    // Warten, bis die Umschaltung erfolgt ist und die Antwort 
                                         // über MasterBus empfangen wurde
      watchDogSchalter++;                // Watchdog, zur Absicherung, falls die Umschaltung nicht funtioniert
      ArduinoOTA.handle();               // Muss regelmäßig aufgerufen werden!!!!! Kann öfter aufgerufen werden.
      ReceiveMasterBus();                // MasterBus Daten empfangen
      if (watchDogSchalter > 100) {      // nach 100 Zyklen abbrechen
         break;
      }
   }
   watchDogSchalter = 0;
   server.send(200, "text/html", SendHTML());  
}


void handle_MaxChargeCurrent5A()  {
   bProtocol = true;
   num = 0;
   canPageCommand(PageConfigRequestID, CFmaxChargeCurrent, 0x40, 0xA0, 0x00);
   while (CfMaxChargeCurrentIst != 5) {    // Warten, bis die Umschaltung erfolgt ist und die Antwort 
                                         // über MasterBus empfangen wurde
      watchDogSchalter++;                // Watchdog, zur Absicherung, falls die Umschaltung nicht funtioniert
      ArduinoOTA.handle();               // Muss regelmäßig aufgerufen werden!!!!! Kann öfter aufgerufen werden.
      ReceiveMasterBus();                // MasterBus Daten empfangen
      if (watchDogSchalter > 100) {      // nach 100 Zyklen abbrechen
         break;
      }
   }
   watchDogSchalter = 0;
   server.send(200, "text/html", SendHTML());  
}

void handle_MaxChargeCurrent25A()  {
   bProtocol = true;
   num = 0;
   canPageCommand(PageConfigRequestID, CFmaxChargeCurrent, 0x41, 0xC8, 0x00);
   while (CfMaxChargeCurrentIst != 25) {    // Warten, bis die Umschaltung erfolgt ist und die Antwort 
                                         // über MasterBus empfangen wurde
      watchDogSchalter++;                // Watchdog, zur Absicherung, falls die Umschaltung nicht funtioniert
      ArduinoOTA.handle();               // Muss regelmäßig aufgerufen werden!!!!! Kann öfter aufgerufen werden.
      ReceiveMasterBus();                // MasterBus Daten empfangen
      if (watchDogSchalter > 100) {      // nach 100 Zyklen abbrechen
         break;
      }
   }
   watchDogSchalter = 0;
   server.send(200, "text/html", SendHTML());  
}

void handle_MaxChargeCurrent60A()  {
   bProtocol = true;
   num = 0;
   canPageCommand(PageConfigRequestID, CFmaxChargeCurrent, 0x42, 0x70, 0x00);
   while (CfMaxChargeCurrentIst != 60) {    // Warten, bis die Umschaltung erfolgt ist und die Antwort 
                                         // über MasterBus empfangen wurde
      watchDogSchalter++;                // Watchdog, zur Absicherung, falls die Umschaltung nicht funtioniert
      ArduinoOTA.handle();               // Muss regelmäßig aufgerufen werden!!!!! Kann öfter aufgerufen werden.
      ReceiveMasterBus();                // MasterBus Daten empfangen
      if (watchDogSchalter > 100) {      // nach 100 Zyklen abbrechen
         break;
      }
   }
   watchDogSchalter = 0;
   server.send(200, "text/html", SendHTML());  
}


void handle_Inverter() {
   bProtocol = true;
   num = 0;
  if (MbInverterOnOffIst == 0) {
      canPageCommand(PageMonRequestID, MBInverterOnOff, 0x3F, 0x80, 0x00);
      delay(5);
      ReceiveMasterBus(); 
      canPageCommand(PageMonRequestID, MBInverterPreOff, 0x00, 0x00, 0x00); 
      delay(5);
      ReceiveMasterBus(); 
      canPageRequest(PageMonRequestID,MBInverterOnOff, 0x00);
      delay(5);
      ReceiveMasterBus(); 
           
      while ((MbInverterOnOffIst != 1) && (MbInverterPreOffIst != 0)) {    // Warten, bis die Umschaltung erfolgt ist und die Antwort 
                                                           // über MasterBus empfangen wurde
         watchDogSchalter++;                               // Watchdog, zur Absicherung, falls die Umschaltung nicht funtioniert
         ArduinoOTA.handle();                              // Muss regelmäßig aufgerufen werden!!!!! Kann öfter aufgerufen werden.
         canPageRequest(PageMonRequestID,MBInverterPreOff, 0x00);
         delay(2);
         ReceiveMasterBus();                               // MasterBus Daten empfangen
         canPageRequest(PageMonRequestID,MBInverterOnOff, 0x00);
         delay(2);
         ReceiveMasterBus();                                      
         if (watchDogSchalter > 1000) {                    // nach 1000 Zyklen (wenige Sekunden) abbrechen
            break;
         }
      }   
   }
  
   else if (MbInverterOnOffIst == 1) {
      canPageCommand(PageMonRequestID, MBInverterOnOff, 0x00, 0x00, 0x00); 
      delay(5);
      ReceiveMasterBus();  
      canPageCommand(PageMonRequestID, MBInverterPreOff, 0x3F, 0x80, 0x00);
      delay(5);
      ReceiveMasterBus();  
      canPageRequest(PageMonRequestID,MBInverterOnOff, 0x00);
      delay(5);
      ReceiveMasterBus();  
      
      while ((MbInverterOnOffIst != 0) && (MbInverterPreOffIst != 1))  {  // Warten, bis die Umschaltung erfolgt ist und die Antwort 
                                                           // über MasterBus empfangen wurde
         watchDogSchalter++;                               // Watchdog, zur Absicherung, falls die Umschaltung nicht funtioniert
         ArduinoOTA.handle();                              // Muss regelmäßig aufgerufen werden!!!!! Kann öfter aufgerufen werden.
         canPageRequest(PageMonRequestID,MBInverterOnOff, 0x00); 
         delay(2); 
         ReceiveMasterBus();                               // MasterBus Daten empfangen
         canPageRequest(PageMonRequestID,MBInverterPreOff, 0x00);
         delay(2);
         ReceiveMasterBus();  
         if (watchDogSchalter > 1000) {                    // nach 1000 Zyklen (wenige Sekunden) abbrechen
            break;
         }
      }
   }
   watchDogSchalter = 0;
   server.send(200, "text/html", SendHTML()); 
}


void handle_InverterEnergySaveState() {
   bProtocol = true;
   num = 0;
   if (CfInverterEnergySaveIst == 0) {
      CfInverterEnergySaveSoll = 1;                      // CfInverterEnergySaveSoll zum Ein- oder Ausschalten Energy save mode, je nach aktuellem Status
      MbDeviceStateSoll = 4;
   }
   if (CfInverterEnergySaveIst == 1) {
      CfInverterEnergySaveSoll = 0;
      MbDeviceStateSoll = 2;
   }

   if (CfInverterEnergySaveSoll == 1) {
      canPageCommand(PageConfigRequestID, CFInverterEnergySave, 0x3F, 0x80, 0x00); // Sende Befehl zum Umschalten Inverter Energy Save Mode
   }  
   if (CfInverterEnergySaveSoll == 0) {
      canPageCommand(PageConfigRequestID, CFInverterEnergySave, 0x00, 0x00, 0x00); // Sende Befehl zum Umschalten Inverter Energy Save Mode
   } 

 
   while (CfInverterEnergySaveIst != CfInverterEnergySaveSoll)  {           // Warten, bis die Umschaltung erfolgt ist und die Antwort 
                                                        // über MasterBus empfangen wurde
      watchDogSchalter++;                               // Watchdog, zur Absicherung, falls die Umschaltung nicht funtioniert
      ArduinoOTA.handle();                              // Muss regelmäßig aufgerufen werden!!!!! Kann öfter aufgerufen werden.
      ReceiveMasterBus();                               // MasterBus Daten empfangen
      delay(1);
      if (watchDogSchalter > 1000) {  // nach 1000 Zyklen (wenige Sekunden) abbrechen
         break;
      }
   }
   watchDogSchalter = 0;
   delay(1000);

   canPageRequest(PageMonRequestID, MBDeviceState, 0x00);   // Sende Befehl zum auffrischen Status Inverter
      
   while (MbDeviceStateIst != MbDeviceStateSoll) {      // Warten, bis aktualisierter Parameter über MasterBus empfangen wird 
                                                                              
      watchDogSchalter++;       // Watchdog, zur Absicherung, falls die Umschaltung nicht funtioniert
      ArduinoOTA.handle();      // Muss regelmäßig aufgerufen werden!!!!! Kann öfter aufgerufen werden.
      ReceiveMasterBus();       // MasterBus Daten empfangen
      delay(1);
      if (watchDogSchalter > 1000) {  // nach 1000 Zyklen (wenige Sekunden) abbrechen
         break;
      }
   }
   watchDogSchalter = 0;
   //if (bDS == true) Serial.println(CfInverterEnergySaveIst);
   server.send(200, "text/html", SendHTML()); 
}

void handle_ACinputSupportMode() {
   bProtocol = true;
   num = 0;
   if (CfACinputSupportModeIst == 0) {
      canPageCommand(PageConfigRequestID, CFACinputSupportMode, 0x3F, 0x80, 0x00); // Sende Befehl zum Umschalten Inverter Energy Save Mode
      
      while (CfACinputSupportModeIst == 0)  {             // Warten, bis die Umschaltung erfolgt ist und die Antwort 
                                                        // über MasterBus empfangen wurde
         watchDogSchalter++;                            // Watchdog, zur Absicherung, falls die Umschaltung nicht funtioniert
         ArduinoOTA.handle();                           // Muss regelmäßig aufgerufen werden!!!!! Kann öfter aufgerufen werden.
         ReceiveMasterBus();                            // MasterBus Daten empfangen
         delay(1);
         if (watchDogSchalter > 1000) {                 // nach 1000 Zyklen (wenige Sekunden) abbrechen
            break;
         }
      }
   }
   
   else if (CfACinputSupportModeIst == 1) {
      canPageCommand(PageConfigRequestID, CFACinputSupportMode, 0x00, 0x00, 0x00); // Sende Befehl zum Umschalten Inverter Energy Save Mode
      
      while (CfACinputSupportModeIst == 1)  {             // Warten, bis die Umschaltung erfolgt ist und die Antwort 
                                                        // über MasterBus empfangen wurde
         watchDogSchalter++;                            // Watchdog, zur Absicherung, falls die Umschaltung nicht funtioniert
         ArduinoOTA.handle();                           // Muss regelmäßig aufgerufen werden!!!!! Kann öfter aufgerufen werden.
         ReceiveMasterBus();                            // MasterBus Daten empfangen
         delay(1);
         if (watchDogSchalter > 1000) {                 // nach 1000 Zyklen (wenige Sekunden) abbrechen
            break;
         }
      }
   }
   watchDogSchalter = 0;
   server.send(200, "text/html", SendHTML()); 
}

void handle_SiLand4A() {
   bProtocol = true;
   num = 0;
   canPageCommand(PageMonRequestID, MBFuseSettingACinput, 0x40, 0x80, 0x00);
   while (AcFuseSettingIst != 4) {   // Warten, bis die Umschaltung erfolgt ist und die Antwort 
                                     // über MasterBus empfangen wurde
      watchDogSchalter++;            // Watchdog, zur Absicherung, falls die Umschaltung nicht funtioniert
      ArduinoOTA.handle();           // Muss regelmäßig aufgerufen werden!!!!! Kann öfter aufgerufen werden.
      ReceiveMasterBus();            // MasterBus Daten empfangen
      if (watchDogSchalter > 100) {  // nach 100 Zyklen abbrechen
         break;
      }
   }
   watchDogSchalter = 0;
   server.send(200, "text/html", SendHTML()); 
}

void handle_SiLand10A() {
   bProtocol = true;
   num = 0;
   canPageCommand(PageMonRequestID, MBFuseSettingACinput, 0x41, 0x20, 0x00);
   while (AcFuseSettingIst != 10) {   // Warten, bis die Umschaltung erfolgt ist und die Antwort 
                                     // über MasterBus empfangen wurde
      watchDogSchalter++;            // Watchdog, zur Absicherung, falls die Umschaltung nicht funtioniert
      ArduinoOTA.handle();           // Muss regelmäßig aufgerufen werden!!!!! Kann öfter aufgerufen werden.
      ReceiveMasterBus();            // MasterBus Daten empfangen
      if (watchDogSchalter > 100) {  // nach 100 Zyklen abbrechen
         break;
      }
   }
   watchDogSchalter = 0;
   server.send(200, "text/html", SendHTML()); 
}

void handle_SiLand16A() {
   bProtocol = true;
   num = 0;
   canPageCommand(PageMonRequestID, MBFuseSettingACinput, 0x41, 0x80, 0x00);
   while (AcFuseSettingIst != 16) {  // Warten, bis die Umschaltung erfolgt ist und die Antwort 
                                     // über MasterBus empfangen wurde
      watchDogSchalter++;            // Watchdog, zur Absicherung, falls die Umschaltung nicht funtioniert
      ArduinoOTA.handle();           // Muss regelmäßig aufgerufen werden!!!!! Kann öfter aufgerufen werden.
      ReceiveMasterBus();            // MasterBus Daten empfangen
      if (watchDogSchalter > 100) {  // nach 100 Zyklen abbrechen
         break;
      }
   }
   watchDogSchalter = 0;
   server.send(200, "text/html", SendHTML()); 
}

void handle_UTC1() {
   gmtOffset_sec = 3600;
   prefs.putInt("gmtOffset", gmtOffset_sec);                         // UTC Offset im EEprom festhalten
   
   configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
   getTime();
   server.send(200, "text/html", SendHTML()); 
}

void handle_UTC2() {
   gmtOffset_sec = 7200;
   prefs.putInt("gmtOffset", gmtOffset_sec);                        // UTC Offset im EEprom festhalten
   configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
   getTime();
   server.send(200, "text/html", SendHTML()); 
}

 

// SendHTML() function is responsible for generating a web page whenever the ESP32 web server gets a request from a web client. 
// It merely concatenates HTML code into a big string and returns to the server.send() function we discussed earlier. 
// The function takes status of LEDs as a parameter to dynamically generate the HTML content.

// The first text you should always send is the <!DOCTYPE> declaration that indicates that we’re sending HTML code.

String SendHTML(){

   //Doc****************************************************************************************************************
   String ptr = "<!DOCTYPE html>\n";
   ptr +="<html lang=\"de\">\n";
 
   //Head***************************************************************************************************************
   ptr +="<head>";
   ptr +="<meta charset=\"utf-8\">\n";
   ptr +="<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n";                           
   ptr +="<title>Libera CombiMaster APP</title>\n";
   ptr +="<style>";
   ptr +="body {background-color: rgb(230,200,160); font-family: Helvetica; }  \n"; 
  
   ptr +="h1 { color:#000000;      font-size:160%;  margin-top: 1em;   margin-bottom: 0.1em;  font-family: Helvetica;   }       \n";     
   ptr +="h2 { color:#000000;      font-size:120%;  margin-top: 1em;   margin-bottom: 0.1em;  font-family: Helvetica;   }       \n";     
   
                         
   // CSS to style the buttons  
   
   ptr +=".button {display: inline-block; background-color: #3498db; border: none; color: white; padding: 10px 15px; text-decoration: none; font-size: 20px; margin: 4px 4px; cursor: pointer; border-radius: 4px;} \n";
                  
   ptr +=".button-1 {background-color: #3498db; margin: 6px 6px;}\n";                    // hellblau = ON
   ptr +=".button-2 {background-color: #2c3e50; margin: 6px 6px;}\n";                    // dunkel = OFF
 
   ptr +=".button-3 {background-color: #3498db; margin: 6px 6px; font-size: small;}\n";  // hellblau = ON, kleine Variante
   ptr +=".button-4 {background-color: #2c3e50; margin: 6px 6px; font-size: small;}\n";  // grau           // dunkel = OFF, kleine Variante

      
   ptr +="</style>\n";

   ptr +="</head>\n";

   //Body***************************************************************************************************************
   ptr +="<body>\n";
 
   // Überschrift--------------------------------------------------------------------------------------------------------- 
 
   ptr += "<h1>CombiMaster  ";
   ptr += "<a class=\"button button-4\" href=\"/ref\"> Refresh </a>\n";      // Taste zum Auffrischen der Seite ohne Auslösen von Aktionen
   ptr += "</h1>\n"; 


 
   // CAN Traffic Plotter--------------------------------------------------------------------------------------------------------- 

   // W i c h t i g :  Daten werden nur beim Betätigen der Taten einmal mitgeschrieben.
   // Um alle mitgeschriebenen Daten anzuzeigen, nach Betätigen der Taste und Empfang der aktualisierten web page nochmals Refresch betätigen.
   
   // ptr += "<h2>CAN Traffic Plotter:</h2>"; 
   // ptr += ("<font face=\"courier\">");
   // for (int32_t i = 0; i < numMax; i++) { 
   //    ptr += canString[i];
   //    ptr += "<br />";
   // }
   // ptr += ("</font>");



   // Tasten Auswahl Batterie Type:---------------------------------------------------------------------------------------------------------------------

   ptr += "<h2>Charger, Batterietype, Ladestrom:</h2>"; 


   if (MbChargerOnOffIst == 1) ptr +="<a class=\"button button-3\" href=\"/Charger\">Charger &#9212</a>\n";
   else ptr +="<a class=\"button button-4\" href=\"/Charger\">Charger &#9212</a>\n";

   if (CfBatterieTypeIst == 4) ptr +="<a class=\"button button-3\" href=\"/BatteryTypeMLI\">LiFePO4</a>\n";
   //                                                                   /BatteryTypeMLI wird an Adresse angefügt
   //                                                                                     Li wird als Text auf Taster ausgegeben
   else    ptr +="<a class=\"button button-4\" href=\"/BatteryTypeMLI\">LiFePO4</a>\n";
 
   if (CfBatterieTypeIst == 5) ptr +="<a class=\"button button-3\" href=\"/BatteryTypeConstant\">13,25V</a>\n";
   else    ptr +="<a class=\"button button-4\" href=\"/BatteryTypeConstant\">13,25V</a>\n";

   ptr += "<br />";
   
   //if (CfMaxChargeCurrentIst == 0) ptr += "<a class=\"button button-3\" href=\"/MaxChargeCurrent0A\">0A</a>\n";
   //else ptr += "<a class=\"button button-4\" href=\"/MaxChargeCurrent0A\">0A</a>\n";

  if (CfMaxChargeCurrentIst == 5) ptr += "<a class=\"button button-3\" href=\"/MaxChargeCurrent5A\">5A</a>\n";
   else ptr += "<a class=\"button button-4\" href=\"/MaxChargeCurrent5A\">5A</a>\n";

   if (CfMaxChargeCurrentIst == 25) ptr += "<a class=\"button button-3\" href=\"/MaxChargeCurrent25A\">25A</a>\n";
   else ptr += "<a class=\"button button-4\" href=\"/MaxChargeCurrent25A\">25A</a>\n";

     if (CfMaxChargeCurrentIst == 60) ptr += "<a class=\"button button-3\" href=\"/MaxChargeCurrent60A\">60A</a>\n";
   else ptr += "<a class=\"button button-4\" href=\"/MaxChargeCurrent60A\">60A</a>\n";

   // ............................................................................................
 
   ptr += "<h2>Inverter, Supportmode, Landsicherung:</h2>"; 

   if (MbInverterOnOffIst == 1) ptr +="<a class=\"button button-3\" href=\"/Inverter\">Inverter &#9212</a>\n";
   else ptr +="<a class=\"button button-4\" href=\"/Inverter\">Inverter &#9212</a>\n";


   if (CfInverterEnergySaveIst == 1) ptr += "<a class=\"button button-3\" href=\"/InvEnergySave\">EnergySave</a>\n";
   else ptr += "<a class=\"button button-4\" href=\"/InvEnergySave\">EnergySave</a>\n";

   if (CfACinputSupportModeIst == 1) ptr += "<a class=\"button button-3\" href=\"/ACinputSupportMode\">Support</a>\n";
   else ptr += "<a class=\"button button-4\" href=\"/ACinputSupportMode\">Support</a>\n";

   if (AcFuseSettingIst == 4) ptr += "<a class=\"button button-3\" href=\"/SiLand4A\">4A</a>\n";
   else ptr += "<a class=\"button button-4\" href=\"/SiLand4A\">4A</a>\n";

   if (AcFuseSettingIst == 10) ptr += "<a class=\"button button-3\" href=\"/SiLand10A\">10A</a>\n";
   else ptr += "<a class=\"button button-4\" href=\"/SiLand10A\">10A</a>\n";

   if (AcFuseSettingIst == 16) ptr += "<a class=\"button button-3\" href=\"/SiLand16A\">16A</a>\n";
   else ptr += "<a class=\"button button-4\" href=\"/SiLand16A\">16A</a>\n";

   // Werte: ------------------------------------------------------------------------------------------------------------------

 
   ptr += "<h2>Monitoring:</h2>"; 

   // Laut Handbuch: Standby, Charging, Inverting, Supporting, Alarm

   ptr += "<table>";     // mit th statt td wirds zentriert
   ptr += "<tr>";
   ptr += "<td>Status:</td>";  
   if (MbDeviceStateIst == 0) ptr += "<td>Off</td>";
   else if (MbDeviceStateIst == 1) ptr += "<td>Charging</td>";
   else if (MbDeviceStateIst == 2) ptr += "<td>Inverter mode</td>";
   else if (MbDeviceStateIst == 3) ptr += "<td>Alarm</td>";
   else if (MbDeviceStateIst == 4) ptr += "<td>Inverter Energy Save</td>";
   else if (MbDeviceStateIst == 5) ptr += "<td>Supporting</td>";
   else if (MbDeviceStateIst == 6) ptr += "<td>Landstrom</td>";
   else if (MbDeviceStateIst > 6) ptr += "<td>nicht bekannt</td>";
   ptr += "</tr>";

   ptr += "<tr>";
   ptr += "<td>Charger State:</td>";  
   if (MbChargerStateIst == 0) ptr += "<td>Off</td>";
   if (MbChargerStateIst == 1) ptr += "<td>Bulk</td>";
   if (MbChargerStateIst == 2) ptr += "<td>Absorption</td>";
   if (MbChargerStateIst == 3) ptr += "<td>Float</td>";
   ptr += "</tr>";

   ptr += "<tr>"; 
   ptr += "<td>Inverter Input voltage:</td>";  
   ptr += "<td>";
   ptr += (MBAcInputVoltage_f);
   ptr += "V"; 
   ptr += "</td>";
   ptr += "</tr>";  

   ptr += "<tr>";
   ptr += "<td>Inverter Input current:</td>"; 
   ptr += "<td>";    
   ptr += (MBAcInputCurrent_f);
   ptr += "A"; 
   ptr += "</td>";   
   ptr += "</tr>";     

   ptr += "<tr>";
   ptr += "<td>Inverter Input frequency:</td>";  
   ptr += "<td>";
   ptr += (MBAcInputFrequency_f);
   ptr += "Hz"; 
   ptr += "</td>";
   ptr += "</tr>";

   ptr += "<tr>";
   ptr += "<td>Inverter Output voltage:</td>";  
   ptr += "<td>";
   ptr += (MBAcOutVoltage_f);
   ptr += "V"; 
   ptr += "</td>";
   ptr += "</tr>";   
   
   ptr += "<tr>";
   ptr += "<td>Inverter Output frequency:</td>";  
   ptr += "<td>";
   ptr += (MBAcOutFrequency_f);
   ptr += "Hz";    
   ptr += "</td>";  
   ptr += "</tr>";

   ptr += "<tr>";
   ptr += "<td>Inverter Output Power:</td>";  
   ptr += "<td>";
   ptr += (MBAcOutPowerW_f);
   ptr += "W"; 
   ptr += "</td>";
   ptr += "</tr>";

   ptr += "<tr>";
   ptr += "<td>Power 2 (?):</td>";  
   ptr += "<td>";
   ptr += (MBAcOutPower2_f);
   ptr += "VA";   
   ptr += "</td>";
   ptr += "</tr>";

   ptr += "<tr>";
   ptr += "<td>Auslastung (?):</td>";  
   ptr += "<td>";
   ptr += (MBAuslastung_f);
   ptr += "%"; 
   ptr += "</td>";   
   ptr += "</tr>";

   //ptr += "<tr>";
   //ptr += "<td>Inverter MBAcNC3_f:</td>";  
   //ptr += "<td>";
   //ptr += (MBAcNC3bin, BIN);
   //ptr += (MBAcNC3_f);
   //ptr += "</td>";
   //ptr += "</tr>";

   ptr += "<tr>";
   ptr += "<td>Battery voltage:</td>";  
   ptr += "<td>";
   ptr += (MBBatterieVoltage_f);
   ptr += "V"; 
   ptr += "</td>";
   ptr += "</tr>";

   ptr += "<tr>";
   ptr += "<td>Battery current:</td>";  
   ptr += "<td>";
   ptr += (MBBatterieCurrent_f);
   ptr += "A"; 
   ptr += "</td>";
   ptr += "</tr>";


   ptr += "<tr>";
   ptr += "<td>Config Page:</td>";  
   ptr += "<td>";
   ptr += " ";  
   ptr += "</td>";
   ptr += "</tr>";

   ptr += "<tr>";
   ptr += "<td>0x0A (Landstrom vorhanden?):</td>";  
   if (CfLandstromIst == 0) ptr += "<td>-0-</td>";
   if (CfLandstromIst == 1) ptr += "<td>-1-</td>";
   if (CfLandstromIst > 1) { 
      ptr += "<td>-";
      ptr += CfLandstromIst;
      ptr += "-</td>";
   }
   ptr += "</tr>";

   ptr += "<tr>";
   ptr += "<td>Inverter DC Low ON:</td>";  
   ptr += "<td>";
   ptr += CFInverterLowVoltON_f;
   ptr += "h";  
   ptr += "</td>";
   ptr += "</tr>";

   ptr += "<tr>";
   ptr += "<td>Inverter DC Low Off:</td>";  
   ptr += "<td>";
   ptr += CFInverterLowVoltOFF_f;
   ptr += "h";  
   ptr += "</td>";
   ptr += "</tr>";

   ptr += "</table>";


   ptr += "<h2>Fehlermeldungen:</h2>"; 
   
   if (ALTemperatureHigh_f == 1.00) ptr += "Internal temperature of the CombiMaster is too high<br />";
   if (ALFanError_f == 1.00) ptr += "Fan error<br />";
   if (ALDChighVoltage_f == 1.00) ptr += "Battery voltage is too high<br />";
   if (ALDClowVoltage_f == 1.00) ptr += "Battery voltage is too low<br />";
   if (ALOverload_f == 1.00) ptr += "The loads exceed the nominal inverter power<br />";
   if (ALDeviceError_f == 1.00) ptr += "Internal CombiMaster error<br />";
   if (ALACinError_f == 1.00) ptr += "AC input voltage or frequency out of range<br />";
   if (ALTemperatureHigh_f == 0.0 && ALFanError_f == 0.0 && ALDChighVoltage_f == 0.0 && ALDClowVoltage_f == 0.0 && ALOverload_f == 0.0 
      && ALDeviceError_f == 0.0 && ALACinError_f == 0.0 && ALTemperatureHigh_f == 0.0) {
      ptr += "- kein Fehler -";
   }

   ptr += "<h2>History:</h2>"; 

   ptr += "<table>";     // mit th statt td werden die Tabellenzeilen zentriert
   ptr += "<tr>";
   ptr += "<td>Inverter Runtime:</td>";  
   ptr += "<td>";
   ptr += HIinverterRunTime_f;
   ptr += "h";
   ptr += "</td>";
   ptr += "</tr>";

   ptr += "<tr>";
   ptr += "<td>Charger runtime:</td>";  
   ptr += "<td>";
   ptr += HIchargerRunTime_f;
   ptr += "h";  
   ptr += "</td>";
   ptr += "</tr>";

   ptr += "</table>";



   // Next Message, Version, sonstiges ------------------------------------------------------------------------------------------------------------------

   ptr += "<h2>Sonstiges:</h2>";   
   // getTime();                         
   // ptr += "Zeit: "; 
   // ptr += TimeDateComplete_char20;   
   // ptr += "<br />";

   ptr += "Vers.:  ";
   ptr += FilenameShort; 
   ptr += ", ";
   ptr += zyklusTimeHighWeb;
   ptr += "ms";     
   //ptr += "</font>";  
   ptr += "<br />";


   //if (gmtOffset_sec == 3600) ptr +="<a class=\"button button-3\" href=\"/UTC1\">Zeit D/I</a>\n";
   //                                                                    /UTC1 wird an Adresse angefügt
   //                                                                            Zeit D/I wird als Text auf Taster ausgegeben
   //else    ptr +="<a class=\"button button-4\" href=\"/UTC1\">Zeit D I</a>\n";

   //if (gmtOffset_sec == 7200) ptr +="<a class=\"button button-3\" href=\"/UTC2\">Zeit Gr</a>\n";
   //else    ptr +="<a class=\"button button-4\" href=\"/UTC2\">Zeit Gr</a>\n";


   //Foot***************************************************************************************************************
   ptr +="</body>\n";
   ptr +="</html>\n";
   return ptr;
}



//------------------------------------------------------------------------------------------------------------------------------------------------
//-- Masterbus Daten empfangen
//------------------------------------------------------------------------------------------------------------------------------------------------

void ReceiveMasterBus()  {

         // if (bDS == true) Serial.print(" and length ");
         // if (bDS == true) Serial.println(packet);


   packet = CAN.parsePacket();                               // try to parse packet
   
   if (packet > 0) {                              // received a packet
      //if (bDS == true) Serial.println (packet);
      //if (bDS == true) Serial.print(CAN.packetId(),HEX);
      //if (bDS == true) Serial.print(" ");

      canString[num] = ("R: .");                             // String zum plotten Can Traffic

      String part (CAN.packetId(), HEX);
      canString[num] += part;
      canString[num] += (" ");
      canString[num] += packet;
      canString[num] += (" ");

    
      deviceUniqueId = (CAN.packetId() & 0x7FFF0000) >> 16;
         //if (bDS == true) Serial.print("deviceUniqueId: "); 
         //if (bDS == true) Serial.print(deviceUniqueId, HEX); 
     
      deviceKindId = (CAN.packetId() & 0x0000FFFF);          // Geräte ID --> CombiMaster
         //if (bDS == true) Serial.print("deviceKindId: "); 
         //if (bDS == true) Serial.println(deviceKindId, HEX);      
      
      bIRequest = (CAN.packetId() & 10000000) != 0;

      while (CAN.available()) {       
         for (int32_t count = 0; count < packet; count++) {
            // if (count > 7) break;                         //maximale Größe array count
            RxBuf[count] = CAN.read();
               //if (RxBuf[count] < 0x10) {
               //  if (bDS == true) Serial.print ("0");      // führende 0 bei Werten kleiner 0x10
                // }
               //if (bDS == true) Serial.print (RxBuf[count], HEX);
               //if (bDS == true) Serial.print (" ");
               String part (RxBuf[count], HEX);
               if (RxBuf[count] < 0x10) canString[num] += ("0");      // führende 0 bei Werten kleiner 0x10
               canString[num] += part;
               if (count < (packet-1)) canString[num] += (" : ");
               
         }
         
         if (bProtocol == true) num++;
         if (num >= numMax) {
            num = 20;
            bProtocol = false;
         }
         
         //if (bDS == true) Serial.print ("\t");                                 // Tab

         inValue_f = decodeIEEE754(&RxBuf[2]);   //  00 01 02 03 04 05 Adresse von array RxBuf[2] wird an Funktion übergeben
         // if (bDS == true) Serial.print("float: ");
         //if (bDS == true) Serial.println(inValue_f);
      }

      // Datenfeld bei Parameteranfrage:
      // 0 - 8 byte möglich
      // 0 = Nummrer des angefragten Parameters
      // 1 =
      // 2 = Wert des angefragten Parameters als IEEE754 Float, byte 0 (niederwertiges
      // 3 = byte 1
      // 4 = byte 2
      // 5 = byte 3 (höherwertiges)
      // 6 =
      // 7 =      

 
      if (deviceKindId == CombiMasterID)  {               // listen CombiMaster      
      
      // Page Monitoring :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::         
      
         if (deviceUniqueId == PageMonitoring)  {
            if (RxBuf[0] == MBDeviceState)  {
               MbDeviceStateIst = (uint8_t) inValue_f;                                     
            }
            if (RxBuf[0] == MBChargerState)  {
               MbChargerStateIst = (uint8_t) inValue_f; 
            }
            if (RxBuf[0] == MBAcInputVoltage)  {
               MBAcInputVoltage_f = inValue_f; 
            }
            if (RxBuf[0] == MBAcInputCurrent)  {
               MBAcInputCurrent_f = inValue_f; 
            }
            if (RxBuf[0] == MBAcInputFrequency)  {
               MBAcInputFrequency_f = inValue_f; 
            }
            if (RxBuf[0] == MBAcOutVoltage)  {
               MBAcOutVoltage_f = inValue_f; 
            }
            if (RxBuf[0] == MBAcOutFrequency)  {
               MBAcOutFrequency_f = inValue_f; 
            }
            if (RxBuf[0] == MBAcOutPowerW)  {
               MBAcOutPowerW_f = inValue_f; 
            }
            if (RxBuf[0] == MBAcOutPower2)  {
               MBAcOutPower2_f = inValue_f; 
            }
            if (RxBuf[0] == MBAuslastung)  {
               MBAuslastung_f = inValue_f; 
            }
            if (RxBuf[0] == MBAcNC3)  {
               MBAcNC3_f = inValue_f;
            }
            if (RxBuf[0] == MBBatterieVoltage)  {
               MBBatterieVoltage_f = inValue_f; 
            }
            if (RxBuf[0] == MBBatterieCurrent)  {
               MBBatterieCurrent_f = inValue_f; 
            }
            if (RxBuf[0] == MBFuseSettingACinput)  {
               MBFuseSettingACinput_f = inValue_f; 
               AcFuseSettingIst = (uint8_t) inValue_f;
            }
            if (RxBuf[0] == MBInverterOnOff)  {
               MbInverterOnOffIst = (uint8_t) inValue_f;
            }    
            if (RxBuf[0] == MBInverterPreOff)  {
               MbInverterPreOffIst = (uint8_t) inValue_f;
            }
            if (RxBuf[0] == MBChargerOnOff)  {
               MbChargerOnOffIst = (uint8_t) inValue_f;
               wert_f = inValue_f;
            }    
            if (RxBuf[0] == MBChargerPreOff)  {
               MbChargerPreOffIst = (uint8_t) inValue_f;
               wert_f = inValue_f;
            }
         }
         
         // Page Alarme :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::         

         if (deviceUniqueId == PageAlarm)  {
            if (RxBuf[0] == ALTemperatureHigh)  {
               ALTemperatureHigh_f = inValue_f; 
            }
            if (RxBuf[0] == ALFanError)  {
               ALFanError_f = inValue_f; 
            }
            if (RxBuf[0] == ALDChighVoltage)  {
               ALDChighVoltage_f = inValue_f; 
            }
            if (RxBuf[0] == ALDClowVoltage)  {
               ALDClowVoltage_f = inValue_f; 
            }
            if (RxBuf[0] == ALOverload)  {
               ALOverload_f = inValue_f; 
            }
            if (RxBuf[0] == ALDeviceError)  {
               ALDeviceError_f = inValue_f; 
            }
            if (RxBuf[0] == ALACinError)  {
               ALACinError_f = inValue_f; 
            }         
         }

         // Page History :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::         

      
         if (deviceUniqueId == PageHistory)  {
            if (RxBuf[0] == HIinverterRunTime)  {
               HIinverterRunTime_f = inValue_f; 
            } 
            if (RxBuf[0] == HIchargerRunTime)  {
               HIchargerRunTime_f = inValue_f; 
            }
         }

         // Page Config :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::         


         if (deviceUniqueId == PageConfig)  {          
            if (RxBuf[0] == CFBatterieType)  {
               CfBatterieTypeIst = (uint8_t) inValue_f;
            }
            if (RxBuf[0] == CFInverterEnergySave)  {
               CfInverterEnergySaveIst = (uint8_t) inValue_f;
            }
            if (RxBuf[0] == CFACinputSupportMode)  {
               CfACinputSupportModeIst = (uint8_t) inValue_f;
            }
            if (RxBuf[0] == CFmaxChargeCurrent)  {
               CfMaxChargeCurrentIst = (uint8_t) inValue_f;
            }
            if (RxBuf[0] == CFLandstrom)  {
               CfLandstromIst = (uint8_t) inValue_f;
            }
            if (RxBuf[0] == CFInverterLowVoltON)  {
               CFInverterLowVoltON_f = inValue_f; 
            }
            if (RxBuf[0] == CFInverterLowVoltOFF)  {
               CFInverterLowVoltOFF_f = inValue_f; 
            }
         }
      }
   }
}
 
void canPageCommand(uint32_t PageRequestID, uint8_t Parameter, uint8_t byte5, uint8_t byte4, uint8_t byte2) {   
                                            // byte 5 und byte 4 sind die höherwertgen bytes des
                                            // über MasterBus übertragenen float Wertes
   
      canString[num] = ("S: ");         // String zum plotten Can Traffic
      String part (PageRequestID, HEX);
      canString[num] += part;
      canString[num] += (" 6 ");      
      
      String part2 (Parameter, HEX);
      if (Parameter < 0x10) canString[num] += ("0");      // führende 0 bei Werten kleiner 0x10     
      canString[num] += part2;     
      
      canString[num] += (" : 00 : ");
      
       String part3 (byte2, HEX);
      if (byte2 < 0x10) canString[num] += ("0");      // führende 0 bei Werten kleiner 0x10         
      canString[num] += part3;
      
      canString[num] += (" : 00 : ");
      
      
      String part4 (byte4, HEX);
      if (byte4 < 0x10) canString[num] += ("0");      // führende 0 bei Werten kleiner 0x10         
      canString[num] += part4;
      canString[num] += (" : ");
      
      String part5 (byte5, HEX);
      if (byte5 < 0x10) canString[num] += ("0");      // führende 0 bei Werten kleiner 0x10              
      canString[num] += part5;
      
       if (bProtocol == true) num++;
       if (num >= numMax) {
          num = 20;
          bProtocol = false;
       }
   
   CAN.beginExtendedPacket(PageRequestID,6);  
   CAN.write(Parameter);                    // Adresse Parameter                     
   CAN.write(0x00);      
   CAN.write(byte2);
   CAN.write(0x00);          
   CAN.write(byte4);                        // Value
   CAN.write(byte5);  
   CAN.endPacket();
   delay(1); 
}

void transmit6C1B() { 
     
   CAN.beginExtendedPacket(MonitorDeviceID,0);
   CAN.endPacket(); 
   delay(1); 
}

void transmitMonitoring(uint8_t MsgBlock)  {
      unsigned char stmp;    
      switch (MsgBlock)
      {
        case 0:
        stmp = MBDeviceState;
        break;
        case 1:
        stmp = MBChargerState;
        break;
        case 2:
        stmp = MBAcInputVoltage;
        break;
        case 3:
        stmp = MBAcInputCurrent;
        break;
        case 4:
        stmp = MBAcInputFrequency;
        break;
        case 5:
        stmp = MBAcOutVoltage;
        break;
        case 6:
        stmp = MBAcOutFrequency;
        break;
        case 7:
        stmp = MBAcOutPowerW;
        break;
        case 8:
        stmp = MBAcOutPower2;
        break;
        case 9:
        stmp = MBAuslastung;
        break;
        case 10:
        stmp = MBAcNC3;
        break;
        case 11:
        stmp = MBBatterieVoltage;
        break;
        case 12:
        stmp = MBBatterieCurrent;
        break;
        case 13:
        stmp = MBFuseSettingACinput;
        break;
        case 14:
        stmp = MBInverterOnOff;
        break;
        case 15:
        stmp = MBInverterPreOff;
        break;        
        case 16:
        stmp = MBChargerOnOff;
        break;
        case 17:
        stmp = MBChargerPreOff;
        break;        

        default:
        break;
      }
      
      canPageRequest(PageMonRequestID, stmp, 0x00);
}

void transmitError(uint8_t MsgBlock)  {
      unsigned char stmp;    
      stmp = MsgBlock;

      canPageRequest(PageAlarmRequestID, stmp, 0x00);
}

void transmitHistory(uint8_t MsgBlock)  {
      unsigned char stmp;    
      stmp = MsgBlock;

      canPageRequest(PageHistoryRequestID, stmp, 0x00);
}


void transmitConfig(uint8_t MsgBlock)  {
      unsigned char stmp;    
      switch (MsgBlock)
      {
        case 0:
        stmp = CFInverterEnergySave;
        break;
        case 1:
        stmp = CFACinputSupportMode;
        break;
        case 2:
        stmp = CFmaxChargeCurrent;
        break;
        case 3:
        stmp = CFBatterieType;
        break;
        case 4:
        stmp = CFAValueConstVoltage;
        break;
        case 5:
        stmp = CFmaxValueAC_IN_Limit;
        break;
        case 6:
        stmp = CFLandstrom;
        break;
        case 7:
        stmp = CFInverterLowVoltON;
        break; 
        case 8:
        stmp = CFInverterLowVoltOFF;
        break; 
        
        default:
        break;
      }
      canPageRequest(PageConfigRequestID, stmp, 0x00);
}


void canPageRequest(uint32_t PageRequestID, uint8_t Byte0, uint8_t Byte1)  {
   
      if (PageRequestID == PageAlarmRequestID) {  // Erstellen String zum plotten Can Traffic
         canString[num] = ("S: 0");               // bei Alarm Page führende 0 einfügen, wegen besserer Lesbarkeit beim mitschreiben
      }
      else canString[num] = ("S: "); 
      String part (PageRequestID, HEX);
      canString[num] += part;
      canString[num] += (" 2 "); 
      String part2 (Byte0, HEX);
      if (Byte0 < 0x10) canString[num] += ("0");      // führende 0 bei Werten kleiner 0x10        
      canString[num] += part2;
      canString[num] += (" : 00     ");
      
      if (bProtocol == true) num++;
      if (num >= numMax) {
         num = 20;
         bProtocol = false;
      }  



     
      CAN.beginExtendedPacket(PageRequestID,2);   // 0x1 steht für eine Anfrage, 
                                               // 0xB98 = PageConfigRequest
                                               // 0xBC5D Geräte ID CombiMaster
      CAN.write(Byte0);                        
      CAN.write(Byte1);      
      CAN.endPacket();  
      delay(1);
}

float decodeIEEE754(uint8_t *data)  {
   uint32_t temp = ((uint32_t)data[3]<< 24 | (uint32_t)data[2]<< 16 | (uint32_t)data[1]<< 8 | (uint32_t)data[0]);
   //float inValue_f = *(float*)&temp;
   return *(float*)&temp;
}

void encodeIEEE754(float f, byte* hex) {
   byte* f_byte = reinterpret_cast<byte*>(&f);
   memcpy(hex, f_byte, 4);
}

//-----------------------------------------------------------------------------------------------------------------
//--Funktion Real Time Clock --
//-----------------------------------------------------------------------------------------------------------------

// aus https://randomnerdtutorials.com/esp32-date-time-ntp-client-server-arduino/
// aus https://www.proggen.org/doku.php?id=c:lib:time:struct_tm

// struct tm ist definiert in der time, die in C über #include "time.h"  eingebunden wird.
// struct tm speichert einen Zeitstempel und zusätzliche Metainformationen in einer Struktur. 
// Dabei werden die einzelnen Informationen des Zeitstempels nicht wie bei time_t in komprimierter Form gehalten, 
// sondern in einzelne Komponenten (Stunde, Minute, Sekunde) unterteilt gespeichert. 
// struct tm enthält neun Einträge vom Typ int, wobei die Reihenfolge innerhalb der Struktur nicht festgelegt ist.:

// struct tm
// {
//   int tm_sec;
//   int tm_min;
//   int tm_hour;
//   int tm_mday;
//   int tm_mon;
//   int tm_year;
//   int tm_wday;
//   int tm_yday;
//   int tm_isdst;  // Sommerzeit Flag; Das Sommerzeit-Flag (tm_isdst) ist größer als 0, wenn es zurzeit Sommerzeit ist. Im Falle von Winterzeit ist es genau 0. 
                    // Ist es kleiner als 0, so ist die Information nicht verfügbar. 
// };
 
void getTime(){

   time_t now;                      // time_t speichert einen Zeitstempel, welcher in ganzen Sekunden beginnend mit dem 1. Januar 1970, 
                                    // 00:00:00 UTC gezählt wird. time_t entspricht in der Regel dem Datentyp int, ist also 32 Bit breit. 
                                    // (ESP32 64 bit!!!11)
                                    // time_t entspricht somit dem Unix-Timestamp. 
                                    // Mit time_t now; wird eine Variable "now" zum Abspeichern der Zeit erstellt. (64 bit) 
                                      
   struct tm *myTm;                 // erstelle zeiger auf struct tm

                                   
   time(&now);                      // Die Funktion time() gibt die aktuelle Systemzeit als time_t-Objekt zurück. 
                                    // Wenn das Argument nicht NULL ist, dann wird der Wert auch in das angegebene time_t-Objekt geschrieben. 
                                    // now enthält ohne Verbindung zum Internet jetzt die aktuelle Systemzeit, 0 Sekunden ab 1. Jan. 1970   00:00:00,
                                    // + / - die in der Konfiguration angegebene Sekunden für die Zeitzone.
                                    // Besteht eine Verbindung zum Internet holt time jetzt die aktuelle Zeit vom NTP Server nach "now" 
                                    // Besteht später keine Verbindung mehr, so bleibt die Systemzeit trotzdem korrekt, ohne Synchronisierung.

 
   myTm = localtime(&now);          // Übersetzt einen Zeitstempel (time_t) in eine tm-Struktur in die lokale Zeitzone. 
                                    // und füllt struct tm mit dem Zeitstempel aus "now" 


  // if (now == ((time_t)-1))   {    
  //    Debug_println("Zeit konnte nicht geholt werden");
  // }

 
   strftime(TimeDateComplete_char20,20, "%d.%m.%Y %X",myTm); //  01.02.2022 00:00:00  
          // strftime ist definiert in der time, die in C über #include "time.h"  eingebunden wird.
          // strftime Erzeugt einem char String aus einzelnen char. Die einzelnen Zeitmodule werden aus der Structur tm entnommen.
          // Für jedes Zeitmodul gibt es ein Zeichen welches mit % beginnt.
          // Der Formatstring darf beliebige Zeichen enthalten. Zeichen, die mit einem % beginnen sind Sonderzeichen und werden entsprechend 
          // der Tabelle --> siehe https://www.proggen.org/doku.php?id=c:lib:time:strftime     

   // Debug_print("Zeit aus Funktion: ");
   // Debug_println(TimeDateComplete_char20);
   
   //TimeStunde = myTm->tm_hour;      // Stunde als int extrahieren
   TimeSZ =  myTm->tm_isdst;        // Sommer oder Winterzeit (siehe oben)


   if ((TimeSZ == 0) && (gmtOffset_sec == 3600)) Zeitzone = "UTC+1";
   if ((TimeSZ == 0) && (gmtOffset_sec == 7200)) Zeitzone = "UTC+2"; 
   if ((TimeSZ == 1) && (gmtOffset_sec == 3600)) Zeitzone = "UTC+2";
   if ((TimeSZ == 1) && (gmtOffset_sec == 7200)) Zeitzone = "UTC+3"; 
}
1 Like

Hello @libera I would be interested to hear more about your project, I am looking to integrate an ESP32 with a Mastervolt Battery Charger. Is there a GitHub link to your project or any other material you found that might be useful?

Thanks

Hello,
if you send me a real email address I will send you all information I have.
greetings,
Manfred

Entschuldigung, ich spreche kein Deutsch.That code looks great and well written. It appears you have done this many times before. I will try to translate this weekend and see what I can make of it. Can you give the make and model of your Mastervolt device. I am use to Cory J Fowler's library. It is similar to something I am working on but all the targets are my design. More as I get into it.

If you could send it to tristan@metrixadvancedtechnologies.com, that would be amazing, thanks.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.