Programm hängt bei Ausgabe

Hallo,

ich habe ein kleines Programm auf einem ESP8266 am laufen um meinen Zähler per Lesekopf auszulesen (SML Protokoll)

Im Prinzip funktioniert alles Wunderbar. Es läuft auch mehrere Stunden. Plötzliche werden aber keine Daten mehr übermittelt. Die Hauptschleife läuft weiter. Der Webserver ist auch weiterhin erreichbar. Es werden aber keine Daten mehr an (in diesem Fall Thingspeak) übermittelt.

Ich vermute ganz Stark das es durch einen Fehler irgendeine Variable, Counter, etc nicht mehr zurücksetze und deswegen alles stehen bleibt. Ich Suche nun schon seit 3 Tagen und kann nichts finden.

Kurz zur Erläuterung: In der mainloop werden nur die einzelnen Fälle durchgespielt. Je nach aktivem Fall.

  1. Es wird aus den Daten der Seriellen Schnittstelle nach der Startsequenz des SML Protokoll gesucht.
  2. Es wird nach der Endsquenz gesucht.
    3/4. Es werden die gewollten Daten aus dem Inhalt rausgefischt
  3. Die Daten werden übergeben, verarbeitet, wie auch immer.

Der Zähler sendet ungefähr jede Sekunde. Deswegen der Counter mit den 120 damit nicht bei jedem Durchlauf, sondern ungefähr alle 2 Minuten Daten übermittelt werden.

Im Optimalfall werden die Daten AB der Startsequenz bis zur Endsequenz gespeichert. Ein Punkt wäre: wenn er nach dem er eine Startsequenz gefunden hat nicht direkt eine Endsequenz findet, läuft die Suche nach der Endsequenz nochmal durch. Die läuft so lange bis er eben eine gefunden hat. Irgendwann passiert das ja. Was dabei eben eintritt ist, das das array sml message solange befüllt wird BIS eben eine Endsequenz gefunden wurde. Was wiederum dazu führt das Daten unter umständen doppelt vorhanden sind. In meinen Augen aber kein Problem. Es kann eben beider Abfrage der Werte in Schritt 3 bzw 4 dazu führen, dass er Werte von vor 30 sekunden hat und nicht der aktuellen. Wäre aber vollkommen egal. Dürfte ja keinen Fehler im Programmablauf verursachen oder?
Das wäre aber auch das Einzige was vllt verbessert werden kann in Form von wieder auf Case 0 setzen wenn zwar eine Startsequenz gefunden wurde aber nicht unmittelbar danach eine Endsequenz.

Hier mal der Code.

#include <SoftwareSerial.h>
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <AsyncElegantOTA.h>
#include <WebSerial.h>
#include <Arduino_JSON.h>
#include <ThingSpeak.h>


SoftwareSerial sSerial(5, 4);
#define LED 2

AsyncWebServer server(80);

const char* ssid = "";
const char* password = "";

void recvMsg(uint8_t* data, size_t len) {
  WebSerial.println("Received Data...");
  String d = "";
  for (int i = 0; i < len; i++) {
    d += char(data[i]);
  }
  WebSerial.println(d);
  if (d == "ON") {
    digitalWrite(LED, LOW);
  }
  if (d == "OFF") {
    digitalWrite(LED, HIGH);
  }
}

byte inByte;                                                                                                                                  //byte to store the serial buffer
byte smlMessage[1000];                                                                                                                        //byte to store the parsed message
const byte startSequence[] = { 0x1B, 0x1B, 0x1B, 0x1B, 0x01, 0x01, 0x01, 0x01 };                                                              //start sequence of SML protocol
const byte stopSequence[] = { 0x1B, 0x1B, 0x1B, 0x1B, 0x1A };                                                                                 //end sequence of SML protocol
const byte powerSequence[] = { 0x07, 0x01, 0x00, 0x10, 0x07, 0x00, 0xFF, 0x01, 0x01, 0x62, 0x1B, 0x52, 0xFE, 0x55 };                          //sequence preceeding the current "Wirkleistung" value (4 Bytes)
const byte consumptionSequence[] = { 0x07, 0x01, 0x00, 0x01, 0x08, 0x00, 0xFF, 0x64, 0x01, 0x01, 0x80, 0x01, 0x62, 0x1E, 0x52, 0xFB, 0x69 };  //sequence predeecing the current "Gesamtverbrauch" value (8 Bytes)
int smlIndex;                                                                                                                                 //index counter within smlMessage array
int startIndex;                                                                                                                               //start index for start sequence search
int stopIndex;                                                                                                                                //start index for stop sequence search
static int stage;                                                                                                                             //index to maneuver through cases
byte power[4];                                                                                                                                //array that holds the extracted 4 byte "Wirkleistung" value
byte consumption[8];                                                                                                                          //array that holds the extracted 8 byte "Gesamtverbrauch" value
int currentpower;                                                                                                                             //variable to hold translated "Wirkleistung" value
int currentconsumption;  //variable to hold translated "Gesamtverbrauch" value
int takt_send = 0;
unsigned long myChannelNumber = 123123;
const char * myWriteAPIKey = "123123";

WiFiClient  client;

void setup() {
  sSerial.begin(9600);  // baud softwareserial
  Serial.begin(9600);   // baud serial
  pinMode(LED, OUTPUT);
  digitalWrite(LED, HIGH);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  if (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.printf("WiFi Failed!\n");
    return;
  }
  Serial.println("IP Address: ");
  Serial.println(WiFi.localIP());

  server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send(200, "text/plain", "Webserver running...");
  });


  AsyncElegantOTA.begin(&server);
  WebSerial.begin(&server);
  WebSerial.msgCallback(recvMsg);
  server.begin();
  ThingSpeak.begin(client);
  Serial.println("Startet");
}


void loop() {
  switch (stage) {
    case 0:
      findStartSequence();  // look for start sequence
      break;
    case 1:
      findStopSequence();  // look for stop sequence
      break;
    case 2:
      findPowerSequence();  //look for power sequence and extract
      break;
    case 3:
      findConsumptionSequence();  //look for consumption sequence and exctract
      break;
    case 4:
      publishMessage();  // do something with the result
      break;
  }
}


void findStartSequence() {
  while (sSerial.available()) {
    inByte = sSerial.read();  //read serial buffer into array
    if (inByte == startSequence[startIndex]) {
      smlMessage[startIndex] = inByte;
      startIndex++;
      if (startIndex == sizeof(startSequence)) {
        //WebSerial.println("Match found");
        stage = 1;
        smlIndex = startIndex;
        startIndex = 0;
      }
    } else {
      startIndex = 0;
    }
  }
}



void findStopSequence() {
  while (sSerial.available()) {
    inByte = sSerial.read();
    smlMessage[smlIndex] = inByte;
    smlIndex++;

    if (inByte == stopSequence[stopIndex]) {
      stopIndex++;
      if (stopIndex == sizeof(stopSequence)) {
        stage = 2;
        stopIndex = 0;
      }
    } else {
      stopIndex = 0;
    }
  }
}

void findPowerSequence() {
  byte temp;
  startIndex = 0;

  for (int x = 0; x < sizeof(smlMessage); x++) {
    temp = smlMessage[x];
    if (temp == powerSequence[startIndex]) {
      startIndex++;
      if (startIndex == sizeof(powerSequence)) {
        for (int y = 0; y < 4; y++) {
          power[y] = smlMessage[x + y + 1];
        }
        stage = 3;  // go to stage 3
        startIndex = 0;
      }
    } else {
      startIndex = 0;
    }
  }
  currentpower = (power[0] << 24 | power[1] << 16 | power[2] << 8 | power[3]);  //merge 4 bytes into single variable to calculate power value
  currentpower = currentpower / 100;
}


void findConsumptionSequence() {
  byte temp;

  startIndex = 0;
  for (int x = 0; x < sizeof(smlMessage); x++) {
    temp = smlMessage[x];
    if (temp == consumptionSequence[startIndex]) {
      startIndex++;
      if (startIndex == sizeof(consumptionSequence)) {
        for (int y = 0; y < 8; y++) {
          //hier muss für die folgenden 8 Bytes hoch gezählt werden
          consumption[y] = smlMessage[x + y + 1];
        }
        stage = 4;
        startIndex = 0;
      }
    } else {
      startIndex = 0;
    }
  }

  currentconsumption = (consumption[0] << 56 | consumption[1] << 48 | consumption[2] << 40 | consumption[32] | consumption[4] << 24 | consumption[5] << 16 | consumption[6] << 8 | consumption[7]);  //combine and turn 8 bytes into one variable
}


void publishMessage() {
  if (takt_send < 120) {
    takt_send++;
  } else {
    if (WiFi.status() == WL_CONNECTED) {
      int x = ThingSpeak.writeField(myChannelNumber, 2, currentpower, myWriteAPIKey);
      WebSerial.println("SEND");
        } else {
        Serial.println("WiFi Disconnected");
        takt_send = 0;
    }
    takt_send = 0;
  }

  memset(smlMessage, 0, sizeof(smlMessage));
  memset(power, 0, sizeof(power));
  memset(consumption, 0, sizeof(consumption));
  //reset case
  smlIndex = 0;
  stage = 0;  // start over
}

IMO sollten die Abbruchkriterien (Indizes) sauber initialisiert werden und ein Zustand auch dann verlassen werden, wenn unerwartete Zeichen gefunden wurden. Sonst kann sich das Programm irgendwo beliebig lange aufhalten.

Zustände müssen nicht sequentiell durchlaufen werden. Nach Fehlern kann/soll ggf. auch der Anfangszustand wiederhergestellt werden.

Namen für die Zustände wären auch sehr hilfreich.

Das mit dem while würde ich nicht machen.
Und dann prüfe mal, ob da Daten drin sind:
(sSerial.available()>0)
bevor Du mit .read() da ran gehst.

Danke für die Antwort.
Aber wie soll ich das in dem Fall anders realisieren? Ich will ja das er solange "einliest" so lange eben daten vorhanden sind? Die Daten vom Zähler werden ja kontinuierlich ca jede Sekunde als Protokoll übertragen.

Das meinte ich ja. Sollte er keine Daten mehr bekommen ist ja klar das er auch keine mehr überträgt. Sollte auch keinen Fehler verursachen in der Form. Fakt ist es kommen Daten und die Daten kommen richtig und trotzdem hängt es. Also an welcher Stelle könnte das passieren?

Wie kann available() < 0 werden?
Sonst ist ein Vergleich überflüssig.

An sich geht das schon. Es kann nicht kleiner 0 es kann aber GLEICH 0 sein :wink:

Mal anders gefragt. Was würde passieren wenn er x mal durch die Stopsequenz durchläuft. sagen wir 1 mio mal...also wirklicht oft. Dann hätte meine array 1mio Werte. Rein theoretisch würde das Programm selbst dann noch weiter laufen wenn beim Versuch 1000001 versuch dann die endsequenz kommt. Aber was passiert mit dem Speicher wenn da zu irgendwann in nem array zu viel drin steht? läuft einfach über und resetet sich <-- könnte dann einen fehler verursachen weil dann die Stopsequenz abgeschlossen wäre aber nicht sichergestelt das auch alle daten im array sind.

Meiner Meinung nach kann der Fehler doch nur entstehen wenn er Start und Stop sequenz hat...dann aber keinen Inhalt findet. dann würde er da für immer festhängen...richtig?

void findConsumptionSequence() {
  byte temp;

  startIndex = 0;
  for (int x = 0; x < sizeof(smlMessage); x++) {
    temp = smlMessage[x];
    if (temp == consumptionSequence[startIndex]) {
      startIndex++;
      if (startIndex == sizeof(consumptionSequence)) {
        for (int y = 0; y < 8; y++) {
          //hier muss für die folgenden 8 Bytes hoch gezählt werden
          consumption[y] = smlMessage[x + y + 1];
        }
        stage = 4;
        startIndex = 0;
      }
    } else {
      startIndex = 0;
    }
  }

  currentconsumption = (consumption[0] << 56 | consumption[1] << 48 | consumption[2] << 40 | consumption[32] | consumption[4] << 24 | consumption[5] << 16 | consumption[6] << 8 | consumption[7]);  //combine and turn 8 bytes into one variable
}

in diesem Teil meine ich. Wenn er da die gesucht sequenz nicht findet hängt er da doch für immer fest?

Eigentlich nicht.
Du willst solange einlesen, bis das Ende des Telegramm erkannt wurde.
Ich würde den buffer füllen, wenn Startsequenz erkannt, dann füllen und dann ständig vergleichen ob die letzten Zeichen der EndeKennung entsprechen.
Dann ist der Buffer voll und kann ausgewertet werden.
Einen etwas anderen Ansatz habe ich vor zwei Tagen verfolgt. Bzw. dann später in #21 - da fehlt mir noch ein Feedback.

Das ist der erste Fehler, bei dem irgendwann der Speicher vollgemüllt ist. Wenn beim Lesen der Startsequenz ein Fehler auftritt dann geht alles wieder von vorne los. Das gilt auch für alle anderen Sequenzen. Erst wenn auch die Stopsequenz erkannt wurde ist die Eingabe vollständig und korrekt und kann ausgewertet werden.

In einem PC Programm würde man bei einem Fehler beepen um anzuzeigen, daß das Programm noch läuft aber noch keine gültigen Daten erhalten hat.

das heißt? kann ich den seriellen speicher löschen oder muss ich das anders angehen?

Ich hatte mal vor Jahren einen Ansatz für die Geschichte in Bascom geschrieben. Da hab ich es aber Zeichen für Zeichen gemacht. Also Geprüft wann 4x 1b und 4x 01 kommt ab da alle Zeichen gespeichert.

Dazu reicht es, die Indizes zu löschen und in den Startzustand überzugehen, in dem auf die nächste Startsequenz gewartet wird.

Die konstanten Sequenzen muß man nicht (dauerhaft) speichern, es reicht die eintreffenden Zeichen mit den erwarteten Zeichen der aktuellen Sequenz zu vergleichen. Wenn die eigentlichen Daten (payload) eine feste Länge haben, dann können die in ein entsprechendes Feld eingelesen werden.

void findStartSequence() {
  while (sSerial.available()) {
    inByte = sSerial.read();  //read serial buffer into array
    if (inByte == startSequence[startIndex]) {
      smlMessage[startIndex] = inByte;
      startIndex++;
      if (startIndex == sizeof(startSequence)) {
        //WebSerial.println("Match found");
        stage = 1;
        smlIndex = startIndex;
        startIndex = 0;
      }
    } else {
      startIndex = 0;
      smlIndex = 0;
      stage = 0;
    }
  }
}

Meinst du so? Steh gerade auf dem Schlauch

So in etwa. Aber warum die geprüften Zeichen auch noch speichern?

Ich hätte die Prüfung auf die Start- und Stopsequenz so angesetzt.

void findStartSequence()
{
  if (sSerial.available() > 0)
  {
    inByte = sSerial.read();  //read serial buffer into array
    if (inByte == startSequence[startIndex])
    {
      startIndex++;
      if (startIndex == sizeof(startSequence))
      {
        //WebSerial.println("Match found");
        stage = 1;
        smlIndex = 0;
        startIndex = 0;
      }
    }
    else
    {
      startIndex = 0;
    }
  }
}



void findStopSequence()
{
  if (sSerial.available() > 0)
  {
    inByte = sSerial.read();
    smlMessage[smlIndex] = inByte;
    smlIndex++;
    if (inByte == stopSequence[stopIndex])
    {
      stopIndex++;
      if (stopIndex == sizeof(stopSequence))
      {
        stage = 2;
        stopIndex = 0;
      }
    }
    else
    {
      if (stopIndex != 0)
      {
        memset(smlMessage, 0, sizeof(smlMessage));
        stage = 1;
      }
      stopIndex = 0;
    }
  }
}

Allerdings ungetestet - ich hab keinen ESP.

Du warst schneller :wink:

Was soll dieser smlIndex? Da nichts gespeichert wurde, zeigt er nirgendwo hin.

Übersehen.

Sollte er nicht vielmehr auf 0 gesetzt werden?

Nein weil er für die Zählung ja erst nach den ersten 8 byte weiter machen soll bei der Stopsequenz. An sich bräuchte ich es wohl gar nicht. Aber damit wäre das ganze Protokoll gespeichert. In der Stopsequenz wird smlindex ja als counter für die restlichen zeichen verwendet.

Wäre er auf 0 würde er die Startsequenz überschreiben. Wäre wie gesagt nicht weiter wild aber naja. Falsch ist es auch nicht.

Da ich die Startsequenz nicht speichere, sondern nur ihre Vollständigkeit prüfe, ist smlIndex beim Start der Prüfung auf die StopSequenz 0.
Das ist so schon richtig....

Bin ich jetzt blöd? smlindex steht zu beginn der Stopsequenz auf 7 nicht auf 0 ?