Serielle Schnittstelle: Daten >128Bytes

Hallo,

ich versuche gerade einem Mega2560 beizubringen mehr als 128 Byte von der seriellen Schnittstelle zu lesen.
Als Grundlage habe ich das SerialEvent aus den Beispielen genommen. Ich sende eine Datei mit 256 Byte an den Arduino. Die 256 Byte sind mit 00...FF gefüllt. Also jedes Byte eine andere Zahl.

String inputString = "";         // a string to hold incoming data
boolean stringComplete = false;  // whether the string is complete

void setup() {
  // initialize serial:
  Serial.begin(9600);
  // reserve 200 bytes for the inputString:
  inputString.reserve(2000);
}

void loop() {
  // print the string when a newline arrives:
  if (stringComplete) {
    Serial.println(inputString); 
    // clear the string:
    inputString = "";
    stringComplete = false;
  }
}

/*
  SerialEvent occurs whenever a new data comes in the
 hardware serial RX.  This routine is run between each
 time loop() runs, so using delay inside loop can delay
 response.  Multiple bytes of data may be available.
 */
void serialEvent() {
  while (Serial.available()) {
    // get the new byte:
    char inChar = (char)Serial.read(); 
    // add it to the inputString:
    inputString += inChar;
    // if the incoming character is a newline, set a flag
    // so the main loop can do something about it:
    if (inChar == 127) {
      stringComplete = true;
    } 
  }
}

Der Code oben läuft. Da schreibt er 128 Byte über die Schnittstelle zurück. (Ich frage ab, wann der Wert 127 über die serielle Schnittstelle kommt.
Und das ist ja bei dem Aufbau der Datei das 128. Byte.
Aber sobald ich einen größeren Wert eingebe, kommt nichts mehr zurück. Das heisst für mnich, daß er da nichts mehr empfängt.
Hole ich die Daten nicht schnell genug ab? oder wo liegt da mein (Denk-)Fehler?

Gruß/hk007

eventuell ein Problem des Datentyps. Char hat einen Bereich von -128 bis +127. Du kannst da keine grösseren Zahlen als 127 speichern.

http://arduino.cc/en/Reference/Char

byte (bzw uint8_t) hat einen Bereich von 0 .. 255

Oh oh.
Da könntest du Recht haben.

Stellt sich jetzt die Frage, wie ein "FF" als Char gelesen wird. Als "-1" oder es wird ignoriert. Werd das mal am Abend testen.

Den Typ Byte hatte ich mir zwischenzeitlich auch schon mal überlegt. Aber da ist mir zum Einlesen nur das Mittel eines Arrays eingefallen und da war dann für mich ein String einfacher. Vor allem, wollte ich dann in den 256 Bytes bestimmte Zeichenketten erkennen können und das ist in einem Array m.E. aufwändiger als in einem String.

Gruß/hk007

machs doch einfach ohne

** **inputString += inChar;** **

String mag ja ganz nett sein, ist aber nicht für Zeichen > 127 gedacht. ( Und ich hab noch keine Arduino-Anwendung gesehen, wo String geholfen hätte. )

Was du willst, ist ein

** **  byte array[200]; // falls FF als 255 interpretiert werden soll, oder ein   char array [200] ; // für -128 .. 127 , und FF = -1** **

   // if the incoming character is a newline, set a flag

// so the main loop can do something about it:
    if (inChar == 127) {
      stringComplete = true;


}

Der Kommentar passt nicht so ganz... Ich verstehe das so , dass du Bytes binär überträgst ? newline ( 0x0A ) ist dann ein ganz normales Zeichen ?

** **String.reserve()** **
ist übrigens in der Arduino Referenz nicht erwähnt...

SerialEvent ist eine Interrupt-Routine, die asynchron zu loop() läuft, denke ich.
Ich würde
[b]volatile[/b] boolean stringComplete; definieren.
Und nur while (stringComplete == false &&  Serial.available() ) { /* das byte array füllen */ }

Hi michael_x,

richtig, der Kommentar passt nicht. Der ist noch aus dem SerialEvent-beispiel von den Arduino Examples. Und da haben sie als Ende-Kennung der seriellen Sequenz das "NL" genommen.
Ich glaube String kann ich wirklich vergessen.

Vllt. sollte ich mal kurz schildern, was ich vorhabe:
Ich will einen Zähler seriell an meinen Arduino koppeln. Da kommen dann als "Telegramm" so ca. 1500 Byte an den Arduino.
Das Ende des Telegrammes wird durch eine spezielle Sequenz aus 4 byte angezeigt.
Also ist es Ziel meines Projektes, die 1500 Byte beim Empfang in einen Puffer (am besten wohl "byte array[1500]") zu schreiben, das Ende des Telegrammes zu erkennen und das Array dann nach dem Senden auf bestimmte Werten zu durchforsten.

Geht das Einlesen irgendwie eleganter als so?

Byte[i] = Serial.read();
i = i+1;
.....

SerialEvent ist eine Interrupt-Routine, die asynchron zu loop() läuft, denke ich.

Ja, immer wenn was im Empfangspuffer ist, dann wird dahin verzweigt.
Erscheint mir aber irgendwie eleganter, als das im Hauptprogramm abzuarbeiten.

String.reserve() ist übrigens in der Arduino Referenz nicht erwähnt...

Kommt auch aus dem Beispiel :wink:

Gruß/hk007

Geht das Einlesen irgendwie eleganter als so?

Byte[i] = Serial.read();

i = i+1;


.....

So hätte ich es auch gedacht. ( Vielleicht ein schönerer Name für den Puffer ; )
Wenn nur i nicht zu groß werden kann...

Ich will einen Zähler seriell an meinen Arduino koppeln. Da kommen dann als "Telegramm" so ca. 1500 Byte an den Arduino.
Das Ende des Telegrammes wird durch eine spezielle Sequenz aus 4 byte angezeigt.

? Ein Zählwert-Telegramm mit 1500 Byte ?
1500 byte wären für einen UNO eine ganze Menge, da bliebe nicht viel übrig für anderes/weiter bearbeiten.
Ein Mega2560 ist nicht ganz so knapp.

Aber bei 1500 byte ist doch viel Overhead im Telegramm, der evtl. schon weggeschmissen werden kann, während es kommt, und der Rest wird während des Lesens schon ausgewertet. ?

SerialEvent() könnte schon noch mehr als Speichern und Ende Erkennen.

CheckSumme auswerten kann schon beim Lesen vorbereitet werden...

So: mit Byte-Array klappts prima. Kann alle 1524 Bytes sauber in das Array einlesen. :smiley:

Aber bei 1500 byte ist doch viel Overhead im Telegramm, der evtl. schon weggeschmissen werden kann, während es kommt, und der Rest wird während des Lesens schon ausgewertet. ?
SerialEvent() könnte schon noch mehr als Speichern und Ende Erkennen.
CheckSumme auswerten kann schon beim Lesen vorbereitet werden...

Ja du hast recht. Ich werde so ca. 20 Werte aus dem Telegramm lesen. Das sind dann max. 100 Byte. Da die Werte im Telegramm immer an der selben Stelle stehen, wäre das auch kein Problem.
Aber ich müsste dann im serialEvent nach dem Lesen immer abfragen, ob die Nummer des gelesene Bytes (also die Stelle im Telegramm) eine der 100 relevanten Bytes ist und dann wegspeichern. Also nach jedem Serial.read() 100 If-Abfragen. Das finde ich nicht elegant, und treibt die CPU-Last wohl stark in die Höhe. Im schlimmsten Fall krieg ich dann nicht mehr alles von der seriellen Schnittstelle mit.
Die serielle hat ja nur 128 Byte Puffer. Was passiert, wenn der voll ist? Glaube nicht, daß ich die andere Seite dann anhalten kann, damit die wartet bis ich meinen Puffer ausgelesen habe. Nicht mit (TX,RX und GND).

Oder weiss jemand zum "Maskieren" der 100Byte eine schönere Lösung?

Gruß/hk007

Wenn es nur 20 Werte sind, brauchst Du auch nur 20 if-Abfragen, Du mußt ja nur den Anfang erwischen. Ich würde das aber eleganter lösen.

Die 20 "Start"-Stellen der Werte kommen in ein Array (position) . Der Zählindex für das Array zeigt auf den ersten Eintrag (current).

Während Du aus der seriellen Schnittstelle liest, vergleichst Du den Zähler für die gelesenen Bytes (z.B. int numbytes) mit der als nächstes zu lesenden Stelle. Ist die erreicht wird der Wert gelesen, wenn nicht, wird das Byte übersprungen.

#define NUM_VALUES 20
int position[NUM_VALUES] = { 25,100,255,300 ...};
int current = 0; //index auf position des nächsten zu lesenden wertes
int numbytes = 0; //anzahl gelesene bytes

....
//hier code zum einlesen der nächsten bytes 
...
//position des nächsten wertes erreicht? oder alle werte schon gelesen?
(if numbytes == position[current] && current < NUM_VALUES) {
  //code zum einlesen des Wertes
  //nicht vergessen auch hier beim einlesen "numbytes" zu erhöhen
  //wert fertig eingelesen, auf nächsten Wert umstellen
  current++;
}

Ja,
gute Idee.
So was entfernt ähnliches hab ich mir auch schon überlegt.
Allerdings hab ich noch das Problem, daß ich da ein zweidimensionales Array aufbauen muss, weil die Anzahl der Bytes bei den 20 Werten unterschiedlich ist.
Ist aber auch nicht so das Drama. Muss halt nur den Code zum Einlesen der Werte variabel auf die Bytelänge des Wertes umprogrammieren.
Momentan mach ich es mal hölzern. Alles auf einmal einlesen und dann parsen.
Aber wie ich mich kenne, werd ich die Software dann wohl in die Richtung trimmen, da ich momentan einen Mega verwende, aber evtl. doch nur einen UNO spendieren will, wenn es klappt.

Noch ne Frage:
Gibt es irgendwo eine Befehlsliste mit der Abarbeitungszeit? Ich würde gerne mal ein Gefühl dafür bekommen, wie lange die Interruptroutine läuft. Dann hab ich auch die Sicherheit, daß mir der Puffer nicht überläuft.

Gruß/hk007

Gibt es irgendwo eine Befehlsliste mit der Abarbeitungszeit? Ich würde gerne mal ein Gefühl dafür bekommen, wie lange die Interruptroutine läuft. Dann hab ich auch die Sicherheit, daß mir der Puffer nicht überläuft.

Gibt es bestimmt, aber das willst du nicht wirklich wissen.

Soll deine ISR über 100 Zeilen Code haben?
Willst du darin float Divisionen durchführen?
Rufst du andere Funktionen darin auf?

3 * NEIN ? Dann wirst du die Dauer knapp in Microsekunden messen können.

Bei 115200 Bd hast du etwa 1 Zeichen in 90 Microsekunden, und der Puffer von Hardware Serial ist 64 Zeichen groß ...

Dein Arduino läuft vermutlich mit 16 MHz und es gibt Befehle, die nur einen Takt brauchen.

ein zweidimensionales

Zwei eindimensionale ... eins für die Position, eins für die Länge.

Bei 115200 Bd hast du etwa 1 Zeichen in 90 Microsekunden, und der Puffer von Hardware Serial ist 64 Zeichen groß ...

9600Baud ist angesagt. Faktor 12 Reserve :slight_smile:
Hmm wo hab ich nur die Info mit den 128 byte her? Hab gerade in der Reference nachgelesen. Da hast du recht mit deinen 64 byte.

Soll deine ISR über 100 Zeilen Code haben?
Willst du darin float Divisionen durchführen?
Rufst du andere Funktionen darin auf?
3 * NEIN ? Dann wirst du die Dauer knapp in Microsekunden messen können.

OK. Das gibt mir das "Gefühl", das ich zum Programmieren brauche.

Danke @all

in früheren Versionen der Arduino IDE war der Serial Buffer 128 Bytes lang