Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Este tópico demonstra como utilizar as APIs do Servidor Bluetooth Generic Attribute (GATT) para aplicações Windows.
Importante
Tem de declarar a capacidade "bluetooth" em Package.appxmanifest.
<Capabilities> <DeviceCapability Name="bluetooth" /> </Capabilities>
Visão geral
O Windows normalmente opera no papel de cliente. No entanto, surgem muitos cenários que exigem que o Windows funcione também como um servidor Bluetooth LE GATT. Quase todos os cenários para dispositivos IoT, juntamente com a maioria das comunicações BLE multiplataforma, exigirão que o Windows seja um Servidor GATT. Além disso, enviar notificações para dispositivos vestíveis próximos tornou-se um cenário popular que também requer esta tecnologia.
As operações do servidor vão girar em torno do Fornecedor de Serviços e do GattLocalCharacteristic. Estas duas classes fornecerão a funcionalidade necessária para declarar, implementar e expor uma hierarquia de dados a um dispositivo remoto.
Defina os serviços suportados
A sua aplicação pode declarar um ou mais serviços que serão publicados pelo Windows. Cada serviço é identificado de forma única por um UUID.
Atributos e UUIDs
Cada serviço, característica e descritor é definido pelo seu próprio UUID único de 128 bits.
Note
As APIs do Windows usam todas o termo GUID, mas o padrão Bluetooth define-as como UUIDs. Para os nossos propósitos, estes dois termos são intercambiáveis, por isso continuaremos a usar o termo UUID.
Se o atributo for padrão e definido pelo Bluetooth SIG, terá também um ID curto correspondente de 16 bits (por exemplo, o UUID do Nível da Bateria é 0000 2A19-0000-1000-8000-00805F9B34FB e o ID curto é 0x2A19). Estes UUIDs padrão podem ser vistos em GattServiceUuids e GattCharacteristicUuids.
Se a sua aplicação estiver a implementar o seu próprio serviço personalizado, terá de ser gerado um UUID personalizado. Isto é feito facilmente no Visual Studio através de Tools > CreateGuid (use a opção 5 para o obter no formato "xxxxxxxx-xxxx-...xxxx"). Este uuid pode agora ser usado para declarar novos serviços, características ou descritores locais.
Serviços Restritos
Os seguintes Serviços estão reservados pelo sistema e não podem ser publicados neste momento:
- Serviço de Informação do Dispositivo (DIS)
- Serviço de Perfis Genéricos de Atributos (GATT)
- Serviço de Perfis de Acesso Genérico (GAP)
- Serviço de Parâmetros de Digitalização (SCP)
Atenção
Tentar criar um serviço bloqueado resultará no retorno do BluetoothError.DisabledByPolicy da chamada para o CreateAsync.
Atributos Gerados
Os seguintes descritores são gerados automaticamente pelo sistema, com base nos GattLocalCharacteristicParameters fornecidos durante a criação da característica:
- Configuração da Característica do Cliente (se a característica estiver marcada como indicável ou notificável).
- Descrição Característica do Utilizador (se a propriedade UserDescription estiver definida). Consulte a propriedade GattLocalCharacteristicParameters.UserDescription para mais informações.
- Formato Característico (um descritor para cada formato de apresentação especificado). Consulte a propriedade GattLocalCharacteristicParameters.PresentationFormats para mais informações.
- Formato Agregado Característico (se for especificado mais do que um formato de apresentação). GattLocalCharacteristicParameters.Veja a propriedade PresentationFormats para mais informações.
- Características Propriedades Estendidas (se a característica estiver marcada com o bit de propriedades estendidas).
Note
O valor do descritor de Propriedades Estendidas é determinado por meio das propriedades de característica ReliableWrites e WritableAuxiliaries.
Atenção
Tentar criar um descritor reservado resultará numa exceção.
Atenção
Broadcast não é suportado neste momento. Especificar a Broadcast GattCharacteristicProperty resultará numa exceção.
Construir a hierarquia de serviços e características
O GattServiceProvider é usado para criar e divulgar a definição raiz do serviço primário. Cada serviço requer o seu próprio objeto ServiceProvider que aceita um GUID:
GattServiceProviderResult result = await GattServiceProvider.CreateAsync(uuid);
if (result.Error == BluetoothError.Success)
{
serviceProvider = result.ServiceProvider;
//
}
Os serviços primários são o nível superior da árvore GATT. Os serviços primários incluem características bem como outros serviços (designados por 'incluídos' ou serviços secundários).
Agora, preencha o serviço com as características e descritores necessários:
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;
Como mostrado acima, este é também um bom local para declarar manipuladores de eventos para as operações que cada característica suporta. Para responder corretamente aos pedidos, uma aplicação deve definir e definir um gestor de eventos para cada tipo de pedido que o atributo suporta. A falha em registar um handler resultará na conclusão imediata do pedido com UnlikelyError pelo sistema.
Características constantes
Por vezes, existem valores característicos que não mudam ao longo da vida útil da aplicação. Nesse caso, é aconselhável declarar uma característica constante para evitar ativação desnecessária da aplicação:
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;
}
Publicar o serviço
Uma vez que o serviço esteja totalmente definido, o passo seguinte é publicar o suporte para o serviço. Isto informa o sistema operativo de que o serviço deve ser devolvido quando dispositivos remotos realizam uma descoberta de serviço. Terá de definir duas propriedades - IsDiscoverable e IsConnectable:
GattServiceProviderAdvertisingParameters advParameters = new GattServiceProviderAdvertisingParameters
{
IsDiscoverable = true,
IsConnectable = true
};
serviceProvider.StartAdvertising(advParameters);
-
IsDiscoverable: Anuncia o nome amigável a dispositivos remotos no anúncio, tornando o dispositivo detetável. -
IsConnectable: Anuncia um anúncio conectável para uso em função periférica.
Quando um serviço é simultaneamente Detectável e Conectável, o sistema adiciona o Service Uuid ao pacote publicitário. Existem apenas 31 bytes no pacote de Publicidade e um UUID de 128 bits ocupa 16 deles!
Quando um serviço é publicado em primeiro plano, uma aplicação tem de invocar StopAdvertising quando entrar em suspensão.
Responder a pedidos de leitura e escrita
Como se viu anteriormente, ao declarar as características exigidas, os GattLocalCharacteristics têm 3 tipos de eventos - ReadRequested, WriteRequested e SubscribedClientsChanged.
Leitura
Quando um dispositivo remoto tenta ler um valor de uma característica (e não é um valor constante), o ReadRequested evento é chamado. A característica em que a leitura foi chamada, bem como o args (contendo informação sobre o dispositivo remoto), é passada ao delegado:
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();
}
Escreve
Quando um dispositivo remoto tenta escrever um valor numa característica, o WriteRequested evento é chamado com detalhes sobre o dispositivo remoto, qual característica escrever e o próprio valor:
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();
}
Existem 2 tipos de Writes - com e sem resposta. Use GattWriteOption (uma propriedade no GattWriteRequest objeto) para perceber que tipo de escrita o dispositivo remoto está a realizar.
Enviar notificações aos clientes subscritos
A operação mais frequente do Servidor GATT, as notificações desempenham a função crítica de enviar dados para os dispositivos remotos. Por vezes, vai querer notificar todos os clientes subscritos, mas outras vezes pode querer escolher para que dispositivos enviar o novo valor:
async void NotifyValue()
{
var writer = new DataWriter();
// Populate writer with data
// ...
await notifyCharacteristic.NotifyValueAsync(writer.DetachBuffer());
}
Quando um novo dispositivo subscreve para receber notificações, o SubscribedClientsChanged evento é chamado:
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
A sua aplicação pode obter o tamanho máximo da notificação de um determinado cliente através da propriedade MaxNotificationSize. Qualquer dado maior que o tamanho máximo será truncado pelo sistema.
Quando gerir o evento GattLocalCharacteristic.SubscribedClientsChanged , pode usar o processo descrito abaixo para determinar toda a informação sobre os dispositivos clientes atualmente subscritos:
- Os args do evento
SubscribedClientsChangedsão um objeto GattLocalCharacteristic. - Aceda à propriedade GattLocalCharacteristic.SubscribedClients desse objeto, que é uma coleção de objetos GattSubscribedClient .
- Percorra essa coleção. Para cada elemento, faça o seguinte:
- Acede à propriedade GattSubscribedClient.Session , que é um objeto GattSession .
- Acede à propriedade GattSession.DeviceId , que é um objeto BluetoothDeviceId .
- Acede à propriedade BluetoothDeviceId.Id , que é a cadeia ID do dispositivo.
- Passe a string ID do dispositivo para BluetoothLEDevice.FromIdAsync para recuperar um objeto BluetoothLEDevice . Pode obter toda a informação sobre o dispositivo a partir desse objeto.
Windows developer