[GELÖST] iPhone und BLE: Was nun? RC Auto steuern

Guten Morgen (oder Mittag?)
Nach einer Woche schierer Verzweifelung mit den Apple-Geräten möcht ich mich an das Forum wenden...

Da ich eher magere Kenntnisse habe im Programmieren, täten mir Ratschläge ganz gut...

Meine App, die ich als brauchbar empfinde (und verwende) ist die App:Tah Play, kostenlos und einfach zu bedienen. Zudem besteht die möglichkeit, 2 oder mehr Tasten gleichzeitig zu betätigen.
Der Aufbau besteht von dem Auto aus einem HM-10 BT Modul, einem Nano, H-Brücke und 2 DC Motoren, die bereits in dem Spielzeugauto eingebaut waren.

Ursprünglich war der Code für HC-05 mit Android optimiert, jedoch wird jetzt auch Apple-Kompatibilität gefordert.

Zum Eigentlichen Problem: Die App verschickt "seriell" Daten (bsp: P1,0,0,1,0) also quasi hintereinander und nicht "in einem Rutsch". Bei der App für Android war das kein Problem, da da immer nur ein Buchstabe als Befehl gesendet wird (ist mit switch case fix erledigt... aber ich schweife ab...)
Sprich: im Seriellen Monitor bekomme ich:

11:52:51.881 -> P
11:52:51.881 -> 1
11:52:51.881 -> ,
11:52:51.881 -> 0
11:52:51.881 -> ,
11:52:51.881 -> 0
11:52:51.881 -> ,
11:52:51.881 -> 1
11:52:51.881 -> ,
11:52:51.881 -> 0

Ich habe mir überlegt, dass das Einlesen der "Befehle" (ich nehme an als char, mit?ohne?) Trennzeichen in ein Array geschrieben wird. Von dort aus kann ich ja mit if Abfragen den Entsprechenden void starten. Natürlich habe ich mit dem Serial Input Basic von Robin2 auch schon dran gewerkelt, aber da will ich mich selbst Zitieren: magere Kenntnisse in Programmieren. Für mich ist der Groschen leider noch nicht gefallen

Beispiel Switch Case:

switch (blueToothVal)
  {
    case 'F':
      Vorwaerts();
      break ;

    case 'B':
      Rueckwaerts();
      break;

    case 'R':
      Rechts();
      break ;

    case 'L':
      Links();
      break ;
}

In den voids zu den Richtungen sind eben "nur" HIGH bzw LOW Anweisungen, die dürften eigentlich nicht von Belang sein...

char blueToothVal;
byte Geschwindigkeit = 255;

// Pin assignment muss natürlich noch angepasst werden...

// Motor Rechts
int A = 6;
int B = 5;
// Motor Links
int C = 11;
int D = 10;
// Für Lichter vorne & hinten
int light = 2;
int light_hinten = 3;
int State = 0;





void setup()
{

  Serial.begin (9600);
  pinMode (light, OUTPUT );
  pinMode (light_hinten, OUTPUT );
  pinMode (A, OUTPUT );
  pinMode (B, OUTPUT );
  pinMode (C, OUTPUT );
  pinMode (D, OUTPUT );

}


void loop ()

{

  if (Serial.available ())
  {
    Bluetooth ();
    Serial.println (blueToothVal);
    blueToothVal = Serial.read ();
  }

}

Und das Problem ist nun?

Serielle Übertragung ist immer seriell, d.h. die Zeichen kommen nacheinander.
Jedes Zeichen kann doch einzeln ausgewertet werden, oder jetzt nicht mehr?

Hi

Deine 'void's sind auch nur Funktionen/Methoden, Die keinen Rückgabewert haben (void).

Du musst doch nur Alles, was rein kommt, der Reihe nach einlesen.
Aber auch ich verstehe nicht, was das I-Phone hier für Probleme haben will - auch bei Android sind die Daten nicht schneller - eigentlich dürfte sich 'Arduino-Seitig' nahezu Nichts geändert haben.

MfG

Erstmal danke für die Antworten!
Ich formuliere mal um: bei einem Tastendruck wird jene Zeichenkette "P1,0,0,2,0" als char übertragen. Eben in einem Durchgang des Programmcodes. Meine Erfahrung beschränkt sich auf ein Zeichen pro Durchlauf des Programmes und nicht auf mehrere.

Das Problem ist, das Empfangene in ein Array zu schreiben aber ohne die Möglichkeit zu haben, die erhaltenen Daten mit einem Start/Stop "Bit" zu kontrollieren. Da ja die Befehle schon vorgegeben sind.

Meine Frage ist ja, wie stelle ich das an. Das Was hatte ich ja bereits erläutert.

@postmaster-ino
Was meinst du genau mit "alles der Reihe nach einlesen"? Ich denke das ist das was ich meine

Hi

Was soll mir die Übertragung P1,0,0,1,0 überhaupt sagen?
Mit FBRL hätte ich ja vll. noch eine Ahnung, Was Da passieren soll (also der Buchstabe, der 'Befehl') - was sagen mir die Zahlen (ganz nebenbei, was sagt mir das P oder P1)?

So lange das Protokoll nicht wirklich klar ist, können wir den Kram in ein Ringspeicher (Array) einlesen ... und dann?

MfG

Das P1 bedeutet wohl soviel wie Phone 1, die anderen Ziffern sind die Digitalwerte in int und sollen den state der anderen Buttons darstellen, also 0 unbetätigt und ein beliebiger Wert bis 127 die Kombination aus den anderen gedrückten Buttons (siehe angehängter Screenshot)

15:21:52.877 -> P
15:21:52.877 -> 1
15:21:52.877 -> ,
15:21:52.877 -> 0
15:21:52.877 -> ,
15:21:52.877 -> 0
15:21:52.877 -> ,
15:21:52.877 -> 6
15:21:52.877 -> ,
15:21:52.877 -> 7
15:21:52.877 -> 0

Der Zeichenbefehl kommt ja von der App (GitHub) und hat als Beispiel:

Wenn ich das Steuerkreuz nach oben betätige

15:14:51.059 -> P
15:14:51.059 -> 1
15:14:51.059 -> ,
15:14:51.059 -> 0
15:14:51.059 -> ,
15:14:51.059 -> 0
15:14:51.059 -> ,
15:14:51.059 -> 2
15:14:51.059 -> ,
15:14:51.059 -> 0

und wenn ich das Steuerkreuz wieder loslasse (0-Stellung)

15:17:26.400 -> P
15:17:26.400 -> 1
15:17:26.400 -> ,
15:17:26.400 -> 0
15:17:26.400 -> ,
15:17:26.400 -> 0
15:17:26.400 -> ,
15:17:26.400 -> 5
15:17:26.400 -> ,
15:17:26.400 -> 0

Wissenslücke: Was ist FBRL?

Und zu deiner Frage, was ich mit den Daten vorhabe, wenn diese in einem Array sind:
Diese sollen in einem Programmteil wieder aufgedröselt werden und weiterverarbeitet werden. Quasi aus dem erhaltenen Befehl die Befehle für die Richtungen des Fahrzeugs herauspicken.

IMG_0030[1].PNG

Hi

F Forward
B Abckward
R Right
L Left
Hatte Das als die 'Befehle' interpretiert - daß also 'F' gesendet wird, wenn's geradeaus gehen soll.
... wohl ein 'freudscher' :wink:

Dann erstelle Dir ein Array beliebiger Größe - unter 256, damit wir für die Zeiger bei Byte bleiben können.
Dann zwei Zeiger byte readpointer,writepointer;
Sobald Du etwas zum Einlesen hast (Serial.available () ) liest Du das eine Byte aus und schreibst Es in das Array an die Position writepointer. Dann writepointer++; writepointer%=groessearray;
Wenn readpointer und writepointer nicht mehr aufeinander liegen, sind Zeichen zur Auswertung da.
Einlesen dann über den Index readpointer, umbrechen am Ende des Array.

Verständlich?

Wie ich Das in meinem Haus-Bus (CAN) verwende:

void addpointer(byte &_pointer) {
  //Quelle https://forum.arduino.cc/index.php?topic=656436.msg4422936#msg4422936
  _pointer++;
  _pointer %= maxnachricht;
  //++_pointer%=maxnachricht; klappt auch, könnte aber vom Kompiler kaputt optimiert werden
}

Aufgerufen dann so:
addpointer(write_out_pointer);//Pointer++
wobei write_out_pointer mein Schreib-Zeiger im Ausgabe-Array ist.
Die Größe ist im Sketch mit maxnachricht festgelegt.

//Ringspeicher für reinkommende und rausgehende Nachrichten.
const byte maxnachricht = 10;
typedef struct {
  uint32_t ID;        //CAN-ID des Senders der Nachricht
  byte len;
  byte _byte[8];            //Nutzbytes der Nachricht
} NACHRICHT;
NACHRICHT nachricht_in[maxnachricht], nachricht_out[maxnachricht];
//Zeiger auf die aktuelle Nachricht, wenn read==write, sind wir 'aktuell'
byte read_in_pointer, read_out_pointer, write_in_pointer, write_out_pointer;

MfG

Ich hab mir das "Protokoll" der App Tah-Play mal angeschaut.

Es ist nicht sehr kompliziert und lässt sich mit den "üblichen Mitteln" leicht auswerten (also Einlesen bis zum Endzeichen, Auswertung danach - siehe z.B. Serial Input Basics - updated )

Die App-Oberfläche
app.png

Hintergrund
Die App sendet eine Nachricht sowohl wenn eine der Tasten gedrückt wird, als auch wenn eine Taste losgelassen wird (also so ähnlich wie eine PC-Tastatur).

Das Endzeichen jeder Nachricht ist P (großes P). Es wird nur das P als Endzeichen gesendet, also kein LF oder CR.

Es gibt 2 "Tastengruppen".
Zur linken Tastengruppe gehören folgende Tasten: UP, DOWN, LEFT, RIGHT
Zur rechten Tastengruppe gehören folgende Tasten: A, B, X, Y, Select, Start
Die gesendeten Zeichen für die linke Tastengruppe sind fix und lassen sich nicht ändern.
Bei der rechten Tastengruppe lassen sich die gesendeten Zeichen für A, B, X, Y einstellen, die für "Select" und "Start" sind fix.

Bei jeder der beiden Tastengruppen kann jeweils (nur) eine Taste gleichzeitig gedrückt sein.
Aber natürlich können 2 Tasten gleichzeitig gedrückt sein, solange es sich um eine Taste der linken und eine Taste der rechten Tastengruppe handelt.

Bei jedem Drücken oder Loslassen einer Taste wird eine Nachricht mit Status-Infos sowohl der linken als auch der rechten Tastengruppe geschickt.

Das Protokoll
Die Nachricht, die die App über BLE verschickt sieht so aus.
Beispiel: Drücken der Taste UP
1,0,0,2,0P

Jede Nachricht wird mit "P" abgeschlossen (Endzeichen: P).

Die ersten 6 Zeichen jeder Nachricht sind immer gleich: 1,0,0,
Das 7. Zeichen repräsentiert den Status der linken Tastengruppe:
5 .... keine Taste gedrückt
2 .... Taste UP gedrückt
4 .... Taste LEFT gedrückt
6 .... Taste RIGHT gedrückt
8 .... Taste DOWN gedrückt

Die Zeichen 9 (und eventuell nachfolgende 1 oder 2 Zeichen) repräsentieren den Status der rechten Tastengruppe:
0 .... keine Taste gedrückt
5 .... Taste SELECT gedrückt
6 .... Taste START gedrückt
Die übertragenen Werte der Tasten A, B, X, Y sind in der App einstellbar und repräsentieren den ASCII-Code des eingestellten Zeichens. Die Werte können also Werte von 32 (Leerzeichen) bis 126 (Zeichen ~) haben, je nach Einstellungen.

Bei "günstiger Einstellung" in der App, kann auf die Auswertung von Zeichen 10 und ev. 11 verzichtet werden.
Da 5 und 6 bereits "vergeben" sind bleiben noch 3x, 4x, 7x, 8x, 9x.
Beispiel, App-Einstellungen:
A = ! (ASCII 33)
B = 1 (ASCII 49)
X = F (ASCII 70)
Y = R (ASCII 82)

Dann gilt für das 9. Zeichen (rechte Tastengruppe):
0 .... keine Taste gedrückt
5 .... Taste SELECT gedrückt
6 .... Taste START gedrückt
3 .... Taste A gedrückt
4 .... Taste B gedrückt
7 .... Taste X gedrückt
8 .... Taste Y gedrückt

Beispiel-Sketch zur Auswertung
Als Basis dient Serial Input Basics - updated.
Das Endzeichen wurde geändert auf "P".

Die Auswertung ist (nur) eine serielle Ausgabe, das muss man auf die eigenen Gegebenheiten anpassen.

/* Test mit BLE und iOS App Tah-Play

   Einstellungen in der App: 
      Taste A:  !   (ASCII 33)
      Taste B:  1   (ASCII 49)
      Taste X:  F   (ASCII 70)
      Taste Y:  R   (ASCII 82)
      
  Hardware:
    Arduino (UNO)
    Bluetooth-Modul HM-10 (für iOS)
*/

// Bluetooth
#include <SoftwareSerial.h>
const byte rxPin = 2;
const byte txPin = 3;
SoftwareSerial btSerial(rxPin, txPin);

// Auswertung BT-Serial
const int SERIAL_BUFFER_SIZE = 16;      // die maximale Zeilenlänge +1
char serialBuffer[SERIAL_BUFFER_SIZE];
byte sbIndex;

void setup() {
  Serial.begin(9600);
  Serial.println("los geht's");
  btSerial.begin(9600);     // Standard speed
}

void loop() {
  if (readSerial(btSerial))      //liefert true wenn das Nachrichten-Ende erreicht
  {
    auswertungDerNachricht();
  }
}

// Einfach so lange alles einlesen bis das Endzeichen (P) kommt.
bool readSerial(Stream& stream) 
{
  while (stream.available()) 
  {
    char c = stream.read();
    if (c == 'P' && sbIndex > 0)        // wenn "P" eingelesen und String länger als 0 ist
    {
      serialBuffer[sbIndex] = '\0';     // String terminieren
      sbIndex = 0;
      return true;               // melden dass String fertig eingelesen wurde
    }
    else if (sbIndex < SERIAL_BUFFER_SIZE - 1)   // solange noch Platz im Puffer ist
    {
      serialBuffer[sbIndex++] = c;    // Zeichen abspeichern und Index inkrementieren
    }
  }
  return false;        // noch nicht fertig
}

void auswertungDerNachricht() {
  // relevant sind nur Zeichen 7 und 9:
  // serialBuffer[6] ... also Zeichen 7: Status der linken Tastengruppe
  // serialBuffer[8] ... also Zeichen 9: Status der rechten Tastengruppe

  switch(serialBuffer[6]) {   // Linke Tastengruppe
    case '2':
      Serial.print("Links: UP");
      break;
    case '4':
      Serial.print("Links: LEFT");
      break;
    case '5':
      Serial.print("Links: ----");
      break;
    case '6':
      Serial.print("Links: RIGHT");
      break;
    case '8':
      Serial.print("Links: DOWN");
      break;
    default:
      break;
  }
 
  Serial.print('\t');
  
  switch(serialBuffer[8]) {   // Rechte Tastengruppe
    case '0':
      Serial.print("Rechts: ----");
      break;
    case '3':
      Serial.print("Rechts: A");
      break;
    case '4':
      Serial.print("Rechts: B");
      break;
    case '5':
      Serial.print("Rechts: Select");
      break;
    case '6':
      Serial.print("Rechts: Start");
      break;
    case '7':
      Serial.print("Rechts: X");
      break;
    case '8':
      Serial.print("Rechts: Y");
      break;
    default:
      break;
  }
  Serial.println();
}

Nachtrag:
Das oben Beschriebene bezieht sich auf die Variante "Joystick" der App Tah-Play (siehe Bild oben).
Es gibt noch eine andere Variante, nämlich "Play Station". Diese ist bei mir gekennzeichnet mit "Experimental" und ich habe sich nicht zum Laufen gebracht, weil das Programm (die App Tah-Play) beim Start von "Play Station" immer abstürzt.

app.png

Erstmal Dankeschön, dass ihr euch Zeit genommen habt, das nochmals anzusehen!!

postmaster-ino:
Hatte Das als die 'Befehle' interpretiert - daß also 'F' gesendet wird, wenn's geradeaus gehen soll.
... wohl ein 'freudscher' :wink:

Ich dachte, "Beispiel Switch Case" sei eindeutig deklariert :wink: war ja noch vom "alten" Sketch für Arduino

postmaster-ino:
Dann erstelle Dir ein Array beliebiger Größe - unter 256, damit wir für die Zeiger bei Byte bleiben können.
Dann zwei Zeiger byte readpointer,writepointer;
Sobald Du etwas zum Einlesen hast (Serial.available () ) liest Du das eine Byte aus und schreibst Es in das Array an die Position writepointer. Dann writepointer++; writepointer%=groessearray;
Wenn readpointer und writepointer nicht mehr aufeinander liegen, sind Zeichen zur Auswertung da.
Einlesen dann über den Index readpointer, umbrechen am Ende des Array.

Verständlich?

Hä? Also was du sagen willst, verstehe ich.. Nur das Umsetzen, dass der Compiler zufrieden ist, fällt mit schwer :slight_smile:

Array beliebige Größe? Check. Ich hab' halt mal 10 genommen ¯_(ツ)_/¯
Zwei Zeiger? Check.
Das mit dem Einlesen und Schreiben? Check...?
Und das wars dann schon mit dem Verstehen
hier mal ein Auszug: mach ich das richtig?

byte readpointer;
byte writepointer;
int ArrayBefehlsdaten[10]={};

*
*
*

void loop ()

{

  if (Serial.available ())
  {
    Serial.read () = writepointer;
    writepointer ++;
    writepointer %= 10;
    Bluetooth ();
    Serial.println (blueToothVal);
    blueToothVal = Serial.read ();
  }

}

Auch großen Dank an uxomm, werde mir dein Code mal genau anschauen und auseinanderfrimeln! Bei Erfolg werde ich natürlich den ganzen Code für alle einbauen, dann haben auch die nächsten was davon...

Aber da sieht man mal, wie man sich an einem "Protokoll" täuschen kann... :disappointed_relieved:

in etwas gekürzter Form

uxomm:
Ich hab mir das "Protokoll" der App Tah-Play mal angeschaut.

Es ist nicht sehr kompliziert und lässt sich mit den "üblichen Mitteln" leicht auswerten (also Einlesen bis zum Endzeichen, Auswertung danach - siehe z.B. Serial Input Basics - updated )

[gekürzt]

Hintergrund
Die App sendet eine Nachricht sowohl wenn eine der Tasten gedrückt wird, als auch wenn eine Taste losgelassen wird (also so ähnlich wie eine PC-Tastatur).

Das Endzeichen jeder Nachricht ist P (großes P). Es wird nur das P als Endzeichen gesendet, also kein LF oder CR.

[gekürzt]

Jede Nachricht wird mit "P" abgeschlossen (Endzeichen: P).

Die ersten 6 Zeichen jeder Nachricht sind immer gleich: 1,0,0,
Das 7. Zeichen repräsentiert den Status der linken Tastengruppe:
5 .... keine Taste gedrückt
2 .... Taste UP gedrückt
4 .... Taste LEFT gedrückt
6 .... Taste RIGHT gedrückt
8 .... Taste DOWN gedrückt

Die Zeichen 9 (und eventuell nachfolgende 1 oder 2 Zeichen) repräsentieren den Status der rechten Tastengruppe:
0 .... keine Taste gedrückt
5 .... Taste SELECT gedrückt
6 .... Taste START gedrückt
Die übertragenen Werte der Tasten A, B, X, Y sind in der App einstellbar und repräsentieren den ASCII-Code des eingestellten Zeichens. Die Werte können also Werte von 32 (Leerzeichen) bis 126 (Zeichen ~) haben, je nach Einstellungen.

Bei "günstiger Einstellung" in der App, kann auf die Auswertung von Zeichen 10 und ev. 11 verzichtet werden.
Da 5 und 6 bereits "vergeben" sind bleiben noch 3x, 4x, 7x, 8x, 9x.
Beispiel, App-Einstellungen:
A = ! (ASCII 33)
B = 1 (ASCII 49)
X = F (ASCII 70)
Y = R (ASCII 82)

Dann gilt für das 9. Zeichen (rechte Tastengruppe):
0 .... keine Taste gedrückt
5 .... Taste SELECT gedrückt
6 .... Taste START gedrückt
3 .... Taste A gedrückt
4 .... Taste B gedrückt
7 .... Taste X gedrückt
8 .... Taste Y gedrückt

So... Es ist vollbracht, der Code steht und funktioniert soweit! Juuhuuu!!

Der Eintrag wird nun mit [gelöst] gekennzeichnet und im Anhang für alle meine (eher uxomm's) Sketche, für jeden frei zugänglich.

Bluetooth1.ino (664 Bytes)

Motor_Steuerung.ino (675 Bytes)

V1.3.2.ino (1.77 KB)

Danke für die Rückmeldung!
Super, dass es funktioniert!