Share via


Bluetooth GATT-Client

In diesem Artikel wird veranschaulicht, wie Sie die GATT-Client-APIs (Bluetooth Generic Attribute) für Universelle Windows-Plattform-Apps (UWP) verwenden.

Wichtig

Sie müssen die Bluetooth-Funktion in Package.appxmanifest deklarieren.

<Capabilities> <DeviceCapability Name="bluetooth" /> </Capabilities>

Wichtige APIs

Übersicht

Entwickler können die APIs im Windows.Devices.Bluetooth.GenericAttributeProfile-Namespace verwenden, um auf Bluetooth LE-Geräte zuzugreifen. Bluetooth LE-Geräte machen ihre Funktionen über eine Sammlung folgender Elemente verfügbar:

  • Dienste
  • Merkmale
  • Deskriptoren

Dienste definieren den Funktionsvertrag des LE-Geräts und enthalten eine Sammlung von Merkmalen, die den Dienst definieren. Diese Merkmale wiederum enthalten Deskriptoren, von denen die Merkmale beschrieben werden. Diese drei Begriffe werden allgemein als Attribute eines Geräts bezeichnet.

Die Bluetooth LE GATT-APIs machen Objekte und Funktionen verfügbar, anstatt auf den Rohdatentransport zuzugreifen. Die GATT-APIs ermöglichen Es Entwicklern auch, mit Bluetooth LE-Geräten zu arbeiten, die die folgenden Aufgaben ausführen können:

  • Durchführen der Attributermittlung
  • Lesen und Schreiben von Attributwerten
  • Registrieren eines Rückrufs für das Characteristic ValueChanged-Ereignis

Um eine nützliche Implementierung zu erstellen, muss ein Entwickler über Kenntnisse der GATT-Dienste und Merkmale verfügen, die die Anwendung nutzen und die spezifischen Merkmalswerte so verarbeiten möchte, dass die von der API bereitgestellten Binärdaten in nützliche Daten umgewandelt werden, bevor sie dem Benutzer angezeigt werden. Die Bluetooth GATT-APIs machen nur die Grundtypen verfügbar, die für die Kommunikation mit einem Bluetooth LE-Gerät erforderlich sind. Zum Interpretieren der Daten muss ein Anwendungsprofil definiert werden, entweder durch ein Bluetooth SIG-Standardprofil oder mit einem benutzerdefinierten Profil, das von einem Geräteanbieter implementiert wird. Ein Profil begründet einen bindenden Vertrag zwischen Anwendung und Gerät darüber, was von den ausgetauschten Daten dargestellt wird und wie sie zu interpretieren sind.

Aus Gründen der Benutzerfreundlichkeit pflegt Bluetooth SIG eine Liste der öffentlichen Profile, die zur Verfügung stehen.

Beispiele

Ein vollständiges Beispiel finden Sie unter Beispiel für Bluetooth Low Energy.

Abfragen von Geräten in der Nähe

Es gibt zwei Standard Methoden zum Abfragen von Geräten in der Nähe:

Die 2. Methode wird ausführlich in der Ankündigungsdokumentation erläutert, sodass sie hier nicht viel besprochen wird, aber die grundlegende Idee ist, die Bluetooth-Adresse von Geräten in der Nähe zu finden, die den jeweiligen Werbefilter erfüllen. Sobald Sie die Adresse haben, können Sie BluetoothLEDevice.FromBluetoothAddressAsync aufrufen, um einen Verweis auf das Gerät zu erhalten.

Kehren Sie nun zur DeviceWatcher-Methode zurück. Ein Bluetooth LE-Gerät ist genau wie jedes andere Gerät in Windows und kann mithilfe der Enumerations-APIs abgefragt werden. Verwenden Sie die DeviceWatcher-Klasse , und übergeben Sie eine Abfragezeichenfolge, die die zu suchenden Geräte angibt:

// Query for extra properties you want returned
string[] requestedProperties = { "System.Devices.Aep.DeviceAddress", "System.Devices.Aep.IsConnected" };

DeviceWatcher deviceWatcher =
            DeviceInformation.CreateWatcher(
                    BluetoothLEDevice.GetDeviceSelectorFromPairingState(false),
                    requestedProperties,
                    DeviceInformationKind.AssociationEndpoint);

// Register event handlers before starting the watcher.
// Added, Updated and Removed are required to get all nearby devices
deviceWatcher.Added += DeviceWatcher_Added;
deviceWatcher.Updated += DeviceWatcher_Updated;
deviceWatcher.Removed += DeviceWatcher_Removed;

// EnumerationCompleted and Stopped are optional to implement.
deviceWatcher.EnumerationCompleted += DeviceWatcher_EnumerationCompleted;
deviceWatcher.Stopped += DeviceWatcher_Stopped;

// Start the watcher.
deviceWatcher.Start();

Nachdem Sie deviceWatcher gestartet haben, erhalten Sie DeviceInformation für jedes Gerät, das die Abfrage im Handler für das Added-Ereignis für die betreffenden Geräte erfüllt. Einen ausführlicheren Blick auf DeviceWatcher finden Sie im vollständigen Beispiel auf GitHub.

Herstellen einer Verbindung mit dem Gerät

Nachdem ein gewünschtes Gerät erkannt wurde, verwenden Sie die DeviceInformation.Id , um das Bluetooth LE-Geräteobjekt für das betreffende Gerät abzurufen:

async void ConnectDevice(DeviceInformation deviceInfo)
{
    // Note: BluetoothLEDevice.FromIdAsync must be called from a UI thread because it may prompt for consent.
    BluetoothLEDevice bluetoothLeDevice = await BluetoothLEDevice.FromIdAsync(deviceInfo.Id);
    // ...
}

Auf der anderen Seite löst das Löschen aller Verweise auf ein BluetoothLEDevice-Objekt für ein Gerät (und wenn keine andere App im System einen Verweis auf das Gerät aufweist) eine automatische Trennung nach einem kurzen Timeoutzeitraum aus.

bluetoothLeDevice.Dispose();

Wenn die App erneut auf das Gerät zugreifen muss, wird das Betriebssystem bei Bedarf durch einfaches Erneutes Erstellen des Geräteobjekts und zugreifen auf ein Merkmal (im nächsten Abschnitt erläutert) dazu führen, dass das Betriebssystem erneut eine Verbindung herstellt. Wenn sich das Gerät in der Nähe befindet, erhalten Sie Zugriff auf das Gerät, andernfalls wird der Fehler DeviceUnreachable zurückgegeben.

Hinweis

Durch das Erstellen eines BluetoothLEDevice-Objekts durch Aufrufen dieser Methode allein wird nicht (notwendigerweise) eine Verbindung initiiert. Um eine Verbindung zu initiieren, legen Sie GattSession.MaintainConnection auf truefest, oder rufen Sie eine nicht zwischengespeicherte Dienstermittlungsmethode auf BluetoothLEDevice auf, oder führen Sie einen Lese-/Schreibvorgang für das Gerät aus.

  • Wenn GattSession.MaintainConnection auf true festgelegt ist, wartet das System unbegrenzt auf eine Verbindung, und es wird eine Verbindung hergestellt, sobald das Gerät verfügbar ist. Es gibt nichts, auf das Ihre Anwendung warten muss, da GattSession.MaintainConnection eine Eigenschaft ist.
  • Bei Dienstermittlungs- und Lese-/Schreibvorgängen im GATT wartet das System auf eine begrenzte, aber variable Zeit. Alles von sofort bis zu wenigen Minuten. Faktoren enthalten den Datenverkehr auf dem Stapel und wie die Anforderung in die Warteschlange eingereiht ist. Wenn keine andere ausstehende Anforderung vorhanden ist und das Remotegerät nicht erreichbar ist, wartet das System sieben (7) Sekunden, bevor das Zeitlimit ausfällt. Wenn weitere ausstehende Anforderungen vorhanden sind, kann die Verarbeitung jeder Der Anforderungen in der Warteschlange sieben (7) Sekunden dauern. Je weiter Sich Ihre in richtung des Hinterteils der Warteschlange befindet, desto länger warten Sie also.

Derzeit können Sie den Verbindungsvorgang nicht abbrechen.

Auflisten unterstützter Dienste und Merkmale

Nachdem Sie nun über ein BluetoothLEDevice-Objekt verfügen, besteht der nächste Schritt darin, zu ermitteln, welche Daten das Gerät verfügbar macht. Der erste Schritt hierfür besteht darin, Dienste abzufragen:

GattDeviceServicesResult result = await bluetoothLeDevice.GetGattServicesAsync();

if (result.Status == GattCommunicationStatus.Success)
{
    var services = result.Services;
    // ...
}

Nachdem der dienst von Interesse identifiziert wurde, besteht der nächste Schritt darin, Merkmale abzufragen.

GattCharacteristicsResult result = await service.GetCharacteristicsAsync();

if (result.Status == GattCommunicationStatus.Success)
{
    var characteristics = result.Characteristics;
    // ...
}

Das Betriebssystem gibt eine ReadOnly-Liste von GattCharacteristic-Objekten zurück, für die Sie dann Vorgänge ausführen können.

Ausführen von Lese-/Schreibvorgängen für ein Merkmal

Das Merkmal ist die grundlegende Einheit der GATT-basierten Kommunikation. Es enthält einen Wert, der eine eindeutige Datenmenge auf dem Gerät darstellt. Beispielsweise weist das Merkmal des Akkustands einen Wert auf, der den Akkustand des Geräts darstellt.

Lesen Sie die charakteristischen Eigenschaften, um zu bestimmen, welche Vorgänge unterstützt werden:

GattCharacteristicProperties properties = characteristic.CharacteristicProperties

if(properties.HasFlag(GattCharacteristicProperties.Read))
{
    // This characteristic supports reading from it.
}
if(properties.HasFlag(GattCharacteristicProperties.Write))
{
    // This characteristic supports writing to it.
}
if(properties.HasFlag(GattCharacteristicProperties.Notify))
{
    // This characteristic supports subscribing to notifications.
}

Wenn Lesevorgänge unterstützt werden, können Sie den Wert lesen:

GattReadResult result = await selectedCharacteristic.ReadValueAsync();
if (result.Status == GattCommunicationStatus.Success)
{
    var reader = DataReader.FromBuffer(result.Value);
    byte[] input = new byte[reader.UnconsumedBufferLength];
    reader.ReadBytes(input);
    // Utilize the data as needed
}

Das Schreiben in ein Merkmal folgt einem ähnlichen Muster:

var writer = new DataWriter();
// WriteByte used for simplicity. Other common functions - WriteInt16 and WriteSingle
writer.WriteByte(0x01);

GattCommunicationStatus result = await selectedCharacteristic.WriteValueAsync(writer.DetachBuffer());
if (result == GattCommunicationStatus.Success)
{
    // Successfully wrote to device
}

Tipp

DataReader und DataWriter sind unverzichtbar, wenn Sie mit den Rohpuffern arbeiten, die Sie von vielen der Bluetooth-APIs erhalten.

Abonnieren von Benachrichtigungen

Stellen Sie sicher, dass das Merkmal entweder Angeben oder Benachrichtigen unterstützt (überprüfen Sie die charakteristischen Eigenschaften, um dies sicherzustellen).

Die Angabe gilt als zuverlässiger, da jedes Wertänderungsereignis mit einer Bestätigung vom Clientgerät gekoppelt ist. Notify ist häufiger, da die meisten GATT-Transaktionen lieber Strom sparen als äußerst zuverlässig sind. In jedem Fall wird all dies auf Controllerebene behandelt, sodass die App nicht beteiligt wird. Wir bezeichnen sie zusammen als "Benachrichtigungen".

Es gibt zwei Dinge zu beachten, bevor Sie Benachrichtigungen erhalten:

  • Schreiben in client characteristic configuration descriptor (CCCD)
  • Behandeln des Characteristic.ValueChanged-Ereignisses

Das Schreiben in die CCCD teilt dem Servergerät mit, dass dieser Client jedes Mal wissen möchte, wenn sich dieser bestimmte Merkmalswert ändert. Aufgabe:

GattCommunicationStatus status = await selectedCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(
                        GattClientCharacteristicConfigurationDescriptorValue.Notify);
if(status == GattCommunicationStatus.Success)
{
    // Server has been informed of clients interest.
}

Nun wird das ValueChanged-Ereignis von GattCharacteristic bei jeder Änderung des Werts auf dem Remotegerät aufgerufen. Sie müssen nur noch den Handler implementieren:

characteristic.ValueChanged += Characteristic_ValueChanged;

...

void Characteristic_ValueChanged(GattCharacteristic sender,
                                    GattValueChangedEventArgs args)
{
    // An Indicate or Notify reported that the value has changed.
    var reader = DataReader.FromBuffer(args.CharacteristicValue)
    // Parse the data however required.
}