Serielles Telegramm lesen und verarbeiten

Hallo zusammen,

ich habe hier ein altes Messgerät das mir Messwerte als ein serielles Telegramm sendet (9600 bps) die ich weiter verarbeiten möchte. (Sollen auf einem Display angezeigt und alle 30 sek in eine SQL DB gespeichert werden)

Display und SQL DB sind kein Thema, das habe ich bereits hinbekommen. Allerdings nur festen Werten ;D

Das Telegramm ist immer gleich lang (740 Byte) und wird jede Sekunde gesendet.
Es beginnt mit einem Header "01 01 01 01 3F 3F 3F 3F" und dann kommen die Nutzdaten - relevant sind für mich 4 Werte die ich mir, nachdem ich das volle Telegramm in einem Array hab einfach raushol und verarbeite. (Die stellen habe ich bereits)

Mein Problem ist momentan:
Wie erkenne ich den Header und fange erst dann an das Telegramm in ein Array zu speichern?
Mir fehlt da irgendwie der Ansatz :-X

Danke.

Ein ähnliches Problem hatten wir im Mai hier schon mal.

Musst halt die gelesenen Bytes testen, ggf. ignorieren oder aber mitzählen.
Typischer Fall von einem Apparat Programm mit mindestens drei Zuständen (0x01 lesen, 0x3F lesen, Nutzbytes lesen).

740byte ist noch eine handhabbare Größe. Gibt es ein bestimmtes Zeichen mit dem das "Telegramm" abschließt?
Das würde sich gut zur Erkennung "Telegramm fertig" eignen.
Die andere Variante ist ein Timeout. Wenn die 740 byte wirklich immer ohne Zwischenpausen hereingesaust kommen
dann könnte man auch einen Timeout verwenden. 740 Byte bei 9600 Baut benötigt ca 78 millisekunden. Wenn das Telegramm nur einmal pro Sekunde kommt. Müsste man mit einem Timeout von 200 bis 500 Millisekunden gut himkommen.
Die Zeichen werden einfach in einem Array of char eingelesen.
Vielleicht eignet sich dafür auch PString. Das ist eine Library die etwas mehr Komfort bietet wie array of char aber keinen Speicherüberlauf produzieren kann wie bei global definierten Strings ( Der Datentyp mit dem großgeschreibenen "S".

Wenn die Werte in jedem Telegramm an absolut feststehende Positionen stehen kann man dann einfach die einzelnen Zeciehn von zum Beispiel Array-Element 17 bis Array-Element 29 in eine andere PString-Variable kopieren (durch fortlaufendes Anhängen des nächsten bytes und dann diese Variablen weiterverarbeiten

viele Grüße Stefan

string.h bietet Vergleiche beispielsweise mit strncmp.

Danke für die Infos erstmal. Werde ich mir anschauen und wenn ich nicht weiterkomme oder die Lösung hab schreib ichs hier rein :slight_smile:

Volles Telegramm kann ich heute Abend posten.

@StefanL38
Das Telegramm endet mit 3F 3F 3F 3F FF FF

@noiasca
Gibt leider weder Marke noch Typ. Es ist ein selbst gebautes Messgerät um Düsenverwirbelungen zu testen.

noiasca:
ja, im Prinzip könntest du den Sketch von Post 18 anpassen.

fylw:
Das Telegramm endet mit 3F 3F 3F 3F FF FF

Darauf basierend mal ein Vorschlag:

const uint16_t erw = 740; // Anzahl erwartete Zeichen
const uint16_t numChars = 750;
byte receivedChars[numChars];

boolean newData = false;

void setup() {
  Serial.begin(115200);
  Serial.println("\n<Arduino is ready>");
  Serial1.begin(9600);
}

void loop() {
  recvWithStartEndMarkers();
  showNewData();
}

void recvWithStartEndMarkers() {
  static uint16_t ndx = 0;
  byte rc;

  while (Serial1.available() > 0 && newData == false) {
    rc = Serial1.read();
    Serial.print(ndx); Serial.print('\t'); Serial.println(rc, HEX);
    receivedChars[ndx] = rc;
    if (ndx > 5 && receivedChars[ndx - 5] == 0x3F && receivedChars[ndx - 4] == 0x3F && receivedChars[ndx - 3] == 0x3F && receivedChars[ndx - 2] == 0x3F && receivedChars[ndx - 1] == 0xFF && receivedChars[ndx] == 0xFF) {
      if (ndx == (erw-1) && receivedChars[ndx - erw+1] == 0x01 && receivedChars[ndx - erw+2] == 0x01 && receivedChars[ndx - erw+3] == 0x01 && receivedChars[ndx - erw+4] == 0x01 && receivedChars[ndx - erw+5] == 0x3F && receivedChars[ndx - erw+6] == 0x3F && receivedChars[ndx - erw+7] == 0x3F && receivedChars[ndx - erw+8] == 0x3F ) {
        newData = true;
      }
      ndx = 0;
    } else {
      ndx = (1 + ndx) % numChars;
    }
  }
}

void showNewData() {
  if (newData == true) {
    Serial.print("\nDaten zur Auswertung: ");
    for (uint16_t i = 0; i < erw; i++)
    {
      if (receivedChars[i] < 0x10) Serial.print("0");
      Serial.print (receivedChars[i], HEX);
      Serial.print(" ");
    }
    Serial.println();
    newData = false;
  }
}

noiasca:
DU hättest aber den ersten If vereinfachen können oder :wink:

Wenn Du mich meinst: Ich sehe verschiedene Verbesserungsmöglichkeiten, andererseits auch die Gefahr, mit Zeigern und dergleichen zu verwirren. Also gebe ich eine Anregung und bin dann gespannt, wie diese aufgenommen wird. Wenn der anschließende Dialog aus bleibt, ist das dann halt so.

Ihr seid der Hammer :smiley:

Hab mich leider vertan, das Telegramm endet nicht mit 3F3F3F3FFFFF sondern mit 2 Werten die sich ständig ändern.

Ich hab ein Telegramm jetzt aufgezeichnet.:

010101013F3F3F3F = Header
44554553454E544553545354415254 = DUESENTESTSTART ASCII
FFFFFFFF = Keinen Plan, ändert sich nie
0101003B0201003B0301003B0102003B0202003B0302003B0103003B0203003B0303003B0104003B0204003B0304003B0105003B0205003B0305003B0106003B0206003B0306003B0107003B0207003B0307003B0108003B0208003B0308003B0109003B0209003B0309003B010A003B020A003B030A003B010B003B020B003B030B003B010C003B020C003B030C003B010D003B020D003B030D003B010E003B020E003B030E003B010F003B020F003B030F003B0110003B0210003B0310003B0111003B0211003B0311003B0112003B0212003B0312003B0113003B0213003B0313003B0114003B0214003B0314003B0115003B0215003B0315003B0116003B0216003B0316003B0117003B0217003B0317003B0118003B0218003B0318003B0119003B0219003B0319003B011A003B021A003B031A003B011B003B021B003B031B003B011C003B021C003B031C003B011D003B021D003B031D003B011E003B021E003B031E003B011F003B021F003B031F003B0120003B0220003B0320003B0121003B0221003B0321003B0122003B0222003B0322003B0123003B0223003B0324003B0124003B0224003B0324003B0125003B0225003B0325003B0126003B0226003B0326003B0127003B0227003B0327003B0128003B0228003B0328003B0129003B0229003B0329003B012A003B022A003B032A003B012B003B022B003B032B003B012C003B022C003B032C003B012D003B022D003B032D003B012E003B022E003B032E003B012F003B022F003B032F003B0130003B0230003B0330003B0131003B0231003B0331003B0132003B0232003B0332003B = Nutzdaten (Sind jeweils 4 Byte, 0 = Ring (1,2,3) 1=Loch (1-50) 2 = Durchfluss 0/1 3=Trennzeichen

518AC0F12B28028114F070968F34F5C51C6CF34F095D89F614FB9DF88839CDBEA9C6C3A657976F24E6A6B421614A4511632017FF4B2EE8257832411C381722FA4CB2BDC7FD72F68FEF784316E1F163DB503262DC7FE3AB77DED6FD31D1D9CEA6DFD336BD8CB4052925 = Immer gleich lang und ändern sich mit jedem Übertrag aber keine Ahnung was da drin steht, da aber oben alle Relevanten Daten drin stehen ists mir egal.

3F3F3F3FFFFF = Endsequenz, immer gleich.
A1FD = Bei jeder Übertragung unterschiedlich, könnte eine Prüfsumme sein

Melde mich später nochmal... :slight_smile:

Das war jetzt ein K(r)ampf.

/*
    Serial Receive 740 byte telegram
    
     0  1  2  3  4  5  6  7
    01 01 01 01 3F 3F 3F 3F     8 Startbytes fix
    Nutzdaten                   740-8-6= 736 Bytes

     -6  -5  -4  -3  -2  -1
    734 735 736 737 738 739
    3F   3F  3F  3F  FF  FF     6 Endbytes fix
    740 bytes ingesamt fix

    by noiasca
    https://forum.arduino.cc/index.php?topic=694031.0

    basierend auf: https://forum.arduino.cc/index.php?topic=682436
*/

const uint16_t numChars = 740;
byte receivedChars[numChars + 1];

boolean newData = false;

void setup() {
  Serial.begin(9600);
  Serial.println(F("<Arduino is ready>"));
}

void loop() {
  recvWithStartEndMarkers();
  showNewData();
}

void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static uint16_t ndx = 0;                    // index
  byte rc;                                    // received character

  while (Serial.available() > 0 && newData == false) {
    rc = Serial.read();
    if (recvInProgress == false)
    {
      //Serial.println(F("\nD041: reset buffer"));
      memset(receivedChars, 0, numChars);
      ndx = 0;
      recvInProgress = true;
      receivedChars[ndx] = rc;
      Serial.println();
    }
    else if (recvInProgress)
    {
      //Serial.println(F("\nD050: ok"));
      ndx++;
      receivedChars[ndx] = rc;
    }
    //Serial.print(ndx); Serial.print(" "); Serial.println(rc, HEX); // debug output received ndx and character

    // Prüfungen
    // Start 01
    if (recvInProgress && ndx <= 3 && rc != 0x01)
    {
      Serial.print(ndx); Serial.println(F(" reset: start not 0x01"));
      ndx = 0;
      recvInProgress = false;
    }
    // Start 3F
    if (recvInProgress && (ndx >= 4 && ndx <= 7) && rc != 0x3F)
    {
      Serial.print(ndx); Serial.println(F(" reset: start not 0x3F"));
      ndx = 0;
      recvInProgress = false;
    }
    // Ende
    if (recvInProgress && (ndx >= numChars - 6 && ndx <= numChars - 3) && rc != 0x3F)  // 734 - 737
    {
      Serial.print(ndx); Serial.println(F(" reset: end not 0x3F"));
      ndx = 0;
      recvInProgress = false;
    }
    if (recvInProgress && (ndx >= numChars - 2 && ndx <= numChars - 1) && rc != 0xFF)  // 738 - 739
    {
      Serial.print(ndx); Serial.println(F(" reset: end not 0xFF"));
      ndx = 0;
      recvInProgress = false;
    }
    // CR and LF will stop receiver with an error
    if (recvInProgress && (rc == 0x10 || rc == 0x13))
    {
      Serial.println(F("reset CR / LF "));
      ndx = 0;
      recvInProgress = false;
    }

    if (ndx >= numChars - 1)   // 739
    {
      Serial.println(F("\n char limit reached without error"));
      ndx = 0;
      newData = true;          // we have received enough new data
      recvInProgress = false;
    }
  }
}

void showNewData() {
  if (newData == true) {
    Serial.print(F("\nThis just in:"));
    for (uint16_t i = 0; i < numChars; i++)
    {
      if (receivedChars[i] < 0x10) Serial.print("0");
      Serial.print (receivedChars[i], HEX);
      Serial.print(" ");
    }
    Serial.println();
    newData = false;
  }
}

im Musterfile ist eine 740byte Nachricht mit den startbytes/endbytes so wie du es ursprünglich definiert hast.

Imho funktioniert es.
Schön ist es nicht.

Da jetzt deine Anforderung anders geworden ist, musst das halt selber umändern.
Hab jetzt keinen Bock mehr.

ps

Ich hab ein Telegramm jetzt aufgezeichnet

stell das als file zur Verfügung, weil dein zerissenes Beispiel will keiner Zusammenkopieren und konvertieren müssen...

musterfile.txt (740 Bytes)

noiasca:
Das war jetzt ein K(r)ampf.
...
Hab jetzt keinen Bock mehr.

Da hilft süßer Schlaf :sleeping:

Gute Nacht!