Hello World для получения данных с Bluetooth (BLE) устройства через C#

в 11:31, , рубрики: .net, .net core, BLE, bluetooth, Bluetooth 4.0, C#, low energy, Интернет вещей

Добрый день.

Пару недель назад я столкнулся с проблемой, что нормальной и современной статьи по первому получению данных с Bluetooth просто нет. И пришлось повозиться чтобы понять с чего начать и как это вообще осуществить. И чтобы люди не повторяли мои ошибки, вот краткое эссе о том как это можно сделать.

Hello World для получения данных с Bluetooth (BLE) устройства через C# - 1


Но для начала пару пафосных слов, для чего это вообще нужно. Современный мир все больше погружается в идеологию интернет вещей. Сейчас всё друг с другом взаимодействует, холодильник с плитой, утюг с пылесосом и т.д. Много шума подняли из-за ЭКГ в 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

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js