Arduino Micro - Wie Buffer leeren?

Erläutere doch kurz, was dein Programmauszug an welcher Stelle genau macht oder stelle, wie Doc_Arduino vorgeschlagen hat, dein ganzes Programm rein.

PS: Wenn du zu Beginn deines Codes code in eckigen Klammern schreibst und am Ende /code in eckigen Klammern wird dein Quellcode formatiert und so angezeigt, wie er es soll.

Hallo Doc_Arduino,

ja genau das wollte ich wissen, ob man den UART Buffer löschen kann, ohne alles auzulesen. Aber in dem Fall wohl nicht. Vielleicht versuche ich es mit einem Zähler, der hochzählt und bis er einen bestimmten wert hat lösche ich einfach die Werte meines Arrays ohne sie zu verarbeiten und immer nur jeder X-te Wert wird dann verwendet und verarbeitet. Hoffe das macht Sinn?

Ich probiers morgen mal aus. Vielen Dank, du hast mir sehr geholfen :)

Hallo,

du kannst mit dem while(Serial.read ... schon den UART Empfangspuffer löschen. Kostet eben auch seine Zeit. Wenige ms.

Ich würde erstmal alles einlesen in ein array. Mit Indexnummer dabei die Einspeicherposition des Arrays hochzählen. Du kennst ja die Reihenfolge der kommenden Daten und damit was wo steht. Dann kannst du an Hand der Indexnummer direkt darauf zugreifen.

Wenn du magst kannste uns wie gesagt die .ino zeigen, vielleicht sehen wir wo dein Sketch Zeit verdrödelt.

Ich verstehe nicht wieso Du den Eingabebuffer der Seriellen Schnittstelle löschen willst. Wenn Dein PC zB10 mal die Sekunde ein Datenpacket schickt dann hat es keinen Sinn nur jedes 19 su beachten.

Den Eingabebuffe löscht Du indem du alle erhaltenen Werte mit Serial.read ausliest. Ob Du diese daten dann weiterverarbeitest oder Nicht ist Dir überlassen.

Ich habe aber den Verdacht daß aus irgendwelchen Programmierfehlern Dein Sketch einfach zu langsam ist um alle Werte zu verarbeiten. Gib uns mal den GESAMTEN Sketch.

Grüße Uwe

Hallo Doc_Arduino,

das ist eine gute Idee und ich werde sie auch testen. Meine Frage hierzu jedoch noch: Solange ich einlese, blockiert ja mein Programm. Meine Strings werden durchgehend von Labview gesendet, was also ist mein Abbruchkriterium? irgendwann möchte ich ja einen Wert dann auch bearbeiten. Nehme ich dann einfach eine höhere Byte Zahl, also beispielsweise 1000 Bytes? und nachdem diese eingelesen sind ziehe ich mir den letzten Wert heraus und verarbeite ihn, oder wie hattest du das gedacht?

Ich glaube, du hast da ein generelles Verständnisproblem mit der Funktionsweise des seriellen Einlesens.

Wenn da laufend Daten kommen, von denen du nur einige verarbeiten willst, woher weißt du, wann die Daten anfangen? Wie synchronisierst du?

Hier ein Beispiel, dass Zeilen von acht Zeichen Länge (exclusive CR/LF) sammelt,
in 4 Bytes aus hex convertiert und das Ergebnis anzeigt.

const byte sCBMax = 33; // line length including '\0'

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

void loop() {
  serialHandler();
}

byte hexVal(char in, bool& valid) {
  char check = toupper(in);
  if (check >= 'A' && check <= 'F') {
    return check - 'A' + 10;
  }
  if (check >= '0' && check <= '9') {
    return check - '0';
  }
  valid = false;
  return 0;
}

byte unhex2(const char *from, bool& valid) {
  return hexVal(from[0], valid) << 4 | hexVal(from[1], valid);
}

void checkLine(const char* iLine) {
  Serial.write('\'');
  Serial.print(iLine);
  Serial.write('\'');
  Serial.println();
  if (strlen(iLine) == 8) {
    byte idx;
    bool allValid = true;
    byte resultBytes[4];
    for (idx = 0; idx < 4 && allValid; idx++) {
      resultBytes[idx] = unhex2(iLine + 2 * idx, allValid);
    }
    Serial.print(F("valid:"));
    for (byte num = 0; num < idx; num++) {
      Serial.print(F(" 0x"));
      if (resultBytes[num] < 16) {
        Serial.write('0');
      }
      Serial.print(resultBytes[num], HEX);
    }
    if (allValid) {
      Serial.print(F(" all valid"));
    }
    Serial.println();
  }
}

void serialHandler()
{
  byte inChar;
  bool doCheck = false;
  static char sCBuffer[sCBMax];
  static byte buffIndex = 0;

  while (Serial.available()) {
    inChar = Serial.read();
    if (inChar == 10) {
      continue;
    } else if (inChar == 13) {
      doCheck = buffIndex;
    } else {
      if ((buffIndex == 0) && isWhitespace(inChar)) {
        continue;
      }
      sCBuffer[buffIndex++] = inChar;
      doCheck = (buffIndex == (sCBMax - 1));
    }
    if (doCheck) {
      sCBuffer[buffIndex] = 0;
      checkLine(sCBuffer);
      buffIndex = 0;
    }
  }
}
'abcdef13'
valid: 0xAB 0xCD 0xEF 0x13 all valid

Da stimmte noch was im Fehlerfall nicht, ich müsste sorgfältiger testen.

Die Version geht besser. :wink:

const byte sCBMax = 33; // line length including '\0'

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

void loop() {
  serialHandler();
}

byte hexVal(char in, bool& valid) {
  char check = toupper(in);
  if (check >= 'A' && check <= 'F') {
    return check - 'A' + 10;
  }
  if (check >= '0' && check <= '9') {
    return check - '0';
  }
  valid = false;
  return 0;
}

byte unhex2(const char *from, bool& valid) {
  return hexVal(from[0], valid) << 4 | hexVal(from[1], valid);
}

void checkLine(const char* iLine) {
  Serial.write('\'');
  Serial.print(iLine);
  Serial.write('\'');
  Serial.println();
  if (strlen(iLine) == 8) {
    byte idx;
    bool allValid = true;
    byte resultBytes[4];
    for (idx = 0; idx < 4; idx++) {
      resultBytes[idx] = unhex2(iLine + 2 * idx, allValid);
      if (!allValid) {
        break;
      }
    }
    Serial.print(F("valid:"));
    for (byte num = 0; num < idx; num++) {
      Serial.print(F(" 0x"));
      if (resultBytes[num] < 16) {
        Serial.write('0');
      }
      Serial.print(resultBytes[num], HEX);
    }
    if (allValid) {
      Serial.print(F(" all valid"));
    }
    Serial.println();
  }
}

void serialHandler()
{
  byte inChar;
  bool doCheck = false;
  static char sCBuffer[sCBMax];
  static byte buffIndex = 0;

  while (Serial.available()) {
    inChar = Serial.read();
    if (inChar == 10) {
      continue;
    } else if (inChar == 13) {
      doCheck = buffIndex;
    } else {
      if ((buffIndex == 0) && isWhitespace(inChar)) {
        continue;
      }
      sCBuffer[buffIndex++] = inChar;
      doCheck = (buffIndex == (sCBMax - 1));
    }
    if (doCheck) {
      sCBuffer[buffIndex] = 0;
      checkLine(sCBuffer);
      buffIndex = 0;
    }
  }
}
'12345678'
valid: 0x12 0x34 0x56 0x78 all valid
'123456bh'
valid: 0x12 0x34 0x56
'12345gtt'
valid: 0x12 0x34

Hallo,

@ Uwe: sie möchte/wollte Zeit einsparen beim einlesen und nur soviele Werte einlesen wie unbedingt benötigt werden und den Rest verwerfen. Wie nun geklärt bringt das keinen Vorteil. Egal wie man das anstellt. Jetzt muß erklärt werden warum weshalb wieso und wie man das eben am dümmsten macht. Du hast doch Ahnung.

Ich denke jetzt auch es liegt ein allgemeines Verständnisproblem zu Grunde zur seriellen Kommunikation.

Dein Protokoll hat hoffentlich eine Ende Termination. Davon gehe ich jetzt aus. Der besagte Null Terminator "\0". Sonst würde es nicht hinhauen.

Der zu übertragende Datensatz, unabhängig jetzt von irgendwelchen Start/Stop Bits, besteht aus deinen Daten in Form von einzelnen Bytes. Am Ende davon gefolgt von einem Null Terminatorzeichen. Das ist der komplette Datensatz der immer eingelesen wird.

Nehmen wir an dein Labview sendet 10 Werte. Dann werden 10 Datenbytes und der Null-Terminator auf die Reise geschickt. Insgesamt 11 Zeichen. Wenn du jetzt die Daten empfängst wird automatisch bis zum Null-Terminator eingelesen. Normalerweise macht man das so und sollte es so machen. Der Ringbuffer der UART Platz für 64 Zeichen. Das heißt, im UART Buffer können 64/11 = 5 komplette Datensätze aufgenommen werden. Der Rest geht verloren wenn das auslesen nicht hinterher kommt. Dir stehen damit erstmal 5 komplette Datensätze zum einlesen zur Verfügung. Alle schön aufgefädelt und getrennt durch das Ende-Zeichen "Null-Terminator". Wenn du jetzt deine Einlesefunktion darauf los läßt, liest diese hintereinander 5 Datensätze ein. Zwischen jeden einlesen wirst du diese Daten bearbeiten. Du liest einen Datensatz ein, die anderen 4 Datensätzen rutschen nach und warten auf erneutes einlesen.

Jetzt gilt es zu klären in welchen zeitlichen Abstand sendet Labview Daten? Ist dein Arduino Sketch dafür wirklich zu langsam? Was ist bei dir langsam? Schon einmal ausgestoppt? Deine Einlesefunktion mit if (Serial. ... ist auf jeden Fall nicht die Bremse.

Wenn das bekannt ist, gibts vielleicht passende Code Beispiele oder andere passende Vorschläge. Wie gesagt, am besten wäre es du zeigt uns zusätzlich den gesamten Sketch.

@ Whandall: ich weis ja das du auch übelste Ahnung hast wie Uwe. Jedoch blockiert deine while (Serial.available Das macht zwar das einlesen selbst schnell blockiert aber den restlichen Sketch. Solange ich nicht weis wo es genau klemmt und wie oft Daten überhaupt reinkommen würde ich keine Bsp. abgeben. Kannste immer noch machen dann in extrem optimierter Form auf das Problem hin. Nicht falsch verstehen - mein guter. ;)

Irrtum, while (Serial.available()) { } blockiert nicht., sondern liest ein solange was da ist, in derRegel nur einmal.

Meine Lösung verarbeitet alle Zeilen sobald sie vollständig sind, egal wie oft, egal wann. Alles andere kann in der Loop weiter laufen. Was mit den Daten nach dem Einlesen passieren soll liegt ja etwas im Dunkeln, das ist dann mit dem Rest der loop zu synchronisieren.

Hier hast du eine Version die jede Sekunde die Schleifendrchläufe anzeigt.

const byte sCBMax = 33; // line length including '\0'

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

void loop() {
  static unsigned long lastSecond;
  static unsigned long loopsPerSecond;
  unsigned long topLoop = millis();
  loopsPerSecond++;

  serialHandler();

  if (topLoop - lastSecond > 1000) {
    lastSecond = topLoop;
    Serial.print(loopsPerSecond);
    Serial.print(F(" lps "));
    unsigned long nanosPerLoop = 1000000000L / loopsPerSecond;
    Serial.print(nanosPerLoop);
    Serial.print(F(" nS "));
    Serial.print(nanosPerLoop / 65);
    Serial.println(F(" cycles"));
    loopsPerSecond = 0;
  }
}

byte hexVal(char in, bool& valid) {
  char check = toupper(in);
  if (check >= 'A' && check <= 'F') {
    return check - 'A' + 10;
  }
  if (check >= '0' && check <= '9') {
    return check - '0';
  }
  valid = false;
  return 0;
}

byte unhex2(const char *from, bool& valid) {
  return hexVal(from[0], valid) << 4 | hexVal(from[1], valid);
}

void checkLine(const char* iLine) {
  Serial.write('\'');
  Serial.print(iLine);
  Serial.write('\'');
  Serial.println();
  if (strlen(iLine) == 8) {
    byte idx;
    bool allValid = true;
    byte resultBytes[4];
    for (idx = 0; idx < 4; idx++) {
      resultBytes[idx] = unhex2(iLine + 2 * idx, allValid);
      if (!allValid) {
        break;
      }
    }
    Serial.print(F("valid:"));
    for (byte num = 0; num < idx; num++) {
      Serial.print(F(" 0x"));
      if (resultBytes[num] < 16) {
        Serial.write('0');
      }
      Serial.print(resultBytes[num], HEX);
    }
    if (allValid) {
      Serial.print(F(" all valid"));
    }
    Serial.println();
  }
}

void serialHandler()
{
  byte inChar;
  bool doCheck = false;
  static char sCBuffer[sCBMax];
  static byte buffIndex = 0;

  while (Serial.available()) {
    inChar = Serial.read();
    if (inChar == 10) {
      continue;
    } else if (inChar == 13) {
      doCheck = buffIndex;
    } else {
      if ((buffIndex == 0) && isWhitespace(inChar)) {
        continue;
      }
      sCBuffer[buffIndex++] = inChar;
      doCheck = (buffIndex == (sCBMax - 1));
    }
    if (doCheck) {
      sCBuffer[buffIndex] = 0;
      checkLine(sCBuffer);
      buffIndex = 0;
    }
  }
}
81256 lps 12306 nS 189 cycles
81168 lps 12320 nS 189 cycles
81085 lps 12332 nS 189 cycles
81168 lps 12320 nS 189 cycles
81085 lps 12332 nS 189 cycles
81168 lps 12320 nS 189 cycles
81085 lps 12332 nS 189 cycles
81168 lps 12320 nS 189 cycles
'abcdefg'
81063 lps 12336 nS 189 cycles
81167 lps 12320 nS 189 cycles
81085 lps 12332 nS 189 cycles
81168 lps 12320 nS 189 cycles
'sdsdsd'
81148 lps 12323 nS 189 cycles
81168 lps 12320 nS 189 cycles
81168 lps 12320 nS 189 cycles
81085 lps 12332 nS 189 cycles
81168 lps 12320 nS 189 cycles
'12345678'
valid: 0x12 0x34 0x56 0x78 all valid
80976 lps 12349 nS 189 cycles
81168 lps 12320 nS 189 cycles
81085 lps 12332 nS 189 cycles
81168 lps 12320 nS 189 cycles
81085 lps 12332 nS 189 cycles

Hallo,

ja ist klar. Das meinte ich doch auch. Es kommt darauf an was Vorrang haben muß. Das einlesen selbst oder der ganze restliche Code. Hier wahrscheinlich das einlesen und while ... Ist aber noch unklar, solange wir das Sende-Intervall nicht kennen. Im Normfall wird nebenbei eingelesen, weil die Daten langsam reinkommen und der µC in der Zeit andere Dinge erledigen kann. Dann würde ich if ... nehmen. Deshalb wollen wir genau wissen wo es wirklich klemmt.

Dein Code sieht dennoch gut aus. :) Hab aber schon einen. :)

Du suchtest ja auch keinen. ;)

Wenn das Format weniger schwammig beschrieben worden wäre, hätte ich mehr darauf eingehen können. In meinem Filter blieben '8 Bytes', 'hex codiert', 'alle als einzelne Zahl' hängen.

Und Buffer braucht man bei der Methode nicht zu leeren, der wird nicht voll solange die loop läuft. Man muss die Zeilen ja nicht verarbeiten, aus dem seriellen Buffer sind sie raus, in Zeilen zerlegt und bearbeitet, oder verworfen.

Aber war ja nur ein Vorschlag, eine Fingerübung.

Hallo,

wenn wir vielleicht morgen nähere Infos bekommen, werde auch schon müde, dann können wir uns darauf stürzen wie die Geier. Und deine Fingerübung, Hut ab, kannste dann gern weiter optimieren. :) Mein Augenmerk lag erstmal auf dem, dass Grundverständnis zur seriellen näher zubringen.

Gute Nacht, bis morgen ...

hi,

Mein Labview Programm sendet mehrere Strings pro Sekunde und mein Arduino kann nur 1 oder 2 pro Sekunde auslesen, weil die Nachverarbeitung der Daten so lange geht.

also geht es nicht darum, daß der arduino "seltsamerweise" so lange braucht, sondern Dein programm dauert einfach länger, als es zeit bis zum nächsten wert hat?

daß Du flush() nicht richtig angewendet hast, ist nicht Deine schuld. früher hatte es wirklich die funktion, die Du verwenden wolltest. das einfach zu ändern, und nicht einen neuen namen zu nehmen,... naja, was soll man zu dieser idiotie sagen?

gruß stefan

Vielleicht wäre commit() or waitForTxEmpty() besser gewesen.

Eisebaer: daß Du flush() nicht richtig angewendet hast, ist nicht Deine schuld. früher hatte es wirklich die funktion, die Du verwenden wolltest. das einfach zu ändern, und nicht einen neuen namen zu nehmen,... naja, was soll man zu dieser idiotie sagen?

Die Idiotie war das jemals flush() zu nennen. In den meisten anderen Sprachen leert flush() immer nur Ausgangspuffer. Jedenfalls in den weit verbreiteten. Man hat also einen Fehler ausgebessert. Außerdem war das schon vor Urzeiten in 1.0

I benötige ein Leeren des Eingangspuffer nicht. Wenn darin Daten sind, werden sie daraus entfernt und zu Zeilen gesammelt. Zeilen verarbeite ich dann, oder ignoriere sie einfach.

Wenn man die Programmlogik so auslegt, dass eingehende Zeilen das Programm steuern, kann man sich das Pollen und Warten sparen. So etwas lässt sich mit der Basismethodik "wir sammeln immer Zeilen und rufen dann eine feste Funktion auf" recht gut hinbekommen.

Noch etwas zum Eingangsproblem:

wenn mehr Daten hereinkommen, als verarbeitet werden können, hat man ein echtes Problem.

Wenn die Verarbeitung nicht weiter optimiert werden kann, bleibt nur den Datenstrom zu senken, oder auf dieser Plattform 'einzupacken'.

Zum Glück ist ziemlich sicher anzunehmen dass die 'Verarbeitung' so weit optimiert werden könnte, dass man es doch schafft, alle Daten zu verarbeiten.

Maren89: allerdings gibt es eine sehr große Verzögerung beim verarbeiten der ankommenden Daten im Arduino.

Beschreibe das zu empfangende Datenprotokoll im Detail und zeige Deinen vollständigenCode!

Wenn Dein Code mit dem Empfangen über die serielle Schnittstelle nicht in Echtzeit mitkommt, liegt das NUR an Deinem Code und einer völlig ungeeigneten Programmlogik oder der Verwendung ungeeigneter Funktionen.

Zeit wird beim Übertragen von Zeichen auf der Schnittstelle gebraucht und die Verarbeitungszeit kannst genüber der Übertragungszeit komplett vernachlässigen. Deshalb wäre es auch völlig kontraproduktiv empfangene Zeichen im Empfangspuffer zu löschen (deren Empfang bereits Zeit gekostet hat) und nochmal auf ein wiederholtes Senden der Daten zu warten (was nochmal wieder Zeit kostet. Es gilt Murphys Law: Alle Fehler beim Programmieren wirken sich im fertigen Programm so aus, dass sich die Fehler zu einem maximal fehlerhaften Programm addieren. Und wenn Dein Programm aufgrund von Programmierfehlern mal hier ein paar Millisekunden unddort eine Sekunde unnütz verbrät, dann verbrät das fertige Programm die Summe der unnötig verbratenen Millisekunden und Sekunden und daran kannst Du mit kosmetischen Maßnahmen nichts mehr ändern, sondern nur noch durch Ersetzen der ungeeigneten Programmlogik durch eine geeignete.

Hallo,

jurs, schön das du auch noch lebst. :) Nur hat Whandall nun wirklich kein Problem. Lies mal Quote in #34. Der Arduino von Maren wird mit Daten bombardiert und kommt im Moment nicht hinterher. Das es nun irgendwo klemmt haben wir auch schon festgestellt. Alles klar? ;) Wir warten alle auf den kompletten Sketch von Maren. Vielleicht hat sie noch keine Zeit gehabt, weil sie noch arbeitet. Darfst ruhig drüber schauen wenn es soweit ist.