Freigeben über


Bluetooth GATT-Client

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

Wichtig

Sie müssen die Funktion „bluetooth“ 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 Funktionalität über eine Sammlung von:

  • Dienste
  • Merkmale
  • Deskriptoren

Dienste definieren den Funktionsvertrag des LE-Geräts und enthalten eine Sammlung von Merkmalen, die den Dienst definieren. Diese Merkmale enthalten wiederum Beschreibungen, die die Merkmale beschreiben. Diese 3 Begriffe werden generisch als Attribute eines Geräts bezeichnet.

Die Bluetooth LE GATT-APIs machen Objekte und Funktionen verfügbar, anstatt auf den unformatierten Transport zuzugreifen. Die GATT-APIs ermöglichen Entwicklern auch die Arbeit mit Bluetooth LE-Geräten mit der Möglichkeit, die folgenden Aufgaben auszuführen:

  • Durchführen der Attributermittlung
  • Lese- und Schreib-Attributwerte
  • Registrieren eines Rückrufs für das Merkmal ValueChanged-Ereignis

Um eine nützliche Implementierung zu erstellen, muss ein Entwickler über vorherige Kenntnisse der GATT-Dienste und Merkmale verfügen, die die Anwendung verwenden und verarbeiten möchte, um die spezifischen Merkmalswerte so zu verarbeiten, dass die von der API bereitgestellten Binärdaten in nützliche Daten umgewandelt werden, bevor sie dem Benutzer präsentiert werden. Die Bluetooth GATT-APIs machen nur die grundlegenden Grundtypen verfügbar, die für die Kommunikation mit einem Bluetooth LE-Gerät erforderlich sind. Um die Daten zu interpretieren, muss ein Anwendungsprofil entweder durch ein Bluetooth SIG-Standardprofil oder ein von einem Geräteanbieter implementiertes benutzerdefiniertes Profil definiert werden. Ein Profil erstellt einen verbindlichen Vertrag zwischen der Anwendung und dem Gerät, was die ausgetauschten Daten darstellen und wie sie interpretiert werden.

Aus Gründen der Einfachheit verwaltet die Bluetooth SIG eine Liste der verfügbaren öffentlichen Profile .

Beispiele

Ein vollständiges Beispiel finden Sie im Bluetooth Low Energy-Beispiel.

Abfrage nach Geräten in der Nähe

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

Die 2. Methode wird in der Ankündigungsdokumentation ausführlich behandelt, damit sie hier nicht viel diskutiert wird, aber die Grundidee besteht darin, die Bluetooth-Adresse von nahe gelegenen Geräten zu finden, die den bestimmten Ankündigungsfilter erfüllen. Sobald Sie über die Adresse verfügen, können Sie BluetoothLEDevice.FromBluetoothAddressAsync aufrufen, um einen Verweis auf das Gerät abzurufen.

Zurück zur DeviceWatcher-Methode. 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 suchden 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 detaillierteren 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 Device-Objekt 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);
    // ...
}

Andererseits löst das Löschen aller Verweise auf ein BluetoothLEDevice-Objekt für ein Gerät (und wenn keine andere App auf dem System über einen Verweis auf das Gerät verfügt) eine automatische Trennung nach einem kleinen Timeoutzeitraum aus.

bluetoothLeDevice.Dispose();

Wenn die App erneut auf das Gerät zugreifen muss, wird die erneute Erstellung des Geräteobjekts und der Zugriff auf ein Merkmal (im nächsten Abschnitt erläutert) das Betriebssystem bei Bedarf auslösen, um eine erneute Verbindung herzustellen. Wenn sich das Gerät in der Nähe befindet, erhalten Sie Zugriff auf das Gerät, andernfalls wird ein DeviceUnreachable-Fehler zurückgegeben.

Hinweis

Das Erstellen eines BluetoothLEDevice-Objekts durch Aufrufen dieser Methode allein initiiert keine Verbindung (notwendigerweise). Um eine Verbindung zu initiieren, legen Sie GattSession.MaintainConnection auf true, 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 auf unbestimmte Zeit auf eine Verbindung, und es wird verbunden, wenn das Gerät verfügbar ist. Da GattSession.MaintainConnection eine Eigenschaft ist, gibt es nichts für die Anwendung, auf die gewartet werden muss.
  • Bei Dienstermittlungs- und Lese-/Schreibvorgängen in GATT wartet das System eine endliche, aber variable Zeit. Alles von sofort bis zu einer Angelegenheit von Minuten. Zu den Faktoren gehören der Datenverkehr im Stapel und wie die Anforderung in die Warteschlange eingereiht wird. Wenn keine andere ausstehende Anforderung vorhanden ist und das Remotegerät nicht erreichbar ist, wartet das System auf sieben (7) Sekunden, bevor es zu einem Zeitüberschreitung kommt. Wenn weitere ausstehende Anforderungen vorhanden sind, kann jede Der Anforderungen in der Warteschlange sieben (7) Sekunden dauern, bis sie verarbeitet werden, sodass sich die weiteren Anforderungen auf die Rückseite der Warteschlange beziehen, desto länger warten Sie.

Derzeit können Sie den Verbindungsprozess nicht abbrechen.

Aufzählen 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 besteht darin, Dienste abzufragen:

GattDeviceServicesResult result = await bluetoothLeDevice.GetGattServicesAsync();

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

Sobald der Interessendienst 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. Sie enthält einen Wert, der ein bestimmtes Datenstück auf dem Gerät darstellt. Beispielsweise weist das Merkmal des Akkustands einen Wert auf, der den Akkustand des Geräts darstellt.

Lesen Sie die Merkmalseigenschaften, 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 das Lesen unterstützt wird, 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 unerlässlich, wenn Sie mit den rohen Puffern arbeiten, die Sie von vielen der Bluetooth-APIs erhalten.

Abonnieren von Benachrichtigungen

Stellen Sie sicher, dass das Merkmal entweder "Indicate" oder "Notify" unterstützt (überprüfen Sie die charakteristischen Eigenschaften, um sicherzustellen).

"Indicate" 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 Energie sparen würden, anstatt extrem zuverlässig zu sein. In jedem Fall wird all dies auf der Controllerebene behandelt, sodass die App nicht einbezogen wird. Wir bezeichnen sie gemeinsam als "Benachrichtigungen".

Es gibt zwei Dinge, die Sie sich kümmern müssen, bevor Sie Benachrichtigungen erhalten:

  • Schreiben in Client-Merkmalskonfigurationsdeskriptor (CCCD)
  • Behandeln des Characteristic.ValueChanged-Ereignisses

Schreiben in CCCD teilt dem Servergerät mit, dass dieser Client jedes Mal wissen möchte, wenn sich dieser Merkmalswert ändert. Gehen Sie hierzu folgendermaßen vor:

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

Jetzt wird das ValueChanged-Ereignis des GattCharacteristic-Ereignisses jedes Mal aufgerufen, wenn der Wert auf dem Remotegerät geändert wird. Alles, was übrig ist, besteht darin, den Handler zu 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.
}