Problem mehrere Werte per Bluetooth an HC05 senden und Auslesen

Hallo liebe Gemeinde,
ich habe ein Problem mehrere Werte über Bluetooth an einen Arduino zu senden.
Eventuell hat ja jemand von euch einen Lösungsansatz.

Mit einer App sollen die Werte eines Joysticks und eines Sliders sowie weitere Befehle per Bluetooth an einen Arduino gesendet werden. Ziel ist es später einen Hexapod anzusteuern.
Leider fällt beim auslesen der Werte auf, dass die gesendeten Werte oft an falscher Stelle stehen.

Zum Erkennen, das die Datenreihe beginnt, wird einmal der Wert 255 gesendet, der darauffolgende Wert ist dann der x z.B. 65, dann y z.B. 65, dann z z.B. 127 und dann control z.B. 24.

Die Verbindung zum Bluetoothmodul HC05 wird aufgabaut und ist Stabil. Daten werden auch zügig übermittelt. Nur geraten eben die Werte teilweise durcheinander.

Ausgabe am Seriellen Monitor:

255, x65, y65, z127, control24
255, x65, y127, z127, control24
255, x65, y127, z24, control24
255, x65, y127, z24, control65
255, x65, y127, z24, control65


int dataIn[5] {0,0,0,0,0};              
int in_byte = 0;
int array_index = 0;       

int joystick_x_ble = 0;      // Joystick x Position
int joystick_y_ble = 0;      // Joystick y Position
int joystick_z_ble = 0;      // Joystick/ Slider z Position
int control_ble = 0;         // Control Nummer

void setup() {
  
 Serial.begin(9600);         // Serieller Monitor
  Serial1.begin(9600);     // Bluetooth-Modul HC05
}

void loop() {
  
 if(Serial1.available()>0){           // Wenn Daten vorhanden
  
    in_byte = Serial1.read();        // Daten lesen
           
   if (in_byte == (255)){            // 255 Anfang der Datenreihe
    array_index = 0;
   }   
   dataIn[array_index]=in_byte;
   array_index = array_index +1;
  }
  
    Serial.print(dataIn[0]);
    Serial.print(", x");
    Serial.print(dataIn[1]);
    Serial.print(", y");
    Serial.print(dataIn[2]);
    Serial.print(", z");
    Serial.print(dataIn[3]);
    Serial.print(", control");
    Serial.println(dataIn[4]);
}

Könnte mir vorstellen, dass ich eine While-Schleife beim Auslesen einbinden muss, bis jede Stelle eingelesen ist. Habe es mal versucht mit:

 if (in_byte == (255)){            // 255 Anfang der Datenreihe
    array_index = 0;
    if (array_index == 0){
       while(array_index <= 4){
   in_byte = Serial1.read();
   dataIn[array_index]=in_byte;
   array_index = array_index +1;
       }
   }
   }   

255, x65, y-1, z-1, control-1
255, x65, y-1, z-1, control-1
255, x65, y65, z127, control-1
255, x65, y65, z127, control-1

Das ist das Ergebnis.... :face_with_raised_eyebrow:

Verwendet wird ein Arduino Mega. Auch mit einem Uno habe ich es getestet.

Hat jemand von euch Erfahrungen dabei, mehrere Werte zu Empfangen und diese auch an richtiger Stelle einzulesen?

Wäre für jeden Tipp dankbar. :slight_smile:

Moin, @caminoto,

die read-Funktion wartet nicht, bis ein Character eingetroffen ist, sondern gibt eine -1 zurück, wenn der serielle Puffer leer ist. So zumindest bei den read-Funktionen, die von Stream abgeleitet werden.

In Deiner while-Schleife muss also noch geprüft werden, ob tatsächlich Daten vorliegen, bevor sie akzeptiert werden. Dann sollte es klappen.

Also die Abfrage

if (Serial1.available()){

  // Hier kann man gesichert ein Byte bzw. einen Character einlesen!

}

Wenn 1 Zeichen da ist, dauert es bei 9600 Baud mindestens 1 ms, bis das nächste gekommen ist.
Dein Eingangspuffer hätte Platz für 64 Zeichen...
Warte einfach nach der 255 per delay(5); ein kleines Weilchen
Oder warte, nachdem du den Start (255) erkannt hast, bis available 4 zurückliefert.
Oder merke dir alles was kommt und werte bei 255 die 4 Zeichen davor aus
Oder ...

Danke für die Hinweise!! :slight_smile:

Habe es jetzt so gelöst:


int dataIn[5] {0,0,0,0,0};              
int in_byte = 0;
int array_index = 0;       

int joystick_x_ble = 0;      // Joystick x Position
int joystick_y_ble = 0;      // Joystick y Position
int joystick_z_ble = 0;      // Joystick/ Slider z Position
int control_ble = 0;         // Control Nummer

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

void loop() {
  
 if(Serial1.available()>0){           // Wenn Daten vorhanden
  
    in_byte = Serial1.read();        // Daten lesen
           
   if (in_byte == (255)){            // 255 Anfang der Datenreihe
    array_index = 1;
    if (array_index == 1){           // Index auf 1, da die Werte sonst an falscher Stelle stehen
       while(array_index <= 4){
  if(Serial1.available()){           // nochmal Prüfen, ob Daten vorhanden sind.
   in_byte = Serial1.read();         // Daten auslesen
   dataIn[array_index]=in_byte;
   array_index = array_index +1;
  }
       }
   }
   }
 }
  
  
    Serial.print(dataIn[0]);
    Serial.print(", x");
    Serial.print(dataIn[1]);
    Serial.print(", y");
    Serial.print(dataIn[2]);
    Serial.print(", z");
    Serial.print(dataIn[3]);
    Serial.print(", control");
    Serial.println(dataIn[4]);
}

Scheint im ersten Test zu funktionierten. Mal sehen ob es sich bewährt. :+1:t4:

Schleifen mit whilesind potentiell blockierend, weshalb ich sie immer mit Argwohn sehe und möglichst vermeide.

Da loop schon eine Schleife ist, kann man diese verwenden. Dann können noch andere Dinge quasi parallel stattfinden, als Beispiel eine blinkende LED als Herzschlag.

Getestet mit Mega2560:

const byte ANZAHL = 4;
byte dataIn[ANZAHL];
byte in_byte = 0;
byte array_index = 0;

void setup()
{
  Serial.begin(9600);
  Serial1.begin(9600);
  pinMode (LED_BUILTIN, OUTPUT);
}

void loop()
{
  herzschlag();
  bool gueltig = false;
  if (Serial1.available() > 0) {     // Wenn Daten vorhanden
    in_byte = Serial1.read();        // rin Byte lesen

    if (in_byte == (255)) {          // 255 Anfang der Datenreihe
      array_index = 0;
    } else {
      dataIn[array_index++] = in_byte;
    }
    if (array_index == 4) gueltig = true;
  }

  if (gueltig)
  {
    Serial.print("x ");
    Serial.print(dataIn[0]);
    Serial.print("\ty ");
    Serial.print(dataIn[1]);
    Serial.print("\tz ");
    Serial.print(dataIn[2]);
    Serial.print("\tcontrol ");
    Serial.println(dataIn[3]);
  }
}

void herzschlag()
{
  uint32_t jetzt = millis();
  static uint32_t vorhin = jetzt;
  const uint32_t intervall = 111;

  if (jetzt - vorhin >= intervall)
  {
    vorhin = jetzt;
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  }
}

Hallo @agmue ,

danke für deine Mühe.
Ich habe es auch mit deinem code getestet und es funktioniert.

Wenn ich diesen dann in mein Steuerungsprogramm einbinde habe ich wieder die Stellenverschiebung. Zudem erfolgt das Auslesen teils sehr langsam :man_shrugging:t4:

Serieller Monitor: Bsp.

x20, y65, z127, control20
x127, y20, z128, control130
x20, y20, z20, control127

Wenn ich in jedem loop durchlauf nur einen Wert auslese, vom Sender aber ja immer alle gesendet werden, ist es dann nicht wahrscheinlich, dass der Arduino ein wenig durcheinanderkommt.
Korrigiere mich bitte falls ich falsch liege.

Deswegen war mein Gedanke, alle Werte gleich hintereinander auslesen. Dann habe ich alle in der richtigen Reihenfolge.

Das if ist sinnlos, stört aber nicht.
Array - Indizes fangen in C/C++ mit 0 an. dataIn[0] wird nie beschrieben, oder ?

Dem Compiler ist es egal, mir wäre es peinlich. :stuck_out_tongue:
Sonst vermeidet dein Code immerhin, dass bei falschen (zuviel) Daten etwas außerhalb von dataIn geschrieben wird. K.A. ob das ein realistisches Problem ist, wenn ja wäre in der Hinsicht deine Version besser als @agmue 's. Und 5 ms loop-Durchlauf sind ja jetzt nicht so lang, dass ein herzschlag-Blinken nicht "fast parallel" laufen könnte

Ich hab mal einen ganz anderen Ansatz.

[edit]
Ich hab grad gesehen, das Du Controlbytes mit verwendest, dann muss das ein wenig anders sein, hab das mal umgebaut.
[/edit]

int dataIn[4] {0, 0, 0, 0};
//int in_byte = 0;
int array_index = 0;

int joystick_x_ble = 0;      // Joystick x Position
int joystick_y_ble = 0;      // Joystick y Position
int joystick_z_ble = 0;      // Joystick/ Slider z Position
int control_ble = 0;         // Control Nummer

void setup()
{
  Serial.begin (9600);        // Serieller Monitor
  Serial1.begin (9600);    // Bluetooth-Modul HC05
}

void loop()
{
  byte in_byte = 255;
  if (Serial1.available() > 0)         // Wenn Daten vorhanden
  {
    in_byte = Serial1.read();         // Daten lesen
  }
  if (in_byte == 255)             // 255 Anfang der Datenreihe
  {
    array_index = 0;
    memset (dataIn, '\0', sizeof (dataIn));
  }
  else
  {
    if (!isControl (in_byte))
    {
      dataIn[array_index] = in_byte;
      array_index = array_index + 1;
    }
    else
    {
      if (array_index < 3)
      {
        Serial.print (F ("Unvollständig! bei Index: "));
        Serial.print (array_index);
        Serial.print (F (" mit Wert: "));
        Serial.println (in_byte);
      }
      else
      {
        dataIn[array_index] = in_byte;
        Serial.print (", x");
        Serial.print (dataIn[0]);
        Serial.print (", y");
        Serial.print (dataIn[1]);
        Serial.print (", z");
        Serial.print (dataIn[2]);
        Serial.print (", control");
        Serial.println (dataIn[3]);
      }
    }
  }
}

Wenn das besser funktioniert, kann es ausgebaut werden.
Beachte!
Das Array ist jetzt nur 4 und nicht 5 gross, da ich die 255 wegwerfe.

@michael_x

Also es wird ja geprüft, ob der Wert 255 ausgelesen wird. Die Darauffolgenden Werte sind dann X, Y, Z, Control. Die Werte aus dataIn[1 bis 4]

Die folgende If Bedingung habe ich genommen, damit die dataIn[1 bis 4] wirklich nur ausgelesen werden, wenn zuvor die 255 kam.

Wenn ich hier den index auf 0 setze, dann liest er die 255 nichtmehr aus, da er diese ja schon ausgelesen hat. Das bedeutet dataIn[0] hat dann den Wert 65, sollte aber 255 sein.
Um das zu vermeiden sollen erst ab dem dataIn[1] die Werte ausgelesen werden.

 if (array_index == 1){           // Index auf 1, da die Werte sonst an falscher Stelle stehen
       while(array_index <= 4){
  if(Serial1.available()){           // nochmal Prüfen, ob Daten vorhanden sind.
   in_byte = Serial1.read();         // Daten auslesen
   dataIn[array_index]=in_byte;
   array_index = array_index +1;

Könnte man das auch eleganter lösen?
Hier nochmal in Schönschrift :grin:


int dataIn[5] {0, 0, 0, 0, 0};
int in_byte = 0;
int array_index = 0;

int joystick_x_ble = 0;      // Joystick x Position
int joystick_y_ble = 0;      // Joystick y Position
int joystick_z_ble = 0;      // Joystick/ Slider z Position
int control_ble = 0;         // Control Nummer

void setup() {

  Serial.begin(9600);
  Serial1.begin(9600);
}

void loop() {

  if (Serial1.available() > 0) {       // Wenn Daten vorhanden

    in_byte = Serial1.read();        // Daten lesen

    if (in_byte == (255)) {           // 255 Anfang der Datenreihe
      array_index = 0;
      if (array_index == 1) {          // Index auf 1, da die Werte sonst an falscher Stelle stehen
        while (array_index <= 4) {
          if (Serial1.available()) {         // nochmal Prüfen, ob Daten vorhanden sind.
            in_byte = Serial1.read();         // Daten auslesen
            dataIn[array_index] = in_byte;
            array_index = array_index + 1;
          }
        }
      }
    }
  }


  Serial.print(dataIn[0]);          // immer 255
  Serial.print(", x");
  Serial.print(dataIn[1]);
  Serial.print(", y");
  Serial.print(dataIn[2]);
  Serial.print(", z");
  Serial.print(dataIn[3]);
  Serial.print(", control");
  Serial.println(dataIn[4]);
}

Wenn ich in jedem loop durchlauf nur einen Wert auslese, vom Sender aber ja immer alle gesendet werden, ist es dann nicht wahrscheinlich, dass der Arduino ein wenig durcheinanderkommt.
Korrigiere mich bitte falls ich falsch liege.

Wie häufig loop() durchlaufen wird, hängt im Wesentlichen von den darin realisierten Funktionen ab. Neben der Tatsache, dass dies - bei Vermeidung blockierender Funktionen - ein Vielfaches gegenüber den Übertragungszeiten der seriellen Schnittstelle bei 9600 Bd ist, liest der Controller die Daten in einen Puffer mit standardmäßig 64 Bytes ein (siehe auch Post 3). Beim Auslesen der Daten per Read() holt man also die Daten nicht direkt von der Schnittstelle sondern aus einem Buffer. Wenn ein Arduino bereits bei 9600 Bd aus dem Tritt käme, was sollte er erst bei 115200 oder einer höheren Baudrate machen ... :wink:

Es wäre möglicherweise hilfreich, wenn Du die Sendeseite posten würdest ... Dann könnte man sehen, in welchem Format Deine Daten gesendet werden. Benutzt Du dort write() oder print()?

Das wird doch nichts!
array_index wird doch nie 1. Oder hab ich da was übersehen?

Noch ne Frage zu

Was kommt denn da tatsächlich an?
Ein Byte mit einem ASCII-Steuercode? (Also ein Byte von 0 bis 29?)

  if (in_byte == (255)) {           // 255 Anfang der Datenreihe
      array_index = 1;

Hatte ich nicht wieder umgeschrieben.

@ec2021

Sender ist eine Android App die ich mit MIT Appinventor erstellt habe. Die Daten werden alle 50ms gesendet.

@my_xy_projekt

Es werden Zahlenwerte gesendet mit denen der Arduino dann z.B. die Geschwindigkeit ändern soll oder die Beleuchtung oder Bewegungsart. Einfach Zahlen von 0-254

Dann versuche es mal mit diesem Sketch :

#include <SoftwareSerial.h>

const byte ANZAHL = 4;
byte dataIn[ANZAHL];
byte in_byte = 0;
byte array_index = 0;

void setup()
{
  Serial.begin(9600);
  Serial1.begin(9600);
  pinMode (LED_BUILTIN, OUTPUT);
  Serial.println();
}

void loop()
{
  herzschlag();
  if ( SerialDataValid())
  {
    Serial.print("x ");
    Serial.print(dataIn[0]);
    Serial.print("\ty ");
    Serial.print(dataIn[1]);
    Serial.print("\tz ");
    Serial.print(dataIn[2]);
    Serial.print("\tcontrol ");
    Serial.println(dataIn[3]);
  }
}

boolean SerialDataValid(){
    static boolean SyncFound = false;
    if (Serial1.available() > 0) {     // Wenn Daten vorhanden
        in_byte = Serial1.read();        // die entsprechenden Bytes lesen
        if (SyncFound) {
          dataIn[array_index] = in_byte;
          array_index++;
        }
        if (in_byte == 255) {
          SyncFound = true;
          array_index = 0;
      }
     if (array_index == ANZAHL) {
       array_index = 0;
       return true;
     } else return false;
  }
}  

byte dataIn[4] {0, 0, 0, 0};
byte in_byte = 0;
byte array_index = 0;

byte joystick_x_ble = 0;      // Joystick x Position
byte joystick_y_ble = 0;      // Joystick y Position
byte joystick_z_ble = 0;      // Joystick/ Slider z Position
byte control_ble = 0;         // Control Nummer

void setup()
{
  Serial.begin (9600);        // Serieller Monitor
  Serial1.begin (9600);    // Bluetooth-Modul HC05
}

void loop()
{
  if (Serial1.available() > 0)         // Wenn Daten vorhanden
  {
    in_byte = Serial1.read();         // Daten lesen
    if (in_byte == 255)               // 255 Anfang der Datenreihe
    {
      if (array_index < 3)
      {
        Serial.print (F ("Letzter Datensatz unvollständig! Fange an von vorn."));
      }
      array_index = 0;
      memset (dataIn, '\0', sizeof (dataIn));
    }
    else
    {
      if (array_index > 3)
      {
        Serial.print (F ("Index Überlauf!"));
        return;
      }
      dataIn[array_index] = in_byte;
      array_index++;
      if (array_index == 4)
      {
        Serial.print (", x");
        Serial.print (dataIn[0]);
        Serial.print (", y");
        Serial.print (dataIn[1]);
        Serial.print (", z");
        Serial.print (dataIn[2]);
        Serial.print (", control");
        Serial.println (dataIn[3]);
        array_index++;
      }
    }
  }
}

Im Seriellen Monitor kommt das dabei raus.
Auch nur die 7 Zeilen, dann stoppt er.

x 0 y 0 z 0 control 0
x 0 y 0 z 0 control 0
x 0 y 0 z 0 control 0
x 0 y 0 z 0 control 0
x 0 y 0 z 0 control 0
x 0 y 0 z 0 control 0
x 0 y 0 z 0 control 0

Die Werte X, Y, Z, werden erfasst bei control ändert sich nicht´s.

Index Überlauf!, x65, y65, z212, control0
Index Überlauf!, x65, y65, z212, control0
Index Überlauf!, x65, y65, z212, control0
Index Überlauf!, x65, y65, z212, control0
Index Überlauf!, x65, y65, z212, control0

Ja, ist mir auch während des tippens aufgefallen - ich hab den Code noch verbessert.... Mach mal nochmal.

Der Überlauf teilt Dir mit, das Du mehr Werte bekommst, als Du tatsächlich aufnehmen kannst.
Deine Reihe besteht nach Deinen Angaben aus: 255, Xwert, Ywert, Zwert, CWert.
Ich schmeisse die 255 weg und beginne ab da das Array zu füllen.
Element0 = Xwert
Element1 = Ywert
Element2 = Zwert
Element3 = CWert
Bekomme ich ein 255, bevor das Element3 gefüllt ist:

Ist die Kette unvollständig.

Bekomme ich ein Zeichen rein und der Index ist:

Damit ist klar, das Du mehr Zeichen bekommst, als Du Elemente im Array hast.

Erst wenn diese beiden Prüfungen durch sind, kommt das:

Damit wird sichergestellt, das das Element3 gefüllt ist und der index schon auf das "nächste Element" zeigt -> Hier ins nirgendwo, da nicht definiert, was Dir einiges einbrockt, wenn das nicht abgefangen wird (Siehe oben).

Zeig mal, was hierbei am Seriellen Monitor rauskommt:

byte index = 0;

void setup()
{
  Serial.begin (9600);        // Serieller Monitor
  Serial1.begin (9600);    // Bluetooth-Modul HC05
}

void loop()
{
  if (Serial1.available() > 0)         // Wenn Daten vorhanden
  {
    Serial.print (Serial1.read(), HEX);
    Serial.print (", ");
    index++;
    if (index >7)
    {
      index = 0;
      Serial.println();
    }
  }
}