Probleme mit Daten über SerialRead verarbeiten.

Hallo zusammen, ich habe eine Füllanlage und bekomme über einen RS232 Ausgang verschiedene Meldungen. Es gab einmal einen DOS Rechner zur Steuerung bzw. Ablaufkontrolle. Ich kann auch über einen MAX 232 die Daten empfangen. Die Daten sehen wie folgt aus. Die Zahlen können sich sehr unterscheiden, sind aber für die Steuerung nicht wichtig. Über eine Hilfe würde ich mich sehr freuen.
Grüße Alex

+Input1: 1,0,0,1,0

+Band1: 1,1,0,1,1,1

+Input2: 2,0,0,1,0

+STOER: 2,3,Zu Voll

Ich möchte je eine LED leuchten lassen wenn +Input1, +Input2, +Input3 und Input4 kommt und die vorherige LED löschen beim nächsten Schritt. Das funktioniert auch so, ebenfalls kann ich auch Text auf einem LCD Display anzeigen lassen. Die Störungsmeldungen +STOER: kann verschiedene Meldungen bringen. Z.b. "Zu Voll" "leer" "Glas pruefen" diese Meldungen möchte ich auf dem Display anzeigen lassen. Im Moment kommt "Stoerung", ich möchte aber die Fehler angezeigt haben.

Hier mein Code für den MEGA:

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27,20,4);  // set the LCD address to 0x20 for a 16 chars and 2 line display

String cmd;
byte led=13;
byte led1=10;
byte led2=11;
byte led3=12;
void setup(){
  lcd.begin(); //startet lcd
  lcd.backlight();
  lcd.print("Fuellanlage");
  
  Serial.begin(9600);
  Serial1.begin(9600);
  pinMode(led,OUTPUT);
  pinMode(led1,OUTPUT);
  pinMode(led2,OUTPUT);
  pinMode(led3,OUTPUT);
}

void loop()
{
  char nextChar;
  if (Serial1.available() > 0)
  {
    nextChar = Serial1.read();
    
    if( nextChar == '+' ) {
     
      cmd = "";
    }
    if( nextChar == ':' ) {
       
      if( cmd == "+Input1" ){
      
      digitalWrite(led1, HIGH);
      Serial.println( "motor1" );
      lcd.clear();
      lcd.print("motor1");
      }
      if( cmd == "+Input2" ){
      digitalWrite(led1, LOW);
      digitalWrite(led2, HIGH);
      Serial.println( "Fuellung" );
      lcd.clear();
      lcd.print("Fuellung");
      }
      if( cmd == "+Input3"){
      digitalWrite(led2, LOW);
      digitalWrite(led3, High);
      Serial.println( "Motor2" );
      lcd.clear();
      lcd.setCursor (8,2);
      lcd.print("Motor2");
      }
      /*if( cmd == "+Input4" ){
      digitalWrite(led3, LOW);
      Serial.println( "fertig" );
      lcd.clear();
      lcd.setCursor (8,2);
      lcd.print("fertig");
      }*/
      lcd.clear();
      if( cmd == "+STOER" ){
      Serial.println( "Stoerung" );
      digitalWrite(led3, HIGH);

      lcd.print("Stoerung");
      }
      
      cmd = "";
    
  }
    else if (nextChar>=32){
      cmd += nextChar;
      
    }
  }
}

Die Störungsmeldungen +STOER: kann verschiedene Meldungen bringen. Z.b. "Zu Voll" "leer" "Glas pruefen" diese Meldungen möchte ich auf dem Display anzeigen lassen. Im Moment kommt "Stoerung", ich möchte aber die Fehler angezeigt haben.

dann solltest im if von +STOER auch die weiteren Daten lesen und nach dem zweiten Komma den folgenden Text für die Feldermeldung am Display verwenden.

Leichter ist es aber vermutlich du liest immer alles von + zu + (oder Zeitablauf!) und parst dann ersten Teil für die Art der Meldung, und im Falle der +STOER eben auch den hinteren Teil.

Klingt sehr gut, aber wie kann ich anstatt ":" das Zeilenende als Eingabeschluss nehmen und wie teile ich den Inhalt mit einem Trennzeichen "," ?

Ich habe schon einiges gelesen komme aber noch nicht auf den "grünen Zweig".
Als hoffentlich letztes; wie bekomme ich dann das nach dem 2. "," auf das Display.

Ich habe vor ca. 3 Wochen mit dem Arduino angefangen und bin schon ganz glücklich über mein Ergebniss.

Ach du hättest eh ein LF oder CR am Ende einer Ausgabe?

Finde raus ob LF oder CR.

Kannst dich drauf verlassen dass das zweite Komma immer an der gleichen Stelle steht oder können da auch mehrstellige Zahlen kommen?

012345678901
+STOER: 2,3,Zu Voll

im einfacheren Fall könntest also einfach ab stelle 12 bis EOL lesen.
geht das?

und wenn nicht, ich sehe du nimmst eh schon ein String Objekt. Da gibts ja eh viele Funktionen zum durchsuchen:

wenn du auf dieser Codebasis weiterarbeiten willst ... lies bis zum Zeilenende, mach deine Vergleiche über substrings oder startsWith und nur im +STOER suchst halt nach dem letzten Beistrich
... da gibts sicher auch was ... z.B. lastIndexOf ...
und dann bis zum Ende und du hast deinen Text.

schön ist es nicht, aber so für die ersten 3 Wochen schon ok :wink:

ach ja und bitte pack deinen code in code tags ...(eckige Klammer code eckige klammer zu)

add on:
dieser Thread ist super zum Lernen von Serieller Kommunikation:
http://forum.arduino.cc/index.php?topic=396450.0

vielen Dank an euch, ich lese dann mal den Beitrag durch, leider sind die Zahlenwerte von unterschiedlicher Länge. Also enfach die Stellen abzählen kann ich da nicht machen.

Aber du hast ja anscheinend gute Trennzeichen:

  • ein Plus vor der "Bezeichnung"
  • den Doppelpunkt bevor die Werte beginnen und
  • Kommas zwischen den Werten
  • CR oder/und LF am Zeilenende.

Damit kann man gut arbeiten.

Am besten du liest bis zum Zeilenende in ein Charakter-Array ein und kannst dann an den Trennzeichen die Nachricht in die einzelnen Teile teilen.
Das kann man mit strtok() und atoi() machen; siehe:
http://forum.arduino.cc/index.php?topic=329469.msg2273780#msg2273780
http://forum.arduino.cc/index.php?topic=350808
http://forum.arduino.cc/index.php?topic=362179.msg2497172#msg2497172

Aus den vielen Beispielen im Forum hab ich mal ein weiteres Beispiel gebastelt :slight_smile:
Es teilt die Zeichenketten (die "Telegramme") aus dem Ausganspost in einzelne Teile auf:

/* Telegrammauswertung
 * Ein Datentelegramm soll in seine Bestandteile zerlegt werden. 
 * Jedes Telegramm wird durch CR oder LF abgeschlossen.
 * Beispiele:
 * +Input1: 1,0,0,1,0
 * +Band1: 1,1,0,1,1,1
 * +Input2: 2,0,0,1,0
 * +STOER: 2,3,Zu Voll
 * 
 * siehe:
 * https://de.wikibooks.org/wiki/C-Programmierung:_Zeichenkettenfunktionen#strtok
*/

const int SERIAL_BUFFER_SIZE = 30;      // die maximale Telegrammlänge (die Endzeichen nicht vergessen)
char serial_buffer[SERIAL_BUFFER_SIZE];

void setup() {
  Serial.begin(9600);
}

void loop() {
  if (read_serial()) 
  {
    parse_serial();
  }
}


// Einlesen einzelner Zeichen und Anhängen an ein Char-Array. 
// True, wenn das Zeilenende (CR oder LF) erreicht ist.
bool read_serial() {
  static byte index;
  while (Serial.available()) {
    char c = Serial.read();

    if (c >= 32 && index < SERIAL_BUFFER_SIZE - 1) {
      serial_buffer[index++] = c;
    }
    else if (c == '\r' || c == '\n') {    // \r ist CR, \n = LF
      serial_buffer[index] = '\0';        // Array abschließen
      index = 0;
      return true;
    }
  }
  return false;
}


// Trennen und Ausgeben der einzelnen Abschnitte des Telegramms
void parse_serial() {
  char trennzeichen[] = {"+:,"};
  char *abschnitt;
  abschnitt = strtok(serial_buffer, trennzeichen);   // Zerteilen
  while (abschnitt != NULL) {    // Ausgabe
    Serial.println(abschnitt);
    abschnitt = strtok(NULL, trennzeichen);
  }
  Serial.println();
}

Du musst das natürlich noch auf deine Gegebenheiten anpassen.
Sowohl die Eingabe als auch die Ausgabe erfolgen über den seriellen Monitor.
Getrennt wird bei folgenden Zeichen: Plus, Doppelpunkt, Komma (+:,)

Die folgenden Schritte könnten sein: Weitere Auswertungen der Abschnitte im Telegramm.

  • Also zum Beispiel ob "Input1", "Input2", "STOER" u.s.w. vorkommt (das geht mit strcmp - siehe Link unten)
  • Oder eine Auswertung der Zahlenwerte (dazu gibt es atoi).
    Je nachdem, was du brauchst.

siehe auch: C-Programmierung: Zeichenkettenfunktionen – Wikibooks, Sammlung freier Lehr-, Sach- und Fachbücher
hier findest du auch "strcmp".

Ich habe nun folgendes gefunden und angepasst. Leider finde ich keine Möglichkeit meinen Sting "cmd" in einen char* zu wandeln.
Wer kann mir da einen Tipp geben?

hiermit kann ich ein Char* teilen und Einzelteile anzeigen lassen.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <stdio.h>
#include <string.h>
LiquidCrystal_I2C lcd(0x27,20,4);
void setup()
{
Serial.begin(9600);
lcd.begin(); //startet lcd
lcd.backlight();

char* value = "+STOER: 2,3,Zu Voll";
char* token = strtok(value, ",");
char* ta = token;
token= strtok(0, ",");
char* tb = token;
token = strtok(0, ",");
char* tc = token;
token = strtok(0, ",");
char* td = token;
token = strtok(0, ",");
char* te = token;

//Serial.println(ta);
//Serial.println(tb);
Serial.print("Fehler: ");
Serial.println(tc);
lcd.clear();
lcd.print(tc);
//Serial.println(td);
//Serial.println(te);
}

void loop()
{
}

Verzichte auf die String Klasse. Lese gleich in ein Array ein (und lerne dabei den Zusammenhang zwischen Array und Zeigern).

Wird in dem Beispiel oben doch gezeigt

Hallo zusammen,
ich habe nun in Ruhe die Link´s und Kommentare durchgeackert. Es waren sehr hilfreiche Tipps, die mir hilfreich zu einem funktionierenden Ergebniss geholfen haben. Vielen Dank für Eure Hilfe. Wahrscheinlich ist meine Lösung nicht optimal, aber es funktioniert :smiley:

Grüße Alex

hier noch mein CODE:

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <stdio.h>
#include <string.h>
const int SERIAL_BUFFER_SIZE = 38;      // die maximale Telegrammlänge (die Endzeichen nicht vergessen)
char serial_buffer[SERIAL_BUFFER_SIZE];
LiquidCrystal_I2C lcd(0x27,20,4);

void setup()
{
Serial.begin(9600);
lcd.begin(); //startet lcd
 lcd.backlight();
 Serial.println("Fuellanlage");
 lcd.print("Fuellanlage");
 delay(1000);
 lcd.clear();
}

void loop() {
if (read_serial()) 
{
  parse_serial();
}
}
// Einlesen einzelner Zeichen und Anhängen an ein Char-Array. 
// True, wenn das Zeilenende (CR oder LF) erreicht ist.
bool read_serial() {
static byte index;
while (Serial.available()) {
  char c = Serial.read();

  if (c >= 32 && index < SERIAL_BUFFER_SIZE - 1) {
    serial_buffer[index++] = c;
  }
  else if (c == '\r' || c == '\n') {    // \r ist CR, \n = LF
    serial_buffer[index] = '\0';        // Array abschließen
    index = 0;
    return true;
  }
}
return false;
}
// Trennen und Ausgeben der einzelnen Abschnitte des Telegramms
void parse_serial() {
char trennzeichen[] = {"+:,"};

char* token = strtok(serial_buffer, ":,");
char* ta = token;
token= strtok(0, ",");
char* tb = token;
token = strtok(0, ",");
char* tc = token;
token = strtok(0, ",");
char* td = token;
token = strtok(0, ",");
char* te = token;
char Steor = ("Stoer");
char In1 = ("Input1");
if(strcmp(ta, Stoer) == 0)
{
digitalWrite(13, HIGH);//Nur zum Testen
Serial.println("Stoer");
Serial.println(td);
lcd.print(td);
}
if(strcmp(ta, In1) == 0)
{
Serial.println("Input1");
digitalWrite(13, LOW);//Nur zum Testen
}
}

Gewöhne dir an Code Tags zu verwenden. Der </> Knopf oben links

Und C Strings vergleicht man mit strcmp() (string compare):
http://www.cplusplus.com/reference/cstring/strcmp/
Liefert bei Gleichheit 0 (nicht true!)

Alles klar, das ist natürlich übersichtlicher. Vielen Dank noch mal an alle.

Alex

if(strcmp(ta, ende) == 0)
{
digitalWrite(13, LOW);
lcd.clear();
lcd.print("frei");
Serial.println("frei");
}
</>