Seriellen String auswerten ??

Hallo,

ich möchte daten die über die serielle Schnittstelle (von einen Modem) kommen auswerten:

Der String sieht z.B. folgendermaßen aus:

CONNECT <%12%3456>

Ich möchte zuerst überprüfen ob CONNECT kommt (wenn nicht eventuell noch andere Strings auswerten) und dann die Ziffern jeweils als einzelne Zahl in eine Byte oder Int Variable speichern. Also in diesem Beispiel Ziffer "1" in Variable A, Ziffer "2" in Variable B, ... Ziffer "6" in Variable F

Jetzt bräuchte ich eine Idee wie man das einfach und elegant machen könnte, als Arduino und C Anfänger fehlt mir hier noch ein wenig der Überblick. Der Befehl atoi könnte wohl nützlich sein... :~

Bin für jeden Tipp und vor allem Cocdschnipsel dankbar :slight_smile:

Christian

Es gab vor kurzem einen ähnlichen Thread - vielleicht hilft der ja weiter:

http://arduino.cc/forum/index.php/topic,137275.0.html

markbee:
Es gab vor kurzem einen ähnlichen Thread - vielleicht hilft der ja weiter:

Einen?

Irgendwie kommen die Threads der Art "wie kann ich Daten von der seriellen Schnittstelle auslesen" alle paar Tage in diesem Forum vor.

Also ich hatte hier in diesem Thema einen Codeschnipsel zum Auslesen von Befehlen über die serielle Schnittstelle gebracht, und das ist auch noch keine zehn Tage her:
http://arduino.cc/forum/index.php/topic,136027.0.html

Danke für den Hinweis, der Code von jurs ist schon mal ein sehr guter Anfang...

Ich habe damit aber das Problem daß der String nicht erkannt wird wenn weitere Zeichen folgen. Also wenn ich nur CONNECT eingebe dann wird es erkannt, bei CONNECT <%11%234> wird aber die Eingabe bereits verworfen.

Und wie kann ich die einzelnen Ziffern herausfiltern und in eine Zahl (BYTE oder INT) verwandeln ?

void setup() {
  // put your setup code here, to run once:
 Serial.begin(115200);
}

void loop(void) 
{
  char commandbuffer[40];
  char c;
  int i=0;
  // Puffer mit Nullbytes fuellen und dadurch loeschen
  memset(commandbuffer,0,sizeof(commandbuffer));
  if (Serial.available())
  {
    delay(100);
    while (Serial.available())
    {
      c=Serial.read();
      if (c>32) // Nur Zeichen mit ASCII-Code oberhalb Leerzeichen
      {
        commandbuffer[i] = c;
        i++;
      }  
    }
  }
  if (strcmp(commandbuffer,"CONNECT")==0)
  {
    Serial.print("Connect erkannt: ");
    Serial.println(commandbuffer);
  }
  else if (strcmp(commandbuffer,"RING")==0)
  {
    Serial.print("Ring erkannt: ");
    Serial.println(commandbuffer);
  }
  else if (strcmp(commandbuffer,"")!=0)
  {
    Serial.print("Eingabe verworfen: ");
    Serial.println(commandbuffer);
  }
}

Christian

elektron_:
Ich habe damit aber das Problem daß der String nicht erkannt wird wenn weitere Zeichen folgen. Also wenn ich nur CONNECT eingebe dann wird es erkannt, bei CONNECT <%11%234> wird aber die Eingabe bereits verworfen.

Und wie kann ich die einzelnen Ziffern herausfiltern und in eine Zahl (BYTE oder INT) verwandeln ?

Zum Programmieren unter Arduino steht Dir die gesamte AVR libc zur Verfügung:
http://www.nongnu.org/avr-libc/

Library Reference: avr-libc: Modules

Mit allen Stringbearbeitungsfunktionen: avr-libc: <string.h>: Strings

Die allererste Maßnahme, wenn Du "CONNECT <%11%234>" finden möchtest, wäre nun, dass Du NICHT AUF GLEICHEIT mit dem gesamten Eingabestring vergleichst, sondern darauf, ob AM ANFANG des Eingabestrings CONNECT steht. Und dann die weitere Verarbeitung (extrahieren der Zahlenwerte) starten.

Ob ein String in einem anderen enthalten ist, dafür gäbe es die Funktion "strstr": String-in-String. Rückgabewert ist ein Pointer auf das erste Vorkommen des Unter-Strings im String (oder einen NULL Pointer, falls nicht gefunden). Ein Pointer auf die erste Fundstelle von CONNECT im gesuchten String wäre nun aber identisch mit dem Eingabestring, denn CONNECT sollte ja ganz am Anfang stehen.

Also streiche diese Zeile:
if (strcmp(commandbuffer,"CONNECT")==0)

Setze diese Zeile:
if (strstr(commandbuffer,"CONNECT")==commandbuffer)

Und schon wird jeder String als Kommando erkannt, wenn er nur mit CONNECT anfängt, z.B. Eingabe CONNECTICUT.

So weit so klar?

Mit dem Rausziehen der beiden Zahlen ist es etwas komplizierter als mit dem Austausch dieser einen Zeile, denn dazu schreibst Du am besten einen Parser – Wikipedia , der Integer-Zahlen im String parsen und in Integer-Variablen wegspeichern kann.

Dafür brauchst Du dann wahrscheinlich auch Hilfestellung, wie Du elegant an die Zahlen drankommst, oder?

Ich fange vorsichtshalber schon mal an zu überlegen...
... so, Funktion ist fertig. Allerdings erkennt diese Funktion nur POSITIVE INTEGER, keine negativen Zahlen.

Und falls die Funktion getIntFromString etwas längere Zahlen zurückliefern soll: Du kannst die Funktion auf den Wertebereich für "positive long Integers" erweitern, einfach indem der Rückgabewert der Funktion getIntFromString auf "long" statt "int" gesetzt wird.

Anbei der gesamte von mir geänderte Einlesecode, der nun auch die beiden Zahlen ermittelt, bzw. die xte Zahl aus einem String. Falls es die gewünschte Zahl im String nicht gibt, wird die Zahl aus einem Null-String ermittelt und liefert dann 0 zurück.

void setup() {
  // put your setup code here, to run once:
 Serial.begin(115200);
}


int getIntFromString (char *stringWithInt, byte num)
// input: pointer to a char array
// returns an integer number from the string (positive numbers only!)
// num=1, returns 1st number from the string
// num=2, returns 2nd number from the string, and so on
{
  char *tail; 
  while (num>0)
  {
    num--;
    // skip non-digits
    while ((!isdigit (*stringWithInt))&&(*stringWithInt!=0)) stringWithInt++;
    tail=stringWithInt;
    // find digits
    while ((isdigit(*tail))&&(*tail!=0)) tail++;
    if (num>0) stringWithInt=tail; // new search string is the string after that number
  }  
  return(strtol(stringWithInt, &tail, 0));
}  


void loop(void) 
{
  char commandbuffer[40];
  char c;
  int i=0;
  int int1, int2;
  // Puffer mit Nullbytes fuellen und dadurch loeschen
  memset(commandbuffer,0,sizeof(commandbuffer));
  if (Serial.available())
  {
    delay(100);
    while (Serial.available())
    {
      c=Serial.read();
      if (c>32) // Nur Zeichen mit ASCII-Code oberhalb Leerzeichen
      {
        commandbuffer[i] = c;
        i++;
      }  
    }
  }
  if (strstr(commandbuffer,"CONNECT")==commandbuffer)
  {
    Serial.print("Connect erkannt: ");
    Serial.println(commandbuffer);
    int1=getIntFromString(commandbuffer,1);
    Serial.print("Erste Zahl : ");Serial.println(int1);
    int2=getIntFromString(commandbuffer,2);
    Serial.print("Zweite Zahl: ");Serial.println(int2);
  }
  else if (strcmp(commandbuffer,"RING")==0)
  {
    Serial.print("Ring erkannt: ");
    Serial.println(commandbuffer);
  }
  else if (strcmp(commandbuffer,"")!=0)
  {
    Serial.print("Eingabe verworfen: ");
    Serial.println(commandbuffer);
  }
}

Wau... Danke!

Das ist ja schon fast die komplette Lösung. Jetzt brauche ich nur noch die Ziffern der beiden Zahlen einzeln. Also statt in der Variable int1 die Zahl 11 und in der Variable int2 die Zahl 234 jede Ziffer einzeln. D.d. 5 eigene (Byte?) Variablen in denen jeweils nur genau 1 Ziffer steht.

Vermutlich wird hier der Weg sein die beiden Variablen dann in einem nächsten Schritt weiter zu "zerlegen", oder gibt es eine bessere Idee?

Edit:
habe eine Lösung gefunden:

    ...
    
    byte dta, dtb, dtc;
    
    ...
    
    dta=int2/100;        // Integer zerlegen
    dtb=int2%100/10;     // Integer zerlegen
    dtc=int2%10;         // Integer Zerlegen
    
    ...

Danke,
Christian

Ps: Über die Feiertage werde ich wohl mal ein wenig C grundlagen lesen :slight_smile: