Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Thema wird die Verwendung der Bluetooth Generic Attribute (GATT)-Server-APIs für Universelle Windows-Plattform (UWP)-Apps veranschaulicht.
Important
Sie müssen die "Bluetooth"-Funktion in "Package.appxmanifest" deklarieren.
<Capabilities> <DeviceCapability Name="bluetooth" /> </Capabilities>
Wichtige APIs
Übersicht
Windows funktioniert in der Regel in der Clientrolle. Dennoch ergeben sich viele Szenarien, in denen auch Windows als Bluetooth LE GATT-Server fungieren müssen. Fast alle Szenarien für IoT-Geräte ebenso wie die meiste plattformübergreifende BLE-Kommunikation setzen voraus, dass Windows als GATT-Server fungiert. Darüber hinaus wurde das Senden von Benachrichtigungen an nahe gelegene tragbare Geräte zu einem beliebten Szenario, das diese Technologie erfordert.
Serveroperationen werden sich um den Service Provider und die GattLocalCharacteristic drehen. Diese beiden Klassen stellen die Funktionalität bereit, die zum Deklarieren, Implementieren und Verfügbarmachen einer Datenhierarchie für ein Remotegerät erforderlich ist.
Definieren der unterstützten Dienste
Ihre App kann einen oder mehrere Dienste deklarieren, die von Windows veröffentlicht werden. Jeder Dienst wird durch eine UUID eindeutig identifiziert.
Attribute und UUIDs
Jeder Dienst, merkmal und Deskriptor wird durch eine eigene 128-Bit-UUID definiert.
Die Windows-APIs verwenden alle den Begriff GUID, aber der Bluetooth-Standard definiert diese als UUIDs. Zu unseren Zwecken sind diese beiden Begriffe austauschbar, sodass wir weiterhin den Begriff UUID verwenden.
Wenn das Attribut standardisiert und von der Bluetooth SIG definiert ist, hat es auch eine entsprechende 16-Bit-Kurz-ID (zum Beispiel ist die UUID für den Akkustand 00002A19-0000-1000-8000-00805F9B34FB und die Kurz-ID ist 0x2A19). Diese Standard-UUIDs sind in GattServiceUuids und GattCharacteristicUuids zu sehen.
Wenn Ihre App einen eigenen benutzerdefinierten Dienst implementiert, muss eine benutzerdefinierte UUID generiert werden. Dies lässt sich in Visual Studio ganz einfach über Extras -> CreateGuid erledigen (verwenden Sie Option 5, um das Format „xxxxxxxx-xxxx-...xxxx“ zu erhalten). Diese uuid kann jetzt verwendet werden, um neue lokale Dienste, Merkmale oder Deskriptoren zu deklarieren.
Eingeschränkte Dienste
Die folgenden Dienste sind vom System reserviert und können zurzeit nicht veröffentlicht werden:
- Geräteinformationsdienst (Device Information Service, DIS)
- Generic Attribute Profile Service (GATT)
- Generic Access Profile Service (GAP)
- Service für Scanparameter (SCP)
Der Versuch, einen blockierten Dienst zu erstellen, führt dazu, dass BluetoothError.DisabledByPolicy vom Aufruf an CreateAsync zurückgegeben wird.
Generierte Attribute
Die folgenden Deskriptoren werden automatisch vom System generiert, basierend auf den GattLocalCharacteristicParameters, die während der Erstellung des Merkmals bereitgestellt werden:
- Client-Merkmalskonfiguration (wenn das Merkmal als angibtd oder notifizierbar gekennzeichnet ist).
- Charakteristische Benutzerbeschreibung (wenn die Eigenschaft „UserDescription“ gesetzt ist). Weitere Informationen finden Sie in der Eigenschaft GattLocalCharacteristicParameters.UserDescription.
- Merkmalsformat (ein Deskriptor für jedes angegebene Präsentationsformat). Weitere Informationen finden Sie unter der Eigenschaft GattLocalCharacteristicParameters.PresentationFormats.
- Charakteristisches Aggregatformat (wenn mehrere Präsentationsformate angegeben werden). GattLocalCharacteristicParameters. Weitere Informationen finden Sie unter der Eigenschaft „PresentationFormats“.
- Charakteristische erweiterte Eigenschaften (wenn das Merkmal mit dem bit der erweiterten Eigenschaften markiert ist).
Der Wert des Deskriptors für erweiterte Eigenschaften wird über die Eigenschaften "ReliableWrites" und "WritableAuxiliaries" bestimmt.
Der Versuch, einen reservierten Deskriptor zu erstellen, führt zu einer Ausnahme.
Beachten Sie, dass die Übertragung zurzeit nicht unterstützt wird. Die Angabe der Broadcast GattCharacteristicProperty führt zu einer Ausnahme.
Aufbau der Hierarchie von Diensten und Merkmalen
Der GattServiceProvider wird zum Erstellen und Ankündigen der primären Stammdienstdefinition verwendet. Jeder Dienst erfordert ein eigenes ServiceProvider-Objekt, das eine GUID verwendet:
GattServiceProviderResult result = await GattServiceProvider.CreateAsync(uuid);
if (result.Error == BluetoothError.Success)
{
serviceProvider = result.ServiceProvider;
//
}
Primäre Dienste sind die oberste Ebene der GATT-Struktur. Primäre Dienste enthalten Merkmale sowie andere Dienste (als "Eingeschlossen" oder sekundäre Dienste bezeichnet).
Füllen Sie nun den Dienst mit den erforderlichen Merkmalen und Beschreibungen auf:
GattLocalCharacteristicResult characteristicResult = await serviceProvider.Service.CreateCharacteristicAsync(uuid1, ReadParameters);
if (characteristicResult.Error != BluetoothError.Success)
{
// An error occurred.
return;
}
_readCharacteristic = characteristicResult.Characteristic;
_readCharacteristic.ReadRequested += ReadCharacteristic_ReadRequested;
characteristicResult = await serviceProvider.Service.CreateCharacteristicAsync(uuid2, WriteParameters);
if (characteristicResult.Error != BluetoothError.Success)
{
// An error occurred.
return;
}
_writeCharacteristic = characteristicResult.Characteristic;
_writeCharacteristic.WriteRequested += WriteCharacteristic_WriteRequested;
characteristicResult = await serviceProvider.Service.CreateCharacteristicAsync(uuid3, NotifyParameters);
if (characteristicResult.Error != BluetoothError.Success)
{
// An error occurred.
return;
}
_notifyCharacteristic = characteristicResult.Characteristic;
_notifyCharacteristic.SubscribedClientsChanged += SubscribedClientsChanged;
Wie oben gezeigt, ist dies auch ein guter Ort zum Deklarieren von Ereignishandlern für die Vorgänge, die von den einzelnen Merkmalen unterstützt werden. Um ordnungsgemäß auf Anforderungen zu reagieren, muss eine App einen Ereignishandler für jeden anforderungstyp festlegen, den das Attribut unterstützt. Wenn Sie keinen Handler registrieren, wird die Anfrage durch das System sofort mit UnlikelyError abgeschlossen.
Konstante Eigenschaften
Manchmal gibt es Merkmalswerte, die sich während der Lebensdauer der App nicht ändern. In diesem Fall ist es ratsam, ein konstantes Merkmal zu deklarieren, um unnötige App-Aktivierung zu verhindern:
byte[] value = new byte[] {0x21};
var constantParameters = new GattLocalCharacteristicParameters
{
CharacteristicProperties = (GattCharacteristicProperties.Read),
StaticValue = value.AsBuffer(),
ReadProtectionLevel = GattProtectionLevel.Plain,
};
var characteristicResult = await serviceProvider.Service.CreateCharacteristicAsync(uuid4, constantParameters);
if (characteristicResult.Error != BluetoothError.Success)
{
// An error occurred.
return;
}
Veröffentlichen des Diensts
Nachdem der Dienst vollständig definiert wurde, besteht der nächste Schritt darin, die Unterstützung für den Dienst zu veröffentlichen. Dadurch wird das Betriebssystem informiert, dass der Dienst zurückgegeben werden soll, wenn Remotegeräte eine Dienstermittlung durchführen. Sie müssen zwei Eigenschaften festlegen: IsDiscoverable und IsConnectable:
GattServiceProviderAdvertisingParameters advParameters = new GattServiceProviderAdvertisingParameters
{
IsDiscoverable = true,
IsConnectable = true
};
serviceProvider.StartAdvertising(advParameters);
- IsDiscoverable: Überträgt den Gerätenamen in der Ankündigung an entfernte Geräte und macht das Gerät dadurch auffindbar.
- IsConnectable: Kündigt eine verbindebare Werbung für die Verwendung in der Peripherierolle an.
Wenn ein Dienst sowohl discoverable als auch Connectable ist, fügt das System den Dienst Uuid zum Ankündigungspaket hinzu. Ein Advertising-Paket enthält nur 31 Byte, und eine 128-Bit-UUID belegt 16 davon!
Beachten Sie, dass eine Anwendung StopAdvertising aufrufen muss, wenn ein Dienst im Vordergrund veröffentlicht wird und die Anwendung suspendiert wird.
Antworten auf Lese- und Schreibanforderungen
Wie wir oben bei der Deklaration der erforderlichen Charakteristiken gesehen haben, verfügen GattLocalCharacteristics über drei Ereignistypen: ReadRequested, WriteRequested und SubscribedClientsChanged.
Lesen Sie
Wenn ein Remotegerät versucht, einen Wert aus einem Merkmal zu lesen (und es ist kein konstanter Wert), wird das ReadRequested-Ereignis aufgerufen. Das Merkmal, für das der Lesevorgang aufgerufen wurde, sowie args (die Informationen über das Remote-Gerät enthalten) werden an den Delegaten übergeben:
characteristic.ReadRequested += Characteristic_ReadRequested;
// ...
async void ReadCharacteristic_ReadRequested(GattLocalCharacteristic sender, GattReadRequestedEventArgs args)
{
var deferral = args.GetDeferral();
// Our familiar friend - DataWriter.
var writer = new DataWriter();
// populate writer w/ some data.
// ...
var request = await args.GetRequestAsync();
request.RespondWithValue(writer.DetachBuffer());
deferral.Complete();
}
Schreiben
Wenn ein Remotegerät versucht, einen Wert in ein Merkmal zu schreiben, wird das WriteRequested-Ereignis mit Details zum Remotegerät aufgerufen, in das das Merkmal geschrieben werden soll, und den Wert selbst:
characteristic.ReadRequested += Characteristic_ReadRequested;
// ...
async void WriteCharacteristic_WriteRequested(GattLocalCharacteristic sender, GattWriteRequestedEventArgs args)
{
var deferral = args.GetDeferral();
var request = await args.GetRequestAsync();
var reader = DataReader.FromBuffer(request.Value);
// Parse data as necessary.
if (request.Option == GattWriteOption.WriteWithResponse)
{
request.Respond();
}
deferral.Complete();
}
Es gibt zwei Typen von Schreibvorgängen – mit und ohne Antwort. Verwenden Sie GattWriteOption (eine Eigenschaft im GattWriteRequest-Objekt), um herauszufinden, welche Art von Schreibzugriff das Remotegerät ausführt.
Senden von Benachrichtigungen an abonnierte Clients
Benachrichtigungen sind die häufigste der GATT-Server-Operationen und übernehmen die entscheidende Funktion, Daten an die entfernten Geräte zu übertragen. Manchmal möchten Sie alle abonnierten Clients benachrichtigen, aber in anderen Fällen können Sie auswählen, an welche Geräte der neue Wert gesendet werden soll:
async void NotifyValue()
{
var writer = new DataWriter();
// Populate writer with data
// ...
await notifyCharacteristic.NotifyValueAsync(writer.DetachBuffer());
}
Wenn ein neues Gerät Benachrichtigungen abonniert, wird das SubscribedClientsChanged-Ereignis aufgerufen:
characteristic.SubscribedClientsChanged += SubscribedClientsChanged;
// ...
void _notifyCharacteristic_SubscribedClientsChanged(GattLocalCharacteristic sender, object args)
{
List<GattSubscribedClient> clients = sender.SubscribedClients;
// Diff the new list of clients from a previously saved one
// to get which device has subscribed for notifications.
// You can also just validate that the list of clients is expected for this app.
}
Note
Ihre Anwendung kann die maximale Benachrichtigungsgröße für einen bestimmten Client mit der MaxNotificationSize-Eigenschaft abrufen. Alle Daten, die größer als die maximale Größe sind, werden vom System abgeschnitten.
Wenn Sie das GattLocalCharacteristic.SubscribedClientsChanged-Ereignis behandeln, können Sie den unten beschriebenen Prozess verwenden, um vollständige Informationen zu den aktuell abonnierten Clientgeräten zu ermitteln:
- Die Argumente des SubscribedClientsChanged-Ereignisses sind ein GattLocalCharacteristic-Objekt .
- Greifen Sie auf die GattLocalCharacteristic.SubscribedClients-Eigenschaft des Objekts zu, die eine Auflistung von GattSubscribedClient-Objekten ist.
- Durchlaufen Sie diese Sammlung. Führen Sie für jedes Element die folgenden Schritte aus:
- Greifen Sie auf die GattSubscribedClient.Session-Eigenschaft zu, bei der es sich um ein GattSession-Objekt handelt.
- Greifen Sie auf die GattSession.DeviceId-Eigenschaft zu, bei der es sich um ein BluetoothDeviceId-Objekt handelt.
- Greifen Sie auf die eigenschaft BluetoothDeviceId.Id zu, bei der es sich um die Geräte-ID-Zeichenfolge handelt.
- Übergeben Sie die Geräte-ID-Zeichenfolge an BluetoothLEDevice.FromIdAsync , um ein BluetoothLEDevice-Objekt abzurufen. Sie können vollständige Informationen über das Gerät aus diesem Objekt abrufen.
Windows developer