Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этом разделе показано, как использовать API-интерфейсы сервера Bluetooth Generic Attribute (GATT) для приложений Windows.
Important
Необходимо объявить функцию Bluetooth в Package.appxmanifest.
<Capabilities> <DeviceCapability Name="bluetooth" /> </Capabilities>
Общие сведения
Windows обычно работает в роли клиента. Тем не менее, возникает множество сценариев, в которых Windows также должна выступать в качестве сервера Bluetooth LE GATT. Почти все сценарии для устройств Интернета вещей, а также большинство кроссплатформенных коммуникаций BLE потребуют Windows быть сервером GATT. Кроме того, отправка уведомлений на близлежащие носимые устройства стала популярным сценарием, который также требует этой технологии.
Серверные операции будут строиться вокруг поставщика услуг и GattLocalCharacteristic. Эти два класса предоставляют функциональные возможности, необходимые для объявления, реализации и предоставления иерархии данных удаленному устройству.
Определение поддерживаемых служб
Ваше приложение может объявить одну или несколько служб, которые будут опубликованы Windows. Каждая служба однозначно определяется идентификатором UUID.
Атрибуты и UUID
Каждая служба, характеристика и дескриптор определяются собственным уникальным 128-разрядным UUID.
Note
Api Windows используют термин GUID, но стандарт Bluetooth определяет их как UUID. В наших целях эти два термина взаимозаменяемы, поэтому мы будем продолжать использовать термин UUID.
Если атрибут является стандартным и определен с помощью bluetooth SIG, он также будет иметь соответствующий 16-разрядный короткий идентификатор (например, идентификатор UUID уровня батареи равен 0000 2A19-000-1000-8000-00805F9B34FB и короткий идентификатор равен 0x2A19). Эти стандартные идентификаторы UUID можно увидеть в GattServiceUuids и GattCharacteristicUuids.
Если приложение реализует собственную пользовательскую службу, необходимо создать пользовательский идентификатор UUID. Это легко сделать в Visual Studio через Tools > CreateGuid (используйте параметр 5, чтобы получить его в формате "xxxxxxxx-xxxx-...xxxx"). Теперь этот uuid можно использовать для объявления новых локальных служб, характеристик или дескрипторов.
Ограниченные службы
Следующие службы зарезервированы системой и не могут быть опубликованы в настоящее время:
- Служба сведений об устройстве (DIS)
- Служба профилей универсальных атрибутов (GATT)
- Универсальная служба профилей доступа (GAP)
- Служба параметров сканирования (SCP)
Предостережение
При попытке создать заблокированную службу вызов CreateAsync вернет BluetoothError.DisabledByPolicy.
Созданные атрибуты
Следующие дескрипторы автоматически создаются системой на основе GattLocalCharacteristicParameters, предоставляемых во время создания характеристик:
- Конфигурация клиентской характеристики (если для характеристики указана поддержка индикации или уведомлений).
- Описание пользователя (если свойство UserDescription задано). Дополнительные сведения см. в свойстве GattLocalCharacteristicParameters.UserDescription.
- Формат характеристик (один дескриптор для каждого указанного формата презентации). Дополнительные сведения см. в свойстве GattLocalCharacteristicParameters.PresentationFormats.
- Формат агрегата характеристик (если задано несколько форматов презентации). Дополнительные сведения см. в свойстве PresentationFormats класса GattLocalCharacteristicParameters.
- Расширенные свойства характеристики (если для характеристики установлен бит расширенных свойств).
Note
Значение дескриптора расширенных свойств определяется на основе свойств характеристики ReliableWrites и WritableAuxiliaries.
Предостережение
Попытка создать зарезервированный дескриптор приведет к исключению.
Предостережение
Broadcast в настоящее время не поддерживается. Указание Broadcast GattCharacteristicProperty приведет к исключению.
Создание иерархии служб и характеристик
GattServiceProvider используется для создания и публикации определения корневого первичного сервиса. Для каждой службы требуется собственный объект ServiceProvider, который принимает идентификатор GUID:
GattServiceProviderResult result = await GattServiceProvider.CreateAsync(uuid);
if (result.Error == BluetoothError.Success)
{
serviceProvider = result.ServiceProvider;
//
}
Основные службы — это верхний уровень дерева GATT. Основные службы содержат характеристики, а также другие службы (называемые включенными или вторичными службами).
Теперь заполните службу необходимыми характеристиками и дескрипторами:
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;
Как показано выше, это также хорошее место для объявления обработчиков событий для операций, поддерживаемых каждой характеристикой. Чтобы правильно реагировать на запросы, приложение должно определить и задать обработчик событий для каждого типа запроса, который поддерживает атрибут. Если не зарегистрировать обработчик, система немедленно завершит запрос с ошибкой UnlikelyError.
Константные характеристики
Иногда существуют характерные значения, которые не изменятся в течение времени существования приложения. В этом случае рекомендуется объявить константную характеристику, чтобы предотвратить ненужную активацию приложения:
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;
}
Опубликовать службу
После полного определения службы следующим шагом является публикация поддержки службы. Это сообщает ОС, что служба должна быть возвращена при выполнении обнаружения служб удаленными устройствами. Необходимо задать два свойства — IsDiscoverable и IsConnectable:
GattServiceProviderAdvertisingParameters advParameters = new GattServiceProviderAdvertisingParameters
{
IsDiscoverable = true,
IsConnectable = true
};
serviceProvider.StartAdvertising(advParameters);
-
IsDiscoverable: объявляет понятное имя удаленным устройствам в объявлении, что делает устройство обнаруживаемым. -
IsConnectable: объявляет подключаемое объявление для использования в роли периферийного устройства.
Если служба может быть обнаружена и к ней можно подключиться, система добавит UUID службы в рекламный пакет. В пакете рекламы есть только 31 байт, а 128-разрядная UUID занимает 16 из них!
При публикации службы на переднем плане приложение должно вызвать StopAdvertising при приостановке приложения.
Реагирование на запросы на чтение и запись
Как видно ранее при объявлении необходимых характеристик, GattLocalCharacteristics имеет 3 типа событий - ReadRequestedWriteRequested и SubscribedClientsChanged.
Чтение
Когда удаленное устройство пытается считать значение характеристики (если это не постоянное значение), вызывается событие ReadRequested. Характеристика, для которой был вызван метод read, а также args (содержащий сведения об удаленном устройстве), передаются делегату:
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();
}
Напишите
Когда удалённое устройство пытается записать значение в характеристику, вызывается событие WriteRequested с информацией об удалённом устройстве, о том, в какую характеристику нужно выполнить запись, и о самом значении:
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();
}
Существует 2 типа операций записи — с ответом и без них. Используйте GattWriteOption (свойство объекта GattWriteRequest ) для определения типа записи удаленного устройства.
Отправляйте уведомления подписанным клиентам
Наиболее частой из операций сервера GATT являются уведомления, которые выполняют критически важную функцию передачи данных удалённым устройствам. Иногда вы хотите уведомить всех подписанных клиентов, но в других случаях может потребоваться выбрать устройства для отправки нового значения:
async void NotifyValue()
{
var writer = new DataWriter();
// Populate writer with data
// ...
await notifyCharacteristic.NotifyValueAsync(writer.DetachBuffer());
}
Когда новое устройство подписывается на уведомления, SubscribedClientsChanged событие вызывается:
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
Приложение может получить максимальный размер уведомления для конкретного клиента с помощью свойства MaxNotificationSize. Все данные, превышающие максимальный размер, будут усечены системой.
При обработке события GattLocalCharacteristic.SubscribedClientsChanged можно использовать описанный ниже процесс, чтобы определить полные сведения о клиентских устройствах, подписанных на данный момент:
- Аргументом события
SubscribedClientsChangedargs является объект GattLocalCharacteristic. - Доступ к свойству GattLocalCharacteristic.SubscribedClients , которое является коллекцией объектов GattSubscribedClient .
- Выполните итерацию по этой коллекции. Для каждого элемента сделайте следующее:
- Доступ к свойству GattSubscribedClient.Session , являющегося объектом GattSession .
- Доступ к свойству GattSession.DeviceId , являющегося объектом BluetoothDeviceId .
- Получите доступ к свойству BluetoothDeviceId.Id , которое является строкой идентификатора устройства.
- Передайте строку идентификатора устройства в BluetoothLEDevice.FromIdAsync , чтобы получить объект BluetoothLEDevice . Вы можете получить полную информацию об устройстве из этого объекта.
Windows developer