Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Este tópico demonstra como usar as APIs de servidor GATT (Atributo Genérico Bluetooth) para aplicativos Windows.
Importante
Você deve declarar a funcionalidade "bluetooth" em Package.appxmanifest.
<Capabilities> <DeviceCapability Name="bluetooth" /> </Capabilities>
Overview
Windows geralmente opera na função de cliente. No entanto, ocorrem muitos cenários que exigem que Windows atuem como um servidor BLUETOOTH LE GATT também. Quase todos os cenários para dispositivos IoT, juntamente com a maioria das comunicações BLE multiplataforma, exigirão que Windows seja um servidor GATT. Além disso, o envio de notificações para dispositivos vestíveis próximos tornou-se um cenário popular que exige essa tecnologia também.
As operações de servidor giram em torno do Provedor de Serviços e do GattLocalCharacteristic. Essas duas classes fornecerão a funcionalidade necessária para declarar, implementar e expor uma hierarquia de dados a um dispositivo remoto.
Definir os serviços com suporte
Seu aplicativo pode declarar um ou mais serviços que serão publicados pelo Windows. Cada serviço é identificado exclusivamente por uma UUID.
Atributos e UUIDs
Cada serviço, característica e descritor é definido por sua própria UUID exclusiva de 128 bits.
Note
As APIs de Windows usam o termo GUID, mas o padrão Bluetooth os define como UUIDs. Para nossos fins, esses dois termos são intercambiáveis, portanto, continuaremos a usar o termo UUID.
Se o atributo for padrão e definido pelo Bluetooth SIG, ele também terá um ID curto correspondente de 16 bits (por exemplo, o UUID do nível da bateria é 00002A19-0000-1000-8000-00805F9B34FB e o ID curto é 0x2A19). Esses UUIDs padrão podem ser vistos em GattServiceUuids e GattCharacteristicUuids.
Se o aplicativo estiver implementando seu próprio serviço personalizado, uma UUID personalizada terá que ser gerada. Isso pode ser feito facilmente no Visual Studio por meio de Tools > CreateGuid (use a opção 5 para obtê-lo no formato "xxxxxxxx-xxxx-...xxxx"). Essa uuid agora pode ser usada para declarar novos serviços locais, características ou descritores.
Serviços Restritos
Os serviços a seguir são reservados pelo sistema e não podem ser publicados no momento:
- Dis (Serviço de Informações do Dispositivo)
- GATT (Serviço de Perfil de Atributo Genérico)
- Serviço de Perfil de Acesso Genérico (GAP)
- SCP (Serviço de Parâmetros de Verificação)
Cuidado
A tentativa de criar um serviço bloqueado resultará no retorno de BluetoothError.DisabledByPolicy da chamada para CreateAsync.
Atributos gerados
Os descritores a seguir 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 do usuário característica (se a propriedade UserDescription estiver definida). Consulte a propriedade GattLocalCharacteristicParameters.UserDescription para obter mais informações.
- Formato de característica (um descritor para cada formato de apresentação especificado). Consulte a propriedade GattLocalCharacteristicParameters.PresentationFormats para obter mais informações.
- Formato de agregação característica (se mais de um formato de apresentação for especificado). GattLocalCharacteristicParameters. Consulte a propriedade PresentationFormats para obter mais informações.
- Propriedades estendidas da característica (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 ReliableWrites e WritableAuxiliaries da característica.
Cuidado
A tentativa de criar um descritor reservado resultará em uma exceção.
Cuidado
Broadcast não é suportado neste momento. Especificar o Broadcast GattCharacteristicProperty resultará em uma exceção.
Criar a hierarquia de serviços e características
O GattServiceProvider é usado para criar e anunciar a definição de serviço primário raiz. Cada serviço requer seu próprio objeto ServiceProvider que recebe 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 contêm características, bem como outros serviços (chamados de 'Incluídos' ou serviços secundários).
Agora, preencha o serviço com as características e os 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;
Conforme mostrado acima, esse também é um bom lugar para declarar manipuladores de eventos para as operações que cada característica dá suporte. Para responder às solicitações corretamente, um aplicativo deve definir e definir um manipulador de eventos para cada tipo de solicitação compatível com o atributo. Não registrar um manipulador fará com que a solicitação seja concluída imediatamente pelo sistema com UnlikelyError.
Características constantes
Às vezes, há valores características que não serão alterados durante o tempo de vida do aplicativo. Nesse caso, é aconselhável declarar uma característica constante para evitar a ativação desnecessária do aplicativo:
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
Depois que o serviço tiver sido totalmente definido, a próxima etapa é publicar o suporte para o serviço. Isso informa ao sistema operacional que o serviço deve ser retornado quando dispositivos remotos executam uma descoberta de serviço. Você terá que definir duas propriedades – IsDiscoverable e IsConnectable:
GattServiceProviderAdvertisingParameters advParameters = new GattServiceProviderAdvertisingParameters
{
IsDiscoverable = true,
IsConnectable = true
};
serviceProvider.StartAdvertising(advParameters);
-
IsDiscoverable: anuncia o nome amigável para dispositivos remotos no anúncio, tornando o dispositivo detectável. -
IsConnectable: anuncia um anúncio conectável para uso na função periférica.
Quando um serviço é detectável e conectável, o sistema adicionará a Uuid de Serviço ao pacote de anúncio. Há apenas 31 bytes no pacote de anúncio e uma UUID de 128 bits ocupa 16 deles!
Quando um serviço é publicado em primeiro plano, um aplicativo deve chamar StopAdvertising quando o aplicativo é suspenso.
Responder a solicitações de leitura e gravação
Como visto anteriormente ao declarar as características necessárias, GattLocalCharacteristics tem 3 tipos de eventos – ReadRequestedWriteRequested 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 na qual a leitura foi chamada, bem como os args (contendo informações sobre o dispositivo remoto), são passados para o 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();
}
Escrever
Quando um dispositivo remoto tenta gravar um valor em uma característica, o WriteRequested evento é chamado com detalhes sobre o dispositivo remoto, qual característica gravar 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();
}
Há 2 tipos de operações de gravação: com e sem resposta. Use GattWriteOption (uma propriedade no GattWriteRequest objeto) para descobrir qual tipo de gravação o dispositivo remoto está executando.
Enviar notificações para clientes inscritos
As notificações são a operação mais frequente do servidor GATT e desempenham a função crítica de enviar dados aos dispositivos remotos. Às vezes, você vai querer notificar todos os clientes inscritos, mas outras vezes você pode querer escolher para quais 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 se inscreve para receber notificações, o evento SubscribedClientsChanged é acionado:
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
Seu aplicativo pode obter o tamanho máximo de notificação para um cliente específico com a MaxNotificationSize propriedade. Qualquer dado maior que o tamanho máximo será truncado pelo sistema.
Ao manipular o evento GattLocalCharacteristic.SubscribedClientsChanged , você pode usar o processo descrito abaixo para determinar informações completas sobre os dispositivos cliente atualmente inscritos:
- O args do
SubscribedClientsChangedevento é um objeto GattLocalCharacteristic . - Acesse a propriedade GattLocalCharacteristic.SubscribedClients desse objeto, que é uma coleção de objetos GattSubscribedClient .
- Percorra essa coleção. Para cada elemento, faça o seguinte:
- Acesse a propriedade GattSubscribedClient.Session , que é um objeto GattSession .
- Acesse a propriedade GattSession.DeviceId , que é um objeto BluetoothDeviceId .
- Acesse a propriedade BluetoothDeviceId.Id , que é a cadeia de caracteres de ID do dispositivo.
- Passe a cadeia de caracteres de ID do dispositivo para BluetoothLEDevice.FromIdAsync para recuperar um objeto BluetoothLEDevice . Você pode obter informações completas sobre o dispositivo a partir daquele objeto.
Windows developer