Daten aus dem Serial auslesen und splitten

Nimm SoftwareSerial (das ist bei der IDE schon dabei) oder AltSoftSerial für die Kommunikation zum PC! Bei 9600 Baud reicht das völlig aus.

ich müsste den FDTI dann nicht an RXI und TXO sondern dann jeweils an 8 und 9 welche ich dann im Code für AltSoftSerial einstellen muss?

Ja. Bei AltSoftSerial sind die Pins fest (d.h. man kann sie im Code auch nicht einstellen). Bei SoftwareSerial kann man sie frei wählen. Ist bei dir aber sowieso egal.

Danke sehr für euren hilfreichen Tipps. Werde es ausprobieren und euch morgen berichten ob es klappt.

Hi habe es heute versucht und es kommt keine Ausgabe im Serial Monitor :(. Habe pin RXC und TX vom FTDI-Chip an 9 und 8 verbunden und die restlichen ganz normal an 3v3, GND BLK, AREF, DTR GND.

Und folgendes im Programm vom Empfänger geändert:

void setup() 
{
  //Serial für XBEE
  Serial.begin(9600);
  //while (!Serial);  // wait for flora/leonardo
  altSerial.begin(9600);
}

void loop() 
{
  if (leseSerial(Serial))
  {
    altSerial.print("lesen: "); altSerial.println(SerialBuffer);
    SplitSerial();
   
  }//end if leseSerial

}//end loop


void SplitSerial()
{
  float wert1 = atof(strtok(SerialBuffer, ";"));
  float wert2 = atof(strtok(NULL, ";"));
  MagAccel_B[put_indexB] = wert1;
  MagGyro_B[put_indexB] = wert2;
  put_indexB = (put_indexB+1)%50;
} 

bool leseSerial(Stream& stream)
{
  static byte index;
  static bool start;
  
  while(stream.available())
  {
    char c = stream.read();
    if(!start && c == 'A')
    {
      start = true;
    }//end if
    
    else if(start && c == '+' && index > 0)
    {
      SerialBuffer[index] = '\0';
      index = 0;
      start = false;
      for (int i=0; i<=50;i++)
      {
        altSerial.print(MagAccel_B[i]);
        altSerial.print(";");
        altSerial.println(MagGyro_B[i]);
     }
      return true;
    }
    
    else if (start && index < BUFFER_SIZE -1)
    {
      SerialBuffer[index++] = c;
    }
    return false;
 }

Iwie komm ich nicht mehr weiter :frowning:

Das hat überhaupt nichts da verloren wo du es machst! In der Lese-Funktion liest du nur. Und sonst nichts!

Die Ausgabe erfolgt in oder nach splitSerial()! Auch ist es unsinnig bei 50 Einträgen nach jedem Eintrag den kompletten Puffer auszugeben. Das kann man vielleicht nach 50 empfangenen Datensätzen einmal machen. Oder man verkleinert mal testweise den Puffer auf eine handvoll Datensätze.

Ein Array der Größe 50 geht übrigens von 0-49. Nicht von 0-50

Gibt er wenigstens das aus?

 altSerial.print("lesen: "); altSerial.println(SerialBuffer);

Wenn nicht mal TX und RX vertauschen

So habe TX un RX vertauscht und daten kommen an , doch iwe gibt er nichts aus :frowning:

milito:
So habe TX un RX vertauscht und daten kommen an , doch iwe gibt er nichts aus :frowning:

Wenn sie nicht ausgegeben werden, wie weißt du dann ob die Daten ankommen?

Und wieso muss man dir immer alles aus der Nase ziehen? Geht AltSoftSerial jetzt oder nicht? Wenn du was einfach in setup() ausgibst kommt das dann auf dem PC an?

Die Ausgabe kann man einfach so machen:

const int VALUE_BUFFER_SIZE = 50;

void SplitSerial()   //Funktionsnamen fangen normal mit Kleinbuchstaben an
{
  float wert1 = atof(strtok(SerialBuffer, ";"));
  float wert2 = atof(strtok(NULL, ";"));

  altSerial.print(wert1);
  altSerial.print(';');
  altSerial.println(wert2);

  MagAccel_B[put_indexB] = wert1;
  MagGyro_B[put_indexB] = wert2;
  put_indexB = (put_indexB + 1) & VALUE_BUFFER_SIZE;

  if (put_indexB == 0)
  {
     altSerial.println(F("Values:"));
     for (int i = 0; i < VALUE_BUFFER_SIZE; i++)
     {
       altSerial.print(MagAccel_B[put_indexB]);
       altSerial.print(';');
       altSerial.println(MagGyro_B[put_indexB]);
     }
     altSerial.println(F("---"));
  }
}

Und VALUE_BUFFER_SIZE mal testweise auf z.B. 5 setzen. Dann wird nicht so viel ausgeben und es bleibt übersichtlicher

Hi so altserial geht, sobald ich was in void setup() wird ausgeben doch werte werden nicht angezeigt bzw es kommt nur ein Wert, der 0 ist.

Kann die Werte , die vom Enddevice kommen icht sehen.

2016-03-10_154339.png

Und was kommt bei altSerial.print("lesen: "); altSerial.println(SerialBuffer); an? Da sollte er einmal die komplette empfangene Zeile anzeigen. So siehst du ob es an der Einlese-Routine liegt oder am Parser. Das ist sehr leicht zu diagnostizieren.

Wenn er überhaupt etwas Parsen will bedeutet dass aber dass er eine Zeile korrekt empfängt. Sonst würde das gar nicht aufgerufen.

Hier ist mal ein minimales Test-Programm für zwei Arduinos. Ohne Sensoren. Statt Float einfach mit Integern.

Zum Senden:

const int ITERATIONS = 10;

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

void loop()
{
  int value1 = 10;
  int value2 = 100;

  for (int i = 0; i < ITERATIONS; i++)
  {
    Serial.print(value1);
    Serial.print(';');
    Serial.println(value2);

    value1 += 10;
    value2 += 100;

    delay(500);
  }
}

Sendet einfach 10 mal 2 Integer. Ohne Startezeichen. Einfach mit einem Linefeed am Ende. Das reicht in den meisten Fällen vollkommen aus

Dann zum Empfangen. Ich habe es mit normalen Arduinos gemacht. Das heißt ich empfange mit altSerial und Sende auf Serial! bei dir ist das anders herum.

Das kannst du einfach hier herumdrehen:

#define SERIAL_R altSerial
#define SERIAL_W Serial

R = Lesen. W = Schreiben

#include <AltSoftSerial.h>

const int SERIAL_BUFFER_SIZE = 20;
char serialBuffer[SERIAL_BUFFER_SIZE];
const int VALUE_BUFFER_SIZE = 10;
int valueBuffer[VALUE_BUFFER_SIZE][2];

AltSoftSerial altSerial;

#define SERIAL_R altSerial
#define SERIAL_W Serial

void setup()
{
  SERIAL_R.begin(9600);
  SERIAL_W.begin(9600);
}

void loop()
{
  if (readSerial(SERIAL_R))
  {
    SERIAL_W.print("Read: "); SERIAL_W.println(serialBuffer);
    parseSerial();
  }

}

void parseSerial()
{
  static byte index;

  int value1 = atoi(strtok(serialBuffer, ",;"));   //atoi() statt atof() für Integer!!
  int value2 = atoi(strtok(NULL, ",;"));

  SERIAL_W.print(value1);
  SERIAL_W.print(',');
  SERIAL_W.println(value2);

  valueBuffer[index][0] = value1;
  valueBuffer[index][1] = value2;

  index++;
  if (index == VALUE_BUFFER_SIZE)
  {
    SERIAL_W.println(F("\nValues:"));

    for (int i = 0; i < VALUE_BUFFER_SIZE; i++)
    {
      SERIAL_W.print(valueBuffer[i][0]);
      SERIAL_W.print(';');
      SERIAL_W.println(valueBuffer[i][1]);
    }
    SERIAL_W.println(F("------\n"));

    index = 0;
  }
}

bool readSerial(Stream& stream)
{
  static byte index;

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

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

Funktioniert wunderbar:

Read: 10;100
10,100
Read: 20;200
20,200
Read: 30;300
30,300
Read: 40;400
40,400
Read: 50;500
50,500
Read: 60;600
60,600
Read: 70;700
70,700
Read: 80;800
80,800
Read: 90;900
90,900
Read: 100;1000
100,1000

Values:
10;100
20;200
30;300
40;400
50;500
60;600
70;700
80;800
90;900
100;1000
------

Hier habe ich zufällig den Anfang erwischt. Das kann natürlich auch mittendrin anfangen

also die die ausgabe sieht folgendermaßen bei mir aus ,da du vorher gefragt hattest(habe zwei bilder vom Coordinator und Enddevice hinzugefügt). es wir bei dem Inhalt des SerialBuffers immer nur der gleiche Wert angezeigt die werden nicht in das Array geschrieben, dieser ist 0.

coord.png

enddevice.png

Du musst jede Zeile mit einem A anfangen. A -> Wert -> Trennzeichen -> Wert -> +

Am besten du lässt das Anfangszeichen weg. Dann hast du eine Fehlerquelle weniger. Siehe der Code oben. Der macht einfach println() am Ende. Dadurch wird ein CR + LF gesendet. Und das LF wird als Endzeichen genommen. Das >= 32 verhindert dass das CR gespeichert wird. Obwohl das auch nicht schlimm wäre.

So habs jetzt so weit, dass die Werte In dem Array geschrieben werden, war mein Fehler in der For-loop. Danke für den Tipp mit dem A. Das brauche ich aber, da ein zweiter Knoten später hinzugefüügt wird, der auch etwas sendet und ich die Daten unterscheiden möcht.
Das einzige Problem is nur , dass immer nur der gleiche Wert ankommt bzw gesoeichert wird, ob diese unterschiedlich sind. bei Lesen ist immer nur der gleiche Wert zu sehen siehe Bild.

Muss jetzt nur noch rausfinden, woran es liegt, dass nur dieser Wert ankommt --> Eventuell Serial Buffer nicht groß genug?

coordArray.png

milito:
So habs jetzt so weit, dass die Werte In dem Array geschrieben werden, war mein Fehler in der For-loop. Danke für den Tipp mit dem A. Das brauche ich aber, da ein zweiter Knoten später hinzugefüügt wird, der auch etwas sendet und ich die Daten unterscheiden möcht.

Dann brauchst du das nicht so wie es ist. Sondern du musst das Anfangszeichen in parseSerial() auswerten. Aber nicht schon in der Einlese-Funktion! Ich dachte du wolltest mit 'A' einfach den Zeilen-Anfang markieren. Nicht die Daten beschreiben.

Das Beispiel oben entsprechend angepasst:

Senden:

const int ITERATIONS = 5;

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

void loop()
{
  int valueA1 = -10;
  int valueA2 = -100;

  int valueB1 = 10;
  int valueB2 = 100;

  for (int i = 0; i < ITERATIONS; i++)
  {
    Serial.print('A');
    Serial.print(valueA1);
    Serial.print(';');
    Serial.println(valueA2);

    Serial.print('B');
    Serial.print(valueB1);
    Serial.print(';');
    Serial.println(valueB2);

    valueA1 -= 10;
    valueA2 -= 100;

    valueB1 += 10;
    valueB2 += 100;

    delay(1000);
  }
}

Sendet 5 mal negative und 5 mal positive Zahlen

Empfangen:

#include <AltSoftSerial.h>

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

const int VALUE_A_BUFFER_SIZE = 5;
const int VALUE_B_BUFFER_SIZE = 5;
int valueABuffer[VALUE_A_BUFFER_SIZE][2];
int valueBBuffer[VALUE_B_BUFFER_SIZE][2];

AltSoftSerial altSerial;

#define SERIAL_R altSerial    //das hier für Fio vertauschen!!
#define SERIAL_W Serial

void setup()
{
  SERIAL_R.begin(9600);
  SERIAL_W.begin(9600);
}

void loop()
{
  if (readSerial(SERIAL_R))
  {
    SERIAL_W.print("Read: "); SERIAL_W.println(serialBuffer);
    parseSerial();
  }

}

void parseSerial()
{
  static byte indexA;
  static byte indexB;

  int value1 = atoi(strtok(serialBuffer + 1, ",;"));   //+ 1 um das Anfangszeichen zu überspringen!!
  int value2 = atoi(strtok(NULL, ",;"));

  SERIAL_W.print(value1);
  SERIAL_W.print(',');
  SERIAL_W.println(value2);

  if (serialBuffer[0] == 'A')
  {
    valueABuffer[indexA][0] = value1;
    valueABuffer[indexA][1] = value2;

    indexA++;
    if (indexA == VALUE_A_BUFFER_SIZE)
    {
      printBuffer(valueABuffer, VALUE_A_BUFFER_SIZE);
      indexA = 0;
    }
  }
  else if (serialBuffer[0] == 'B')
  {
    valueBBuffer[indexB][0] = value1;
    valueBBuffer[indexB][1] = value2;

    indexB++;
    if (indexB == VALUE_A_BUFFER_SIZE)
    {
      printBuffer(valueBBuffer, VALUE_B_BUFFER_SIZE);
      indexB = 0;
    }
  }
}

bool readSerial(Stream& stream)
{
  static byte index;

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

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

void printBuffer(int buffer[][2], int bufferSize)
{
  SERIAL_W.println(F("\nValues:"));

  for (int i = 0; i < bufferSize; i++)
  {
    SERIAL_W.print(buffer[i][0]);
    SERIAL_W.print(';');
    SERIAL_W.println(buffer[i][1]);
  }
  SERIAL_W.println(F("------\n"));
}

Man stellt einfach 'A' oder 'B' vor jede Zeile um anzugeben wozu die Daten gehören. Und fragt darauf ab. Aber erst nachdem die ganze Zeile eingelesen wurde!! In readSerial() fragt man nur auf das Endzeichen ab.

Man übergibt serialBuffer + 1 an strtok() damit die Funktion nach dem 'A' oder 'B' anfängt. Und mit serialBuffer[0] kann man auf das Zeichen selbst abfragen

A und B sind unabhängig mit eigenen Variablen für alles. Das kann man aber auch schöner machen.

Jedenfalls werden A und B durcheinander gesendet, aber korrekt in die jeweiligen Array geschrieben. Und beim Überlauf des Indizes wird es einmal ausgegeben.

Ok danke sehr für deine Mühe und ausführliche Erklärung. Wirklich danke :).

Ok werde es so ausprobieren und ein Feedback senden. Muss zuerste das Problem herausbekommen, warum immer nur der gleiche Werte ankommt bzw reingeschrieben wird :(.
Werde das auf die Art und weise machen mit dem altserial auch für den Sender, sodass ich sehen kann, was wirklich rausgesendet wird.

Wirklich sehr vielen Dank berichte dir sobald ich Ergebnisse habe.

Eine Frage noch wenn ich dan den zweiten Knoten hinzufüge, muss ich ja den Serial Buffer vergrössern oder, sonst nimmt er keine Werte mehr auf?

Versuche doch mal nachzuvollziehen was gemacht wird.

1.) Es wird eine Zeile eingelesen bis zu einem Endzeichen
2.) Wenn die Zeile angekommen ist, wird sie ausgewertet

In serialBuffer steht immer nur eine Zeile. Bisher sind das zwei Werte pro Zeile. Also muss Platz sein für: Anfangszeichen (wenn vorhanden) + Werte + Delimiter. Das Endzeichen wird nicht abspeichert.

Der Code aus #52 sendet das:
A-10,-100
B10,100
A-20,-200
B20,200
...

Jeweils mit einem CR + LF am Ende

Du könntest auch erst alle A Werte senden und dann alle B Werte. Oder beliebig anders. Da man jedesmal auf 'A' oder 'B' abfragt landet dass dann immer im richtigen Array.

Und es stehen eben jedesmal nur zwei Werte in einer Zeile. Man könnte auch A und B zusammen in einer Zeile senden. Aber das macht nur die Auswertung komplizierter und der Puffer müsste evtl. größer sein. Aber so hat man A in einer Zeile und B steht in einer anderen Zeile.

Du musst dich nur für ein Format entscheiden und den Parser entsprechend anpassen. Optionen gibt es da viele. Aber so ist es wahrscheinlich am einfachsten

Werde das auf die Art und weise machen mit dem altserial auch für den Sender, sodass ich sehen kann, was wirklich rausgesendet wird.

Das geht aber nicht für XBee! XBee sendet fest auf Serial. Es ist aber gar kein Problem das rumzudrehen und mit Serial zu senden und die Daten per altSerial und FTDI Adapter zum PC zu senden. Deshalb habe ich die zwei #define Zeilen einfügt. Damit muss am Code sonst nichts anpassen.

Ich würde dir aber mal raten die XBee Module erst mal wegzulassen und die Daten wie in dem Beispiel oben fest vorzugeben. Das Format kannst du auch ruhig anpassen. Aber du hast so eine Fehlerquelle weniger. Wenn das geht kann man weiter gehen auf XBee + feste Werte. Und am Ende dann die Sensoren dran.
Oder wenn du sicher bist dass XBee richtig geht (was glaube ich der Fall ist), dann nimm gleich XBee, aber erst mal ohne die Sensoren.

Außerdem beachten, dass der Test-Code Integer verwendet. Aber das lässt sich auch leicht auf Float umstellen. Man muss nur die Datentypen ändern und atoi() gegen atof() austauschen

Hi so bin jetzt am verbessern des Codes, so wie Serenify in post 52 mir das vorgeschlagen hat.

Eine Frage wozu ein zweidimensionales Array : valueABuffer[indexA][0] = value1; ?

das verstehe ich nicht ganz würde ein eindimmensionales nicht reichen?

Eine Frage warum byte bei dem indexA und indexB `? ---> Wäre array[50][2] auch richtig ?

Ich dachte du wolltest für zwei Sensoren/Gruppen jeweils zwei Werte übertragen? In #51 hast du gesagt, dass da noch was dazu kommt. Und schon im ersten Post hast du MagAccel_B/MagGyro_B und MagAccel_O/MagGyro_O. Das sind 4 Arrays. Ich habe zwei zwei-dimensionale draus gemacht.

Vielleicht habe ich auch wieder was falsch verstanden. Das kann man natürlich auch anders machen. Der Code ist sehr, sehr flexibel. An der Einlese-Funktion wird nichts geändert. Statt dessen macht man eventuell die die Auswertung des Strings anders und schreibt die Daten dahin wo man sie haben will. Das ist keine große Änderung.

In dem Test-Code habe ich die zwei verschiedenen Datenströme A und B genannt. Die Arrays haben A/B hinten dran und 'A' und 'B' werden als Buchstaben vor dem String verwendet um die Daten zu identifizieren. Auch das kann man anpassen.

Wichtig ist halt erst mal, dass man die Identifizierung der Daten aus der Einlese-Funktion entfernt. Da hatte ich missverstanden was das 'A' bezwecken sollte. Ich dachte das sollte nur den Anfang markieren. Nicht beschreiben was die Daten überhaupt sind. Das gehört in die Parse-Funktion. Dann kann man es bequem auswerten und entscheiden wo die konvertierten Daten gespeichert werden sollen.

Nein mir war es vorerst nicht klar ich danke dir wirklich für deine Mühe. Du hast alles richtig verstanden anstatt 4 arrays zu verwenden hast du 2 2-Dimmensionale verwendet wo jeweils Acceleration und Gyrodkop gespeichert wird.
D.h. du hast einen Array aus 5 zeilen und zwei spalten --> 0 spalte entspricht --> Beschleunigung , 1 spalte = Gyroskop.

Ich könnte theoretisch gesehen anstatt 4 arrays mit 50 einträgen - 2x Array[50][2] -> 50 zeilen und 2 spalten?

Stimmts?

Gebe dir morgen bescheid, ob es klappt

Ich könnte theoretisch gesehen anstatt 4 arrays mit 50 einträgen - 2x Array[50][2] -> 50 zeilen und 2 spalten?

Ja. Du kannst aber auch vier Arrays nehmen. Wenn du willst. Und das einzige was sich ändert ist das:

    valueABuffer[indexA][0] = value1;
    valueABuffer[indexA][1] = value2;

...

    valueBBuffer[indexB][0] = value1;
    valueBBuffer[indexB][1] = value2;

Du schreibst es nur in andere Arrays. Fertig.

Du kannst auch statt 'A' und 'B' einfach andere Buchstaben nehmen. z.B. 'A' und 'G'. Man kann auch komplette Strings voranstellen. Dann darfst du aber auf keinen Fall == zum Vergleich nehmen! Das geht schief.

Lege aber die Größe der Arrays wie ich mit globalen Konstanten fest. Die muss man dann nur an einer Stelle ändern und der Rest des Codes passt sich an!

P.S.: daran denken atoi() durch atof() zu ersetzen wenn du dann Float Werte überträgst!