Erstes Programm, läuft - nach ca. 1-2 Std. gibts falsche Werte

Moin,

mein erstes Programm für Einspeisungssteuerung läuft soweit erstmal.

ABER nach nicht genau definierter Zeit ( ca. 1-3 Std. Laufzeit meist ) bekommt die Variable pwr Werte die VIEL zu hoch sind.
Die Schleife läuft aber weiter ( sieht man an den Dioden am RX485 - der Wert ändert sich aber nicht wieder auf das Reale - die Checksumme habe ich noch ignoriert weil ich die Berechnung noch nicht begriffen habe - das sollte aber ja nicht das Problem sein weil ein falscher Wert beim nächsten Schleifendurchlauf ja wieder geändert würde - und wenn der Fehler auftritt bleibt die Variable pwr statisch auf dem einmal entstandenen Falschwert )

pwr ist die aktuell bezogene Energie vom Netz, ausgelesen mit einem DS100-00B über RS485

Das geht dann über zweiten RS485 Adapter an den Soyosource.

Probleme gibts noch das die Variable pwr - bei Der funktioniert nicht die Abfrage auf die Art pwr < 0 - um auf Negativ zu prüfen ( wenn der pwr-Wert durch das BKW ins negative geht ).

Das habe ich dadurch prov. gelöst das ich das HighByte der 32 Bit Variable auf 0xFF prüfe - weil das ist der Fall wenn pwr negativ ist - und so groß wird pwr eh nie.

Um mich an den Fehler ranzutasten habe ich die Anzeige im LCD Display - tritt der Fehler auf wird die Ausgabe der Leistung extrem hoch ( unrealistisch ) und bleibt auf diesem unrealistischen Wert.

Dazu lasse ich mir die Laufzeit des Programms im Display anzeigen um die typ. Zeit bis zum Fehler anzeigen zu lassen.

Dann habe ich alle Variablendefinitionen aus der Hauptschleife rausgenommen ( ohne Verbesserung - Idee war -> frisst er sonst mehr Speicher ?

Und es ist jetzt eine Abfrage drin die bei Überschreitung des Wertes pwr > 10kW einen Reset auslöst - was gerade in der Praxis passierte ( Runtime 75 Minuten ) - der Reset geht ja schnell - die Funktion ist so schon mal gegeben.

Wo ist mein Fehler ( Variablen aufklaren - Speicherprobleme - Bufferüberlauf ?!? )

Die Berechnung von pwr aus den 4 Bytes ist sicher noch nicht toll gelöst - funktioniert aber.
Die Variable wtts übernimmt den Wert von pwr bevor die geändert wird, pwr geht aber an das Display zum Monitoring - im Fehlerfall müsste wtts aber ja den selben falschen Wert haben wie pwr.

Proc: ESP32_DevKitV3
Die HardwareSerial nutze ich nicht da das mit der OnboardDiode Probleme gab und Baud eh niedrig ist ( sein kann ).

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include "driver/uart.h"
#include <SoftwareSerial.h>

//Variablen definieren
int pwr; 
int DATEN[8];    //Array definieren
uint16_t wtts;                      //unsigned Variable für Watt-senden def.
char incomingByte1;
char incomingByte2;
char incomingByte3;
char incomingByte4;
char incomingByte5;
char incomingByte6;
char incomingByte7;
char incomingByte8;
char incomingByte9;
uint16_t CS;
uint8_t i;
int runtime;

LiquidCrystal_I2C lcd(0x3F,16,2);  // set the LCD address to 0x27 for a 16 chars and 2 line display
SoftwareSerial mySerial(6, 7); //rx tx
SoftwareSerial SoyoSerial(2, 3); //rx tx

void setup()
{
Serial.begin(9600);
mySerial.begin(9600, SWSERIAL_8N1);
SoyoSerial.begin(4800, SWSERIAL_8N1);

Wire.begin(SDA, SCL);

  lcd.init();                      // initialize the lcd 
  lcd.backlight();
  lcd.clear();
}

void loop()
{
  pwr = 0;
    
  // Datenabfrage senden
  
  
  DATEN[0] = 0x01; // ModbusAdresse ?
  DATEN[1] = 0x04;
  DATEN[2] = 0x04;
  DATEN[3] = 0x20; //Combined_Aktive_Power
  DATEN[4] = 0x00;
  DATEN[5] = 0x02;
  DATEN[6] = 0x71;
  DATEN[7] = 0x31;
  for(uint8_t i=0; i<8; i++)  {
  mySerial.write(DATEN[i]);
  }
 
  if (mySerial.available()> 0) {
    // wait a bit for the entire message to arrive
    delay(100);
    // clear the screen
    lcd.clear();
    lcd.setCursor(0, 0);
        
      incomingByte1=mySerial.read(); // 1.Byte einlesen & verwerfen
      incomingByte1 = incomingByte1;      //Dummynutzung der Variable
      incomingByte2=mySerial.read(); // 2.Byte einlesen & verwerfen
      incomingByte2 = incomingByte2;      //Dummynutzung der Variable
      incomingByte3=mySerial.read(); // 3.Byte einlesen & verwerfen
      incomingByte3 = incomingByte3;      //Dummynutzung der Variable

      incomingByte4=mySerial.read();
      pwr = (pwr+incomingByte4*16777216); // Highest Byte - pwr zusammenbauen
      
      incomingByte5=mySerial.read();
      pwr = (pwr+incomingByte5*65536); // 2.Highest Byte

      incomingByte6=mySerial.read();
      pwr = (pwr+incomingByte6*256); // 2.Lowest Byte
      
      incomingByte7=mySerial.read();
      pwr = (pwr+incomingByte7); // Lowest Byte
    

    // ab hier CS auslesen und verwerfen - Serial_Input frei machen

    incomingByte8=mySerial.read(); // 8.Byte ( CS ? ) einlesen & verwerfen
    incomingByte8 = incomingByte8;      //Dummynutzung der Variable
    incomingByte9=mySerial.read(); // 9.Byte ( CS ? ) einlesen & verwerfen
    incomingByte9 = incomingByte9;      //Dummynutzung der Variable

    
    wtts = pwr;                         //Zahl "retten"
   
    if (wtts > 700) wtts = 700; //Obergrenze

    //Abfrage des Highbytes der 32Bit Zahl vom Zähler
    if (incomingByte4 >= 255) wtts = 0; //bei Ueberfluss keine Einspeisung
    //wenn diese 255/0xFF ist kann man vom negativen Wert ausgehen
    
    if (incomingByte4 >= 255) {
    lcd.print("\xF5");
    lcd.print("bereinspeisung:");
    pwr=pwr*-1; //setze positiv für Anzeige
    } else
  lcd.print("Bedarf: ");
  if (pwr > 10000) ESP.restart();
  lcd.setCursor(0, 1);
  lcd.print(pwr);
  
  lcd.print(" W RT=");
  runtime = millis();
  runtime = runtime / 60000;
  lcd.print (runtime);
  lcd.print(" Min");
  
  //Daten an den Soyo senden:

  CS = 264;
  DATEN[0] = 0x24;
  DATEN[1] = 0x56;
  DATEN[2] = 0x00;
  DATEN[3] = 0x21;
  DATEN[4] = (wtts & 0xFF00)>>8;
  DATEN[5] = (wtts & 0x00FF);
  DATEN[6] = 0x80;
  CS = CS - DATEN[4] - DATEN[5];
  if(CS > 0xFF) CS = 8;
  DATEN[7] = CS;
  for(i=0; i<8; i++)  {
    SoyoSerial.write(DATEN[i]); // .print ??
  }
   
  }
  delay(900); // 0.9 Sekunden warten = 1 sec. Intervall
}

Was soll dieses Programm machen?
Welche Hardware ist angeschlossen (Schaltbild)?

klingt nach einem Überlauf der Variable.
Und wenn ich mir deine Multiplikationen ansehe wundert mich das auch nicht.

"Versuche" *) es mal mit einer int32_t oder wenns nur positiv sein kann uint32_t.

*) Eigentlich sollst du aber nicht versuchen sondern genau ermitteln wie groß die Variable sein muss bzw. Maßnahmen ergreifen diese nicht zum überlauf zu bringen.

die Variable ist 32 Bit - nicht explezit festgelegt - aber int ist in dem System 32 Bit - das funktioniert auch - aber warum funktioniert es in z.B. über 6000 Schleifendurchläufen - und dann mit einem mal nicht ?

die Variable müsste dann int32_t sein - ich werde das mal testen - denke aber nicht ( da Sie ja schon 32 Bit signed ist ) das Dies der Fehler ist

Das hat hier sicher nichts mit der Beschaltung zu tun, wenn mit EMV - mit ganz viel Phantasie.

Hardware ist ( wie im Eingangspost erwähnt ) DS100-00B Stromzähler.
( Dessen Daten sind die gesuchten ( und gefundenen ) )

Frage ist - warum gibt es nach längerer Zeit den Fehler - das Programm läuft !
( Also kann es nur mit irgendwelchen Hardware/Libraryeinschränkungen zu tun haben )

Zusatz:

ist pwr 32 Bit negativ = 0xFF000001 als Beispiel - funktioniert alles einwandfrei - das Programm zeigt dann brav Überschuss: 1 W an - die Variable geht so nicht in den Überlauf.
Sobald in der Praxis ein negativer Wert kommt ist das HighByte ja 0xFF - das läuft.

ich habe ESP32 überlesen.
Rechne es dir aus.
Wenns nicht reicht, es gibt auch 64bit Variablen.

Du hast 2 RS485 Busse? Gib es da noch mehr Teilnehmer?

Problem in deinem Sketch: Du prüfst die ankommenden Daten nicht: Daher als Zwischenlösung:

//Buffer leeren	
  while (mySerial.available() > 0) mySerial.read();

Versuche es mal so:

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include "driver/uart.h"
#include <SoftwareSerial.h>

//Variablen definieren
int32_t pwr; 
int32_t wtts;
byte DATEN[8] = {0x01,0x04,0x04,0x20,0x00,0x02,0x71,0x31};    //Array definieren   
byte OUT[8];                  
char incomingByte1;
char incomingByte2;
char incomingByte3;
char incomingByte4;
char incomingByte5;
char incomingByte6;
char incomingByte7;
char incomingByte8;
char incomingByte9;
uint16_t CS;
uint8_t i;
int runtime;

LiquidCrystal_I2C lcd(0x3F,16,2);  // set the LCD address to 0x27 for a 16 chars and 2 line display
SoftwareSerial mySerial(6, 7); //rx tx
SoftwareSerial SoyoSerial(2, 3); //rx tx

void setup(){
  Serial.begin(9600);
  mySerial.begin(9600, SWSERIAL_8N1);
  SoyoSerial.begin(4800, SWSERIAL_8N1);

  Wire.begin(SDA, SCL);

  lcd.init();                      // initialize the lcd 
  lcd.backlight();
  lcd.clear();
}

void loop(){
  //Buffer leeren	
  while (mySerial.available() > 0) mySerial.read();
	
  // Datenabfrage senden
  for(uint8_t i=0; i<8; i++)  {
    mySerial.write(DATEN[i]);
  }
  
  // wait a bit for the entire message to arrive
  delay(100);
  
  if (mySerial.available() >= 8) {           
	incomingByte1=mySerial.read(); // 1.Byte einlesen & verwerfen
	incomingByte1 = incomingByte1;      //Dummynutzung der Variable
	incomingByte2=mySerial.read(); // 2.Byte einlesen & verwerfen
	incomingByte2 = incomingByte2;      //Dummynutzung der Variable
	incomingByte3=mySerial.read(); // 3.Byte einlesen & verwerfen
	incomingByte3 = incomingByte3;      //Dummynutzung der Variable
	incomingByte4=mySerial.read();
	incomingByte5=mySerial.read();
	incomingByte6=mySerial.read();
	incomingByte7=mySerial.read();	
    // ab hier CS auslesen und verwerfen - Serial_Input frei machen
    incomingByte8=mySerial.read(); // 8.Byte ( CS ? ) einlesen & verwerfen
    incomingByte8 = incomingByte8;      //Dummynutzung der Variable
    incomingByte9=mySerial.read(); // 9.Byte ( CS ? ) einlesen & verwerfen
    incomingByte9 = incomingByte9;      //Dummynutzung der Variable

    pwr = (incomingByte4 << 24) | (incomingByte5 << 16) | (incomingByte6] << 8) | incomingByte7;
    
    wtts = pwr;                         //Zahl "retten"
   
    if (wtts > 700) 		
	  wtts = 700; //Obergrenze
  
    // clear the screen
    lcd.clear();
    lcd.setCursor(0, 0);

	if (wtts < 0) {
	  wtts = 0; //bei Ueberfluss keine Einspeisung
      lcd.print("\xF5");
      lcd.print("bereinspeisung:");
      pwr=pwr*-1; //setze positiv für Anzeige
    } 
	else 
      lcd.print("Bedarf: ");
  
	lcd.setCursor(0, 1);
	lcd.print(pwr);
	lcd.print(" W RT=");
	runtime = millis();
	runtime = runtime / 60000;
	lcd.print (runtime);
	lcd.print(" Min");

	if (pwr > 10000) ESP.restart();

	//Daten an den Soyo senden:

	CS = 264;
	OUT[0] = 0x24;
	OUT[1] = 0x56;
	OUT[2] = 0x00;
	OUT[3] = 0x21;
	OUT[4] = (wtts & 0xFF00)>>8;
	OUT[5] = (wtts & 0x00FF);
	OUT[6] = 0x80;
	CS = CS - OUT[4] - OUT[5];
	if(CS > 0xFF) CS = 8;
	OUT[7] = CS;
	for(i=0; i<8; i++)  {
	  SoyoSerial.write(OUT[i]);
	}   
  }
  delay(900); // 0.9 Sekunden warten = 1 sec. Intervall
}

wenn ich alles mit max multipliziere komme ich mit 32Bit aus - das passt
pwr als 64 Bit definieren - ok, könnte man testen - aber eigentlich sollte das passen

Das muss ich mir heute Abend mal genau anschauen - das "Zusammenbauen" von pwr aus den 4 Bytes sieht bei Dir aber schon mal sauberer aus - die Syntax fand ich bisher nicht.
( Ahnend das es da gibt (facepalm) )

Edit - die Variable wtts sollte nur 16 Bit sein - die unteren 2 Bytes von pwr und das unsigned

-> was wäre da die saubere Syntax ?

Ja, es sind 2 Busse - das eine Gerät hält sich nicht sauber ans Modbusprotokoll und hat andere Baud als der Rest .

glaub ich dir nicht.

      incomingByte4=mySerial.read();
      pwr = (pwr+incomingByte4*16777216); // Highest Byte - pwr zusammenbauen
      
      incomingByte5=mySerial.read();
      pwr = (pwr+incomingByte5*65536); // 2.Highest Byte

      incomingByte6=mySerial.read();
      pwr = (pwr+incomingByte6*256); // 2.Lowest Byte
      
      incomingByte7=mySerial.read();
      pwr = (pwr+incomingByte7); // Lowest Byte

byte4 = 255.
255 * 16777216 = ‭4278190080‬.

‭11111111000000000000000000000000‬

bei der nächsten Iteration wird das zu .

4278190080‬ + (255 * 16777216) =

‭000111111110000000000000000000000000‬
da ist dein Überlauf.

Wenn ich die Variable wtts als 16 Bit brauche - und Die mit den unteren 2 Bytes von der 32 Bit signed pwr Variable beschreiben möchte - was ist da die saubere Syntax ?

Die oberen 2 Bytes von pwr brauche ich nur zur Erkennung ob die Zahl negativ ist.
( Daher das Prüfen auf negativ - wenn pwr negativ ist muss wtts eh 0 sein weil dann keine Leistung eingespeisst werden soll )

Dann wäre die Art der "Zusammensetzung" von pwr falsch ?

ich denke Du bekommst was anderes, als Du erwartest.
Gibt es denn irgendwo eine Doku für die Schnittstelle?

Ich habe Deine Zeile mal eingefügt und meine Bastelei ersetzt.

Programm läuft damit - nachher sehe ich mehr.
( Wenn Runtime in 3 Std. 180Min ist :wink: )

Danke schon mal Allen für die Mühe !

( nun aber erstmal andere Dinge wichtig - ESP32 läuft im Test )

... werde berichten

Man bekommt hier ja sehr schnell geballten Input :+1:

Ja, Doku ist umfangreich - und die Daten passen auch.

Ich vermute das noiasca Recht hat und das mit der Syntax von wwerner das Problem gelöst sein könnte - rechne ich meine Vorgehensweise mit dem Taschenrechner passt das - aber ich rechne in der Abfolge evt. ja auch falsch bzw. anders als c++ bzw. der Compiler.

Das Einzige was mir an Doku fehlt ist noch die Berechnung der Checksumme.
Und wenn es dann bei Fehlübertragung und nicht Erkennen wegen fehlender CS zum Überlauf durch meine Bastelsyntax kommt haben wir den Fehler ?

Wenn du den Buffer nicht leerst bevor du etwas empfängst für dein Problem weiter so existieren.

nicht nur.
Deine ganze Leseroutine ist ungehärtet und somit fehleranfällig.
Du solltest mindestens die Serial Input Basics - updated - Using Arduino / Introductory Tutorials - Arduino Forum durchmachen und anwenden.

Dass ich vom halbherzig "nachgebauten" Modbus nichts halte, habe ich schon im anderen Thread mitgeteilt.

Geduld - ich mache grad die ersten Gehversuche mit C++ und ESP32 !