Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questo argomento illustra come usare le API del server GATT (Bluetooth Generic Attribute) per le app Windows.
Important
È necessario dichiarare la funzionalità "bluetooth" in Package.appxmanifest.
<Capabilities> <DeviceCapability Name="bluetooth" /> </Capabilities>
Overview
Windows in genere opera nel ruolo client. Tuttavia, molti scenari si presentano che richiedono Windows di agire anche come server GATT Bluetooth LE. Quasi tutti gli scenari per i dispositivi IoT, insieme alla maggior parte delle comunicazioni BLE multipiattaforma, richiederanno Windows essere un server GATT. Inoltre, l'invio di notifiche ai dispositivi indossabili nelle vicinanze è diventato uno scenario popolare che richiede anche questa tecnologia.
Le operazioni del server ruotano attorno al provider di servizi e al GattLocalCharacteristic. Queste due classi forniranno le funzionalità necessarie per dichiarare, implementare ed esporre una gerarchia di dati a un dispositivo remoto.
Definire i servizi supportati
L'app può dichiarare uno o più servizi che verranno pubblicati da Windows. Ogni servizio viene identificato in modo univoco da un UUID.
Attributi e UUID
Ogni servizio, caratteristica e descrittore è definito da un UUID univoco a 128 bit.
Note
Tutte le API Windows usano il termine GUID, ma lo standard Bluetooth definisce questi come UUID. Ai fini dei nostri scopi, questi due termini sono intercambiabili, quindi continueremo a usare il termine UUID.
Se l'attributo è standard e definito dal Bluetooth SIG, avrà anche un ID breve corrispondente a 16 bit (ad esempio, l'UUID del livello della batteria è 00002A19-0000-1000-8000-00805F9B34FB e l'ID breve è 0x2A19). Questi UUID standard possono essere visualizzati in GattServiceUuids e GattCharacteristicUuids.
Se l'app implementa il proprio servizio personalizzato, sarà necessario generare un UUID personalizzato. Questa operazione viene eseguita facilmente in Visual Studio tramite Tools > CreateGuid (usare l'opzione 5 per ottenerla in "xxxxxxxx-xxxx-... xxxx" format). Questo uuid può ora essere usato per dichiarare nuovi servizi locali, caratteristiche o descrittori.
Servizi con restrizioni
I servizi seguenti sono riservati dal sistema e non possono essere pubblicati in questo momento:
- Servizio informazioni sul dispositivo (DIS)
- Generic Attribute Profile Service (GATT)
- Servizio profilo di accesso generico (GAP)
- Servizio di analisi dei parametri (SCP)
Attenzione
Il tentativo di creare un servizio bloccato comporterà la restituzione di BluetoothError.DisabledByPolicy dalla chiamata a CreateAsync.
Attributi generati
I descrittori seguenti vengono generati automaticamente dal sistema, in base ai GattLocalCharacteristicParameters forniti durante la creazione della caratteristica:
- Configurazione caratteristica client (se la caratteristica è contrassegnata come indicabile o notificabile).
- Descrizione utente caratteristica (se la proprietà UserDescription è impostata). Per altre informazioni, vedi la proprietà GattLocalCharacteristicParameters.UserDescription.
- Formato caratteristica (un descrittore per ogni formato di presentazione specificato). Per altre info, vedi la proprietà GattLocalCharacteristicParameters.PresentationFormats.
- Formato aggregazione caratteristica (se è specificato più di un formato di presentazione). GattLocalCharacteristicParameters. Vedere la proprietà PresentationFormats per altre informazioni.
- Proprietà estese della caratteristica (se per la caratteristica è impostato il bit delle proprietà estese).
Note
Il valore del descrittore Proprietà estese è determinato tramite le proprietà di caratteristica ReliableWrites e WritableAuxiliaries.
Attenzione
Il tentativo di creare un descrittore riservato genererà un'eccezione.
Attenzione
Broadcast non è attualmente supportato. Se si specifica GattCharacteristicProperty Broadcast , verrà generata un'eccezione.
Creare la gerarchia dei servizi e delle caratteristiche
Il GattServiceProvider viene utilizzato per creare e pubblicizzare la definizione del servizio primario radice. Ogni servizio richiede un proprio oggetto ServiceProvider che accetta un GUID:
GattServiceProviderResult result = await GattServiceProvider.CreateAsync(uuid);
if (result.Error == BluetoothError.Success)
{
serviceProvider = result.ServiceProvider;
//
}
I servizi primari sono il primo livello dell'albero GATT. I servizi primari contengono caratteristiche e altri servizi (denominati "Inclusi" o servizi secondari).
Popolare ora il servizio con le caratteristiche e i descrittori necessari:
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;
Come illustrato in precedenza, questo è anche un buon punto di riferimento per dichiarare i gestori eventi per le operazioni supportate da ogni caratteristica. Per rispondere correttamente alle richieste, un'app deve definire e impostare un gestore eventi per ogni tipo di richiesta supportato dall'attributo. Se non si registra un gestore, la richiesta verrà completata immediatamente con UnlikelyError dal sistema.
Caratteristiche costanti
In alcuni casi, esistono valori di caratteristica che non cambieranno durante il corso della durata dell'app. In tal caso, è consigliabile dichiarare una caratteristica costante per impedire l'attivazione di app non necessarie:
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;
}
Pubblicare il servizio
Dopo aver definito completamente il servizio, il passaggio successivo consiste nel pubblicare il supporto per il servizio. In questo modo il sistema operativo indica che il servizio deve essere restituito quando i dispositivi remoti eseguono un'individuazione del servizio. Sarà necessario impostare due proprietà - IsDiscoverable e IsConnectable:
GattServiceProviderAdvertisingParameters advParameters = new GattServiceProviderAdvertisingParameters
{
IsDiscoverable = true,
IsConnectable = true
};
serviceProvider.StartAdvertising(advParameters);
-
IsDiscoverable: annuncia il nome descrittivo ai dispositivi remoti nell'annuncio pubblicitario, rendendo individuabile il dispositivo. -
IsConnectable: pubblicizza un pacchetto pubblicitario con possibilità di connessione, utilizzabile nel ruolo di periferica.
Quando un servizio è individuabile e connettibile, il sistema aggiungerà l'Uuid del servizio al pacchetto pubblicitario. Nel pacchetto Annuncio sono presenti solo 31 byte e un UUID a 128 bit ne occupa 16!
Quando un servizio viene pubblicato in primo piano, un'applicazione deve chiamare StopAdvertising quando l'applicazione viene sospesa.
Rispondere alle richieste di lettura e scrittura
Come illustrato in precedenza durante la dichiarazione delle caratteristiche necessarie, GattLocalCharacteristics ha 3 tipi di eventi - ReadRequestede WriteRequestedSubscribedClientsChanged .
Leggi
Quando un dispositivo remoto tenta di leggere un valore da una caratteristica (e non è un valore costante), viene chiamato l'evento ReadRequested . La caratteristica su cui è stato eseguito l’accesso in lettura, nonché args (contenente informazioni sul dispositivo remoto), vengono passati al delegato:
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();
}
Scrittura
Quando un dispositivo remoto tenta di scrivere un valore in una caratteristica, l'evento WriteRequested viene chiamato con i dettagli sul dispositivo remoto, la caratteristica in cui scrivere e il valore stesso:
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();
}
Esistono due tipi di scritture, con e senza risposta. Usare GattWriteOption (una proprietà nell'oggetto GattWriteRequest ) per determinare quale tipo di scrittura sta eseguendo il dispositivo remoto.
Inviare notifiche ai client sottoscritti
Tra le operazioni del server GATT, le notifiche sono le più frequenti e svolgono la funzione critica di inviare dati ai dispositivi remoti. In alcuni casi, è consigliabile inviare una notifica a tutti i client sottoscritti, ma altre volte è possibile scegliere i dispositivi a cui inviare il nuovo valore:
async void NotifyValue()
{
var writer = new DataWriter();
// Populate writer with data
// ...
await notifyCharacteristic.NotifyValueAsync(writer.DetachBuffer());
}
Quando un nuovo dispositivo sottoscrive le notifiche, l'evento SubscribedClientsChanged viene chiamato:
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
L'applicazione può ottenere le dimensioni massime delle notifiche per un determinato client con la MaxNotificationSize proprietà . Tutti i dati maggiori della dimensione massima verranno troncati dal sistema.
Quando si gestisce l'evento GattLocalCharacteristic.SubscribedClientsChanged , è possibile utilizzare il processo descritto di seguito per determinare le informazioni complete sui dispositivi client attualmente sottoscritti:
- Gli argomenti dell'evento
SubscribedClientsChangedsono un oggetto GattLocalCharacteristic . - Accedi alla proprietà GattLocalCharacteristic.SubscribedClients di quell'oggetto, che è una raccolta di oggetti GattSubscribedClient.
- Scorrere la raccolta. Per ogni elemento, eseguire le operazioni seguenti:
- Accedi alla proprietà GattSubscribedClient.Session, che è un oggetto GattSession.
- Accedere alla proprietà GattSession.DeviceId , ovvero un oggetto BluetoothDeviceId .
- Accedere alla proprietà BluetoothDeviceId.Id , ovvero la stringa ID dispositivo.
- Passare la stringa ID dispositivo a BluetoothLEDevice.FromIdAsync per recuperare un oggetto BluetoothLEDevice . È possibile ottenere informazioni complete sul dispositivo da tale oggetto.