Client GATT BluetoothBluetooth GATT Client

Questo articolo illustra l'uso delle API client del GATT (Bluetooth Generic Attribute) per le app piattaforma UWP (Universal Windows Platform) (UWP), insieme al codice di esempio per le attività client GATT comuni: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:

  • Eseguire una query per i dispositivi viciniQuery for nearby devices
  • Connetti al dispositivoConnect to device
  • Enumerare i servizi e le caratteristiche supportati del dispositivoEnumerate the supported services and characteristics of the device
  • Lettura e scrittura in una caratteristicaRead and write to a characteristic
  • Sottoscrivere le notifiche quando cambia il valore caratteristicoSubscribe for notifications when characteristic value changes

Importante

È necessario dichiarare la funzionalità "Bluetooth" in Package. appxmanifest.You must declare the "bluetooth" capability in Package.appxmanifest.

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

API importantiImportant APIs

PanoramicaOverview

Gli sviluppatori possono usare le API nello spazio dei nomi Windows. Devices. Bluetooth. GenericAttributeProfile per accedere ai dispositivi Bluetooth le.Developers can use the APIs in the Windows.Devices.Bluetooth.GenericAttributeProfile namespace to access Bluetooth LE devices. I dispositivi Bluetooth LE espongono le proprie funzionalità attraverso una raccolta di:Bluetooth LE devices expose their functionality through a collection of:

  • ServiziServices
  • CaratteristicheCharacteristics
  • DescrittoriDescriptors

I servizi definiscono il contratto funzionale del dispositivo LE e contengono una raccolta di caratteristiche che definiscono il servizio.Services define the functional contract of the LE device and contain a collection of characteristics that define the service. Queste caratteristiche contengono a loro volta descrittori delle caratteristiche.Those characteristics, in turn, contain descriptors that describe the characteristics. Questi tre termini sono noti genericamente come attributi di un dispositivo.These 3 terms are generically known as the attributes of a device.

Le API del GATT Bluetooth LE espongono oggetti e funzioni, anziché l'accesso al trasporto non elaborato.The Bluetooth LE GATT APIs expose objects and functions, rather than access to the raw transport. Le API GATT consentono inoltre agli sviluppatori di lavorare con i dispositivi Bluetooth LE con la possibilità di eseguire le attività seguenti:The GATT APIs also enable developers to work with Bluetooth LE devices with the ability to perform the following tasks:

  • Eseguire l'individuazione degli attributiPerform attribute discovery
  • Lettura e scrittura dei valori di attributoRead and Write attribute values
  • Registrare un callback per un evento ValueChanged caratteristicoRegister a callback for Characteristic ValueChanged event

Per creare un'implementazione utile, uno sviluppatore deve avere una conoscenza preliminare dei servizi GATT e delle caratteristiche che l'applicazione intende utilizzare ed elaborare i valori caratteristici specifici in modo che i dati binari forniti dall'API vengano trasformati in dati utili prima di essere presentati all'utente.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. Le API GATT Bluetooth espongono solo le primitive di base necessarie per comunicare con il dispositivo a basso consumo Bluetooth.The Bluetooth GATT APIs expose only the basic primitives required to communicate with a Bluetooth LE device. Per interpretare i dati, è necessario definire un profilo dell'applicazione, tramite un profilo standard del gruppo SIG (Special Interest Group) Bluetooth o un profilo personalizzato implementato da un fornitore di dispositivo.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. Un profilo crea un contratto di associazione tra l'applicazione e il dispositivo, relativo a cosa rappresentano i dati scambiati e come interpretarli.A profile creates a binding contract between the application and the device, as to what the exchanged data represents and how to interpret it.

Per comodità, il gruppo SIG Bluetooth mantiene a disposizione un elenco di profili pubblici.For convenience the Bluetooth SIG maintains a list of public profiles available.

Eseguire una query per i dispositivi viciniQuery for nearby devices

Sono disponibili due metodi principali per eseguire una query per i dispositivi nelle vicinanze:There are two main methods to query for nearby devices:

  • DeviceWatcher in Windows. Devices. EnumerationDeviceWatcher in Windows.Devices.Enumeration
  • AdvertisementWatcher in Windows. Devices. Bluetooth. AdvertisingAdvertisementWatcher in Windows.Devices.Bluetooth.Advertisement

Il secondo metodo viene discusso a lungo nella documentazione dell' annuncio , quindi non verrà discusso molto qui, ma l'idea di base è trovare l'indirizzo Bluetooth dei dispositivi vicini che soddisfano il particolare filtro pubblicitario.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. Una volta ottenuto l'indirizzo, è possibile chiamare BluetoothLEDevice. FromBluetoothAddressAsync per ottenere un riferimento al dispositivo.Once you have the address, you can call BluetoothLEDevice.FromBluetoothAddressAsync to get a reference to the device.

Tornare ora al metodo DeviceWatcher.Now, back to the DeviceWatcher method. Un dispositivo Bluetooth LE è analogo a qualsiasi altro dispositivo in Windows e può essere sottoposto a query usando le API di enumerazione.A Bluetooth LE device is just like any other device in Windows and can be queried using the Enumeration APIs. Usare la classe DeviceWatcher e passare una stringa di query che specifica i dispositivi da cercare: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();

Una volta avviato il DeviceWatcher, si riceverà DeviceInformation per ogni dispositivo che soddisfa la query nel gestore per l'evento aggiunto per i dispositivi in questione.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. Per informazioni più dettagliate su DeviceWatcher, vedere l'esempio completo su GitHub.For a more detailed look at DeviceWatcher see the complete sample on Github.

Connessione al dispositivoConnecting to the device

Una volta individuato un dispositivo desiderato, usare DeviceInformation.ID per ottenere l'oggetto dispositivo Bluetooth le per il dispositivo in questione: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);
    // ...
}

D'altra parte, se si eliminano tutti i riferimenti a un oggetto BluetoothLEDevice per un dispositivo (e se nessun'altra app nel sistema ha un riferimento al dispositivo), si attiverà una disconnessione automatica dopo un periodo di timeout ridotto.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();

Se l'app deve accedere di nuovo al dispositivo, è sufficiente creare nuovamente l'oggetto dispositivo e accedere a una caratteristica, descritta nella sezione successiva, per attivare la riconnessione del sistema operativo quando necessario.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. Se il dispositivo è vicino, si otterrà l'accesso al dispositivo altrimenti verrà restituito un errore DeviceUnreachable.If the device is nearby, you'll get access to the device otherwise it will return w/ a DeviceUnreachable error.

Nota

La creazione di un oggetto BluetoothLEDevice chiamando solo questo metodo non consente (necessariamente) di avviare una connessione.Creating a BluetoothLEDevice object by calling this method alone doesn't (necessarily) initiate a connection. Per avviare una connessione, impostare GattSession. MaintainConnection su true o chiamare un metodo di individuazione del servizio non memorizzato nella cache su BluetoothLEDeviceoppure eseguire un'operazione di lettura/scrittura sul dispositivo.To 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.

  • Se GattSession. MaintainConnection è impostato su true, il sistema attende per un periodo di tempo illimitato per una connessione e si connetterà quando il dispositivo sarà disponibile.If GattSession.MaintainConnection is set to true, then the system waits indefinitely for a connection, and it will connect when the device is available. Non c'è niente da attendere per l'applicazione, poiché GattSession. MaintainConnection è una proprietà.There's nothing for your application to wait on, since GattSession.MaintainConnection is a property.
  • Per l'individuazione dei servizi e le operazioni di lettura/scrittura in GATT, il sistema attende un tempo limitato ma variabile.For service discovery and read/write operations in GATT, the system waits a finite but variable time. Qualsiasi cosa, da istantanea a una faccenda di minuti.Anything from instantaneous to a matter of minutes. I fattori inclueno il traffico sullo stack e il modo in cui la richiesta è stata accodata.Factors inclue the traffic on the stack, and how queued up the request is. Se non sono presenti altre richieste in sospeso e il dispositivo remoto non è raggiungibile, il sistema attenderà sette (7) secondi prima del timeout. Se sono presenti altre richieste in sospeso, tutte le richieste nella coda possono richiedere sette (7) secondi per l'elaborazione, quindi l'ulteriore utente si trova verso il retro della coda, maggiore sarà il tempo di attesa.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.

Attualmente, non è possibile annullare il processo di connessione.Currently, you can't cancel the connection process.

Enumerazione dei servizi e delle caratteristiche supportatiEnumerating supported services and characteristics

Ora che si dispone di un oggetto BluetoothLEDevice, il passaggio successivo consiste nell'individuare i dati esposti dal dispositivo.Now that you have a BluetoothLEDevice object, the next step is to discover what data the device exposes. Il primo passaggio per eseguire questa operazione consiste nell'eseguire una query per i servizi: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;
    // ...
}

Una volta identificato il servizio di interesse, il passaggio successivo consiste nell'eseguire una query per le caratteristiche.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;
    // ...
}

Il sistema operativo restituisce un elenco di sola lettura degli oggetti GattCharacteristic su cui è possibile eseguire operazioni.The OS returns a ReadOnly list of GattCharacteristic objects that you can then perform operations on.

Eseguire operazioni di lettura/scrittura su una caratteristicaPerform Read/Write operations on a characteristic

La caratteristica è l'unità fondamentale della comunicazione basata su GATT.The characteristic is the fundamental unit of GATT based communication. Contiene un valore che rappresenta una parte di dati distinta nel dispositivo.It contains a value that represents a distinct piece of data on the device. La caratteristica a livello di batteria, ad esempio, ha un valore che rappresenta il livello di batteria del dispositivo.For example, the battery level characteristic has a value that represents the battery level of the device.

Leggere le proprietà caratteristiche per determinare quali operazioni sono supportate: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.
}

Se la lettura è supportata, è possibile leggere il valore: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
}

La scrittura in una caratteristica segue un modello simile: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
}

Suggerimento: DataReader e DataWriter sono indispensabili quando si lavora con i buffer non elaborati che si ottengono da molte delle API Bluetooth.Tip: DataReader and DataWriter are indispensible when working with the raw buffers you get from many of the Bluetooth APIs.

Sottoscrizione per le notificheSubscribing for notifications

Assicurarsi che la caratteristica supporti indicare o notificare (verificare le proprietà caratteristiche per assicurarsi).Make sure the characteristic supports either Indicate or Notify (check the characteristic properties to make sure).

Aside: l'indicazione è considerata più affidabile perché ogni evento di modifica del valore è associato a un riconoscimento dal dispositivo client.Aside: Indicate is considered more reliable because each value changed event is coupled with an acknowledgement from the client device. La notifica è più prevalente perché la maggior parte delle transazioni GATT è piuttosto affidabile.Notify is more prevalent because most GATT transactions would rather conserve power rather than be extremely reliable. In ogni caso, tutte le gestioni vengono gestite a livello di controller, in modo che l'app non venga utilizzata.In any case, all of that is handled at the controller layer so the app does not get involved. Ci riferiamo collettivamente come semplici "notifiche", ma ora che conosci.We'll collectively refer to them as simply "notifications" but now you know.

Prima di ricevere le notifiche, è necessario eseguire due operazioni:There are two things to take care of before getting notifications:

  • Scrivi in descrittore di configurazione caratteristica client (CCCD)Write to Client Characteristic Configuration Descriptor (CCCD)
  • Gestire l'evento caratteristic. ValueChangedHandle the Characteristic.ValueChanged event

La scrittura in CCCD indica al dispositivo server che questo client desidera conoscere ogni volta che viene modificato un particolare valore caratteristico.Writing to the CCCD tells the Server device that this client wants to know each time that particular characteristic value changes. Per eseguire questa operazione:To do this:

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

A questo punto, l'evento ValueChanged di GattCharacteristic verrà chiamato ogni volta che il valore viene modificato nel dispositivo remoto.Now, the GattCharacteristic's ValueChanged event will get called each time the value gets changed on the remote device. A questo proposito, è necessario implementare il gestore: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.
}