Понадобилась мне программа на windows, которая поддерживает работу с BLE. Конечно же бесплатная. Работа с BLE должна быть следующей:
- Поиск BLE устройств;
- Подключение к выбранному устройству;
- Чтение/запись характеристик устройства;
Написать решил свою программу на языке C# с использованием библиотеки Windows. Сразу оговорюсь, программу писал под свои цели, она не является универсальной. В статье расскажу и покажу, какие методы и свойства использовал для программирование BLE.
Работа с BLE: поиск устройств BLE
Для программирования BLE использую библиотеку Windows. Devices.Bluetooth. Для корректной работы необходимо в using подключить библиотеку следующим образом:
using Windows.Devices.Bluetooth using Windows.Devices.Bluetooth.Advertisement
Теперь мы можем приступить к написанию кода для поиска BLE устройств. Предварительно установим несколько ограничений для поиска.
private void btnSearchBLE_Click(object sender, EventArgs e) { listDevice.Items.Clear(); AdressBLE.Clear(); _devices.Clear(); // Переменная _watcher объявлена глобальной типа BluetoothLEAdvertisementWather if (_watcher == null) _watcher = new BluetoothLEAdvertisementWatcher(); _watcher.Stop(); // отсеиваем устройства по качеству сигнала _watcher.SignalStrengthFilter.InRangeThresholdInDBm = -110; _watcher.SignalStrengthFilter.OutOfRangeThresholdInDBm = -110; // Устанавливаем активный режим поиска _watcher.ScanningMode = BluetoothLEScanningMode.Active; // переопределяем свойство Resived (Что происходит когда устройство обнаружено) _watcher.Received += Wather_Recived; _watcher.Start(); } private async void Wather_Recived(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args) { dev = await BluetoothLEDevice.FromBluetoothAddressAsync(args.BluetoothAddress); if (dev != null) { if (!_devices.ContainsKey(args.BluetoothAddress)) { _devices.Add(args.BluetoothAddress, dev); listDevice.Invoke(new Action(() => { String Name = ""; String Addrr = dev.BluetoothAddress.ToString(); AdressBLE.Add(Addrr); if (dev.Name == null) Name = "None"; else Name = dev.Name; int item = listDevice.Items.IndexOf(Addrr + " " + Name); if (item == -1) listDevice.Items.Add(dev.BluetoothAddress + " " + Name); })); } } }
Собственно. здесь все просто, запускаем поиск устройств BLE в активном режиме и добавляем найденное устройство в Dictionary с ключём в виде мак адреса устройства.

Подключение, чтение служб и характеристик BLE
После того как мы нашли нужное нам устройство, необходимо подключиться и прочитать какие у него есть службы. А так же какие характеристики содержат эти службы.
private async void btnConnectBLE_Click(object sender, EventArgs e) { trView.Nodes.Clear(); dev = await BluetoothLEDevice.FromBluetoothAddressAsync(Convert.ToUInt64(AdressBLE.ElementAt(IndexBLENow))); if (dev != null) { var servisesResult = await dev.GetGattServicesAsync(); if (servisesResult.Status == Windows.Devices.Bluetooth.GenericAttributeProfile.GattCommunicationStatus.Success) { var services = servisesResult.Services; for (int j = 0; j < services.Count;j++) { var service = services[j]; trView.Nodes.Add($"Служба - {service.Uuid}"); var characteristicsResult = await service.GetCharacteristicsAsync(); if (characteristicsResult.Status == Windows.Devices.Bluetooth.GenericAttributeProfile.GattCommunicationStatus.Success) { var characteristics = characteristicsResult.Characteristics; var characteristic = characteristics[0]; for (int i = 0; i < characteristics.Count; i++) { characteristic = characteristics[i]; trView.Nodes[trView.Nodes.Count - 1].Nodes.Add($"Характеристика - {characteristic.Uuid}"); } characteristic.Service.Dispose(); } service.Session.Dispose(); } } }else MessageBox.Show("Не могу подключить устройство!"); }
Демонстрация кода на рисунке ниже.

Из работы программы мы видим, что наше устройство имеет 3 службы. Меня интересовала третья служба с 4 характеристиками. Последняя характеристика была сделана на чтение и запись, остальные только на чтение.
Чтение-запись характеристики
На по следок рассмотрим как читать и записывать характеристики в устройстве BLE.
private async void btnSendtxt_Click(object sender, EventArgs e) { if (value != "") { String guu = value; if (guu.Contains("Характеристика")) { guu = guu.Replace("Характеристика - ", ""); String GuuiServis = "000000ff-0000-1000-8000-00805f9b34fb"; if (dev != null) dev.Dispose(); dev = await BluetoothLEDevice.FromBluetoothAddressAsync(Convert.ToUInt64(AdressBLE.ElementAt(IndexBLENow))); if (dev != null) { var servisesResult = await dev.GetGattServicesForUuidAsync(Guid.Parse(GuuiServis)); if (servisesResult.Status == Windows.Devices.Bluetooth.GenericAttributeProfile.GattCommunicationStatus.Success) { var services = servisesResult.Services; var service = services[0]; try { var characteristicsResult = await service.GetCharacteristicsForUuidAsync(Guid.Parse(guu)); if (characteristicsResult.Status == Windows.Devices.Bluetooth.GenericAttributeProfile.GattCommunicationStatus.Success) { var characteristics = characteristicsResult.Characteristics; for (int i = 0; i < characteristics.Count; i++) { var characteristic = characteristics[i]; if (characteristic.Uuid == Guid.Parse(guu)) { var property = characteristic.CharacteristicProperties; String Data = tbSendTxt.Text; byte[] input = new byte[Data.Length]; input = Encoding.Default.GetBytes(Data); var writer = new DataWriter(); writer.WriteBytes(input); var result = await characteristic.WriteValueAsync(writer.DetachBuffer()); } } } service.Dispose(); } catch { } } } } } }
Вышеприведенный код демонстрирует запись в характеристику. Что бы записать значение в характеристику у нее должно быть свойство записи. Иначе ничего не произойдет.
private async void trView_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) { value = e.Node.Text; if (value != null) { String guu = value; if (guu.Contains("Характеристика")) { String rootNode = e.Node.Parent.Text; guu = guu.Replace("Характеристика - ", ""); GuuiServis = rootNode.Replace("Служба - ", "");//"000000ff-0000-1000-8000-00805f9b34fb"; if (dev != null) { var servisesResult = await dev.GetGattServicesForUuidAsync(Guid.Parse(GuuiServis)); if (servisesResult.Status == Windows.Devices.Bluetooth.GenericAttributeProfile.GattCommunicationStatus.Success) { var services = servisesResult.Services; var service = services[0]; try { var characteristicsResult = await service.GetCharacteristicsAsync(); if (characteristicsResult.Status == Windows.Devices.Bluetooth.GenericAttributeProfile.GattCommunicationStatus.Success) { var characteristics = characteristicsResult.Characteristics; for (int i = 0; i < characteristics.Count; i++) { var characteristic = characteristics[i]; if (characteristic.Uuid == Guid.Parse(guu)) { var result = await characteristic.ReadValueAsync(BluetoothCacheMode.Uncached); if (result.Status == Windows.Devices.Bluetooth.GenericAttributeProfile.GattCommunicationStatus.Success) { var val = DataReader.FromBuffer(result.Value); byte[] input = new byte[val.UnconsumedBufferLength]; val.ReadBytes(input); label1.Text = "Значение характеристики: " + System.Text.Encoding.ASCII.GetString(input); } characteristic.Service.Dispose(); } } } service.Session.Dispose(); characteristicsResult = null; servisesResult = null; } catch { } } } } } }
Представленный выше код реализует следующую логику:
Нажав на характеристику мы получаем ее ID и подключаемся к ней. Затем пытаемся считать то что записано в этой этой характеристике.
Работа с BLE: Закрытие соединения с устройством BLE
private void btnDiscon_Click(object sender, EventArgs e) { if (dev != null) { dev.Dispose(); dev = null; listDevice.Items.Clear(); trView.Nodes.Clear(); AdressBLE.Clear(); _devices.Clear(); GC.Collect(); } }
Что бы закрыть соединение BLE необходимо вызвать метод Dispose, а также отдельно запустить сборщик мусора GC.Collect(). Иначе соединение не будет закрыто.
Скачать весь проект можно по ссылке.
Внимание! Проект проверен на windows 10. В других операционных системах работоспособность не гарантируется.