Die void-Schleife wird auch dann ausgeführt, wenn die While-Schleife in Arduino wahr ist

Ich habe eine einfache mobile Anwendung erstellt, um LED_ON oder LED_OFF zu senden, wenn auf eine Schaltfläche geklickt wird. Im folgenden Code wird die While-Schleife während der Ausführung nicht vollständig abgeschlossen, aber der Controller geht zur void-Schleife () und fährt von dort fort.

char command;
String my_final="LED_OFF";
#define led 9

void setup(){
  Serial.begin(9600);
  pinMode(led, OUTPUT);
  Serial.println("ready");
}

void loop(){
  if(Serial.available() > 0){
    my_final = "";
    while(Serial.available() > 0){
      command = (byte)Serial.read();
      my_final += command;
      Serial.println("test");
    }
    Serial.println(my_final);
    }

  if(my_final == "LED_ON"){
    Serial.println(my_final);
    analogWrite(led, 255);
    my_final == "LED_ON";
  }

  if(my_final == "LED_OFF"){
    Serial.println(my_final);
    analogWrite(led, 0);
    my_final == "LED_OFF";
  }
}

Das Hauptproblem tritt auf, my_final=""da ich dies tun muss, um neue Eingaben vom Bluetooth zu akzeptieren. Ich kann einfach keinen Weg finden, dieses Problem zu umgehen.

BEARBEITEN

Das bekomme ich im seriellen Monitor. test L test E test D test _ test O test N.

Was Sie in Ihrer Frage beschreiben, ist ziemlich unmöglich. Haben Sie wirklich im Debugger gesehen, dass die Ausführung die whileSchleife verlässt, wobei die Bedingung Serial.available()immer noch wahr ist?
das bekomme ich im seriellen monitor..test L test E test D test _ test O test N
Vielleicht sollten Sie dann in Ihrer Frage angeben, was Sie im seriellen Monitor erhalten. Derzeit liest sich Ihre Frage so, als hätten Sie gesehen, wie die Ausführung aus der whileSchleife gesprungen ist, was mit einem seriellen Monitor nicht zu sehen ist.
Was my_final == "LED_ON";soll tun? (am Ende des if(my_final == "LED_ON"){}Blocks, und ebenso für OFF)
Woher wissen Sie, dass die While-Schleife vollständig abgeschlossen ist?
Ich habe aus irgendeinem Grund vergessen, diese beiden Zeilen zu entfernen, die ich zuvor verwendet habe.

Antworten (4)

Bei der seriellen Kommunikation werden Daten byteweise übertragen. Ihr Code ist schnell genug, um ein Byte zu lesen und zu verarbeiten, bevor das nächste empfangen wird.

Es gibt viele mögliche Lösungen dafür, und die Verwendung von Verzögerungen ist keine gute.

Sie könnten am Ende jedes Befehls ein Markierungszeichen senden, z . B. LED_ON!. Dann wissen Sie, dass Sie Ihrem Array weitere Zeichen hinzufügen müssen, bis Sie ein Ausrufezeichen sehen. Ein häufiger Kandidat für eine Markierung ist das Zeilenumbruchzeichen.

Eine andere Lösung besteht darin sicherzustellen, dass alle Befehle die gleiche Länge haben (z. B. 6 Bytes). Dann könnten Sie einfach warten, bis Sie so viele Zeichen erhalten

if(Serial.available() >= 6) ...

Beachten Sie, dass Sie bei der zweiten Lösung Befehle nicht korrekt empfangen können, wenn Sie ein einzelnes Byte verlieren, bis Sie erneut synchronisieren. Um erneut zu synchronisieren, könnten Sie beispielsweise den Inhalt von Serialnach einem Timeout verwerfen, wenn ein unvollständiger Befehl zu lange dort sitzt.

Eine weitere Möglichkeit: Zeichen einzeln in einen Puffer verschieben, das älteste für jedes neue löschen und prüfen, ob der Inhalt des Puffers ein gültiger Befehl ist. Oder verwenden Sie einzelne Zeichenbefehle.
@Jules, das einen Ringpuffer verwendet, würde eine Garantie erfordern, dass Befehle keine anderen Befehle als Teilzeichenfolgen enthalten können.
Ein erweiterbares Protokoll könnte darin bestehen, die ersten paar (1 bis 4, abhängig von der maximal erwarteten Befehls-/Nachrichtenlänge) Bytes zum Codieren der Länge der Nachricht zu reservieren. Dann lautet Ihr Algorithmus im Grunde "Lesen Sie die Nachrichtengröße -> Lesen Sie die Nachricht".
@aroth Klar, das geht auch. Der Resynchronisationsalgorithmus wäre jedoch immer noch erforderlich.

@9600 Baudrate, Sie empfangen Daten mit 10 Bit pro Millisekunde. Ein Zeichen besteht aus 8 Bits + Start- und Stoppbits in einem Frame. Sie lesen den Puffer schneller als er. Eine einfache Lösung wäre also, eine Verzögerung von > 1 ms oder so nach dem seriellen Lesen () in die Schleife einzufügen.

Dies ist vielleicht das Einfachste, was das OP versuchen könnte, um die Ursache ihres Problems zu bestätigen. Auf Dauer würde ich es aber nicht empfehlen. Es gibt so viele Möglichkeiten, wie verzögerungsbasierter Code fehlschlagen kann ...
Ja, stimmt... Datenverlust kann auftreten, wenn mit höherer Baudrate und kontinuierlichem Datenstrom gearbeitet wird...
Ja. Pufferüberlauf in diesem Fall. Einverstanden.
Um einen Pufferüberlauf zu vermeiden, können Sie nur delay(1)nach dem read()when pingenSerial.available() == 0
@MITURAJ Ich meinte eigentlich etwas anderes. Wenn Sie einen String wie „LED_OFF“ an einen COM-Port eines Computers schreiben, gibt es keine Garantie dafür, dass alle Zeichen ohne Verzögerung gesendet werden. Dasselbe gilt für Mikrocontroller, bei denen der UART ISR nicht die höchste Priorität hat.

Das Puffern bis zum Zeilenvorschub könnte so aussehen (mein Arduino-Code ist etwas eingerostet, also bitte verzeihen Sie einfache Fehler - der Algorithmus sollte stimmen):

finished = false;
command = "";
while (!finished) {
   if (Serial.available() > 0) {
      c = (byte)Serial.read();
      if (c == '\n') {
         finished = true;
      }
      else {
         command += c;
      }
   }
}

Diese Schleife liest einfach weiter Zeichen und fügt diese Zeichen dem Variablenbefehl hinzu.

Dies setzt voraus, dass die Kommunikationsverbindung perfekt ist, da sie keine Zeichen fallen lässt oder verstümmelt. Um dies robuster zu machen, könnten Sie eine Überprüfung vornehmen, um sicherzustellen, dass die Zeit zum Empfangen des Befehls nicht zu lang ist.

Vielleicht möchten Sie auch auf jeden Befehl entweder mit einer Bestätigung oder einem Huh antworten? (nack) Antwort, die dem Server mitteilt, was vor sich geht.

Viel Spaß beim Machen!

Was passiert, wenn der eingehende Datenstrom so aussieht:

LE..Td.. D_O..Td.. N?

Td ist eine zufällige kleine Verzögerung.

Wenn Ihr while(Serial.available() > 0){schneller als Td ist, haben Sie nur Reset my_final = "";mit LEdrin. Das heißt, als nächstes erhalten Sie D_Ound setzen es dann wieder zurück.

Genau das ist das Problem, mit dem ich hier konfrontiert bin. Gibt es eine Möglichkeit, dies zu überwinden?
@ganeshvicky Puffer bis zum Zeilenvorschub.
Du meinst die Funktion delay() verwenden? Es tut mir leid, ich bin irgendwie neu in dieser Sache.
@ganeshvicky Nein, mit Verzögerung können Sie die serielle Schnittstelle nicht lesen. Sie müssen puffern, bis Sie eine erhalten \n, aber Sie müssen trotzdem abfragen Serial.available(), also müssen Sie etwas herausfinden.