Добрый день.
Пару недель назад я столкнулся с проблемой, что нормальной и современной статьи по первому получению данных с Bluetooth просто нет. И пришлось повозиться чтобы понять с чего начать и как это вообще осуществить. И чтобы люди не повторяли мои ошибки, вот краткое эссе о том как это можно сделать.
Но для начала пару пафосных слов, для чего это вообще нужно. Современный мир все больше погружается в идеологию интернет вещей. Сейчас всё друг с другом взаимодействует, холодильник с плитой, утюг с пылесосом и т.д. Много шума подняли из-за ЭКГ в Apple Watch, но современные тонометры, пульсометры, термометры уже давно умеют передавать данные через Bluetooth. И вот это всё нужно как-то соединить в единую сеть. И ключевым элементом в этой сети является, как ни крути, компьютер. В связи с этим возникла задача получать данные с определённого устройства через Bluetooth.
Начну с того, что уже у нас было и что усугубило поиск решения. А у нас было приложение написанное на .Net Core. В чем суть приложения неважно, для простоты будем считать, что у нас просто консоль на .Net Core. Ну а устройство буду называть литерой N.
Первые попытки найти что-то работающее с Bluetooth через C# приведут к библиотеке 32feet.
В NuGet пакетах она звучит так 32feet.NET.
И она, кстати, в своей последней продуктовой редакции, даже находит устройства Bluetooth, но не стандарта BLE [как выяснилось гораздо позже]. Например тот же OnePlus 5T стабильно отыскивался, но необходимое устройство N нет. Параллельно с этим удалось отыскать и официальный ответ автора, что его библиотека с BLE не взаимодействует в принципе, и даже нет смысла пробовать. Хотя на Github и есть предварительная версия InTheHand.Devices.Bluetooth, которая должна поддерживать BLE, но в ней так много все подменено, а документации вовсе никакой нет, что даже скомпилировать проект с идеями взятыми с 32feet.NET не сложилось.
Новые изыскания привели меня к более стандартным решениям, а именно к Universal Windows Platform (UWP). При разработке данной платформы, Microsoft, охваченная идеей универсальности и единым приложением для компьютера и телефона, постаралась и сделала взаимодействие с Bluetooth. И вот здесь как раз всё хорошо работает, но… У нас проект на .Net Core… И этим уже ничего не поделаешь.
Сразу скажу, что решения по взаимодействию библиотек UWP с .Net Core отыскать не удалось и проект пришлось переключать на 4.7.1., благо это не сложно. Хотя были мысли как оставить проект на .Net Core и например сделать отдельный Windows сервис с передачей данных по именованный каналам (named pipe) или поднять WCF сервис и с ним уже наладить взаимодействие, но в нашем случае это не имело практического смысла.
Так что в итоге мы имеем перед стартом:
- Проект на 4.7.1.
- Win10 обновлен до версии Version 10.0.17134 Build 17134.
Для начала нужно подключить пару библиотек ручками, а именно
- «Windows от Universal Windows Platform»
C:Program Files (x86)Windows Kits10UnionMetadata10.0.17134.0Windows.winmd - «System.Runtime.WindowsRuntime»
C:Program Files (x86)Reference AssembliesMicrosoftFramework.NETCorev4.5System.Runtime.WindowsRuntime.dll
И собственно всё, далее сама работа с устройством идет по документация без проблем.
Взаимодействовать с BLE осуществляется через класс BluetoothLEAdvertisementWatcher
Но нужно не забывать, что не имея API самого устройства что-то толковое сделать не получиться.
Вот мой пример кода, как получить данные с устройства.
Данный код рассчитан на то, что устройство уже добавлено (соединено).
public class BluetoothObserver
{
BluetoothLEAdvertisementWatcher Watcher { get; set; }
public void Start()
{
Watcher = new BluetoothLEAdvertisementWatcher()
{
ScanningMode = BluetoothLEScanningMode.Active
};
Watcher.Received += Watcher_Received;
Watcher.Stopped += Watcher_Stopped;
Watcher.Start();
}
private bool isFindDevice { get; set; } = false;
private async void Watcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
{
if (isFindDevice)
return;
if (args.Advertisement.LocalName.Contains("deviceName"))
{
isFindDevice = true;
BluetoothLEDevice bluetoothLeDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(args.BluetoothAddress);
GattDeviceServicesResult result = await bluetoothLeDevice.GetGattServicesAsync();
if (result.Status == GattCommunicationStatus.Success)
{
var services = result.Services;
foreach (var service in services)
{
if (!service.Uuid.ToString().StartsWith("serviceName"))
{
continue;
}
GattCharacteristicsResult characteristicsResult = await service.GetCharacteristicsAsync();
if (characteristicsResult.Status == GattCommunicationStatus.Success)
{
var characteristics = characteristicsResult.Characteristics;
foreach (var characteristic in characteristics)
{
if (!characteristic.Uuid.ToString().StartsWith("characteristicName"))
{
continue;
}
GattCharacteristicProperties properties = characteristic.CharacteristicProperties;
if (properties.HasFlag(GattCharacteristicProperties.Indicate))
{
characteristic.ValueChanged += Characteristic_ValueChanged;
GattWriteResult status = await characteristic.WriteClientCharacteristicConfigurationDescriptorWithResultAsync(GattClientCharacteristicConfigurationDescriptorValue.Indicate);
return;
}
if (properties.HasFlag(GattCharacteristicProperties.Read))
{
GattReadResult gattResult = await characteristic.ReadValueAsync();
if (gattResult.Status == GattCommunicationStatus.Success)
{
var reader = DataReader.FromBuffer(gattResult.Value);
byte[] input = new byte[reader.UnconsumedBufferLength];
reader.ReadBytes(input);
//Читаем input
}
}
}
}
}
}
}
}
private void Characteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
{
var reader = DataReader.FromBuffer(args.CharacteristicValue);
byte[] input = new byte[reader.UnconsumedBufferLength];
reader.ReadBytes(input);
//Читаем input
}
private void Watcher_Stopped(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementWatcherStoppedEventArgs args)
{
;
}
}
Спасибо за внимание.
Автор: RussianDragon