Forum: Mikrocontroller und Digitale Elektronik Bluetooth LE Verständnissproblem ATT, GATT usw.


von C. L. (calle)


Lesenswert?

Hallo,

Ich habe hier ein BLE Server (Lichtring mit Gyro) und möchte via ESP32 
(und PlatformIO mit Arduino Plugin) damit Daten austauschen. Das 
connecten und Daten lesen über eine bestimmte Service/Characteristic 
funktioniert auch soweit mit dieser Arduino LIB.
https://github.com/nkolban/ESP32_BLE_Arduino/tree/master/examples

Die Connection wird aber nach ca. 5 Sec immer wieder abgebrochen.
Wenn ich mich testweise mit der nrfConnect App auf den Server verbinde, 
bricht die Verbindung dann auch wieder ab mit dem Fehlercode 19.

Recherchiert und M.m.n. müsste ich nun von ESP32 aus erstmal die 
Connection Parameter an den Server senden, damit die Connection 
dauerhaft erhalten bleibt. Wie mache ich das?

Es gibt ja auch verschiedene Layer;
ATT, GATT, L2Layer, LL, LE LL, Connection Parameter usw.
Was muss ich wie senden oder gibt es da einen Kniff?

Das in einem Service die Characteristics sind und dazu Descriptor 
gehören, auch das Thema UUIDs (kurz oder lang) ist mir bekannt.

Zu dem Server gehört original eigentlich eine APP und ich habe diesen 
Datenaustausch mit Wireshark mal mitgesnifft. Hier wird dann erstmal ein 
LL_Connection_Update an den Server gesendet, dann passiert jede Mende 
ATT und GATT hin und her und zum Schluss nochmal Connection Parameter. 
Dieses möchte ich quasi mit dem ESP nachbilden, das ich mit dem Server 
und dem ESP32 ohne App dann Daten austauschen kann.

Hat die o.g. LIB jemand im Einsatz und kann mir mehr über dieses Thema 
erklären? Ich stecke da fest.

... Und bitte nicht "nutz Google" usw. bisher habe ich da was nicht 
verstanden, denke ich.

Nützliche Code Snippets gerne willkommen.


Viele Grüße


Carsten

: Verschoben durch Moderator
von Joachim S. (oyo)


Lesenswert?

C. L. schrieb:
> Recherchiert und M.m.n. müsste ich nun von ESP32 aus erstmal die
> Connection Parameter an den Server senden, damit die Connection
> dauerhaft erhalten bleibt. Wie mache ich das?
>
> Es gibt ja auch verschiedene Layer;
> ATT, GATT, L2Layer, LL, LE LL, Connection Parameter usw.
> Was muss ich wie senden oder gibt es da einen Kniff?

Keine Ahnung, ob Dir das irgendwie hilft, aber hier mal meine 2 Cents:
Ich habe auch schon mehrere BLE-Geräte gesehen, mit denen man sich zwar 
verbinden kann, die dann aber nach wenigen Sekunden von sich aus die 
Verbindung trennen.

Bei diesen Geräten war es dann immer so, dass man unmittelbar nach dem 
Verbinden quasi zur Initialisierung irgendwelche Daten an irgendwelche 
Characteristics senden musste. Wenn das nicht binnen weniger Sekunden 
geschah, beendeten sie die Verbindung.
Mit irgendwelchen standardisierten "Connection Parametern" habe ich das 
bislang nie in Verbindung gebracht - wenn es sowas bei BLE gibt, hatte 
ich bislang nicht wissentlich damit zu tun, das hat die BLE-Library dann 
vermutlich für mich transparent von selbst erledigt.

Die "Lösung" war für mich in solchen Fällen ziemlich genau das, was Du 
bereits angefangen hast:
- die "Original-App" verwenden
- die Kommunikation zwischen App und BLE-Gadget mitschneiden, zumindest 
die ersten Sekunden
- und zwar am besten mehrfach, damit man weiss, ob sich irgendwelche 
Teile ändern
- In der eigenen Software dann die Kommunikation beim Verbindungsaufbau 
imitieren
- Wenn's erst einmal klappt und das BLE-Gerät die Verbindung nicht mehr 
kappt: Nach und nach einzelne Schritte der Kommunikation entfernen, 
schauen, ob es dann immer noch klappt ohne dass die Verbindung getrennt 
wird, auf diese Weise den herausfinden, welche Schritte wirklich 
erforderlich sind und welche nicht. Meiner Erfahrung nach ist es 
meistens nur ein einziger Schritt, der erforderlich ist, damit die 
Verbindung nicht mehr getrennt wird

von C. L. (calle)


Lesenswert?

Joachim S. schrieb:
> Meiner Erfahrung nach ist es
> meistens nur ein einziger Schritt, der erforderlich ist, damit die
> Verbindung nicht mehr getrennt wird

... da habe ich noch gar nicht drüber nach gedacht, das es 
möglicherweise nur ein einziger Schritt ist das die Verbindung dann 
gehalten wird.
Es scheint auch so zu sein, das irgendwas erst "getriggert" werden muss, 
bevor der Lichtring von Peripheral sich steuern lässt.
Aber genau das ist das nächste Problem.
Ich kann via Wireshark bei der Original App gar nicht erkennen, was 
genau bzw. welcher Inhalt wie gesendet wird, damit irgendwas weitergeht, 
deswegen war ich bei den Connection Parametern.
Da werden Sent Requests geschickt, das kann ich nicht interpretieren.
Muss ichmich weiter informieren.

Ich werde mal weitersuchen und mit Wireshark weiter filtern. Der ganze 
Datenverkehr ist ja voll mit Empty PDU´s

Carsten

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Hallo Carsten,

C. L. schrieb:

> Die Connection wird aber nach ca. 5 Sec immer wieder abgebrochen.
> Wenn ich mich testweise mit der nrfConnect App auf den Server verbinde,
> bricht die Verbindung dann auch wieder ab mit dem Fehlercode 19.

Error code 19 entspricht: REMOTE USER TERMINATED CONNECTION

Wenn Du einen GATT Client hat, der auf eine fehlerfreie GATT 
Implementierung zurück greift (iOS zum Beispiel), dann kannst Du da 
eigentlich nichts falsch machen, das dazu führt, dass die Verbindung 
abgebrochen wird. Du kannst mit Wireshark aber auch noch mal sicher 
stellen, dass die LL_TERMINATE_IND vom Server kommt.

Die Verbindung mit niedrigem Connection Interval zu starten und dann 
irgend wann, nach der Discovery auf ein höheres Interval zu gehen, um 
Energie zu sparen, ist nicht unüblich. Meine Vermutung ist, dass das 
nichts mit Deinem Problem zu tun hat.

Für mich sieht es so aus, als würde der GATT Server einfach einen Timer 
aufsetzen und nach 5 Sekunden Inaktivität (wahrscheinlich auf 
GATT-Ebene), die Verbindung einfach schließen. Jetzt müsstest Du einfach 
damit leben und bei Bedarf die Verbindung einfach wieder aufbauen, oder 
mal die original App angucken, ob die ggf. irgend etwas periodisch 
macht, dass dieses Timeout aushebelt. Ggf. wird dieses Timeout aber auch 
ausgesetzt, wenn sich ein Client auf Notifications oder Indications 
einer Characteristic subscribed.

Meine Empfehlung: Frag doch einfach mal den Hersteller. Der verdient 
sein Geld damit, dass er Hardware verkauft. Jedes Projekt, dass seine 
Hardware nutzbarer macht, kann die Verkaufszahlen eigentlich nur 
verbessern.

Schöne Grüße

Torsten

von C. L. (calle)


Lesenswert?

Hey Torsten,

gestern noch auf YT ein Video von Dir gesehen zu dem Thema...

Torsten R. schrieb:
> Du kannst mit Wireshark aber auch noch mal sicher
> stellen, dass die LL_TERMINATE_IND vom Server kommt.

Habe ich gemacht, kommt vom Server.
Ich habe mich mit der nrf Connect app verbunden, auf Abbruch gewartet 
und das mitgesnifft.

Torsten R. schrieb:
> Für mich sieht es so aus, als würde der GATT Server einfach einen Timer
> aufsetzen und nach 5 Sekunden Inaktivität (wahrscheinlich auf
> GATT-Ebene), die Verbindung einfach schließen.

Heisst das, ich muss auf GATT Ebene irgend was tun?
Momentan kenne ich nur die Thematik Service/Characteristic und damit 
dann Werte lesen und schreiben. Hilf da noch mal bitte.
Vlt ist die o.g. LIB dafür gar nicht geeignet?

Torsten R. schrieb:
> Ggf. wird dieses Timeout aber auch
> ausgesetzt, wenn sich ein Client auf Notifications oder Indications
> einer Characteristic subscribed.

Wie genau abonniere ich denn? ist das sowas hier? UUID usw. ist klar...
1
if(l_BLERemoteChar->canNotify())
2
    l_BLERemoteChar->registerForNotify(notifyCallback);

Und in der Notify Routine kann ich dann auch schon den Gyro lesen.
Ist das dann abonnieren?
Jetzt noch rausfinden, was benötigt wird um die Connection zu halten und 
den Lichtring zu steuern. Die Datenwerte um den Lichtring zu steuern 
liegen mir vor und sehe ich in der Original App via Wireshark auch.

Kann es vlt. sein das ich den Batterielevel erst abonnieren muss?
Den um diese "Nebensächlichen Dinge", wie auch FW Strings und 
Seriennummer und co habe ich mich noch nicht gekümmert.

VG

Carsten

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

C. L. schrieb:

> Wie genau abonniere ich denn? ist das sowas hier? UUID usw. ist klar...
>
1
> if(l_BLERemoteChar->canNotify())
2
>     l_BLERemoteChar->registerForNotify(notifyCallback);
3
>

ja, das sieht korrekt aus. Bei Notifications / Indications sendet Dir 
der GATT-Server direkt den Wert der Characteristic (üblicherweise, wenn 
sich der Wert geändert hat).

Das ist aber alles nur eine Vermutung. Ich vermute, dass die 5s Timeout 
auf der Server-Seite implementiert sind. Von daher kommt es darauf an, 
woran die Kollegen dieses Timeout fest machen.

Wenn Du Dir die Characteristics in nRfConnect anguckst, kannst Du 
erkennen, welche Characteristics Notifications / Indications 
unterstützen.

> Und in der Notify Routine kann ich dann auch schon den Gyro lesen.

Im Callback, solltest Du bereits den neuen / geänderten Wert der 
Characteristic haben,

> Ist das dann abonnieren?

Jep.

> Jetzt noch rausfinden, was benötigt wird um die Connection zu halten und
> den Lichtring zu steuern.

Vielleicht doch einfach den Hersteller fragen? ;-)

> Kann es vlt. sein das ich den Batterielevel erst abonnieren muss?

Kann sein. Du kannst so lange rum probieren, bis Du es heraus bekommen 
hast, oder Du fragst halt ;-)

von C. L. (calle)


Angehängte Dateien:

Lesenswert?

Torsten R. schrieb:
> GATT-Server direkt den Wert der Characteristic (üblicherweise, wenn
> sich der Wert geändert hat).

ok, das wäre bei dem Gyro dann der Fall, denn da habe ich die 
Callbackroutine und die gibt mir die Gyro Werte raus, sobald ich das 
Teil bewege, dann passt das also.

Torsten R. schrieb:
> Wenn Du Dir die Characteristics in nRfConnect anguckst, kannst Du
> erkennen, welche Characteristics Notifications / Indications
> unterstützen.

Ich habe was gefunden:

Service 0x1801
Char 0x2A05 INDICATE
=> das soll dann Service changed sein, wozu das gut ist weiss ich noch 
nicht.

Service 0x180F
Char 0x2A19 NOTIFY, READ
=> Batterie Level

Service unique 128Bit ID
Char unique 128Bit ID NOTIFY, WRITE
=> Experimental Buttonless DFU, ist das fürs Firmware Update der Server 
aus der Original APP wahrscheinlich?

Service unique 128Bit ID
Char unique 128Bit ID NOTIFY
Char unique 128Bit ID WRITE, WRITE NO RESP
=> Nordic UART, der obere ist TX und der untere RX.
Wie kann man denn an RX schreiben? - ist warscheinlich das übliche mit 
den Sichtweisen und den Verwechslungen.
Hier habe ich in Wireschark gesehen, das vom Master zum Slave UART TX 
immer  am Anfang 7 Bytes gesschickt werden. Habe ich mal angehangen.
Das kann doch dann nur der Char mit WRITE, WRITE NO RESP sein.
Sollte man das mal probieren?
Wozu braucht man den RX/TX wenn die Firmware Update Geschichte einen 
eigenen Dienst hat.

Dann habe ich noch einen gefunden, einmal ist das der Lichtring und das 
andere ist der Gyro, das weis ich genau.
Dann haben wir noch den Service mit 0x180A und da werden dann über die 
Char die Values gelesen wie Device, SN, Hard-/ und Software Revision, 
alles READ.

Und noch 0x180 und da ist über die Char dann Name, Appearance und 
Peripheral preferred connection parameters. Auch alles READ.

Dann wäre doch der nächste Ansatz jetzt mal die 7 Bytes zu senden, oder 
sollte man die Serv/Char jetzt mal alle sauber abbonieren und lesen?
So richtig schön mit Service suchen, dann Char suchen, die Callbacks 
anlegen registrieren wie es sein soll.

Was meinst Du?

Übrigens hier erstmal vielen Dank an alle die mir da helfen/geholfen 
haben.

Carsten

Achsooooo...
Mit Anfragen beim Hersteller habe ich wenig gute Erfahrungen.
Bis man da erstmal in der Entwickler-Ebene ist, hat man schon selbst 
alles geproggt, finde ich irgendwie.

Aber anfragen kostet nichts, vlt. mache ich das dann doch mal.

: Bearbeitet durch User
von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

C. L. schrieb:
> Ich habe was gefunden:
>
> Service 0x1801
> Char 0x2A05 INDICATE
> => das soll dann Service changed sein, wozu das gut ist weiss ich noch
> nicht.

Ist von Bluetooth definiert. Wenn Du die beim Discovery festgestellten 
Paarungen von UUID zu Handle persistent speichern möchtest, um bei der 
nächsten Verbindung auf das Discovern verzichten zu können, dann kannst 
Du Dich auf diese Characteristic subscriben. Wenn sich dann auf dem 
Server die Zuordnung von UUID zu Handle ändert (z.B. durch einen 
Firmware-Update), dann bekommst Du hier eine Indication gesendet.

> Service unique 128Bit ID
> Char unique 128Bit ID NOTIFY
> Char unique 128Bit ID WRITE, WRITE NO RESP
> => Nordic UART, der obere ist TX und der untere RX.
> Wie kann man denn an RX schreiben? - ist warscheinlich das übliche mit
> den Sichtweisen und den Verwechslungen.
> Hier habe ich in Wireschark gesehen, das vom Master zum Slave UART TX
> immer  am Anfang 7 Bytes gesschickt werden. Habe ich mal angehangen.
> Das kann doch dann nur der Char mit WRITE, WRITE NO RESP sein.
> Sollte man das mal probieren?

Ja, probiere es mal aus.

> Wozu braucht man den RX/TX wenn die Firmware Update Geschichte einen
> eigenen Dienst hat.

Damit man zwischen Firmware-Update und Command / Response im normalen 
Betrieb unterscheiden kann. Ich denke, die Kollegen haben sich einfach 
einige Komponenten aus dem Nordic SDK genommen um Ihre Software fertig 
zu stellen.

> Dann haben wir noch den Service mit 0x180A und da werden dann über die
> Char die Values gelesen wie Device, SN, Hard-/ und Software Revision,
> alles READ.

Ja, das ist der Device Information Service (DIS). Die Details zu den 
services und characteristics mit 16 bit UUIDs kannst Du alle auf der 
Webseite von der Bluetooth SIG nachlesen: https://www.bluetooth.com

> Dann wäre doch der nächste Ansatz jetzt mal die 7 Bytes zu senden, oder
> sollte man die Serv/Char jetzt mal alle sauber abbonieren und lesen?
> So richtig schön mit Service suchen, dann Char suchen, die Callbacks
> anlegen registrieren wie es sein soll.

Erst abonnieren, dann schreiben. Es könnte auch sein, dass die Software 
prüft, ob sie überhaupt antworten kann.

von C. L. (calle)



Lesenswert?

Sooo....

Ich bin noch nicht wirklich weiter, die Verbindung bricht immer noch ab.
Ich kann nun den Batterielevel via READ lesen und sehen. Auch ein Notify 
habe ich darauf angelegt.
Hier direkt die erste Frage: Kann der Server mitbekommen, das auf der 
Gegenseite ein Notify darauf lauscht, um daran z.B. zu entscheiden?
Es hiess ja, das wäre dann abonniert.
Also könnte der Server auch sagen, wenn nicht abboniert, dann mach ich 
auch nix.

Dann habe ich den Service/Char vom Nordic so bedient, wie es die 
Original App auch tut. Allerdings sind die hinten 4 Bytes im Original 
immer anders. Was da noch für ein Algorythmus hintersteckt weiß ich noch 
nicht.
Im Wireshark sieht es aber genaus so aus wie bei einem Mitschnitt.

Bisher alles der Reihe nach, so wie es auch im Original Mitschnitt 
erkennbar ist.

Dann muss mir nochmal jemand erklären, wie das mit den Handles usw. ist.
Denn da kann ich noch Unterschiede sehen, die mir aber noch nicht klar 
sind.
Ich habe hier im Mitschnitt ein Send Write Request, Sent Read Request,
Send Write Command... Wo bzw. was ist da der Unterschied und wie stehen 
die Handles dazu in Verbindung.
Komisch kommt mir vor, das ich Sent Write Request sehe, obwohl ich gar 
nichts sende aus dem ESP32. Den von mir gesendeten Commands (die 7 Bytes 
über Nordic Char) sehe ich in Wireshark. Was bzw. wer sendet denn da 
noch?
Kann es sein, das ein registerNotify(); was zum Server sendet und darauf 
geantwortet wird? Ist das dann noch Teil der Pairing Kommunikation?

Z.B. das Bild im Anhang...
Die Anforderung habe ich im ESP garnicht gemacht - Kommt das dann aus 
der o.g. Bluetooth SIG und ist das dann gar nicht weiter zu beachten?

Kernfrage: kann der Server penibel genau checken was der Client so liest 
bzw. möchte? Hatte immer gedacht, der Server legt das in die 
Characteristic und kümmert sich nicht mehr drum und auf der anderen 
Seite wird das abgeholt wenn es gebraucht wird.
Wenn das nicht so ist, dann muss ich das möglicherweise extakt nach 
emulieren, dafür fehlt mir aber noch das Verständnis.

VG Carsten

@ Moderation
Die Bilder können bis auf eines gelöscht werden, ist bei der Vorschau 
weg gegangen und ich habe die immer neu angehangen - sry

: Bearbeitet durch User
von Joachim S. (oyo)


Lesenswert?

C. L. schrieb:
> Hier direkt die erste Frage: Kann der Server mitbekommen, das auf der
> Gegenseite ein Notify darauf lauscht, um daran z.B. zu entscheiden?
> Es hiess ja, das wäre dann abonniert.
> Also könnte der Server auch sagen, wenn nicht abboniert, dann mach ich
> auch nix.

Ja, der Server kann das schon mitbekommen. Und ich halte es zumindest 
für denkbar, dass das Gerät deshalb die Verbindung beenden könnte, weil 
Du eine Characteristic nicht subscribed hast, die die Original-App 
üblicherweise subscriben würde.
Neben der "Nordic UART RX"-Characteristic, an die Du die sieben Bytes 
sendest, gibt es ja z.B. auch noch die "Nordic UART TX"-Characteristic 
(UUID: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E) für die Gegenrichtung, zum 
Empfangen. Auch die würde ich daher einfach mal subscriben, einfach um 
das als potentielle Fehlerquelle auszuschliessen. Ausserdem könnte dort 
ja als Reaktion auf die sieben gesendeten Bytes irgendwas 
zurückgeschickt werden.

> Ich habe hier im Mitschnitt ein Send Write Request, Sent Read Request,
> Send Write Command... Wo bzw. was ist da der Unterschied und wie stehen
> die Handles dazu in Verbindung.

Der Unterschied zwischen einem "Write Request" und einem "Write Command" 
ist damit vergleichbar, ob Du ein Paket via TCP oder UDP verschickst:
Das "Write Command" ist mit UDP vergleichbar - der Empfänger bestätigt 
den Empfang des Schreib-Befehls nicht, daher kann man nicht 100% sicher 
sein, dass der Befehl korrekt übermittelt wurde.
Der "Write Request" hingegen macht im Grunde exakt das gleiche, nur dass 
hier das BLE-Gerät den Empfang bestätigt - falls der Schreib-Befehl also 
nicht erfolgreich übermittelt wurde, dann bekommt die Seite, die den 
"Write Request" gesendet hat, das mit.

In der Gegenrichtung gibt es das auch, als Unterschied zwischen "notify" 
und "indicate": Es ist quasi das Gleiche, nur dass bei "notify" der 
Empfang nicht quittiert wird.

Edit: Ich bin eben zufällig noch auf einen GitHub-Issue gestossen, der 
mit der ESP32-BLE-Library und genau diesem Unterschied zwischen "Write 
Request" und "Write Command" zu tun hat:
https://github.com/nkolban/esp32-snippets/issues/382
Wenn ich das richtig verstehe, hat oder hatte die ESP32-BLE-Library da 
einen Bug, dass Write Requests (Opcode 0x12) fälschlicherweise als Write 
Commands (Opcode 0x52) gesendet werden. Ich konnte beim kurzen 
Überfliegen allerdings nicht erkennen, ob das mittlerweile behoben ist.

> Komisch kommt mir vor, das ich Sent Write Request sehe, obwohl ich gar
> nichts sende aus dem ESP32. Den von mir gesendeten Commands (die 7 Bytes
> über Nordic Char) sehe ich in Wireshark. Was bzw. wer sendet denn da
> noch?
> Kann es sein, das ein registerNotify(); was zum Server sendet und darauf
> geantwortet wird? Ist das dann noch Teil der Pairing Kommunikation?
>
> Z.B. das Bild im Anhang...
> Die Anforderung habe ich im ESP garnicht gemacht - Kommt das dann aus
> der o.g. Bluetooth SIG und ist das dann gar nicht weiter zu beachten?

Bei diesem Abschnitt bin ich mir nicht sicher, ob ich Dich richtig 
verstehe. z.B. der Screenshot: Soll das ein mitgesniffter Teil der 
Kommunikation zwischen dem ESP32 und dem BLE-Gerät ESP32 sein? Und man 
sieht da, dass die Firmware-Version und der Batterie-Stand abgefragt 
wird, obwohl Du das in Deinem ESP32-Code gar nicht tust?
Falls ja, fände ich das in der Tat höchst merkwürdig. Dass die 
ESP32-BLE-Library völlig ungefragt einfach mal die Firmware-Revision und 
den Batteriestand abfragt, kann ich mir irgendwie nicht vorstellen.

> Kernfrage: kann der Server penibel genau checken was der Client so liest
> bzw. möchte? Hatte immer gedacht, der Server legt das in die
> Characteristic und kümmert sich nicht mehr drum und auf der anderen
> Seite wird das abgeholt wenn es gebraucht wird.
> Wenn das nicht so ist, dann muss ich das möglicherweise extakt nach
> emulieren, dafür fehlt mir aber noch das Verständnis.

In "High-Level"-BLE-Libraries wie eben der von Nick Kolban für den ESP32 
kümmert man sich um sowas normalerweise nicht, aber grundsätzlich könnte 
ein BLE-Gerät natürlich schon genau mitkriegen, was ein Client so liest, 
schreibt, abonniert etc. Die Frage ist, ob das hier wirklich das Problem 
ist.

: Bearbeitet durch User
von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

C. L. schrieb:

> Hier direkt die erste Frage: Kann der Server mitbekommen, das auf der
> Gegenseite ein Notify darauf lauscht, um daran z.B. zu entscheiden?

Ja. Wenn Du auf Server-Seite zwischen dem Bluetooth-Stack und der 
Applikation unterscheidest, dann muss der Stack das sogar wissen, der er 
sendet aktiv Notifications und Indications, wenn die Applikation 
geänderte Daten meldet und sich ein verbundener Client auf diese Daten 
(Characteristic) abonniert hat.

Auf der Applikations-Ebene ist diese Information (ob ein Client 
abonniert ist) in der Regel auch verfügbar.

> Komisch kommt mir vor, das ich Sent Write Request sehe, obwohl ich gar
> nichts sende aus dem ESP32. Den von mir gesendeten Commands (die 7 Bytes
> über Nordic Char) sehe ich in Wireshark. Was bzw. wer sendet denn da
> noch?
> Kann es sein, das ein registerNotify(); was zum Server sendet und darauf
> geantwortet wird?

Ja, technisch besteht eine Characteristic auf die man sich abonnieren 
kann aus mindestens 3 Attributen: Characteristic Description, 
Characteristic Value und Characteristic Client Configuration Descriptor 
(CCCD). Auf den letzten schreibt ein Client, damit der Server weis, dass 
der Client abonniert ist.

> Ist das dann noch Teil der Pairing Kommunikation?

Nein, Pairing ist im wesentlichen der Austausch von Schlüsseln für die 
Verschlüsselte Kommunikation.

> Kernfrage: kann der Server penibel genau checken was der Client so liest
> bzw. möchte?

Ja, der Bluetooth Stack kann der Applikation sicher einiges abnehmen, in 
der Regel hat aber jeder Stack die Möglichkeit, dass die Applikation 
diese Informationen bekommt.

> Hatte immer gedacht, der Server legt das in die
> Characteristic und kümmert sich nicht mehr drum und auf der anderen
> Seite wird das abgeholt wenn es gebraucht wird.

Abholen bedeute, der Client schickt einen Read Request und der Server 
antwortet mit einem Read Response.

Du kannst Dir die Details auch direkt in den entsprechenden 
Spezifikationen angucken. Für ATT/GATT wäre das dann Core Spec. Vol 3; 
Part F und Part G. Die Spezifikation ist sehr leserlich geschrieben.

von C. L. (calle)


Lesenswert?

Joachim S. schrieb:
> Neben der "Nordic UART RX"-Characteristic, an die Du die sieben Bytes
> sendest, gibt es ja z.B. auch noch die "Nordic UART TX"-Characteristic
> (UUID: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E) für die Gegenrichtung, zum
> Empfangen. Auch die würde ich daher einfach mal subscriben, einfach um
> das als potentielle Fehlerquelle auszuschliessen. Ausserdem könnte dort
> ja als Reaktion auf die sieben gesendeten Bytes irgendwas
> zurückgeschickt werden.

Habe ich gemacht, kommt keine Reaktion auf die von mir gesendeten Daten.
Dabei stellt sich mir direkt die Frage nochmal mit dem subscriben.
Wenn ich sowas hier mache:
1
pRemoteChar_NORDICUART_TX->registerForNotify(notifyCallbackTX);
dann erschließt sich mir, das da was abonniert wird.
Aber wie ist es denn z.B. beim READ? Abonniere ich automatisch, wenn ich 
einmal gelesen habe? Denn in der LIB habe ich kein "register for READ" 
o.ä.
Oder auch beim WRITE - muss ich einmal schreiben um zu abbonnieren?


Joachim S. schrieb:
> Der "Write Request" hingegen macht im Grunde exakt das gleiche, nur dass
> hier das BLE-Gerät den Empfang bestätigt - falls der Schreib-Befehl also
> nicht erfolgreich übermittelt wurde, dann bekommt die Seite, die den
> "Write Request" gesendet hat, das mit.
>
> In der Gegenrichtung gibt es das auch, als Unterschied zwischen "notify"
> und "indicate": Es ist quasi das Gleiche, nur dass bei "notify" der
> Empfang nicht quittiert wird.

Verstanden!

GitHub Issue checke ich mal und das hier...

Joachim S. schrieb:
> Bei diesem Abschnitt bin ich mir nicht sicher, ob ich Dich richtig
> verstehe. z.B. der Screenshot: Soll das ein mitgesniffter Teil der
> Kommunikation zwischen dem ESP32 und dem BLE-Gerät ESP32 sein? Und man
> sieht da, dass die Firmware-Version und der Batterie-Stand abgefragt
> wird, obwohl Du das in Deinem ESP32-Code gar nicht tust?

sieht in der Tat so aus, aber ich muss auch aufpassen, das ich mich 
nicht verrenne bei den ganzen Baustellen - das checke ich lieber auch 
nochmal ;-)

Torsten R. schrieb:
> Ja, technisch besteht eine Characteristic auf die man sich abonnieren
> kann aus mindestens 3 Attributen: Characteristic Description,
> Characteristic Value und Characteristic Client Configuration Descriptor
> (CCCD). Auf den letzten schreibt ein Client, damit der Server weis, dass
> der Client abonniert ist.

Sooo... Das ist neu für mich...
Ich hatte es so verstanden, das der Descriptor quasi die Interpretation 
ist. Also bei BatLvl z.b. das % Zeichen oder bei anderen Werten eine 
Scalierung o.ä.
Jetzt ist die Frage, wie ich auf den Characteristic Client Configuration 
Descriptor schreiben kann, bzw. was ich da schreiben muss. Das muss ich 
mir u.a. in der Lib mal genauer ansehen.

Torsten R. schrieb:
> Du kannst Dir die Details auch direkt in den entsprechenden
> Spezifikationen angucken. Für ATT/GATT wäre das dann Core Spec. Vol 3;
> Part F und Part G. Die Spezifikation ist sehr leserlich geschrieben.

Aslo auf der Seite ist man ja erschlagen von Dokumenten.
Bin jetzt in der GATT Specification Supplement, aber da ist kein Part 
F/G sondern nur Zahlen: 3 z.B. Characteristic, 4 Descriptor..
Kannst Du das von Dir genannte Dokument mal verlinken?

Bei dem Thema Descriptor bin ich unsicher.
Ich mache ja:
1
BLERemoteService* pRemoteServiceBat = pClient->getService(serviceUUID_BatLvL);
2
3
BLERemoteCharacteristic* pRemoteChar_Bat = pRemoteServiceBat->getCharacteristic(charUUID_BatLvL);
4
5
BLERemoteDescriptor* BatLvLDes = pRemoteChar_Bat->getDescriptor(charUUID_BatLvLDes);
6
  std::string BatLvLDesc = pRemoteChar_Bat->readValue();

In den Sektionen passiert natürlich das Wesentliche.
Service suchen, prüfen ob da...
Char suchen, prüfen ob Read oder Notify geht und dann lesen oder 
registieren.
Descriptor suchen, prüfen und lesen - in dem Fall hier kommt aber nur
HEX 3D3D03E0DE bei raus...   ?

in einem Beispiel im Internet habe ich das hier gefunden
1
XYCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notificationOn, 2, true);

ist das dann das mitteilen an den Server, das hier ein notify passiert?
Das habe ich ja in meinem Programm noch gar nicht eingebaut.

BTW
Ich finde Eure Antworten und die Art wie Ihr das hier erklärt wirklich 
super, vielen Dank an Euch, denn das ist nicht normal in diesem Forum.
#credits!!!! ;)

VG Carsten

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

C. L. schrieb:

> Sooo... Das ist neu für mich...
> Ich hatte es so verstanden, das der Descriptor quasi die Interpretation
> ist. Also bei BatLvl z.b. das % Zeichen oder bei anderen Werten eine
> Scalierung o.ä.

Es gibt verschiedene Descriptors, die entsprechend unterschiedliche 
Aufgaben haben. Eine Characteristic muss einen CCCD haben, wenn sie als 
Characteristic Property Notify oder Indicate hat.

> Jetzt ist die Frage, wie ich auf den Characteristic Client Configuration
> Descriptor schreiben kann, bzw. was ich da schreiben muss.

Du must da überhaupt nichts schreiben. Wenn Du in deiner Library die 
Funktion `registerForNotify()` aufrufst, dann sucht diese Funktion das 
Handle zur CCCD der Characteristic raus und schreibt dann den 16 Bit 
Wert 0x0001.

> Aslo auf der Seite ist man ja erschlagen von Dokumenten.
> Bin jetzt in der GATT Specification Supplement, aber da ist kein Part
> F/G sondern nur Zahlen: 3 z.B. Characteristic, 4 Descriptor..
> Kannst Du das von Dir genannte Dokument mal verlinken?

https://www.bluetooth.com/specifications/specs/core-specification-5-4/

gleich das erste Dokument. (ggf. muss Du da einen Account anlegen).

> Bei dem Thema Descriptor bin ich unsicher.
> Ich mache ja:

Da ich die API nicht kenne, kann ich nichts dazu sagen.

> in einem Beispiel im Internet habe ich das hier gefunden
>
1
> XYCharacteristic->getDescriptor(BLEUUID((uint16_t)0x2902))->writeValue((uint8_t*)notificationOn, 
2
> 2, true);
3
>

Kann ich auch nichts zu sagen. Kenne die API nicht. Habe mal unter 
Android was gemacht und da musste man erstaunlicherweise auch explizit 
auf den Descriptor schreiben (Wert 1 für Notifications; 2 für 
Indications),

> ist das dann das mitteilen an den Server, das hier ein notify passiert?

Nein, der Client empfängt die Notifications ja. Das Schreiben auf den 
CCCD teilt dem Server mit, dass der Client Notifications empfangen 
möchte. Der Application-Teil des Servers entscheidet dann in der Regel, 
dass eine Notification für eine Characteristic gesendet werden soll.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.