Projektvorstellung Womo-Monitor

Danke.

Wirste noch erfahren.

Auf was fürn Controller läuft das?

Benutzt wird ein 7" Touch-Display mit einem SSD1963 Treiber
Touchtreiber ist ein TF5206

Falls du die angepasste UTFT und die TF6206 auch noch brauchst, kann ich dir die auch noch zippen

Ne, was für ein Board benutzt Du? Nen UNO dürfte das ja nicht sein...

ach soooo....ein mega 2560 mit shield

Bin mir grad nicht sicher, aber ich glaube die BME280 könnte sogar im Manager drin sein

Hast Du unter - DATEI - VOREINSTELLUNGEN
das so eingestellt?
grafik
Dann kompiliere mal und scroll mal durch die Meldungen unten.
Warnungen sind das nicht ganz ohne Grund.

Bei mir kompiliert der recht moderat:

Der Sketch verwendet 140498 Bytes (55%) des Programmspeicherplatzes. Das Maximum sind 253952 Bytes.
Globale Variablen verwenden 4237 Bytes (51%) des dynamischen Speichers, 3955 Bytes für lokale Variablen verbleiben. Das Maximum sind 8192 Bytes.

Na mal sehen, ob sich irgendwas noch findet.

diese Meldung kommt wenn ichs nur kompiliere:

C:\Users\rolik\Documents\Arduino\libraries\UTFT_Geometry\UTFT_Geometry.cpp:26:0: warning: "swap" redefined
 #define swap(a, b) { int16_t t = a; a = b; b = t; }
 
In file included from C:\Users\rolik\Documents\Arduino\libraries\UTFT/UTFT.h:161:0,
                 from C:\Users\rolik\Documents\Arduino\libraries\UTFT_Geometry\UTFT_Geometry.h:28,
                 from C:\Users\rolik\Documents\Arduino\libraries\UTFT_Geometry\UTFT_Geometry.cpp:23:
C:\Users\rolik\Documents\Arduino\libraries\UTFT/hardware/avr/HW_AVR_defines.h:19:0: note: this is the location of the previous definition
 #define swap(type, i, j) {type t = i; i = j; j = t;}

Beim Hochladen geht er ohne Warnung durch

Prozente Decken sich mit Deinen

Der Sketch verwendet 140402 Bytes (55%) des Programmspeicherplatzes. Das Maximum sind 253952 Bytes.
Globale Variablen verwenden 4237 Bytes (51%) des dynamischen Speichers, 3955 Bytes für lokale Variablen verbleiben. Das Maximum sind 8192 Bytes.

@my_xy_projekt
kurzes update: in der Nacht gab es einen Neustart. Ich werde mal die Ausgaben auf den Seriellen Monitor auf Serial2 umleiten (für die Verzögerung) und auf serial1 alle 5 minuten eine Ausgabe machen, damit ich ungefähr sehe, wann neu gestartet wird.

1 Like

so...
Ich hab gestern abend noch die Hände über den Kopf zusammengeschlagen.
Ich werd Dir auch erklären warum.
Aber erst will ich die Ausgabe sehen, die folgender Schnipsel ausgibt.

Da kommt ne ganze Menge auf dem SerMon.
Du wirst aber sehen, wo der spannende Teil anfängt.
Wo er aufhört, ergibt sich von selbst :slight_smile:
Und dann schreib mal dazu, wie Dein Zeitbudget heute aussieht. Wenn ich das anfasse, will (nein muss) es heute fertig werden.
Das Du eine zusätzliche lib brauchst, steht im Code.

// Forensketch 2x Solarregler auslesen 
// https://forum.arduino.cc/t/projektvorstellung-womo-monitor/1162849/109

#include <Streaming.h>     // https://github.com/janelia-arduino/Streaming

constexpr byte solReg {2};
uint8_t umlauf[solReg] {0};
uint32_t startTime[solReg] {0};
uint32_t stopTime[solReg] {0};

void setup()
{
  Serial.begin(115200);
  Serial1.begin(19200);
  Serial3.begin(19200);
  delay(500);
  Serial << (F("\r\nStart...\r\n")) << endl;
}

void  loop()

{getSolar();}

void splitSolar(const byte regler, const char myChar, char *myBuf, byte &idx, const byte maxIdx)
{
  if (isControl(myChar) && myChar != '\t')
  {
    char *label = strtok(myBuf, "\t");
    char *value = strtok(NULL, "\t");
    Serial << regler << ':' << label << ':' << value << endl;
    if (!strcmp(label, "PID"))
    {
      //strcpy(solarWerte[regler].PID, value);
      umlauf[regler]++;
      if (umlauf[regler] == 3)
      {
        Serial << (F("Start Aufzeichnung \r\n *********************")) << endl;
        startTime[solReg] = millis();
      }
      if (umlauf[regler] == 4)
      {
        stopTime[solReg] = millis();
      }
      if (umlauf[0] > 3 && umlauf[1] > 3)
      {
        for (byte b = 0; b < solReg; b++)
        {
          Serial << (F("Umlaufzeit (start/stop/umlauf): "));
          Serial << startTime[b] << ' ' << stopTime[b] << ' ' << stopTime[b] - startTime[b] << endl;
        }
        Serial << (F("STOP - Neustart mit RESET"));
        while (1);
      }
    }
  }
  else if (idx < maxIdx)
  {
    myBuf[idx] = myChar;
    idx++;
  }
}


void getSolar()
{
  constexpr byte maxIdx {40};
  char myChar = '\0';
  static char myBuf[solReg][maxIdx + 1] = {'\0'};
  static byte idx[solReg] = {0};
  if (Serial1.available())
  {
    myChar = Serial1.read();
    splitSolar(0, myChar, myBuf[0], idx[0], maxIdx);
  }
  if (Serial3.available())
  {
    myChar = Serial3.read();
    splitSolar(1, myChar, myBuf[1], idx[1], maxIdx);
  }
}

Ab ca.15.00 uhr hätte ich 2 Stunden Kapazität

Die nehm ich.
Gibt es für die Solarregler ne Bezeichnung, das ich mal nach ner Doku / Feldbbeschreibeung suchen kann?
Bis dahin hab ich dann auch Deinen Sketch durchforstet...

vieles findest Du hier unter Punkt 4.2

https://www.victronenergy.com/live/open_source:start

Die Regler selber sind Victron Smart Solar MPPT 75/15

Was für dich allerdings eher was hilft ist das Ve.Direct Protokoll

1 Like

Ich hab noch nirgend eine Datensatzbeschreibung gefunden, was da tatächlich auf der Schnittstelle passiert.
aber hier: https://github.com/karioja/vedirect/blob/master/vedirect/vedirectsim.py scheint ein Simulationsfile drin zu sein, welches den Inhalt einer Übertragung abbildet.
Dann sieht das aber ganz anders aus...
Nehmen wir mal die ':' als delemiter für das Array, dann gibt es da noch mehr Werte und wichtig - und was ich eigentlich tatsächlich vermisst hatte - eine Checksum!
Und genau die brauchst Du auch.

Kannst ja trotzdem mal den Teil oben einspielen, ob da was raus kommt. Ich bau aber nachher noch was dran rum.
@agmue kannst Du mal da in den link schauen? Das werden wir brauchen. Ich stammel mir da was zusammen, aber py ist nicht meins.

Das kommt raus, wenn ich am Serial3 einen Laderegler dran habe (hab leider nur einen zum testen, da die anderen beiden im womo sind)
Al simulatoren habe ich 2 Nanos dran, die einfach die Daten senden, damit ich sehe was angezeigt wird.

14:56:31.115 -> 
14:56:31.115 -> Start...
14:56:31.115 -> 
14:56:31.115 -> 1::
14:56:31.115 -> 1::
14:56:31.115 -> 1:PID:0xA060
14:56:31.115 -> 1:PID:
14:56:31.115 -> 1:PID:
14:56:31.115 -> Start Aufzeichnung 
14:56:31.115 ->  *********************
14:56:31.115 -> 1:PID:
14:56:31.115 -> 1:PID:
14:56:31.115 -> Start Aufzeichnung 
14:56:31.162 ->  *********************
14:56:31.162 -> 1:PID:
14:56:31.162 -> 1:PID:
14:56:31.162 -> Start Aufzeichnung 
14:56:31.162 ->  *********************
14:56:31.162 -> 1:PID:
14:56:31.162 -> Start Aufzeichnung 
14:56:31.162 ->  *********************
14:56:31.162 -> 1:PID:
14:56:31.162 -> Start Aufzeichnung 
14:56:31.162 ->  *********************
14:56:31.162 -> 1:PID:
14:56:31.162 -> Start Aufzeichnung 
14:56:31.162 ->  *********************
14:56:31.162 -> 1:PID:
14:56:31.162 -> Start Aufzeichnung 
14:56:31.162 ->  *********************
14:56:31.861 -> 1:PID:
14:56:31.861 -> Start Aufzeichnung 
14:56:31.861 ->  *********************
14:56:31.861 -> Umlaufzeit (start/stop/umlauf): 1207 0 4294966089
14:56:31.861 -> Umlaufzeit (start/stop/umlauf): 0 0 0
14:56:31.861 -> STOP - Neustart mit RESET

Die Ausgelesene PID stimmt schonmal. Das Testgerät ist ein Smart Solar 100/20 (0xA060, laut Protokoll)
Ich habe das allerdings mit meinem Sketch getestet. Da kommen zuverlässig die richtigen Daten an. Das ist ja auch nicht das primäre Problem, da ich nicht denke, dass dieses für die Neustarts verantwortlich ist, da die Funktion im normalen Umlauf des Loops nicht aufgerufen wird.
Es erscheint mir wirklich so, als ob etwas zu schnell läuft, wenn der Serielle Monitor nix ausgibt. Seit dem Neustart in der Nacht ist es sauber durchgelaufen.

vielleicht hilft dir das noch

Checksum calculation

In der Hoffnng, das der Regler das gleiche ausgibt.

Doch das ist ein Problem.
Du holst die Daten nicht auf der Schnittstelle ab wie sie anfallen, sondern liest den Schnittstellenpuffer aus, wenn Du denkst das Du die Daten brauchst.

Du hast z.B. sowas hier:

void drawSolarScreen()//Solar gesamt
{
  radius = 120;
  down = 0;
  for (byte r = 0; r < 10; r++)
  {
    getSolar200();
    getSolar160();
  }

Damit liest Du 10x aus.

Was glaubst Du, wird von dem hier:

void getSolar200()
{
  if (Serial3.available() > 10) {
    label200 = Serial3.readStringUntil('\t');
    val200 = Serial3.readStringUntil('\n');
    if (label200 == "PID") {
      PID200 = val200;
    } else if (label200 == "FW") {
      FW200 = val200;
    } else if (label200 == "SER#") {
      SER200 = val200;
    } else if (label200 == "V") {
      float temp = val200.toFloat();
      temp = temp / 1000;
      V200 = temp;
    } else if (label200 == "I") {
      float temp = val200.toFloat();
      temp = temp / 1000;
      Ib200 = temp;
    } else if (label200 == "VPV") {
      float temp = val200.toFloat();
      temp = temp / 1000;
      VPV200 = temp;
    } else if (label200 == "PPV") {
      PPV200 = val200.toFloat();
    } else if (label200 == "CS") {
      CS200 = val200.toInt();
    } else if (label200 == "MPPT") {
      MPPT200 = val200;
    } else if (label200 == "OR") {
      OR200 = val200;
    } else if (label200 == "ERR") {
      ERR200 = val200.toInt();
    } else if (label200 == "LOAD") {
      LOAD200 = val200.toFloat();
    } else if (label200 == "IL") {
      IL200 = val200.toFloat();
    } else if (label200 == "H19") {
      int temp = val200.toInt();
      temp = temp / 10;
      H19200 = temp;
    } else if (label200 == "H20") {
      int temp = val200.toInt();
      temp = temp / 10;
      H20200 = temp;
    } else if (label200 == "H21") {
      float temp = val200.toInt();
      temp = temp / 1000;
      H21200 = val200.toInt();
    } else if (label200 == "H22") {
      int temp = val200.toInt();
      temp = temp / 10;
      H22200 = temp;
    } else if (label200 == "H23") {
      float temp = val200.toInt();
      temp = temp / 1000;
      H23200 = val200.toInt();
    } else if (label200 == "HSDS") {
      float temp = val200.toInt();
      temp = temp / 100;
      HSDS200 = val200.toInt();
    }
  }
}

abgearbeitet.
Zusatzfrage: Was passiert mit den restlichen Werten, die der Solarregler sendet?

Ich kann Dir das beantworten.
Nachdem dein if durchlaufen ist UND tatsächlich jedesmal auch mehr als 10 bytes im Empfangspuffer sind, kommt der letzte Wert vielleicht bei OR raus. Dann ist Schluss.
Wird der case danach verlassen, kommst Du da auch nicht mehr hin.

Alle weiteren Daten, die dann auf der seriellen Schnittstelle ankommen, wenn - natürlich - der Empfangspuffer voll ist, werden verworfen. Bis das erste Byte wieder frei wird.
Und wenn Du Pech hast, hast Du dann zweimal ein tab in dem Satz oder what ever. Oder Spassenshalber erwartest Du die PID bekommst aber die SER - Nur der Einfachheit mal abgekürzt.

Du weisst also gar nicht, was Du da bekommst.
Und aufgrund des Stringkonstrukts ist das nicht einmal über die Länge gesichert.

Das macht dann noch sein übriges.
Es entstehen Speicherprobleme
Und Du liest einen mehr als 16000 Zeichen umfassendes uint16_t als Grafik ein.
Da reichen ein paar Fragmente und schon überschreibst Du verwendeten Speicher.
DingDong.

Las den mal laufen.
Ich hab das jetzt auf einen Regler runtergekürzt, der an Serial1 dran hängt. Notfalls umschreiben.

// Forensketch
// https://forum.arduino.cc/

#include <Streaming.h>     // https://github.com/janelia-arduino/Streaming


uint8_t umlauf;
uint32_t startTime;
uint32_t stopTime;

void setup()
{
  Serial.begin(115200);
  Serial1.begin(19200);
  delay(500);
  Serial << (F("\r\nStart...\r\n")) << endl;
}

void  loop()

{getSolar();}

void splitSolar(const char myChar, char *myBuf, byte &idx, const byte maxIdx)
{
  if (isControl(myChar) && myChar != '\t')
  {
    char *label = strtok(myBuf, "\t");
    char *value = strtok(NULL, "\t");
    Serial << label << ':' << value << endl;
    memset(myBuf, '\0', maxIdx);
    idx = 0;
    if (!strcmp(label, "PID"))
    {
      //strcpy(solarWerte[regler].PID, value);
      umlauf++;
      if (umlauf == 3)
      {
        Serial << (F("Start Aufzeichnung \r\n *********************")) << endl;
        startTime = millis();
      }
      if (umlauf == 4)
      {
        stopTime = millis();
      }
      if (umlauf > 3)
      {
        Serial << (F("Umlaufzeit (start/stop/umlauf): "));
        Serial << startTime << ' ' << stopTime << ' ' << stopTime - startTime << endl;
      }
      Serial << (F("STOP - Neustart mit RESET"));
      while (1);
    }
  }
  else if (idx < maxIdx)
  {
    myBuf[idx] = myChar;
    idx++;
  }
}


void getSolar()
{
  constexpr byte maxIdx {40};
  char myChar = '\0';
  static char myBuf[maxIdx + 1] = {'\0'};
  static byte idx = 0;
  if (Serial1.available())
  {
    myChar = Serial1.read();
    splitSolar(myChar, myBuf, idx, maxIdx);
  }
}

In der Hoffnung nix vergessen zu haben...

15:58:51.168 -> PID:0xA060
15:58:51.168 -> :
15:58:51.168 -> FW:161
15:58:51.168 -> :
15:58:51.216 -> SER#:HQ2309FYEXK
15:58:51.216 -> :
15:58:51.216 -> V:12010
15:58:51.216 -> :
15:58:51.216 -> I:-30
15:58:51.216 -> :
15:58:51.216 -> VPV:10
15:58:51.216 -> :
15:58:51.216 -> PPV:0
15:58:51.216 -> :
15:58:51.216 -> CS:0
15:58:51.216 -> :
15:58:51.216 -> MPPT:0
15:58:51.216 -> :
15:58:51.216 -> OR:0x00000005
15:58:51.216 -> :
15:58:51.216 -> ERR:0
15:58:51.216 -> :
15:58:51.263 -> LOAD:ON
15:58:51.263 -> :
15:58:51.263 -> IL:0
15:58:51.263 -> :
15:58:51.263 -> H19:0
15:58:51.263 -> :
15:58:51.263 -> H20:0
15:58:51.263 -> :
15:58:51.263 -> H21:0
15:58:51.263 -> :
15:58:51.263 -> H22:0
15:58:51.263 -> :
15:58:51.263 -> H23:0
15:58:51.263 -> :
15:58:51.263 -> HSDS:0
15:58:51.263 -> :
15:58:52.162 -> Checksum:⸮
15:58:52.162 -> :
15:58:52.210 -> PID:0xA060
15:58:52.210 -> :
15:58:52.210 -> FW:161
15:58:52.210 -> :
15:58:52.210 -> SER#:HQ2309FYEXK
15:58:52.210 -> :
15:58:52.210 -> V:12010
15:58:52.210 -> :
15:58:52.210 -> I:-30
15:58:52.210 -> :
15:58:52.210 -> VPV:10
15:58:52.210 -> :
15:58:52.210 -> PPV:0
15:58:52.210 -> :
15:58:52.210 -> CS:0
15:58:52.210 -> :
15:58:52.210 -> MPPT:0
15:58:52.210 -> :
15:58:52.210 -> OR:0x00000005
15:58:52.210 -> :
15:58:52.257 -> ERR:0
15:58:52.257 -> :
15:58:52.257 -> LOAD:ON
15:58:52.257 -> :
15:58:52.257 -> IL:0
15:58:52.257 -> :
15:58:52.257 -> H19:0
15:58:52.257 -> :
15:58:52.257 -> H20:0
15:58:52.257 -> :
15:58:52.257 -> H21:0
15:58:52.257 -> :
15:58:52.257 -> H22:0
15:58:52.257 -> :
15:58:52.257 -> H23:0
15:58:52.257 -> :
15:58:52.257 -> HSDS:0
15:58:52.257 -> :
15:58:53.159 -> Checksum:⸮
15:58:53.159 -> :

die vielen "0" erklären sich damit, dass keine PV dran ist. Die Batterie Voltage stimmt auf jeden Fall. Seriennummer stimmt auch.

Ich bin fasziniert

Nicht schlecht.
Also richtig - es kommt \r\n und ich werte beide aus, müsste auf \n matchen. Ist eine Änderung.
Hm... Jetzt muss ich nur durch die Checksum durch.
Das mit den 0 ist mir egal - Ich seh ja, was da kommt, da ich nicht auf einen Wert warte, sondern alles nehme, was da anfällt. Und da sieht man dann auch die checksum :slight_smile:

Das ist natürlich so nicht schick, weil ich natürlich von anderen Voraussetzungen ausgegangen bin. Damit ist mein gesamtes Konstrukt hinfällig.
Den muss ich nun noch ausbauen.
Aber nur um Dir mal zu zeigen, wie Dein solar-tab jetzt aussieht und genau das gleiche (falsche) macht:


constexpr byte solReg {2};
constexpr char solRegler[][solReg] = {"160", "200"};
struct SOLARWERTE
{
  char *PID;         //Produkt-ID
  char *FW;          //Firmware Version
  char *SER;         //Seriennummer
  /*float*/ char *V;          //[V]   Batteriespannung
  /*float*/ char *Ib;         //[mA]  Batterieladestrom
  /*float*/ char *VPV;        //[V] Solar-Panel Spannung
  /*float*/ char *PPV;        //[W] Solar-Panel Leistung
  /*byte*/ char *CS;          //Charger-Status 0=Aus, 2=Error, 3=Laden, 4=Entladen, 5=Batt V halten
  char *MPPT;        //Unbekannter Wert
  char *OR;          //Unbekannter Wert
  /*byte*/ char *ERR;         //Error-Status 0=Kein Error, 2=Batt V zu hoch, 17=Temp zu hoch, 18=Überstrom, 19=Amp umgekehrt, 20=Ladezeitlimit abgelaufen, 21=Amp Sensor Fehler, 26=Anschlüsse überhitzt, 33=Solar V zu hoch, 34=Solar A zu hoch, 38=Input abgeschaltet, 116=WerkseinstellungenDatenVerloren, 117=Falsche Firmware, 119=Einstellungen falsch
  /*float*/ char *LOAD;       //Lastausgang ON/OFF
  /*float*/ char *IL;         //[mA] Lastausgang-Strom
  /*float*/ char *H19;        //[kWh] Ertrag über die gesamte Lebensdauer des Gerätes
  /*int*/ char *H20;          //[kWh] Ertrag Heute
  /*int*/ char *H21;          //[W] Max-Leistung Heute
  /*int*/ char *H22;          //[kWh] Ertrag Gestern
  /*int*/ char *H23;          //[W] Max-Leistung Gestern
  /*int*/ char *HSDS;         //[T] Anzahl Tage über die gesamte Lebensdauer des Gerätes
} solarWerte[solReg];

void splitSolar(const byte regler, const char myChar, char *myBuf, byte &idx, const byte maxIdx)
{
  if (isControl(myChar) && myChar != '\t')
  {
    char *label = strtok(myBuf, "\t");
    char *value = strtok(NULL, "\t");
    Serial.print(label);
    Serial.print(':');
    Serial.println(value);
    if (!strcmp(label, "PID"))
    { strcpy(solarWerte[regler].PID, value); }
    else if (!strcmp(label, "FW"))
    { strcpy(solarWerte[regler].FW, value); }
    else if (!strcmp(label, "SER#"))
    { strcpy(solarWerte[regler].SER, value); }
    else if (!strcmp(label, "V"))
    { strcpy(solarWerte[regler].V, value); } //V200 = val200.toFloat() / 1000; }
    else if (!strcmp(label, "I"))
    { strcpy(solarWerte[regler].Ib, value); }//Ib200 = val200.toFloat() / 1000; }
    else if (!strcmp(label, "VPV"))
    { strcpy(solarWerte[regler].VPV, value); } //VPV200 = val200.toFloat() / 1000; }
    else if (!strcmp(label, "PPV"))
    { strcpy(solarWerte[regler].PPV, value); } //PPV200 = val200.toFloat(); }
    else if (!strcmp(label, "CS"))
    { strcpy(solarWerte[regler].CS, value); } //CS200 = val200.toInt(); }
    else if (!strcmp(label, "MPPT"))
    { strcpy(solarWerte[regler].MPPT, value); } //MPPT200 = val200; }
    else if (!strcmp(label, "OR"))
    { strcpy(solarWerte[regler].OR, value); } //OR200 = val200; }
    else if (!strcmp(label, "ERR"))
    { strcpy(solarWerte[regler].ERR, value); } //ERR200 = val200.toInt(); }
    else if (!strcmp(label, "LOAD"))
    { strcpy(solarWerte[regler].LOAD, value); } //LOAD200 = val200.toFloat(); }
    else if (!strcmp(label, "IL"))
    { strcpy(solarWerte[regler].IL, value); } //IL200 = val200.toFloat(); }
    else if (!strcmp(label, "H19"))
    { strcpy(solarWerte[regler].H19, value); } //H19200 = val200.toInt() / 10;}
    else if (!strcmp(label, "H20"))
    { strcpy(solarWerte[regler].H20, value); } //H20200 = val200.toInt() / 10; }
    else if (!strcmp(label, "H21"))
    { strcpy(solarWerte[regler].H21, value); } //H21200 = val200.toInt() / 1000; }
    else if (!strcmp(label, "H22"))
    { strcpy(solarWerte[regler].H22, value); } //H22200 = val200.toInt() / 10;}
    else if (!strcmp(label, "H23"))
    { strcpy(solarWerte[regler].H23, value); } //H23200 = val200.toInt() / 1000;}
    else if (!strcmp(label, "HSDS"))
    { strcpy(solarWerte[regler].HSDS, value); } //HSDS200 = val200.toInt() / 100; }
    memset(myBuf, '\0', maxIdx);
  }
  else if (idx < maxIdx)
  {
    myBuf[idx] = myChar;
    idx++;
  }
}

void showSolar()
{
  for (byte b = 0; b < solReg; b++)
  {
    Serial.print(F("------"));
    Serial.print(solRegler[b]);
    Serial.println(F("------"));
    Serial.print(F("Produkt ID : "));
    Serial.println(solarWerte[b].PID);
    Serial.print(F("Firmware : "));
    Serial.println(solarWerte[b].FW);
    Serial.print(F("Serien#: "));
    Serial.println(solarWerte[b].SER);
    Serial.print(F("Betteriespannung : "));
    Serial.print(solarWerte[b].V);
    Serial.println(F(" V"));
    Serial.print(F("Batterie Ladestrom : "));
    Serial.print(solarWerte[b].Ib);
    Serial.println(F(" A"));
    Serial.print(F("Solarspannung : "));
    Serial.print(solarWerte[b].VPV);
    Serial.println(F(" V"));
    Serial.print(F("Solarleistung : "));
    Serial.print(solarWerte[b].PPV);
    Serial.println(F(" W"));
    Serial.print(F("Ladestatus : "));
    Serial.print(solarWerte[b].CS);
    Serial.println(F(" -> 0 = Aus, 2 = Err, 3 = Lad, 4 = Entl, 5 = halten"));
    Serial.print(F("MPPT : "));
    Serial.println(solarWerte[b].MPPT);
    Serial.print(F("OR : "));
    Serial.println(solarWerte[b].OR);
    Serial.print(F("Error : "));
    Serial.print(solarWerte[b].ERR);
    Serial.println(F(" -> 0 = OK, 2 = Batt V zu hoch, 17 = Temp zu hoch, 18 = Überstrom, 21 = Amp Sensor Fehler, 33 = Solar V zu hoch, 34 = Solar A zu hoch"));
    Serial.print(F("Lastausgang : "));
    Serial.println(solarWerte[b].LOAD);
    Serial.print(F("Lastausgang Strom : "));
    Serial.print(solarWerte[b].IL);
    Serial.println(F(" A"));
    Serial.print(F("Ertrag gesamt : "));
    Serial.print(solarWerte[b].H19);
    Serial.println(F(" kWh"));
    Serial.print(F("Ertrag heute : "));
    Serial.print(solarWerte[b].H20);
    Serial.println(F(" Wh"));
    Serial.print(F("Maximalleistung heute : "));
    Serial.print(solarWerte[b].H21);
    Serial.println(F(" W"));
    Serial.print(F("Leistung gestern : "));
    Serial.print(solarWerte[b].H22);
    Serial.println(F(" Wh"));
    Serial.print(F("Maximalleistung gestern : "));
    Serial.print(solarWerte[b].H23);
    Serial.println(F(" W"));
    Serial.print(F("HSDS : "));
    Serial.println(solarWerte[b].HSDS);
  }
}
//****************************************
//========================================
void getSolar()
{
  constexpr byte maxIdx {30};
  char myChar = '\0';
  static char myBuf[solReg][maxIdx + 1] = {'\0'};
  static byte idx[solReg] = {0};
  if (Serial.available())
  {
    myChar = Serial1.read();
    splitSolar(0, myChar, myBuf[0], idx[0], maxIdx);
  }
  if (Serial3.available())
  {
    myChar = Serial3.read();
    splitSolar(1, myChar, myBuf[1], idx[1], maxIdx);
  }
}

Auf der Hauptseite sind die einzelnen Variablen noch entfallen, da die jetzt alle in dem Struct hängen. kein einzige String mehr drin.
Ich muss mal sehen, wie ich das jetzt baue um damit auch rechnen zu können. Wird aber in der nächsten halben Stunde nx...

Für die beiden Dokus danke - da sind auch alle Werte aufgeführt, ich bau Dir das fertig.

Kein Problem....hoffe ich kriege das dann alles im sketch untergebracht :face_with_peeking_eye:
Wenn das am Schluss alles läuft hast Du was gut bei mir. :+1:

Ist das noch notwendig?

Ich bau das live in Deinem Code!
Kannst ja rein schauen...
Womo_Monitor_Gross_Struct_ohne_Debug-230915a.zip (42,1 KB)

Hm.
Willst Du unterstützend mit der Chksum zugreifen?
Die Referenzimplementation Q8 scheint ein C(++) zu sein. Aber irgendwie reicht mein englisch dann nicht, um rauszufinden, was da was ist.
Denn die matchen auf checksum == 0 was ja bedeuten würde, das irgendwo aufgefüllt werden müsste.
Mir fehlt da der Faden. Oder einfach die Zeit.