Клиент Bluetooth GATT
В этой статье показано, как использовать API клиента Bluetooth Generic Attribute (GATT) для приложений универсальная платформа Windows (UWP).
Внимание
Необходимо объявить функцию Bluetooth в Package.appxmanifest.
<Capabilities> <DeviceCapability Name="bluetooth" /> </Capabilities>
Важные API
Обзор
Разработчики могут использовать API в пространстве имен Windows.Devices.Bluetooth.GenericAttributeProfile для доступа к устройствам Bluetooth LE. Устройства Bluetooth LE предоставляют свои функциональные возможности через коллекцию:
- Службы
- Характеристики
- Дескрипторы
Службы определяют функциональный контракт устройства LE и содержат коллекцию характеристик, определяющих службу. Эти характеристики, в свою очередь, содержат дескрипторы, описывающие характеристики. Эти 3 термины называются атрибутами устройства.
API Bluetooth LE GATT предоставляют объекты и функции, а не доступ к необработанному транспорту. API GATT также позволяют разработчикам работать с устройствами Bluetooth LE с возможностью выполнения следующих задач:
- Выполнение обнаружения атрибутов
- Чтение и запись значений атрибутов
- Регистрация обратного вызова для события Characteristic ValueChanged
Чтобы создать полезную реализацию, разработчик должен иметь предварительные знания о службах GATT и характеристиках, которые приложение намерено использовать и обрабатывать конкретные значения характеристик, таким образом, чтобы двоичные данные, предоставляемые API, преобразовывались в полезные данные перед отправкой пользователю. API Bluetooth GATT предоставляют только основные примитивы, необходимые для взаимодействия с устройством Bluetooth LE. Чтобы интерпретировать данные, необходимо определить профиль приложения либо стандартным профилем Bluetooth SIG, либо пользовательским профилем, реализованным поставщиком устройств. Профиль создает контракт привязки между приложением и устройством, как представляет обмен данными и как его интерпретировать.
Для удобства Bluetooth SIG поддерживает список общедоступных профилей .
Примеры
Полный пример см. в примере Bluetooth Low Energy.
Запрос на близлежащие устройства
Существует два основных метода запроса для близлежащих устройств:
- DeviceWatcher в Windows.Devices.Enumeration
- BluetoothLEAdvertisementWatcher в Windows.Devices.Bluetooth.AdvertisementWatcher
2-й метод подробно рассматривается в документации по объявлению , поэтому здесь не будет обсуждаться много, но основная идея заключается в поиске Bluetooth-адреса близлежащих устройств, удовлетворяющих конкретному фильтру рекламы. После получения адреса можно вызвать BluetoothLEDevice.FromBluetoothAddressAsync , чтобы получить ссылку на устройство.
Теперь вернитесь к методу DeviceWatcher. Устройство Bluetooth LE аналогично любому другому устройству в Windows и может запрашиваться с помощью API перечисления. Используйте класс DeviceWatcher и передайте строку запроса, указывающую устройства для поиска:
// 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();
После запуска DeviceWatcher вы получите DeviceInformation для каждого устройства, которое удовлетворяет запросу в обработчике для события "Добавлено " для устройств, которые задаются. Дополнительные сведения об DeviceWatcher см. в полном примере на сайте Github.
Подключение к устройству
После обнаружения нужного устройства используйте DeviceInformation.Id , чтобы получить объект Устройства Bluetooth LE для устройства под вопросом:
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);
// ...
}
С другой стороны, удаление всех ссылок на объект BluetoothLEDevice для устройства (и если другое приложение в системе не имеет ссылки на устройство) активирует автоматическое отключение после небольшого периода ожидания.
bluetoothLeDevice.Dispose();
Если приложению нужно снова получить доступ к устройству, просто создайте объект устройства и получите доступ к характеристике (описанной в следующем разделе) активирует ОПЕРАЦИОННую систему для повторного подключения при необходимости. Если устройство находится рядом, вы получите доступ к устройству в противном случае он вернет ошибку DeviceUnreachable.
Примечание.
Создание объекта BluetoothLEDevice путем вызова этого метода не обязательно инициирует подключение. Чтобы инициировать подключение, задайте для параметра GattSession.MaintainConnection true
значение или вызовите метод обнаружения некшированных служб в BluetoothLEDevice или выполните операцию чтения и записи на устройстве.
- Если для GattSession.MaintainConnection задано значение true, система ожидает неограниченное время подключения и будет подключаться, когда устройство доступно. Нет ничего для приложения, чтобы ждать, так как GattSession.MaintainConnection является свойством.
- Для операций обнаружения служб и операций чтения и записи в GATT система ожидает конечного, но переменного времени. Что-нибудь от мгновенного до нескольких минут. Факторы включают трафик в стеке, а также способ очереди запроса. Если нет другого ожидающего запроса, а удаленное устройство недоступно, система будет ожидать семь секунд (7) до истечения времени ожидания. Если есть другие ожидающие запросы, то каждое из запросов в очереди может занять семь (7) секунд для обработки, так что дальше ваш находится в стороне очереди, чем дольше вы будете ждать.
В настоящее время невозможно отменить процесс подключения.
Перечисление поддерживаемых служб и характеристик
Теперь, когда у вас есть объект BluetoothLEDevice, следующим шагом является обнаружение данных, предоставляемых устройством. Первым шагом этого является запрос к службам:
GattDeviceServicesResult result = await bluetoothLeDevice.GetGattServicesAsync();
if (result.Status == GattCommunicationStatus.Success)
{
var services = result.Services;
// ...
}
После идентификации службы, интересующей вас, следующий шаг — запрос на характеристики.
GattCharacteristicsResult result = await service.GetCharacteristicsAsync();
if (result.Status == GattCommunicationStatus.Success)
{
var characteristics = result.Characteristics;
// ...
}
ОС возвращает список объектов GattCharacteristic ReadOnly, с которыми можно выполнять операции.
Выполнение операций чтения и записи с характеристикой
Особенностью является основная единица взаимодействия на основе GATT. Он содержит значение, представляющее отдельный фрагмент данных на устройстве. Например, характеристика уровня батареи имеет значение, представляющее уровень батареи устройства.
Прочитайте свойства характеристик, чтобы определить, какие операции поддерживаются:
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.
}
Если поддерживается чтение, можно прочитать это значение:
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
}
Написание характеристик следует аналогичному шаблону:
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
}
Совет
DataReader и DataWriter являются обязательными при работе с необработанными буферами, которые вы получаете от многих API Bluetooth.
Подписка на уведомления
Убедитесь, что характеристика поддерживает значение "Указание" или "Уведомить" (проверьте свойства характеристик, чтобы убедиться в том, что они должны быть уверены).
Указывает, что считается более надежным, так как каждое событие изменения значения связано с подтверждением с клиентского устройства. Уведомление более распространено, так как большинство транзакций GATT скорее экономит мощность, а не очень надежна. В любом случае все это обрабатывается на уровне контроллера, поэтому приложение не участвует. Мы будем совместно ссылаться на них как на "уведомления".
Перед получением уведомлений необходимо позаботиться о двух вещах:
- Запись в дескриптор конфигурации характеристик клиента (CCCD)
- Обработка события Characteristic.ValueChanged
Запись в CCCD сообщает устройству Сервера, что этот клиент хочет знать каждый раз, когда изменяется определенное значение характеристик. Для этого:
GattCommunicationStatus status = await selectedCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(
GattClientCharacteristicConfigurationDescriptorValue.Notify);
if(status == GattCommunicationStatus.Success)
{
// Server has been informed of clients interest.
}
Теперь событие ValueChanged gattCharacteristic будет вызываться каждый раз, когда значение изменяется на удаленном устройстве. Все, что осталось, заключается в реализации обработчика:
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.
}