Bluetooth GATT-ClientBluetooth GATT Client

In diesem Artikel wird die Verwendung der Client-APIs für das Bluetooth Generic Attribute (GATT) für universelle Windows-Plattform-Apps (UWP) sowie Beispielcode für gängige GATT-Client Aufgaben veranschaulicht:This article demonstrates usage of the Bluetooth Generic Attribute (GATT) Client APIs for Universal Windows Platform (UWP) apps, along with sample code for common GATT client tasks:

  • Abfragen von Geräten in der NäheQuery for nearby devices
  • Verbindung mit Gerät herstellenConnect to device
  • Auflisten der unterstützten Dienste und Merkmale des GerätsEnumerate the supported services and characteristics of the device
  • Lese-und Schreibzugriff auf ein MerkmalRead and write to a characteristic
  • Benachrichtigungen abonnieren, wenn der Merkmal Wert geändert wirdSubscribe for notifications when characteristic value changes

Wichtig

Sie müssen die Funktion "Bluetooth" in " Package. appxmanifest" deklarieren.You must declare the "bluetooth" capability in Package.appxmanifest.

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

Wichtige APIsImportant APIs

ÜbersichtOverview

Entwickler können die APIs im Windows. Devices. Bluetooth. generiertributeprofile -Namespace verwenden, um auf Bluetooth Le-Geräte zuzugreifen.Developers can use the APIs in the Windows.Devices.Bluetooth.GenericAttributeProfile namespace to access Bluetooth LE devices. Bluetooth LE-Geräte machen ihre Funktionen über eine Sammlung folgender Elemente verfügbar:Bluetooth LE devices expose their functionality through a collection of:

  • DiensteServices
  • MerkmaleCharacteristics
  • DeskriptorenDescriptors

Dienste definieren den funktionalen Vertrag des Le-Geräts und enthalten eine Sammlung von Merkmalen, die den Dienst definieren.Services define the functional contract of the LE device and contain a collection of characteristics that define the service. Diese Merkmale wiederum enthalten Deskriptoren, von denen die Merkmale beschrieben werden.Those characteristics, in turn, contain descriptors that describe the characteristics. Diese drei Begriffe werden generisch als die Attribute eines Geräts bezeichnet.These 3 terms are generically known as the attributes of a device.

Die Bluetooth Le GATT-APIs machen anstelle des Zugriffs auf den unformatierten Transport Objekte und Funktionen verfügbar.The Bluetooth LE GATT APIs expose objects and functions, rather than access to the raw transport. Die GATT-APIs ermöglichen auch Entwicklern die Arbeit mit Bluetooth Le-Geräten mit der Möglichkeit, die folgenden Aufgaben auszuführen:The GATT APIs also enable developers to work with Bluetooth LE devices with the ability to perform the following tasks:

  • Attribut Ermittlung ausführenPerform attribute discovery
  • Lese-und Schreib AttributwerteRead and Write attribute values
  • Registrieren eines Rückrufs für das Merkmal "ValueChanged"-EreignisRegister a callback for Characteristic ValueChanged event

Um eine nützliche Implementierung zu erstellen, muss ein Entwickler über vorherige Kenntnisse über die GATT-Dienste und-Eigenschaften verfügen, die von der Anwendung verwendet werden sollen, und um die spezifischen Merkmals Werte zu verarbeiten, sodass die von der API bereitgestellten Binärdaten in nützliche Daten transformiert werden, bevor Sie dem Benutzer präsentiert werden.To create a useful implementation a developer must have prior knowledge of the GATT services and characteristics the application intends to consume and to process the specific characteristic values such that the binary data provided by the API is transformed into useful data before being presented to the user. Die Bluetooth GATT-APIs machen nur die Grundtypen verfügbar, die für die Kommunikation mit einem Bluetooth LE-Gerät erforderlich sind.The Bluetooth GATT APIs expose only the basic primitives required to communicate with a Bluetooth LE device. 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.To interpret the data, an application profile must be defined, either by a Bluetooth SIG standard profile, or a custom profile implemented by a device vendor. 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.A profile creates a binding contract between the application and the device, as to what the exchanged data represents and how to interpret it.

Aus Gründen der Benutzerfreundlichkeit pflegt Bluetooth SIG eine Liste der öffentlichen Profile, die zur Verfügung stehen.For convenience the Bluetooth SIG maintains a list of public profiles available.

Abfragen von Geräten in der NäheQuery for nearby devices

Es gibt zwei Hauptmethoden, um Geräte in der Nähe abzufragen:There are two main methods to query for nearby devices:

  • Devicewatcher in Windows. Devices. EnumerationDeviceWatcher in Windows.Devices.Enumeration
  • Ankündigen in Windows. Devices. Bluetooth. Ankündigungs-WatcherAdvertisementWatcher in Windows.Devices.Bluetooth.Advertisement

Die zweite Methode wird in der Ankündigungs Dokumentation ausführlich erläutert, sodass Sie hier nicht ausführlich erläutert wird. die grundlegende Idee besteht jedoch darin, die Bluetooth-Adresse von Geräten in der Nähe zu finden, die den jeweiligen Ankündigungs Filtererfüllen.The 2nd method is discussed at length in the Advertisement documentation so it won't be discussed much here but the basic idea is to find the Bluetooth address of nearby devices that satisfy the particular Advertisement Filter. Sobald Sie über die Adresse verfügen, können Sie bluetoothledevice. frombluetoothaddressasync aufrufen, um einen Verweis auf das Gerät zu erhalten.Once you have the address, you can call BluetoothLEDevice.FromBluetoothAddressAsync to get a reference to the device.

Nun zurück zur devicewatcher-Methode.Now, back to the DeviceWatcher method. Ein Bluetooth-Le-Gerät ist genau wie jedes andere Gerät in Windows und kann mithilfe der enumerationsapisabgefragt werden.A Bluetooth LE device is just like any other device in Windows and can be queried using the Enumeration APIs. Verwenden Sie die devicewatcher -Klasse, und übergeben Sie eine Abfrage Zeichenfolge, die die zu suchenden Geräte angibt:Use the DeviceWatcher class and pass a query string specifying the devices to look for:

// 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 den devicewatcher gestartet haben, erhalten Sie DeviceInformation für jedes Gerät, das die Abfrage im Handler für das hinzugefügte Ereignis für die fraglichen Geräte erfüllt.Once you've started the DeviceWatcher, you will receive DeviceInformation for each device that satisfies the query in the handler for the Added event for the devices in question. Eine ausführlichere Betrachtung von devicewatcher finden Sie im kompletten Beispiel auf GitHub.For a more detailed look at DeviceWatcher see the complete sample on Github.

Verbindung mit dem Gerät wird hergestelltConnecting to the device

Nachdem ein gewünschtes Gerät ermittelt wurde, verwenden Sie das DeviceInformation.ID -Objekt, um das Bluetooth Le-Geräte Objekt für das betreffende Gerät zu erhalten:Once a desired device is discovered, use the DeviceInformation.Id to get the Bluetooth LE Device object for the device in question:

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);
    // ...
}

Wenn Sie allerdings alle Verweise auf ein bluetoothledevice-Objekt für ein Gerät verwerfen (und wenn keine andere APP auf dem System über einen Verweis auf das Gerät verfügt), wird nach einem kurzen Timeout eine automatische Verbindung getrennt.On the other hand, disposing of all references to a BluetoothLEDevice object for a device (and if no other app on the system has a reference to the device) will trigger an automatic disconnect after a small timeout period.

bluetoothLeDevice.Dispose();

Wenn die APP erneut auf das Gerät zugreifen muss, wird durch einfaches erneutes Erstellen des Geräte Objekts und durch den Zugriff auf ein Merkmal (im nächsten Abschnitt erläutert) das Betriebssystem zum erneuten Herstellen der Verbindung bei Bedarf zurückgeführt.If the app needs to access the device again, simply re-creating the device object and accessing a characteristic (discussed in the next section) will trigger the OS to re-connect when necessary. Wenn sich das Gerät in der Nähe befindet, erhalten Sie Zugriff auf das Gerät. andernfalls wird ein Fehler mit der Fehlermeldung zurückgegeben.If the device is nearby, you'll get access to the device otherwise it will return w/ a DeviceUnreachable error.

Hinweis

Durch das Erstellen eines bluetoothledevice -Objekts durch Aufrufen dieser Methode allein (notwendigerweise) wird keine Verbindung initiiert.Creating a BluetoothLEDevice object by calling this method alone doesn't (necessarily) initiate a connection. Wenn Sie eine Verbindung initiieren möchten, legen Sie " gattsession. wart Connection " auf fest true , oder führen Sie eine nicht zwischengespeicherte Dienst Ermittlungsmethode für bluetoothledeviceaus, oder führen Sie einen Lese-/Schreibvorgang für das Gerät ausTo initiate a connection, set GattSession.MaintainConnection to true, or call an uncached service discovery method on BluetoothLEDevice, or perform a read/write operation against the device.

  • Wenn " gattsession. wart Connection " auf "true" festgelegt ist, wartet das System unbegrenzt auf eine Verbindung, und die Verbindung wird hergestellt, wenn das Gerät verfügbar ist.If GattSession.MaintainConnection is set to true, then the system waits indefinitely for a connection, and it will connect when the device is available. Es gibt nichts, auf den Ihre Anwendung warten kann, da " gattsession. wart Connection " eine Eigenschaft ist.There's nothing for your application to wait on, since GattSession.MaintainConnection is a property.
  • Für Dienst Ermittlung und Lese-/Schreibvorgänge in GATT wartet das System eine endliche, aber Variable Zeit.For service discovery and read/write operations in GATT, the system waits a finite but variable time. Alles von sofortiger Zeit bis zu wenigen Minuten.Anything from instantaneous to a matter of minutes. Faktoren beinhaltet den Datenverkehr auf dem Stapel und wie die Anforderung in die Warteschlange eingereiht wird.Factors inclue the traffic on the stack, and how queued up the request is. Wenn keine andere ausstehende Anforderung vorhanden ist und das Remote Gerät nicht erreichbar ist, wartet das System sieben (7) Sekunden, bevor ein Timeout eintritt. Wenn andere ausstehende Anforderungen vorhanden sind, kann jede der Anforderungen in der Warteschlange sieben (7) Sekunden in Anspruch nehmen, sodass sich die weiter unten in der Warteschlange befindet, je länger Sie warten.If there are no other pending request, and the remote device is unreachable, then the system will wait for seven (7) seconds before it times out. If there are other pending requests, then each of the requests in the queue can take seven (7) seconds to process, so the further yours is toward the back of the queue, the longer you'll wait.

Der Verbindungsprozess kann derzeit nicht abgebrochen werden.Currently, you can't cancel the connection process.

Auflisten unterstützter Dienste und MerkmaleEnumerating supported services and characteristics

Nachdem Sie nun über ein bluetoothledevice-Objekt verfügen, besteht der nächste Schritt darin, herauszufinden, welche Daten das Gerät verfügbar macht.Now that you have a BluetoothLEDevice object, the next step is to discover what data the device exposes. Der erste Schritt besteht darin, Dienste abzufragen:The first step to do this is to query for services:

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 in der Abfrage von Merkmalen.Once the service of interest has been identified, the next step is to query for characteristics.

GattCharacteristicsResult result = await service.GetCharacteristicsAsync();

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

Das Betriebssystem gibt eine schreibgeschützte Liste von gattobjects-Objekten zurück, für die Sie dann Vorgänge ausführen können.The OS returns a ReadOnly list of GattCharacteristic objects that you can then perform operations on.

Ausführen von Lese-/Schreibvorgängen für ein MerkmalPerform Read/Write operations on a characteristic

Das Merkmal ist die grundlegende Einheit der GATT-basierten Kommunikation.The characteristic is the fundamental unit of GATT based communication. Sie enthält einen Wert, der ein bestimmtes Datenelement auf dem Gerät darstellt.It contains a value that represents a distinct piece of data on the device. Beispielsweise verfügt das Merkmal der Akku Ebene über einen Wert, der den Akku Pegel des Geräts darstellt.For example, the battery level characteristic has a value that represents the battery level of the device.

Lesen Sie die Merkmal Eigenschaften, um zu bestimmen, welche Vorgänge unterstützt werden:Read the characteristic properties to determine what operations are supported:

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 Read unterstützt wird, können Sie den Wert lesen:If read is supported, you can read the value:

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:Writing to a characteristic follows a similar pattern:

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 bei der Arbeit mit den rohpuffern, die Sie aus vielen der Bluetooth-APIs erhalten, nicht funktionsunfähig.Tip: DataReader and DataWriter are indispensible when working with the raw buffers you get from many of the Bluetooth APIs.

Abonnieren von BenachrichtigungenSubscribing for notifications

Stellen Sie sicher, dass das Merkmal entweder angeben oder Benachrichtigen unterstützt (überprüfen Sie die Merkmals Eigenschaften, um sicherzustellen).Make sure the characteristic supports either Indicate or Notify (check the characteristic properties to make sure).

Abgesehenvon: die Angabe ist zuverlässiger als zuverlässiger, da jedes geänderte Ereignis mit einer Bestätigung vom Client Gerät gekoppelt ist.Aside: Indicate is considered more reliable because each value changed event is coupled with an acknowledgement from the client device. Die Benachrichtigung ist häufiger, da die meisten GATT-Transaktionen die Stromversorgung Verb einsparen und nicht äußerst zuverlässig sein würden.Notify is more prevalent because most GATT transactions would rather conserve power rather than be extremely reliable. In jedem Fall werden alle auf der Controller Ebene verarbeitet, sodass die APP nicht beteiligt ist.In any case, all of that is handled at the controller layer so the app does not get involved. Wir bezeichnen Sie kollektiv als einfach "Benachrichtigungen", aber jetzt wissen Sie.We'll collectively refer to them as simply "notifications" but now you know.

Vor dem erhalten von Benachrichtigungen sind zwei Dinge zu berücksichtigen:There are two things to take care of before getting notifications:

  • In Client Merkmal konfigurationsdeskriptor schreiben (cccd)Write to Client Characteristic Configuration Descriptor (CCCD)
  • Behandeln des Merkmals. ValueChanged-EreignissesHandle the Characteristic.ValueChanged event

Beim Schreiben auf die cccd wird dem Server Gerät mitgeteilt, dass dieser Client jedes Mal wissen möchte, wenn sich ein bestimmter Merkmals Wert ändert.Writing to the CCCD tells the Server device that this client wants to know each time that particular characteristic value changes. Gehen Sie dazu folgendermaßen vor:To do this:

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

Nun wird das ValueChanged-Ereignis des GATT-Merkmals immer dann aufgerufen, wenn der Wert auf dem Remote Gerät geändert wird.Now, the GattCharacteristic's ValueChanged event will get called each time the value gets changed on the remote device. Der Handler muss nur noch implementiert werden:All that's left is to implement the handler:

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.
}