Warten auf neuen Char-Datensatz

Hallo an alle Forummitglieder

Ich habe immer noch nur einfachste Grundkentnisse mit Arduino.

Ich möchte den Eigenverbrauch meiner Photovoltaikanlage erhöhen und lese meinen Semax Smartmeter mit DMSR v5.0.2- Protokll über ein RJ12 Kabel und Arduino Uno aus. Mein Ziel ist es, über Relais (an Pin 2,3,4,5,6,7,11,12), Verbraucher anzusteuern.

Dank dem Forum (Dezimalzahl aus Char-Datensatz auslesen) kann ich die nötigen Daten erfolgreich auslesen, aber komme mit der Ansteuerung der Relais nicht weiter.

Mein Ziel ist es, nach jeder einzelnen If-Bedingung (Relais ein oder aus) einen neuen Datensatz abzuwarten:

if (leistung > 1.300) {
    digitalWrite(Relais1_10A_Shield_2, HIGH);
   *//neuer Datensatz abwarten* 

}
    
if (leistung > 0.200) {
    digitalWrite(Relais2_10A_Shield_2, HIGH);
    *//neuer Datensatz abwarten* 
    }

Vom Stromzähler wird alle 10s einen neuen Datensatz gesendet.

Ich habe das Mal damit versucht:

while (!altSerial.available());

Ich glaube jedoch, dass sich diese Bedingung auch auf den vorgängigen Datensastz bezieht, anstatt auf einen neu gesendeten. Daher kam mir der Gedanke den alten Datensatz mit memset zu löschen. Aber ich hatte mit meinen Versuchen als Anfänger wenig erfolg.

Wie würdet ihr vorgehen? Habt ihr ein Beispiel, dass mir weiterhelfen könnte?

Ich hoffe ihr könnt mir weiterhelfen.
Grüße

Hier noch der aktuelle komplette Sketch:

#include <AltSoftSerial.h>

#define RX_PIN 8
#define TX_PIN 9

AltSoftSerial altSerial;
char c; 

  int Relais1_3A_Shield_1=7;
  int Relais2_3A_Shield_1=6;
  int Relais3_3A_Shield_1=5;
  int Relais4_3A_Shield_1=4;
  int Relais1_10A_Shield_2=2;
  int Relais2_10A_Shield_2=3;
  int Relais3_10A_Shield_2=11;
  int Relais4_10A_Shield_2=12;


void setup() {
  Serial.begin(115200);
  Serial.println("\nStart");
  //altSerial.begin(115200);
  altSerial.begin(115200);
  pinMode(2, OUTPUT); //Relais 1 10A Shield2
  pinMode(3, OUTPUT); //Relais 2 10A Shield2
  pinMode(4, OUTPUT); //Relais 4 3A Shield1
  pinMode(5, OUTPUT); //Relais 3 3A Shield1
  pinMode(6, OUTPUT); //Relais 2 3A Shield1
  pinMode(7, OUTPUT); //Relais 1 3A Shield1
  pinMode(11, OUTPUT);//Relais 3 10A Shield2
  pinMode(12, OUTPUT);//Relais 4 10A Shield2
}

void recvWithEndMarker(float &leistung) {
  const byte maxChars = 32;
  static char receivedChars[maxChars] = {'\0'};
  static byte ndx = 0;
  const char endMarker = '\n';
  char rc;

  if (altSerial.available()) {
    c = altSerial.read();

    c &= ~(1 << 7);
        char inChar = (char)c;
        
            Serial.print(c);

    if (c != endMarker) {
      if (ndx < maxChars - 1) {
        receivedChars[ndx] = c;
        ndx++;
        receivedChars[ndx] = '\0';
      }
    } else {
      //Serial.println(receivedChars);
      if (!strncmp(receivedChars, "1-0:2.7.0(", 10))
      {
        char* zeigerKlammerAuf = strchr(receivedChars, '(');
        leistung = strtod(zeigerKlammerAuf + 1, NULL); // stoppt automatisch beim *
      }
      receivedChars[ndx] = '\0'; // terminate the string
      ndx = 0;
    }
  }
}

void loop() {
  float leistung = -1;  // doch wieder jeden loop neu:
    recvWithEndMarker(leistung);
    if (leistung >= 0) {
      Serial.print(F("Leistung = "));
      Serial.println(leistung, 3);
      if (leistung > 0.000) {
        Serial.println("true");
      } else {
        Serial.println("false");
  }
  
  if (leistung > 1.300) {
    digitalWrite(Relais1_10A_Shield_2, HIGH);
    delay (12000);
 }
    
if (leistung > 0.200) {
    digitalWrite(Relais2_10A_Shield_2, HIGH);
    delay (12000);
    }
 
    if (leistung > 1.500) {
    digitalWrite(Relais3_10A_Shield_2, HIGH);
    delay (12000);
    }
 if (leistung < 0.200) {
    digitalWrite(Relais3_10A_Shield_2, LOW);
    delay (12000);
    }

    if (leistung < 0.100) {
    digitalWrite(Relais2_10A_Shield_2, LOW);
    delay (12000);
    }
    if (leistung < 0.010) {
    digitalWrite(Relais1_10A_Shield_2, LOW);
    delay (12000);
   }
 } 
 }
 

Ich glaube Du machst Dir das zu umständlich.
Vor allem fällt Dir früher oder später das delay() auf die Füße. Immerhin bist Du 12 Sekunden lang für alles andere blind.

Wenn ich Dich richtig verstanden habe, willst Du alle 12 Sekunden vergleichen und dannn dementsprechend drauf reagieren?

wenn > 0.2 nur den ersten Kreis an, wenn > 1.3 den ersten und den zweiten und wenn > 1.5 alle?
Das geht eindeutig einfacher :wink:

Kannst Du überhaupt mal erklären, was Du mit den vielen delay(12000) erreichen willst?

Wenn Du damit erreichen willst, dass der gesamte loop nur alle 12 Sekunden abgearbeitet wird, und dazwischen wirklich NICHTS passieren soll, dann muss das delay ganz ans Ende vom loop ( und da nur einmal ).
Erweiterbar ist der Sketch mit dem Konzept aber nicht wirklich. Wie schon geschrieben, bist Du da einfach 12Sekunden lang 'blind' und es passiert nichts.

Danke für deine Antwort. Genau, mit delay komm ich nicht weiter, weil ich damit immer den aktuellen Datensatz verpasse.

Ich möchte alle 10Sekunden den neuen Datensatz des Zählers vergleichen.

Das geht leider nicht so einfach: Z.Bsp. möchte ich Prioritäten unabhängig vom Verbrauch setzten.

Und: wenn Relais 1 (Boiler/1.3KW) und Relais 3 (Elekroheizung/1.4KW) zusammen in Betrieb sind müsste ich den Schwellwert auf 2.7KW setzen. Falls der Boiler aufgeladen ist, sollte der Schwellwert für Relais 1+3=1.4KW betragen.
Also bleibt mein Wunsch, mich mit den Relais einzeln auf den Wert "Leistung" zu beziehen.

Wetten: er will die Vergleiche alle 12 Sekunden und vermeiden, das ein nächstes Mal die ifs ausgwertet werden...

Doch, das geht.
Schrittkette mit Ausstiegsmöglichkeit.
Ich denke verstanden zu haben, was Du willst - mal sehen, ob mir ein wenig Zeit bleibt; das ist nur Logik :wink:

Es ist mir selbst klar, dass delay hier keine Lösung ist. Darum frage ich nach anderen Möglichkeiten. Der Grundgedanke war, dass sich während den 12sec. ein neuer Datensatz gebildet ha. das hat aber nicht funktioniert, weil ja der Sketch da hängen gelieben ist und momentan will ich damit einfach erreichen, dass die Relais nicht schnell hin und her schalten.

Würde ich so global noch nicht einmal sagen. Wenn Du alle 10 Sekunden deine Leistung messen willst, und abhängig davon deine Relais schalten willst, könnte das für eine einfache Lösung schon gehen. Aber nicht in jedem if-Block
So wie ich das verstehe, hast Du eine zur Verfügung stehende Leistung, und willst die auf deine Verbraucher aufteilen? Hast Du eine Priorität, wer am wichtigsten ist? Bevor Du ans Programmieren gehst, musst Du dir erst deine Logik wie Du die Verbraucher einschalten willst genau klar machen - erstmal ganz unabhängig vom Programmieren.
Beschreib das mal genau - damit es uns - und Dir :wink: - klar wird.

Die Logik ist folgendermassen:

Wert vergleichen:
true-> pin Schalten->nächsten Datensatz abwarten-> zur nächsten if-Bedingung
false->zur nächsten if-Bedingung

Wie bereits zu Anfang beschrieben:

if (leistung > 1.300) {
    digitalWrite(Relais1_10A_Shield_2, HIGH);
   *neuer Datensatz abwarten* 

if (leistung > 0.200) {
    digitalWrite(Relais2_10A_Shield_2, HIGH);
    *neuer Datensatz abwarten* 

if (leistung > 1.400) {
    digitalWrite(Relais3_10A_Shield_2, HIGH);
    *neuer Datensatz abwarten* 
u.s.w
}

Gaaanz falsch. Du denkst viel zu viel ans Programmieren.
Wie ist die Logik von Leistung zu ein/ausgeschalteten Verbrauchern. Ganz unabhängig vom Programmieren.
Stell dir vor Du müsstest das von Hand machen. Du hast eine Anzeige der Leistung und musst entscheiden was Du ein- und was ausschalten sollst. Nach welchen Kriterien gehst Du vor?

Dann nimm doch deine Empfangsfunktion als Entscheidungskriterium her. Da drin ist doch bekannt, wann es einen neuen Datensatz gegeben hat.
Änderungen in Deinem Sketch mit // >>>> gekennzeichnet und kommentiert. Nicht kompiliert oder getestet.

#include <AltSoftSerial.h>

#define RX_PIN 8
#define TX_PIN 9

AltSoftSerial altSerial;
char c;

int Relais1_3A_Shield_1 = 7;
int Relais2_3A_Shield_1 = 6;
int Relais3_3A_Shield_1 = 5;
int Relais4_3A_Shield_1 = 4;
int Relais1_10A_Shield_2 = 2;
int Relais2_10A_Shield_2 = 3;
int Relais3_10A_Shield_2 = 11;
int Relais4_10A_Shield_2 = 12;


void setup()
{
  Serial.begin(115200);
  Serial.println("\nStart");
  //altSerial.begin(115200);
  altSerial.begin(115200);
  pinMode(2, OUTPUT); //Relais 1 10A Shield2
  pinMode(3, OUTPUT); //Relais 2 10A Shield2
  pinMode(4, OUTPUT); //Relais 4 3A Shield1
  pinMode(5, OUTPUT); //Relais 3 3A Shield1
  pinMode(6, OUTPUT); //Relais 2 3A Shield1
  pinMode(7, OUTPUT); //Relais 1 3A Shield1
  pinMode(11, OUTPUT);//Relais 3 10A Shield2
  pinMode(12, OUTPUT);//Relais 4 10A Shield2
}

// >>>>
// geänderte Signatur:
// Die Funktion gibt "true" zurück, wenn ein neuer Datensatz anliegt.
// Ansonsten wird "false" gemeldet
bool recvWithEndMarker(float &leistung)
{
  const byte maxChars = 32;
  static char receivedChars[maxChars] = {'\0'};
  static byte ndx = 0;
  const char endMarker = '\n';
  char rc;
  // >>>>
  // Merker für erfolgreichen Empfang eines neuen Datensatzes
  // Annahme: (Noch) kein Datensatz komplett
  bool retValue = false;
  // <<<<

  if (altSerial.available())
  {
    c = altSerial.read();

    c &= ~(1 << 7);
    char inChar = (char)c;

    Serial.print(c);

    if (c != endMarker)
    {
      if (ndx < maxChars - 1)
      {
        receivedChars[ndx] = c;
        ndx++;
        receivedChars[ndx] = '\0';
      }
    }
    else
    {
      //Serial.println(receivedChars);
      if (!strncmp(receivedChars, "1-0:2.7.0(", 10))
      {
        char* zeigerKlammerAuf = strchr(receivedChars, '(');
        leistung = strtod(zeigerKlammerAuf + 1, NULL); // stoppt automatisch beim *
        // >>>>
        // Hier ist die einzige Stelle, am der ein neuer kompletter Datensatz (Leistungswert) anliegt.
        retValue = true;
        // <<<<
      }
      receivedChars[ndx] = '\0'; // terminate the string
      ndx = 0;
    }
  }
  // >>>>
  return retValue;
  // <<<<
}

void loop()
{
  float leistung = -1;  // doch wieder jeden loop neu:

  // >>>>
  // Hier den Rückkgabewert der Empfangsfunktion verwenden
  if (recvWithEndMarker(leistung))
  {
    Serial.print(F("Leistung = "));
    Serial.println(leistung, 3);
    if (leistung > 0.000)
    {
      Serial.println("true");
    }
    else
    {
      Serial.println("false");
    }

    // >>>>
    // Schalterei der Übersichtlichkeit halber nach Leistungswerten geordnet
    // die 'else if' sorgen dafür, dass nur einmal pro Datensatz geschaltet wird.
    // <<<<
    if (leistung < 0.010)
    {
      digitalWrite(Relais1_10A_Shield_2, LOW);
    }
    else if (leistung < 0.100)
    {
      digitalWrite(Relais2_10A_Shield_2, LOW);
    }
    else if (leistung < 0.200)
    {
      digitalWrite(Relais3_10A_Shield_2, LOW);
    }
    else if (leistung > 0.200)
    {
      digitalWrite(Relais2_10A_Shield_2, HIGH);
    }
    else if (leistung > 1.300)
    {
      digitalWrite(Relais1_10A_Shield_2, HIGH);
    }
    else if (leistung > 1.500)
    {
      digitalWrite(Relais3_10A_Shield_2, HIGH);
    }
  }
}

Statt auf den neuen Datensatz zu warten könntest Du doch auch den durch 'Einschalten' verbrauchten Wert von der Leistung abziehen, und schauen, was für die anderen noch übrig bleibt.
Oder Du machst eine Funktion für das Messen der Leistung, und die rufst Du nach dem Einschalten eines Verbrauchers erneut auf ( evtl. mit kurzer Verzögerung, damit sich das auch bemerkbar macht), um zu sehen, was für Eigenverbrauch noch zur Verfügung steht.

P.S. Die programmunabhängige Logik des Ganzen hast Du immer noch nicht erklärt.

Relais1 einstalten bei einem Wert über 1.3. und auf den neuen Datensatz warten oder Relais 1 nicht einschalten bei einem Wert unter 1.3.
Zur nächsten Bedingung.
Relais2 einstalten bei einem Wert über 0.2. und auf den neuen Datensatz warten. Relais 1 nicht einschalten bei einem Wert unter 0.2.

Das wird's nicht.
Du schaltest abhängig vom Wert.
Er schrub doch, das er zeitabhängig den Wert haben möchte und dann den nächsten Schritt :wink:

@stiago2 Ich glaub ich habs.... ... ich Schreib noch Kommentare

Der Verbrauch ist nicht konstant.

Hab ich gemacht.

#include <AltSoftSerial.h>

#define RX_PIN 8
#define TX_PIN 9

AltSoftSerial altserial;
char c;

const byte relaisShieldOnePin[] = {7, 6, 5, 4};   // jedes Shield festlegen - nicht jeden PIN einzeln
const byte relaisShieldTwoPin[] = {2, 3, 11, 12};
const uint32_t intervall = 12000;                 // Wiederholung der Messung nach xxx ms
uint32_t lastmillis = 0;                          // Merker Letzte Messung


void setup()
{
  Serial.begin(115200);
  Serial.println("\nStart");
  //altserial.begin(115200);
  altserial.begin(115200);
  for (byte b = 0; b < sizeof(relaisShieldOnePin); b++)   // verkürzte Form des PinSet
    pinMode(relaisShieldOnePin[b], OUTPUT);
  for (byte b = 0; b < sizeof(relaisShieldTwoPin); b++)
    pinMode(relaisShieldTwoPin[b], OUTPUT);
}

void recvWithEndMarker(float &leistung)
{
  const byte maxChars = 32;
  static char receivedChars[maxChars] = {'\0'};
  static byte ndx = 0;
  const char endMarker = '\n';
  char rc;
  if (altserial.available())
  {
    c = altserial.read();
    c &= ~(1 << 7);
    char inChar = (char)c;
    Serial.print(c);
    if (c != endMarker)
    {
      if (ndx < maxChars - 1)
      {
        receivedChars[ndx] = c;
        ndx++;
        receivedChars[ndx] = '\0';
      }
    }
    else
    {
      //Serial.println(receivedChars);
      if (!strncmp(receivedChars, "1-0:2.7.0(", 10))
      {
        char *zeigerKlammerAuf = strchr(receivedChars, '(');
        leistung = strtod(zeigerKlammerAuf + 1, NULL); // stoppt automatisch beim *
      }
      receivedChars[ndx] = '\0'; // terminate the string
      ndx = 0;
    }
  }
}

void loop()
{
  if (millis() - lastmillis >= intervall)    // Nur wenn erfüllt
  {
    setRelayShieldTwo();                     // schau mal was sich ändert
    lastmillis = millis();                   // Merke die Zeit
  }
}


void setRelayShieldTwo()
{
  static uint8_t schritt = 0;                 // Merker für aktuelle Position
  float leistung = -1;                        // Vorbelegung
  recvWithEndMarker(leistung);                // Datum holen
  if (leistung >= 0)                          // und nur wenn da was zurück kommt
  {
    Serial.print(F("Leistung = "));           // etwas ausgaben
    Serial.println(leistung, 3);
    if (leistung > 0.000)
    {
      Serial.println("true");
    }
    else
    {
      Serial.println("false");
    }
  }
  switch (schritt)                            // Der schritt ist unabhängig der Leistung
  {
    case 0:
      if (leistung > 1.300)                   // Jetzt kommt richtig Power aus der Anlage
      {
        digitalWrite(relaisShieldTwoPin[0], HIGH); // Dann relais setzen
        schritt++;                                 // und nächste Stufe einstellen
      }
      break;
    case 1:                                   // und so wie oben geht es hier weiter....
      if (leistung > 0.200)
      {
        digitalWrite(relaisShieldTwoPin[1], HIGH);
        schritt++;
      }
      break;
    case 2:
      if (leistung > 1.500)
      {
        digitalWrite(relaisShieldTwoPin[2], HIGH);
        schritt++;
      }
      break;
    case 4:
      if (leistung < 0.200)
      {
        digitalWrite(relaisShieldTwoPin[2], LOW);
        schritt++;
      }
      break;
    case 5:
      if (leistung < 0.100)
      {
        digitalWrite(relaisShieldTwoPin[1], LOW);
        schritt++;
      }
      break;
    case 6:
      if (leistung < 0.010)
      {
        digitalWrite(relaisShieldTwoPin[0], LOW);
        schritt = 0;
      }
      break;
  }
}

Wenn Dir irgendeine Erklärung fehlt, dann schreib es - das ist Dein IF-Konstrukt ohne delay() von oben.

Hör doch bitte mal auf in Programmierung zu denken. Was ist der Sinn und Zweck des Ganzen. Wenn ich dich richtig verstehe hast Du verschiedne Verbraucher mit unterschiedlichem Leistungsbedarf. Wenn Deine Anlage Leistung erzeugt, möchtest Du sie nicht ins Netz einspeisen, sondern möglichst optimal selbst nutzen. Ist das so richtig?
Jetzt ist die Frage nach welchen Kriterien, deine Verbraucher ein-, bzw. ausgeschaltet werden sollen. Wie ist die Priorität, wie ihr Leistungsverbrauch. Es können ja evtl. auch mehrere kleinere statt eines größeren Verbrauchers eingeschaltet werden. Diese ganzen Vorüberlegungen fehlen mir noch vollständig. Das hat noch garnichts mit if's und digitalWrites zu tun.
Erst wenn das 'auf dem Tisch liegt' kann man sinnvoll anfangen sich eine Programmstruktur zu überlegen. Du zäumst das Pferd von hinten auf - zumindest aus unserer Sicht, die wir ja deine Anlage nicht kennen. Du bist aber nicht allein, ist durchaus ein übliches Problem bei Programmieranfängern :wink:

Messen tut doch der Zähler bereits? Und ich habe den genauen Wert, wieviel Überproduktion ich habe.

Was ist dabei noch unklar?

genau!

Relais 1 (Boiler) Priorität 1/ Verbrauch 1.3KW oder 0KW
Relais 2 (diverse Verbraucher) Priorität 2 / Verbrauch nicht konstant/undefiniert (Smartphone/Laptop/Akkuschrauber/E-BikeAkku Ladegerät u.s.w)
Relais 3 (Heizung) Priorität 3/ Verbrauch 1.4KW oder 0KW

Die Logik der Steuerung ist nicht mein Problem, sondern das abwarten eines neuen Datensatzes im Sketch!