Bluetooth: einzelne Zeichen verschwinden im Nirwana

Also, ich habe da gerade folgendes Problem:

Ich habe einen LED Strip (FastLED Library) und ein HC-05 Bluetooth Modul am Ardu angeschlossen. Der Sketch enthält diverse LED-Effekte, die man mit einer Android App durchwechseln kann.

Die Bedienung erfolgt via App. Wird dort ein Button gedrückt, sendet die App Kommandos in folgendem Format zum Bluetooth Modul:

<byte,byte,byte,byte>

oder nur

<byte,byte>

oder

Also bis zu vier, durch Kommata getrennte Zahlen im Bereich von 0-255, die mit < und > umschlossen sind. Zudem wird am Ende ein Newline mitgeschickt.

In einem sehr einfachen Test-Sketch funktioniert der Empfang dieser Werte tadellos:

const byte numChars = 16;     // BUFFER SIZE
char receivedChars[numChars];
char tempChars[numChars];       
boolean newData = false;

//============

void setup() 
  {
  Serial.begin(9600);
  Serial.println("This demo expects up to 4 bytes of incoming data, separated by commas and \nenclosed between < and >:");
  Serial.println("Example: <1,22,65,255>");
  Serial.println("Example: <1,22>");
  Serial.println();
  }

//============

void loop() 
  {
  ReceiveSerialData(); if (newData == true)  ParseSerialData();
  }


// COLLECT INCOMING DATA TO AN ARRAY OF CHARS
void ReceiveSerialData() 
  {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  while (Serial.available() > 0 && newData == false) 
    {
    rc = Serial.read();

    if (recvInProgress == true) 
      {
      if (rc != endMarker) 
        {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) { ndx = numChars - 1; }
        }
      else 
        {
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;
        }
      }

    else if (rc == startMarker) { recvInProgress = true; }
    }
  }


// SPLIT RECEIVED DATA INTO PARTS AND DO STUFF
void ParseSerialData() 
  {
  strcpy(tempChars, receivedChars);
  newData = false;
  byte command = 0;
  byte num1 = 0;
  byte num2 = 0;
  byte num3 = 0;
    
  char * strtokIndx; 

  strtokIndx = strtok(tempChars,",");
  command = atoi(strtokIndx);

  strtokIndx = strtok(NULL, ","); 
  num1 = atoi(strtokIndx);   

  strtokIndx = strtok(NULL, ","); 
  num2 = atoi(strtokIndx);     

  strtokIndx = strtok(NULL, ","); 
  num3 = atoi(strtokIndx);     

  Serial.println("");
  Serial.println("COMMAND: ");
  Serial.println(command);
  Serial.print("NUM1: ");
  Serial.println(num1);
  Serial.print("NUM2: ");
  Serial.println(num2);
  Serial.print("NUM3: ");
  Serial.println(num3);

}

Sobald ich aber diesen Code in mein LED-Projekt transferiere, werden einzelne Zeichen der zu empfangenen Daten verschluckt oder kommen einfach nicht an. Es gehen anscheinend immer wieder willkürlich einzelne Zeichen auf der Reise verloren. Wenn ich z.B. öfter hintereinander <2,17> sende (egal ob aus der App heraus oder direkt in den Serial Monitor eingebe), kommt das hier im Ardu an:

<2,17>
<2,17>
<2,17
<2,17>
<2,17
<2,17>
<2,17>
<2,>
<2,>
<2,17>
<2,17>
<2,17>
<217>

Es fehlen also seltsamerweise immer wieder einzelne Zeichen am Schluss oder auch in der Mitte, so das ein vernünftiges Handling der empfangen Daten gar nicht möglich ist. Siehe z.B. den letzten Wert - durch das verschluckte Komma wird aus 2,17 auf einmal 217.

Kann das irgendetwas mit der FastLib Library und Interrupts zu tun haben? Zudem nutze ich im Projekt auch die SSD1306Ascii Library mit Wire.h und einem 128x32 OLED Display. Kann es da Wechselwirkungen geben?

Mir fällt sonst leider nicht ein, woran das liegen könnte - und auch nicht, wie ich von Seiten der App aus sicher gehen könnte, das wirklich alle Daten korrekt übertragen wurden - eine Art Übertragunsprotokoll wie bei TCP gibt es bei Bluetooth ja nicht, oder? :o

Ich kann den kompletten Code des LED-Projekts gerne auch noch mal posten, falls nötig - der für den seriellen Empfang nötige Code ist aber genau der selbe, wie im Testsketch weiter oben. Und dort scheint es zu funktionieren. Seltsam. Vielleicht kann mich jemand ja erhellen, ob dieses Verhalten bei Bluetooth-Übertragung “normal” oder irgendwelche Interferenzen zwischen Bluetooth und LEDs oder dem OLED-Display möglich sind - keine Ahnung, was da los ist :confused:

EDIT:
Das Senden vom Ardu zur App via Serial.print scheint dagegen problemlos zu funktionieren. Da kommt alles an, soweit ich sehen kann. Es betrifft also wohl nur den Empfang der Daten im Ardu.

Hallo,
warum zeigst du uns den Sketch, der funktioniert ?
Der Sketch, der den Fehler hat, wäre hier besser angebracht.
Da können wir sicher mehr sehen.

maumau777:
[...] und ein HC-05 Bluetooth Modul am Ardu angeschlossen. [...]

Wie?
Lass mich raten: Per Software Serial?
Softwareserial macht nichts anderes als ebenfalls einen Interrupt zu setzen und die Eingehenden Daten in einen Buffer zu schreiben. Das läuft natürlich in eine Racecondition mit anderen Interrrupts.
Es könnte hier schon helfen die Baudrate zu senken.
Oder nach Ende der Testphase auf den HardwareSerial zu wechseln.
Das alles ist erstmal nur geraten, da Dein Beispiel-Sketch genau diese noralgischen Punkte auslässt.

(deleted)

Peter-CAD-HST:
Moin maumau777
Nimm eine Suchmaschine deiner Wahl und füttere diese mit FastLED Library +serial. :slight_smile:
Gruß Peter
Take Care

Hm ja, da scheint es tatsächlich Probleme zu geben, dachte schon bei mir spukt's :smiley: Ganz schön zickig, diese WS28 LEDs. Wenn ich das vorher gewusst hätte (kommt sich mit IRRemote Lib und Tone Lib in die Quere, stört Serial), hätte ich evtl nen anderen genommen.

ABER:
Ich habe das jetzt in zwei Schritten mehr oder weniger lösen können:

In meinem LED-Sketch habe ich FastLED.show() im Loop-Block ergänzt mit

if (Serial.available() == 0) FastLED.show();

Das hat die Fehlerquote schon deutlich minimiert, aber noch nicht ganz behoben.

Also habe ich noch an der App gewerkelt. Die merkt sich nun, welcher Befehl mit welchen Parametern zuletzt gesendet wurde. Der Ardu sendet nach jedem empfangenen Befehl eine komplette Liste aller User-Settings (ausgewählter Effekt usw) zurück. So kann die App vergleichen, ob der Ardu die gesendeten Daten auch richtig bekommen hat. Wenn nicht, schickt die App genau den gleichen Befehl einfach noch mal. Der User merkt davon nichts.

Das funktioniert nun soweit.

Wäre es nicht deutlich performanter und (Daten-)sparsamer auf eine Checksumme zu setzen?

TriB:
Softwareserial macht nichts anderes als ebenfalls einen Interrupt zu setzen und die Eingehenden Daten in einen Buffer zu schreiben. Das läuft natürlich in eine Racecondition mit anderen Interrrupts.

Interrupts können sich erst mal ruhig gegenseitig blockierend. Wenn sie schnell genug abgearbeitet werden kommt nach dem Beenden eines Interrupts einer dran der während der Zeit eintraf.

Allerdings schaltet FastLED die Interrupts zwischenzeitlich einfach ab um die Zeiten einzuhalten. Das verursacht Probleme

(deleted)

TriB:
Wäre es nicht deutlich performanter und (Daten-)sparsamer auf eine Checksumme zu setzen?

An so etwas dachte ich zuerst auch, aber App-seitig steht ja genug Performance (im Vergleich zum Ardu) zur Verfügung und der komplette Block an User-Settings, die der Ardu nach jedem empfangenen Befehl zurück an die App sendet, besteht nur aus ganzen 7 Bytes - das lässt sich also problemlos machen.

Da ich FastLed.show() ja pausiere, sobald serielle Daten empfangen werden, schlägt mittlerweile nur noch etwa einer von 10 Sendevorgängen fehl - und nur dann wird der Befehl von der App aus noch einmal gesendet. Also eigentlich nicht wild.

Ich denke, eine Checksummenprüfung auf beiden Seiten hätte da mehr Overhead.

Das Senden vom Ardu zur App funktioniert ja sowieso problemlos. Da wird nichts verstümmelt. Nur von der App zum Ardu, also wenn er empfängt, gibt es hin und wieder eine Verstümmelung. Ein Pieper am Ardu signalisiert, sobald ein Befehl von der App empfangen wurde. Und wie gesagt, nur noch etwa jedes 10. mal macht er statt PIEP eben PIEP-PIEP, weil der Befehl noch einmal gesendet werden musste :grin:

maumau777:
Das Senden vom Ardu zur App funktioniert ja sowieso problemlos.

Das sind ja auch zwei völlig verschiedene Sachen