ASCII-String in zwei Einzelstrings teilen

Hallo,

wie kann ich einen ASCII-String in zwei Einzelstrings zerlegen ?
Der User gibt im Serial-Monitor einen String ein, bestehend aus Befehl und Parameter.
zB.: MoveTo 12345

Der Befehl ist "MoveTo", der Parameter ist "12345".
Zwischen Befehl und Parameter ist immer ein Leerzeichen.
Der Befehl ist aber in seiner Länge verschieden, da es eine Vielzahl von unterschiedlichen Befehlen geben soll. Ebenfalls ist die Länge des Parameters verschieden. Diese variiert zwischen 1- und 5-Stellig.
Wenn ich den Parameter "Wert" mal in einem eigenen String habe, kann ich diesen problemlos in einen Integerwert umwandeln.

Eingelesen wird der Gesamtstring einfach mit Serial.readString();

String Gesamtstring = Serial.readString();

Für eine Hilfestellung wäre ich sehr dankbar :slight_smile:
Thomas

Eingelesen wird der Gesamtstring einfach mit Serial.readString();

Das ist dein erster Fehler. Du hast hier keinen C String, sondern die Arduino String Klasse. Damit sollte das aber trivial sein, wenn du dir mal die Doku zur String Klasse anschaust.

Besser und viel ressourcenschonender geht das aber mit C Strings:

const int SERIAL_BUFFER_SIZE = 16;
char serialBuffer[SERIAL_BUFFER_SIZE];

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

void loop()
{
  if (readSerial())
    parseSerial();
}

bool readSerial()
{
  static byte index;

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

    if (c >= 32 && index < SERIAL_BUFFER_SIZE - 1)
    {
      serialBuffer[index++] = c;
    }
    else if (c == '\n')
    {
      serialBuffer[index] = '\0';
      index = 0;
      return true;
    }
  }
  return false;
}

void parseSerial()
{
  char* command = strtok(serialBuffer, " ");    //Zeiger auf Kommando
  char* parameter = strtok(NULL, " ");    //Zeiger auf Parameter

  unsigned long parameterAsInt = strtoul(parameter, NULL, 10);   //wandle Paramter in Integer

  if (strcasecmp_P(command, PSTR("MoveTo")) == 0)  //vergleiche Kommando, ignoriere Groß/Klein-Schreibung
  {
    Serial.print(F("Command: ")); Serial.println(command);
    Serial.print(F("Parameter as String: ")); Serial.println(parameter);
    Serial.print(F("Parameter as Integer: ")); Serial.println(parameterAsInt);
  }
  else
  {
    Serial.println(F("Invalid command"));
  }

  Serial.println();
}

Stelle dabei den Serial Monitor so ein, dass er am Ende ein Linefeed/Newline sendet! Sonst geht das nicht

strtok() Doku:
http://www.cplusplus.com/reference/cstring/strtok/

strcmp() Doku:
http://www.cplusplus.com/reference/cstring/strcmp/
(strcmp_P() ist hier eine Spezial-Version bei der Vergleichsstring kein RAM belegt

strtoul() Doku:
http://www.cplusplus.com/reference/cstdlib/strtoul/

Wobei statt strtoul() eigentlich atol() ausreicht:
http://www.cplusplus.com/reference/cstdlib/atol/

long parameterAsInt = atol(parameter);

Das braucht weniger Flash als strtoul()

Hallo Serenifly,

vielen Dank :slight_smile: Funktioniert perfekt !
Noch eine Frage:

Zu welcher Void gehört der Bereich, beginnend bei Zeile 15 ?
Also beginnend bei

bool readSerial()

Gehört das zur Void Loop oder steht das einfach so ohne Zugehörigkeit im Programmtext ?

Bin in C noch nicht so fit ! Komme von Delphi.
Da denkt man immer in "Proceduren".

Ich möchte in meinem Programm per void serialEvent() darauf reagieren.

Grüße
Thomas

Sowas wie "eine void" gibt es nicht. void ist ein Platzhalter für Datentypen:

Die Dinger heißen Funktionen, oder wenn sie zu Klassen gehören Methoden. Und bei Funktionen gibt "void" an dass man nichts zurück gibt. Man kann auch bei der Parameter-Liste void reinschreiben, aber das ist optional und wird nur manchmal bei Funktionszeigern gemacht.

bool readSerial() {} definiert eine Funktion die keine Parameter hat und einen bool zurück gibt.

Siehe:

Ich möchte in meinem Programm per void serialEvent() darauf reagieren.

Unnötig. serialEvent() wird auch nur am Ende von loop() aufgerufen wenn Daten da sind.

Du kannst genauso jedesmal per Hand eine Funktion in loop() aufrufen wie hier readSerial(). Damit bist du flexibler, weil die wie hier einfach per Rückgabe-Wert melden kannst ob das Einlesen fertig ist. Das ist nötig da readSerial() nicht blockiert. Das liest einfach ein was gerade da ist und gibt false zurück wenn nichts da ist oder wenn das Endzeichen noch nicht eingelesen wurde.

Hast mir wirklich toll weitergeholfen.

Vielen Dank noch einmal und beste Grüße
Thomas

Wie gesagt strtoul() würde ich noch durch atol() austauschen. Aber es geht so oder so :slight_smile:

Es gibt bei sowas aber meistens mehre Möglichkeit das zu machen. Auch das Splitten des Strings könnte man hier anders lösen, aber strtok() ist da immer noch am bequemsten und wenn du mal mehr als nur zwei Teil-Strings hast brauchst das sowieso.

Ich habe das Serial.Event rausgeschmissen und rufe die Function readSerial direkt in der Loop auf.
So wie in Deinem Code. Die Reaktionszeit ist jetzt sogar schneller als über das Event.

Nochmals vielen Dank
Thomas