Bluetooth Low Energy (BTH-LE)

Meine kostengünstige Armbanduhr mit Herzschlag und Blutdrucksensor schickt tägliche ihre Daten zur Smartphone App unter Android.
Harald Lesch nennt solche Geräte zu Recht “digitale Diktatoren” die nicht gerade ein positives Beispiel unserer Gesellschaft sind:

Weil wir zu faul sind, zu Fuß zur Arbeit zu marschieren und mit Öffis und Autos durch die Straßen brettern, brauchen wir am Abend die “Erinnerungsfunktion” am Handgelenk, dass noch X Schritte zu machen sind, damit wir uns als “gesund” titulieren können.

However … der spannendere Aspekt des “digitalen Diktators” ist, dass er über Bluetooth LE kommuniziert und so seine gesammelten Daten meldet und neue Konfigurationen abholt.


Das GATT Profile (Generic Attribute Profile) ist in etwas das für Bluetooth, was HID (Human Interface Device) für USB (Universal Serial Bus) ist.
Anstatt dass jedes Gerät sein eigenes Datenprotokoll definiert, wird ein generisches gewählt, das eine variable Sammlung von “Services” bereitstellen kann, und jedes Service hat eigene “Characteristics” und “Attributes”, die man entweder nur lesen, nur schreiben oder beides kann.

Soetwas ist für die Programmierung super, weil man mit einer kleinen Anzahl von APIs jedes mögliche GATT / Bluetooth LE Gerät steueren und auslesen kann. Die Komplexität steckt dabei aber im Wissen, welches Service welche ID hat, und welche Eigenschaft (Characteristic) bzw. welche Eigenschaftskombinationen welche finalen Auswirkungen und Bedeutungen hat.

Es gibt natürlich das eine oder andere “generische” Service, das jedes Bluetooth-LE-Gerät bereit stellt um z.B. seinen Namen, Hersteller und die Version auszulesen, doch die Details zu vielen weiteren Services stehen nur in der Spezifikation des Herstellers oder … nirgends, weil sie nicht veröffentlicht sind.

Um so mehr Spaß macht es dann, diese Details herauszufinden. Denn es gibt Tools, die alle Byteblöcke eines BTH-LE-Gerätes auslesen und anzeigen.
Und daher sah ich mich schon fast gezwungen, diese APIs auch ins GATE Projekt aufzunehmen um etwas damit “spielen” zu können.

Seit Windows 8 existieren die BluetoothGATT* Funktionen um eben genau diese Details auszulesen. Doch wie so oft unter Windows fehlt die zusammenhängende Dokumentation, wie man die Pfade für die Geräte kommt.

Hier die Zusammenfassung:

Will man die Characteristics eines BTH-LE-Services auf einem Gerät lesen oder schreiben, wird es etwas “tricky”:

  • Wir suchen mit der SetupDI* API nach allen BTH-LE Services über die Klassen-Guid des Services, die wir zuvor mit BluetoothGATTGetServices() erhalten haben.
    • Hier gibt es noch den Fall, dass ein Service eine “Short-ID” hat, wo nur die ersten Felder der GUID befüllt sind und die restlichen ausgenullt sind. Bei der Such muss eine solche GUID aber immer auf: XXXXXXXX-0000-1000-8000-00805f9b34fb enden.
  • Auch hier erhalten wir wieder einen Pfad zum “Service-Gerät”, den wir mit CreateFile() öffnen können.
  • Mit der geöffnet Gerätedatei können wir nun die anderen APIs wie BluetoothGATTGetCharacteristics nutzen um jede Einstellung auszulesen oder zu schreiben.

Dieser Schritt war mir nämlich lange nicht klar, und so scheiterte ich stets daran, dass das HANDLE zum Bluetooth-Geräte mit den Characteristic-APIs immer den Code ERROR_INVALID_FUNCTION zurück gab. Nur wenn man genau hinsieht, ist bei BluetoothGATTGetServices() von HANDLE hDevice die Rede, während BluetoothGATTGetDescriptorValue() von einem “Service HANDLE” spricht.

Doch niemand sagt einem, dass das Service-HANDLE vom Device-HANDLE zu unterscheiden ist.

Daher nochmals:

  1. BTH-LE-GUID -> Devices -> Device -> Service-Guids
  2. Service->Guid -> Service-Path -> Service-Handle

So, jetzt kann ich alle Characteristics aller Services meiner Armbanduhr “sehen” … allein fehlt mir jetzt noch die Info, was die Bytes bedeuten sollen.

… also wird es Zeit für ein bisschen Herumprobieren.


Wenn sich eine triviale Erkenntnis mit Dummheit in der Interpretation paart, dann gibt es in der Regel Kollateralschäden in der Anwendung.
frei zitiert nach A. Van der Bellen
... also dann paaren wir mal eine komplexe Erkenntnis mit Klugheit in der Interpretation!