Seriell eingelesene Daten verarbeiten

Guten Abend!

Ich benötige mal etwas Hilfe bei einem Vorhaben! Ich bin noch recht neu in der Arduino Programmierung, bin aber bereit etwas zu lernen. Ich sitze seit ein paar Tagen vor einem Problem, wo ich nicht ganz weiter komme.

Und zwar folgendes:
Ich habe einen Arylic Up2Stream Pro V3 hier, welcher mir per UART serielle Daten zusendet. Hierbei handelt es sich beispielsweise um Infos, wenn die Lautstärke verändert wird oder Trackinformationen kommen.

Um die letzteren geht es mir auch, denn diese würde ich im Endeffekt gerne einlesen wollen und auf einem OLED mit SSD1306 treiber anzeigen wollen.
Der vom Streamer gesendete Zeichenstring sieht folgendermaßen aus: ELP:2257/144266;TIT:titel;ART:artist;ALB:album;VND:spotify;ELP:2257/224066;.

Aus diesem Zeichenstring würde ich gerne auf dem Display den Titel, den Artist und das Album angezeigt haben. Jedoch ohne TIT:, ART: und ALB: davor.

Soweit die Theorie. In der Praxis habe ich bereits einen Sketch, welcher mir die seriellen Rohdachten direkt in den seriellen Monitor schreibt.

char Daten;

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

void loop() {
String text = String(Daten);
  if (Serial.available()) {
    Daten = Serial.read();
    Serial.print(text);
  }
}

Der Code ist jetzt definitiv nichts wildes, aber das funktioniert schonmal. Wobei ich jetzt einfach nicht weiter komme, ist, wie ich die Titel-, Artitst- und Album-Informationen in extra strings verpacken kann um diese dann auf dem OLED anzeigen zu können.

Mit strtok habe ich mich schonmal beschäftigt, aber leider nichts ansatzweise funktionelles ans laufen gekriegt.

Hat jemand von euch eine Idee zu meinem Problem? Das wäre super. Vielen Dank schonmal :slightly_smiling_face:

Wenn ich irgendwelche Infos vergessen habe, bitte Bescheid sagen. Liefer ich dann nach!

Viele Grüße,
Chris

P.S.: Es soll ein MEGA2560 zum Einsatz kommen.

Im englischen Teil des Forum müssen die Beiträge und Diskussionen in englischer Sprache verfasst werden. Deswegen wurde diese Diskussion in den deutschen Teil des Forums verschoben.

mfg ein Moderator.

Strtok ist für dein Problem schon richtig.
Hier ist ein Beispiel

1 Like

Du musst erst mal das Einlesen vernünftig machen und alles in ein Array schreiben. Erst danach kannst du es auswerten.

Nur ein Zeichen einlesen und das direkt ausgeben bringt nichts

const unsigned int READ_BUFFER_SIZE = 80;

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

void loop()
{
  char* str = readLine(Serial);
  if (str != nullptr)      //ist wahr wenn das LF eingelesen wurde
  {
    Serial.print("Eingelesen: ");
    Serial.println(str);

    parseSerial(str);
  }
}

char* readLine(Stream& stream)
{
  static byte index;
  static char buffer[READ_BUFFER_SIZE + 1];

  while (stream.available())
  {
    char c = stream.read();

    if (c == '\n')              //wenn LF eingelesen
    {
      buffer[index] = '\0';     //String terminieren
      index = 0;
      return buffer;            //melden dass String fertig eingelesen wurde
    }
    else if (c >= 32 && index < READ_BUFFER_SIZE - 1)   //solange noch Platz im Puffer ist
    {
      buffer[index++] = c;    //Zeichen abspeichern und Index inkrementieren
    }
  }
  return nullptr;                //noch nicht fertig
}

void parseSerial(char* str)
{
  char* ptr = strtok(str, ";");
  Serial.println();
  
  while (ptr)
  {
    Serial.println(ptr);
    ptr = strtok(nullptr, ";");  
  }
  Serial.println();
}

Serial Monitor so einstellen dass ein LF/newline am Ende gesendet wird! Auswertung ist nur vereinfacht.

1 Like

Wie fit bist Du? Kannst Du aus einem anderen Code entnehmen, was für Dich gültig ist?
Dann darf ich Dich einladen.
Das geht ab da noch weiter.
Wenn Fragen sind.. Bau mal was, dann schaun wa mal.

1 Like

Das ist missverständlich.
Das würde bedeuten, das erstmal irgendwie alles aufgenommen werden müsste.
Da kommt kein LF.
Das ist eine fortlaufende mit einem ';' separierte Zeichenkette.
Das Ende ist jeweils das Semikolon.
Geteilt wird dann mit dem Doppelpunkt und dann (Ich heute -> struct) Einstieg: Array für jeden Wert.
Das sehe dann so aus: (Ohne Längenprüfung beim einlesen)

// char myBuf[] = "ELP:2257/144266;TIT:titel;ART:artist;ALB:album;VND:spotify;ELP:2257/224066";
const uint8_t maxLen = 80;
char elp[maxLen] = {'\0'};
char tit[maxLen] = {'\0'};
char art[maxLen] = {'\0'};
char alb[maxLen] = {'\0'};
char vnd[maxLen] = {'\0'};
bool ausgabe = false;
void setup()
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
}
void loop()
{
  readSerial();
  serMon();
}
void readSerial()
{
  const uint32_t breakTimeRead = 50;
  char myChar;             // verwertbares Zeichen
  byte x = 0;              // Position im Array
  char buf[maxLen] = {'\0'};   // Zwischenspeicher
  unsigned long lastmillis = millis(); // Startzeit merken...
  while (Serial.available() > 0 &&
         millis() - lastmillis < breakTimeRead)
  {
    myChar = Serial.read();
    if (myChar == ';')
    {
      ausgabe = true;
      Serial.print(F("Teile :")); Serial.println(buf);
      teileBuf(buf);
      x = 0;
    }
    else
    {
      buf[x] = myChar;
      x++;
    }
  }
}

void teileBuf(char *buf)
{
  char *c;
  c = strtok(buf, ":");
  if (!strncmp(c, "ELP", 3))
  {
    c = strtok(NULL, ":");
    strcpy(elp, c);
  }
  if (!strncmp(c, "TIT", 3))
  {
    c = strtok(NULL, ":");
    strcpy(tit, c);
  }
  if (!strncmp(c, "ART", 3))
  {
    c = strtok(NULL, ":");
    strcpy(art, c);
  }
  if (!strncmp(c, "ALB", 3))
  {
    c = strtok(NULL, ":");
    strcpy(alb, c);
  }
  if (!strncmp(c, "VND", 3))
  {
    c = strtok(NULL, ":");
    strcpy(vnd, c);
  }
  memset(buf, '\0', maxLen);
}

void serMon()
{
  if (ausgabe == true)
  {
    ausgabe = false;
    Serial.print(F("ELP: "));
    Serial.println(elp);
    Serial.print(F("TIT: "));
    Serial.println(tit);
    Serial.print(F("ART: "));
    Serial.println(art);
    Serial.print(F("ALB: "));
    Serial.println(alb);
    Serial.print(F("VND: "));
    Serial.println(vnd);
  }
}

ungetestet, aber compiliert
@agmue: Ich seh Dich :slight_smile:

1 Like

Mein SH1106 hängt gerade an meinem ESP32, daher habe ich damit getestet, sollte aber keinen Unterschied machen.

Viele Wege führen nach Rom, hier meiner:

#include <U8g2lib.h>
#include <Wire.h>
U8G2_SH1106_128X64_NONAME_F_HW_I2C display(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

char inbuf[21] = "\0";  // Dieser Wert hängt von der Fontgröße ab

void setup()
{
  delay(500);
  display.begin();
  display.setFont(u8g2_font_8x13B_tf);
  Serial.begin(115200);
  Serial.println("\nStart ESP32...");
  Serial2.begin(115200);
  memset(inbuf, ' ', sizeof(inbuf));
  inbuf[sizeof(inbuf) - 1] = '\0';
}

void loop()
{
  static uint8_t zeiger = 0;
  uint8_t zeile = 0;

  if (Serial2.available() > 0)       // Wenn Daten vorhanden
  {
    char in = Serial2.read();        // Zeichen lesen
    Serial.print(in);
    if (in == ';') Serial.println();
    if (in == ';')
    {
      if ( !strncmp(inbuf, "TIT:", 4) ) zeile = 15;
      if ( !strncmp(inbuf, "ART:", 4) ) zeile = 31;
      if ( !strncmp(inbuf, "ALB:", 4) ) zeile = 47;
      if (zeile)
      {
        display.drawStr(0, zeile, inbuf + 4);
        display.sendBuffer();
      }
      memset(inbuf, ' ', sizeof(inbuf));
      inbuf[sizeof(inbuf) - 1] = '\0';
      zeiger = 0;
    }
    else
    {
      if (zeiger < sizeof(inbuf) - 1)
      {
        inbuf[zeiger] = in;
        inbuf[++zeiger] = '\0';
      }
    }
  }
}

memset
strncmp

Beim ESP32 nutze ich Serial2, der Mega2560 hat ja auch ein paar freie UARTs Serial1 bis Serial3.

1 Like

Danke, war ein Fehler. Eigentlich sollte der Thread nicht ins englische Forum :wink:

Danke @Serenifly und @my_xy_projekt!

Ich merke, dass das ganze komplizierter wird als gedacht, und das mir was die Programmiersprachen angeht eine Menge Wissen fehlt.

Den Sketch habe ich gerade ausprobiert und mal eine weile laufen lassen. Auf dem seriellen Monitor werden die festen Strings auch richtig angezeigt, nur werden die Werte nicht korrekt dargestellt. Wertänderungen werden hin und wieder hinter "Teile :" geschrieben, aber die Variablen ELP, TIT, ART, ALB und VND werden wenn, dann erst nach langer Zeit beschrieben.

Ich hänge gleich mal eine Textdatei mit der Ausgabe vom seriellen Monitor an.

Bis gleich!
Viele Grüße und einen schönen Sonntag, Chris!

Danke an alle schonmal, den anderen Forenbeitrag lese ich mir gleich durch, den Code von @agmue auch! Danke auch an Dich. :slight_smile:

Log_seriell.zip (406 Bytes)
Hallo, anbei die Logdatei vom seriellen Monitor. Es dauert recht lange, bis die Daten in die Variablen geschrieben werden. Der Titel wird zum Beispiel nie geschrieben. Habt ihr da eine Idee zu?

Danke! :slight_smile:

Noch nicht, aber vielleicht..
Wie schnell sendet der Up2Stream?

Ich hab hier einen Nano dran - grad mal durchgespielt...
Wenn nach dem ; die nächsten 3 Zeichen nicht mit der Vorgabe stimmen, , z.B. noch nenLeerzeichen vorher, könnte es schief gehen.
Aber die Zeichenkette, so wie sie oben im Code drin steht, geht Fehlerfrei,wenn die beiden Serial-Ports sauber getrennt sind.
Was für einen Controller hast Du?
Wenn einen MEGA, hast Du weitere HardwareSerial - dann z.B. auf Serial1 einlesen.
Wenn UNO/NANO, dann musst einen Sofwtareserial aufmachen.
Nicht auf dem Serial gleichzeitig einlesen und auf dem SerMon ausgeben!

1 Like

Hi!

Danke für die schnelle Antwort!

Der Up2Stream sendet den kompletten String etwa jede halbe Minute.
Die Vorgabe stimmt eigentlich immer, ich habe die Rohdaten vom Up2Stream längere Zeit mit PuTTY beobachtet, Leerzeichen gab es bis lang nie vor Trennzeichen.

Ich habe einen MEGA2560, ich bau das mal auf Serial1 um. Danke, das einlesen und SerMon gleichzeitig doof sind ist logisch, habe ich aber so erstmal nicht bedacht.

Bis gleich :wink:

Mit welcher Geschwindigkeit? 9600? 4800? 115200?

115200 baud!

Ja, daher habe ich das in meinem Programm etwas anders gemacht. Auf SSD1306 ändern, probieren und berichten.

Wenn es keine Besserung gibt, kommen mit der Zeichenkette vermutlich noch andere Zeichen.

1 Like

Danke Agmue, das versuche ich gleich auch! Habe heute zum Glück ein paar Stündchen Zeit für das Projekt!

Dann brauch ich nen Moment - das geht nicht mit nem Nano und SoftwareSerial.

Noch runter gekürzt und mit 115200 getestet:

// char myBuf[] = "ELP:2257/144266;TIT:titel;ART:artist;ALB:album;VND:spotify;ELP:2257/224066";
const uint8_t maxLen = 80;
char elp[maxLen] = {'\0'};
char tit[maxLen] = {'\0'};
char art[maxLen] = {'\0'};
char alb[maxLen] = {'\0'};
char vnd[maxLen] = {'\0'};
bool ausgabe = false;

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
  Serial1.begin(115200);
}
void loop()
{
  readSerial();
  serMon();
}
void readSerial()
{
  char myChar;             // verwertbares Zeichen
  static byte x = 0;              // Position im Array
  static char buf[maxLen] = {'\0'};   // Zwischenspeicher
  if (Serial1.available() > 0)
  {
    myChar = Serial1.read();
    buf[x] = myChar;
    x++;
    if (myChar == ';')
    {
      Serial.print(F("Teile: ")); Serial.println(buf);
      teileBuf(buf);
      x = 0;
      ausgabe=true;
    }
  }
}

void teileBuf(char *buf)
{
  char *c;
  c = strtok(buf, ":");
  if (!strncmp(c, "ELP: ", 3))
  {
    c = strtok(NULL, ":");
    strcpy(elp, c);
  }
  if (!strncmp(c, "TIT: ", 3))
  {
    c = strtok(NULL, ":");
    strcpy(tit, c);
  }
  if (!strncmp(c, "ART: ", 3))
  {
    c = strtok(NULL, ":");
    strcpy(art, c);
  }
  if (!strncmp(c, "ALB: ", 3))
  {
    c = strtok(NULL, ":");
    strcpy(alb, c);
  }
  if (!strncmp(c, "VND: ", 3))
  {
    c = strtok(NULL, ":");
    strcpy(vnd, c);
  }
  memset(buf, '\0', maxLen);
}
void serMon()
{
  if (ausgabe == true)
  {
    ausgabe = false;
    Serial.print(F("ELP: "));
    Serial.println(elp);
    Serial.print(F("TIT: "));
    Serial.println(tit);
    Serial.print(F("ART: "));
    Serial.println(art);
    Serial.print(F("ALB: "));
    Serial.println(alb);
    Serial.print(F("VND: "));
    Serial.println(vnd);
  }
}

Nicht wichtig - ist vielleicht auch eine Geschmacksfrage :wink:

Ich bin kein Freund davon, den Prüfstring länger zu machen als das was tatsächlich verglichen werden soll - noch dazu, wo das Leerzeichen ja nie auftreten sollte.
Solange die 3 eine drei bleibt geht es natürlich.

// ELP:2257/144266
if (!strncmp(c, "ELP", 3))
if (!strncmp(c, "ELP:", 4))