C#和低功耗蓝牙BLE通讯

唠嗑一夏 2024-07-23 10:05:05 阅读 88

@TOC

前言

以前都是用手机APP和蓝牙设备通信,现在在一个项目中需要更改为用电脑端上位机与蓝牙通讯的需求,在此记录自己走过的坑和积攒的经验。

特别感谢

未曾接触过的东西,首先就是在网上搜索资料,站在巨人的肩膀上好办事。在此特别感谢以下几个博客,就是参考大神们的博客写出来的。

1 C# ble 4.0 低能耗 蓝牙交互 : https://blog.csdn.net/code_long/article/details/105636398

这篇文章给出了完整的示例代码,也有70+的讨论,对一些搜索缓慢的问题做了修复。以及一些dll引用的问题和路径。

2 Win10 平台C#与低功耗蓝牙BLE设备通信案例 https://blog.csdn.net/shengfakun1234/article/details/110928783

第二篇文章对于获取不到蓝牙服务特征列表的问题进行了修复

3 MSDN有关蓝牙的介绍

1 通用 Windows 平台应用中的低能耗蓝牙: https://learn.microsoft.com/zh-cn/windows/uwp/devices-sensors/bluetooth-low-energy-overview

2 DeviceInformation.CreateWatcher 方法: https://learn.microsoft.com/zh-cn/uwp/api/windows.devices.enumeration.deviceinformation.createwatcher?view=winrt-22621

3 Bluetooth 硬件: https://learn.microsoft.com/zh-cn/windows-hardware/design/component-guidelines/bluetooth?redirectedfrom=MSDN

4 蓝牙调试助手 https://github.com/shijianoo/BluetoothLEAssistant/blob/master/%E8%93%9D%E7%89%99%E8%B0%83%E8%AF%95%E5%8A%A9%E6%89%8B/ViewModel/WatcherViewModel.cs

踩过的坑

1 使用的dll

需要注意的是我们使用的低功耗蓝牙BLE,如果使用一些库32feet.NETInTheHand等,是搜索不到低功耗蓝牙的,只能搜索到经典蓝牙。

我们需要用MSDN自带的库:System.Runtime.WindowsRuntime.dllWindows.winmd。路径如下,自己安装在不同磁盘路径可能稍微有点不同:

C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETCore\v4.5\System.Runtime.WindowsRuntime.dll

C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.18362.0\Windows.winmd

2 上位机运行环境

必须是win10系统, win7系统不支持BLE通信,win8没有尝试过。 笔记本自带蓝牙,台式电脑没有蓝牙,可以买个蓝牙适配器插在电脑上用。

3 如何过滤蓝牙

现象是这样子的:第一次搜索到蓝牙A之后,关闭蓝牙A的电源,再次搜索还能搜索到蓝牙A,这是不科学的。 https://www.icode9.com/content-3-288234.html 同一个问题,它这个答案我这儿也不适用,感兴趣的可以看看。

这种不可学的情况是我在使用DeviceWatcher类搜索蓝牙的时候发现的,无论怎么设置过滤条件,都不起作用。

“System.Devices.Aep.DeviceAddress”,

“System.Devices.Aep.IsConnected”,

“System.Devices.Aep.Bluetooth.Le.IsConnectable” ,

“System.Devices.Aep.SignalStrength”,

“System.Devices.Aep.IsPresent”

最后是换了BluetoothLEAdvertisementWatcher类去搜索蓝牙,就解决了上述问题。参考这篇博文Win10 平台C#与低功耗蓝牙BLE设备通信案例的蓝牙搜索部分即可。

4 搜索不到蓝牙的服务和特征值

这一种方法在我电脑上是搜索不到服务的。

/// <summary>

/// 获取蓝牙服务

/// </summary>

public async void FindService()

{

var GattServices = this.CurrentDevice.GattServices;

foreach (GattDeviceService ser in GattServices)

{

this.GattDeviceServiceAdded(ser);

}

}

改成下面的这种写法才能获取服务,特征值也是一样的道理。

/// <summary>

/// 获取蓝牙服务

/// </summary>

public async void FindService()

{

this.CurrentDevice.GetGattServicesAsync().Completed = async (asyncInfo, asyncStatu) =>

{

if(asyncStatu == AsyncStatus.Completed)

{

var sevices = asyncInfo.GetResults().Services;

foreach(GattDeviceService ser in sevices)

{

this.GattDeviceServiceAdded(ser);

}

}

};

}

5 如何收发蓝牙数据

如果还是刚刚接触到蓝牙,什么都不懂的,可以先看看蓝牙通讯协议,可参考该博文:经典蓝牙与低功耗蓝牙BLE开发基础知识:服务、特征、属性、UUIDhttps://blog.csdn.net/weixin_51484460/article/details/124906595

简单来说就是,Service是服务,Characteristic则是特征值。一般来说,蓝牙里面有多个Service,一个Service里面包括多个Characteristic。

一个蓝牙协议里面包含的Service和Characteristic是比较多的 ,那么这么多的同名属性用什么来区分呢?

就是UUID,每个Service或者Characteristic都有一个 128 bit 的UUID来标识。

Service可以理解为一个功能集合,而Characteristic比较重要,蓝牙设备正是通过Characteristic来进行设备间的交互的,这些Characteristic又包含一些属性Property,如读、写、订阅等操作。

我们得按照自己的需求更改SetOpteron(GattCharacteristic gattCharacteristic)这个函数,而不能照搬这个函数。

蓝牙包含哪些服务,服务下有含有哪些特征值,这个特征值的属性是可读,可写还是可订阅,这些可以通过蓝牙厂商获取,或如果是自己公司开发的话,那问相关的开发人员会更清楚。

效果图

在这里插入图片描述

蓝牙类的代码

<code>using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using Windows.Devices.Bluetooth;

using Windows.Devices.Bluetooth.Advertisement;

using Windows.Devices.Bluetooth.GenericAttributeProfile;

using Windows.Devices.Enumeration;

using Windows.Foundation;

using Windows.Security.Cryptography;

namespace Bluetoolth

{

public enum MsgType

{

NotifyTxt,

BleDevice,

BleSendData,

BleRecData,

}

public class BleCore

{

private bool asyncLock = false;

/// <summary>

/// 搜索蓝牙设备对象

/// </summary>

private DeviceWatcher deviceWatcher;

/// <summary>

/// 当前连接的服务

/// </summary>

public GattDeviceService CurrentService { get; set; }

/// <summary>

/// 当前连接的蓝牙设备

/// </summary>

public BluetoothLEDevice CurrentDevice { get; set; }

/// <summary>

/// 写特征对象

/// </summary>

public GattCharacteristic CurrentWriteCharacteristic { get; set; }

/// <summary>

/// 写名称特征对象

/// </summary>

public GattCharacteristic CurrentNameCharacteristic { get; set; }

/// <summary>

/// 通知特征对象

/// </summary>

public GattCharacteristic CurrentNotifyCharacteristic { get; set; }

/// <summary>

/// 特性通知类型通知启用

/// </summary>

private const GattClientCharacteristicConfigurationDescriptorValue

CHARACTERSITIC_NOTIFICATION_TYPE = GattClientCharacteristicConfigurationDescriptorValue.Notify;

/// <summary>

/// 存储检测到的设备

/// </summary>

private List<BluetoothLEDevice> DevicesList = new List<BluetoothLEDevice>();

/// <summary>

/// 定义搜索蓝牙设备委托

/// </summary>

/// <param name="type"></param>code>

/// <param name="bluetoothLEDevice"></param>code>

public delegate void DevicewatcherChangedEvent(MsgType type, BluetoothLEDevice bluetoothLEDevice);

/// <summary>

/// 搜索蓝牙事件

/// </summary>

public event DevicewatcherChangedEvent DevicewatcherChanged;

/// <summary>

/// 获取服务委托

/// </summary>

/// <param name="gattDeviceService"></param>code>

public delegate void GattDeviceServiceAddedEvent(GattDeviceService gattDeviceService);

/// <summary>

/// 获取服务事件

/// </summary>

public event GattDeviceServiceAddedEvent GattDeviceServiceAdded;

/// <summary>

/// 获取特征委托

/// </summary>

/// <param name="gattCharacteristic"></param>code>

public delegate void CharacteristicAddedEvent(GattCharacteristic gattCharacteristic);

/// <summary>

/// 获取特征事件

/// </summary>

public event CharacteristicAddedEvent CharacteristicAdded;

/// <summary>

/// 提示信息委托

/// </summary>

/// <param name="type"></param>code>

/// <param name="message"></param>code>

/// <param name="data"></param>code>

public delegate void MessageChangedEvent(MsgType type, string message, byte[] data = null);

/// <summary>

/// 提示信息事件

/// </summary>

public event MessageChangedEvent MessageChanged;

/// <summary>

/// 当前连接蓝牙的Mac

/// </summary>

private string CurrentDeviceMAC { get; set; }

public BleCore()

{

}

/// <summary>

/// 搜索蓝牙设备 方法1

/// </summary>

public void StartBleDevicewatcher_1()

{

string[] requestedProperties = {

"System.Devices.Aep.DeviceAddress",

"System.Devices.Aep.IsConnected",

"System.Devices.Aep.Bluetooth.Le.IsConnectable" ,

"System.Devices.Aep.SignalStrength",

"System.Devices.Aep.IsPresent"

};

string aqsAllBluetoothLEDevices = "(System.Devices.Aep.ProtocolId:=\"{bb7bb05e-5972-42b5-94fc-76eaa7084d49}\")";

string Selector = "System.Devices.Aep.ProtocolId:=\"{bb7bb05e-5972-42b5-94fc-76eaa7084d49}\"";

string selector = "(" + Selector + ")" + " AND (System.Devices.Aep.CanPair:=System.StructuredQueryType.Boolean#True OR System.Devices.Aep.IsPaired:=System.StructuredQueryType.Boolean#True)";

this.deviceWatcher =

DeviceInformation.CreateWatcher(

// aqsAllBluetoothLEDevices,

// BluetoothLEDevice.GetDeviceSelectorFromPairingState(false),

selector,

requestedProperties,

DeviceInformationKind.AssociationEndpoint);

//在监控之前注册事件

this.deviceWatcher.Added += DeviceWatcher_Added;

this.deviceWatcher.Stopped += DeviceWatcher_Stopped;

this.deviceWatcher.Updated += DeviceWatcher_Updated; ;

this.deviceWatcher.Start();

string msg = "自动发现设备中..";

this.MessageChanged(MsgType.NotifyTxt, msg);

}

BluetoothLEAdvertisementWatcher watcher;

/// <summary>

/// 搜索蓝牙设备 方法2

/// </summary>

public void StartBleDevicewatcher()

{

watcher = new BluetoothLEAdvertisementWatcher();

watcher.ScanningMode = BluetoothLEScanningMode.Active;

// only activate the watcher when we're recieving values >= -80

watcher.SignalStrengthFilter.InRangeThresholdInDBm = -80;

// stop watching if the value drops below -90 (user walked away)

watcher.SignalStrengthFilter.OutOfRangeThresholdInDBm = -90;

// register callback for when we see an advertisements

watcher.Received += OnAdvertisementReceived;

watcher.Stopped += Watcher_Stopped;

// wait 5 seconds to make sure the device is really out of range

watcher.SignalStrengthFilter.OutOfRangeTimeout = TimeSpan.FromMilliseconds(5000);

watcher.SignalStrengthFilter.SamplingInterval = TimeSpan.FromMilliseconds(2000);

// starting watching for advertisements

watcher.Start();

string msg = "自动发现设备中..";

this.MessageChanged(MsgType.NotifyTxt, msg);

}

private void Watcher_Stopped(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementWatcherStoppedEventArgs args)

{

DevicesList.Clear();

string msg = "自动发现设备停止";

this.MessageChanged(MsgType.NotifyTxt, msg);

}

private void OnAdvertisementReceived(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs eventArgs)

{

BluetoothLEDevice.FromBluetoothAddressAsync(eventArgs.BluetoothAddress).Completed = async (asyncInfo, asyncStatus) =>

{

if (asyncStatus == AsyncStatus.Completed)

{

if (asyncInfo.GetResults() == null)

{

//this.MessAgeChanged(MsgType.NotifyTxt, "没有得到结果集");

}

else

{

BluetoothLEDevice currentDevice = asyncInfo.GetResults();

if (currentDevice.Name.StartsWith("Bluetooth"))

{

return;

}

Boolean contain = false;

foreach (BluetoothLEDevice device in DevicesList)//过滤重复的设备

{

if (device.DeviceId == currentDevice.DeviceId)

{

contain = true;

}

}

if (!contain)

{

byte[] _Bytes1 = BitConverter.GetBytes(currentDevice.BluetoothAddress);

Array.Reverse(_Bytes1);

this.DevicesList.Add(currentDevice);

this.MessageChanged(MsgType.NotifyTxt, "发现设备:" + currentDevice.Name + " address:" + BitConverter.ToString(_Bytes1, 2, 6).Replace('-', ':').ToLower());

this.DevicewatcherChanged(MsgType.BleDevice, currentDevice);

}

}

}

};

}

/// <summary>

/// 停止搜索

/// </summary>

public void StopBleDeviceWatcher()

{

if(deviceWatcher != null)

this.deviceWatcher.Stop();

if (watcher != null && watcher.Status == BluetoothLEAdvertisementWatcherStatus.Started)

watcher.Stop();

}

/// <summary>

/// 获取发现的蓝牙设备

/// </summary>

/// <param name="sender"></param>code>

/// <param name="args"></param>code>

private void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation args)

{

this.MessageChanged(MsgType.NotifyTxt, "发现设备:" + args.Id);

this.Matching(args.Id, args);

}

private void DeviceWatcher_Updated(DeviceWatcher sender, DeviceInformationUpdate args)

{

}

/// <summary>

/// 停止搜索蓝牙设备

/// </summary>

/// <param name="sender"></param>code>

/// <param name="args"></param>code>

private void DeviceWatcher_Stopped(DeviceWatcher sender, object args)

{

string msg = "自动发现设备停止";

this.MessageChanged(MsgType.NotifyTxt, msg);

}

/// <summary>

/// 匹配

/// </summary>

/// <param name="device"></param>code>

public void StartMatching(BluetoothLEDevice device)

{

this.CurrentDevice = device;

}

/// <summary>

/// 获取蓝牙服务

/// </summary>

public async void FindService()

{

/*var gattServices = this.CurrentDevice.GattServices;

foreach(GattDeviceService ser in gattServices)

{

this.GattDeviceServiceAdded(ser);

}*/

this.CurrentDevice.GetGattServicesAsync().Completed = async (asyncInfo, asyncStatu) =>

{

if(asyncStatu == AsyncStatus.Completed)

{

var sevices = asyncInfo.GetResults().Services;

foreach(GattDeviceService ser in sevices)

{

this.GattDeviceServiceAdded(ser);

// FindCharacteristic(ser);

}

}

};

}

public async void FindCharacteristic(GattDeviceService gattDeviceService)

{

/* this.CurrentService = gattDeviceService;

foreach(var c in gattDeviceService.GetAllCharacteristics())

{

this.CharacteristicAdded(c);

}*/

gattDeviceService.GetCharacteristicsAsync().Completed = async (asyncInfo, asyncStatu) =>

{

if (asyncStatu == AsyncStatus.Completed)

{

var chara = asyncInfo.GetResults().Characteristics;

foreach (GattCharacteristic c in chara)

{

this.CharacteristicAdded(c);

}

}

};

}

/// <summary>

/// 获取操作

/// </summary>

/// <param name="gattCharacteristic"></param>code>

/// <returns></returns>

public async Task SetOpteron(GattCharacteristic gattCharacteristic)

{

if(gattCharacteristic.CharacteristicProperties == GattCharacteristicProperties.WriteWithoutResponse)

{

this.CurrentWriteCharacteristic = gattCharacteristic;

}

if (gattCharacteristic.CharacteristicProperties == GattCharacteristicProperties.Notify)

{

this.CurrentNotifyCharacteristic = gattCharacteristic;

this.CurrentNotifyCharacteristic.ProtectionLevel = GattProtectionLevel.Plain;

this.CurrentNotifyCharacteristic.ValueChanged += Characteristic_ValueChanged;

await this.EnableNotifications(CurrentNotifyCharacteristic);

}

if (gattCharacteristic.CharacteristicProperties == ( GattCharacteristicProperties.Read | GattCharacteristicProperties.Write))

{

this.CurrentNameCharacteristic = gattCharacteristic;

}

this.Connect();

}

private async Task Connect()

{

byte[] _Bytes1 = BitConverter.GetBytes(this.CurrentDevice.BluetoothAddress);

Array.Reverse(_Bytes1);

this.CurrentDeviceMAC = BitConverter.ToString(_Bytes1, 2, 6).Replace('-', ':').ToLower();

string msg = "正在连接设备<" + this.CurrentDeviceMAC + "> ..";

this.MessageChanged(MsgType.NotifyTxt, msg);

this.CurrentDevice.ConnectionStatusChanged += CurrentDevice_ConnectionStatusChanged;

}

private void CurrentDevice_ConnectionStatusChanged(BluetoothLEDevice sender, object args)

{

if(sender.ConnectionStatus == BluetoothConnectionStatus.Disconnected &&

CurrentDeviceMAC != null)

{

string msg = "设备已断开,自动重连";

MessageChanged(MsgType.NotifyTxt, msg);

if(!asyncLock)

{

asyncLock = true;

this.CurrentDevice.Dispose();

this.CurrentDevice = null;

this.CurrentNotifyCharacteristic = null;

this.CurrentWriteCharacteristic = null;

SelectDeviceFromIdAsync(CurrentDeviceMAC);

}

}else

{

string msg = "设备已连接";

MessageChanged(MsgType.NotifyTxt, msg);

}

}

/// <summary>

/// 搜索到的蓝牙设备

/// </summary>

/// <param name="id"></param>code>

/// <returns></returns>

private async Task Matching(string id, DeviceInformation args = null)

{

try

{

BluetoothLEDevice.FromIdAsync(id).Completed = async (asyncInfo, asyncStatus) =>

{

if (asyncStatus == AsyncStatus.Completed)

{

BluetoothLEDevice bleDevice = asyncInfo.GetResults();

if (bleDevice.Name.StartsWith("Bluetooth"))

{

return;

}

BluetoothLEDevice tmp = this.DevicesList.Where(p => p.Name == bleDevice.Name).FirstOrDefault();

if(tmp == null)

{ //没有添加过

// bool state = IsConnectable(bleDevice.DeviceInformation);

this.DevicesList.Add(bleDevice);

this.DevicewatcherChanged(MsgType.BleDevice, bleDevice);

}

}

};

}

catch(Exception e)

{

string msg = "没有发现设备" + e.ToString();

this.MessageChanged(MsgType.NotifyTxt, msg);

this.StopBleDeviceWatcher();

}

}

/// <summary>

/// 主动断开连接

/// </summary>

public void Dispose()

{

CurrentDeviceMAC = null;

CurrentService?.Dispose();

CurrentDevice?.Dispose();

CurrentDevice = null;

CurrentService = null;

CurrentWriteCharacteristic = null;

CurrentNotifyCharacteristic = null;

this.MessageChanged(MsgType.NotifyTxt, "主动断开连接");

}

/// <summary>

/// 按MAC地址直接组装设备ID查找设备

/// </summary>

/// <param name="MAC"></param>code>

/// <returns></returns>

public async Task SelectDeviceFromIdAsync(string MAC)

{

CurrentDeviceMAC = MAC;

CurrentDevice = null;

BluetoothAdapter.GetDefaultAsync().Completed = async (asyncInfo, asyncStatus) =>

{

if (asyncStatus == AsyncStatus.Completed)

{

BluetoothAdapter bluetoothAdapter = asyncInfo.GetResults();

// ulong 转为byte数组

byte[] _Bytes1 = BitConverter.GetBytes(bluetoothAdapter.BluetoothAddress);

Array.Reverse(_Bytes1);

string macAddress = BitConverter.ToString(_Bytes1, 2, 6).Replace('-', ':').ToLower();

string Id = "BluetoothLe#BluetoothLe" + macAddress + "-" + MAC;

await Matching(Id);

}

};

}

/// <summary>

/// 设置特征对象为接收通知对象

/// </summary>

/// <param name="characteristic"></param>code>

/// <returns></returns>

public async Task EnableNotifications(GattCharacteristic characteristic)

{

string msg = "收通知对象=" + CurrentDevice.ConnectionStatus;

this.MessageChanged(MsgType.NotifyTxt, msg);

characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(CHARACTERSITIC_NOTIFICATION_TYPE).Completed = async (asyncInfo, asyncStatus) =>

{

if (asyncStatus == AsyncStatus.Completed)

{

GattCommunicationStatus status = asyncInfo.GetResults();

if (status == GattCommunicationStatus.Unreachable)

{

msg = "设备不可用";

this.MessageChanged(MsgType.NotifyTxt, msg);

if (CurrentNotifyCharacteristic != null && !asyncLock)

{

await this.EnableNotifications(CurrentNotifyCharacteristic);

}

}

asyncLock = false;

msg = "设备连接状态" + status;

this.MessageChanged(MsgType.NotifyTxt, msg);

}

};

}

/// <summary>

/// 接受到蓝牙数据

/// </summary>

private void Characteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)

{

byte[] data;

CryptographicBuffer.CopyToByteArray(args.CharacteristicValue, out data);

string str = BitConverter.ToString(data);

this.MessageChanged(MsgType.BleRecData, str, data);

}

/// <summary>

/// 发送数据接口

/// </summary>

/// <returns></returns>

public async Task Write(byte[] data)

{

if (CurrentWriteCharacteristic != null)

{

CurrentWriteCharacteristic.WriteValueAsync(CryptographicBuffer.CreateFromByteArray(data), GattWriteOption.WriteWithResponse);

string str = "发送数据:" + BitConverter.ToString(data);

this.MessageChanged(MsgType.BleSendData, str, data);

}

}

/// <summary>

/// 发送数据接口

/// </summary>

/// <returns></returns>

public async Task WriteName(byte[] data)

{

if (CurrentNameCharacteristic != null)

{

CurrentNameCharacteristic.WriteValueAsync(CryptographicBuffer.CreateFromByteArray(data), GattWriteOption.WriteWithResponse);

string str = "发送数据:" + BitConverter.ToString(data);

this.MessageChanged(MsgType.BleSendData, str, data);

}

}

}

}

调用类

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

using System.Windows.Forms;

using Windows.Devices.Bluetooth;

using Windows.Devices.Bluetooth.Advertisement;

using Windows.Devices.Bluetooth.GenericAttributeProfile;

using Windows.Devices.Enumeration;

using Windows.Foundation;

using System.Net.NetworkInformation;

namespace HPBluetoolth

{

public partial class BlueForm : Form

{

BleCore bleCore = new BleCore();

/// <summary>

/// 存储检测到的设备

/// </summary>

List<Windows.Devices.Bluetooth.BluetoothLEDevice> DeviceList = new List<Windows.Devices.Bluetooth.BluetoothLEDevice>();

/// <summary>

/// 当前蓝牙服务列表

/// </summary>

List<Windows.Devices.Bluetooth.GenericAttributeProfile.GattDeviceService> GattDeviceServices = new List<Windows.Devices.Bluetooth.GenericAttributeProfile.GattDeviceService>();

/// <summary>

/// 当前蓝牙服务特征列表

/// </summary>

List<Windows.Devices.Bluetooth.GenericAttributeProfile.GattCharacteristic> GattCharacteristics = new List<Windows.Devices.Bluetooth.GenericAttributeProfile.GattCharacteristic>();

public BlueForm()

{

InitializeComponent();

CheckForIllegalCrossThreadCalls = false;

this.bleCore.MessageChanged += BleCore_MessAgeChanged;

this.bleCore.DevicewatcherChanged += BleCore_DeviceWatcherChanged;

this.bleCore.GattDeviceServiceAdded += BleCore_GattDeviceServiceAdded;

this.bleCore.CharacteristicAdded += BleCore_CharacteristicAdded;

this.Init();

}

private void Init()

{

this.Load += BlueForm_Load;

//开始

btnStart.Click += BtnStart_Click;

//搜索蓝牙

this.btnSearch.Click += btnSearch_Click;

this.btnConnect.Click += BtnConnect_Click;

//获取服务

this.btnServes.Click += BtnServes_Click;

//获取特征

this.btnFeatures.Click += BtnFeatures_Click;

//获取操作

this.btnOpt.Click += BtnOpt_Click;

//读取

this.btnRead.Click += BtnRead_Click;

//发送

this.btnWrite.Click += BtnWrite_Click;

this.FormClosing += BlueForm_FormClosing;

}

private void BlueForm_Load(object sender, EventArgs e)

{

}

private void BtnStart_Click(object sender, EventArgs e)

{

string str = tbCode.Text;

byte[] buffer = Encoding.UTF8.GetBytes(str);

this.bleCore.WriteName(buffer);

}

bool Closing = false;

private void BlueForm_FormClosing(object sender, FormClosingEventArgs e)

{

Closing = true;

bleCore.Dispose();

}

// 异步线程

public static void RunAsync(Action action)

{

((Action)(delegate ()

{

action.Invoke();

})).BeginInvoke(null, null);

}

/// <summary>

/// 搜索蓝牙

/// </summary>

/// <param name="sender"></param>code>

/// <param name="e"></param>code>

private void btnSearch_Click(object sender, EventArgs e)

{

try

{

if (this.btnSearch.Text == "查找")

{

this.listboxMessage.Items.Clear();

this.listboxBleDevice.Items.Clear();

this.bleCore.StartBleDevicewatcher();

this.btnSearch.Text = "停止";

}

else

{

this.bleCore.StopBleDeviceWatcher();

this.btnSearch.Text = "查找";

}

}catch(Exception ex)

{

MessageBox.Show(ex.Message);

}

}

/// <summary>

/// 提示消息

/// </summary>

private void BleCore_MessAgeChanged(MsgType type, string message, byte[] data)

{

if (Closing) return;

RunAsync(() =>

{

this.listboxMessage.Items.Add(message);

});

}

/// <summary>

/// 搜索蓝牙设备列表

/// </summary>

private void BleCore_DeviceWatcherChanged(MsgType type, Windows.Devices.Bluetooth.BluetoothLEDevice bluetoothLEDevice)

{

if (Closing) return;

RunAsync(() =>

{

this.listboxBleDevice.Items.Add(bluetoothLEDevice.Name);

this.DeviceList.Add(bluetoothLEDevice);

});

}

/// <summary>

/// 连接蓝牙

/// </summary>

/// <param name="sender"></param>code>

/// <param name="e"></param>code>

private void BtnConnect_Click(object sender, EventArgs e)

{

if(this.listboxBleDevice.SelectedItem == null)

{

MessageBox.Show("请选择连接的蓝牙");

return;

}

string deviceName = this.listboxBleDevice.SelectedItem.ToString();

Windows.Devices.Bluetooth.BluetoothLEDevice bluetoothLEDevice =

this.DeviceList.Where(u => u.Name == deviceName).FirstOrDefault();

if(bluetoothLEDevice == null)

{

MessageBox.Show("没有发现此蓝牙,请重新搜索");

return;

}

//两个蓝牙进行匹配

bleCore.StartMatching(bluetoothLEDevice);

}

/// <summary>

/// 获取服务

/// </summary>

/// <param name="sender"></param>code>

/// <param name="e"></param>code>

private void BtnServes_Click(object sender, EventArgs e)

{

this.cmbServer.Items.Clear();

this.bleCore.FindService();

}

/// <summary>

/// 获取服务列表

/// </summary>

/// <param name="gattDeviceService"></param>code>

private void BleCore_GattDeviceServiceAdded(GattDeviceService gattDeviceService)

{

// RunAsync(() =>

// {

this.cmbServer.Items.Add(gattDeviceService.Uuid.ToString());

this.GattDeviceServices.Add(gattDeviceService);

this.btnFeatures.Enabled = true;

// });

}

/// <summary>

/// 获取特征

/// </summary>

/// <param name="sender"></param>code>

/// <param name="e"></param>code>

private void BtnFeatures_Click(object sender, EventArgs e)

{

this.cmbFeatures.Items.Clear();

if(this.cmbServer.SelectedItem == null)

{

MessageBox.Show("选择蓝牙服务");

return;

}else

{

var item = this.GattDeviceServices.Where(u => u.Uuid ==

new Guid(this.cmbServer.SelectedItem.ToString())).FirstOrDefault();

//获取蓝牙特征

this.bleCore.FindCharacteristic(item);

}

}

/// <summary>

/// 获取特征列表

/// </summary>

/// <param name="gattCharacteristic"></param>code>

private void BleCore_CharacteristicAdded(GattCharacteristic gattCharacteristic)

{

// RunAsync(() =>

// {

this.cmbFeatures.Items.Add(gattCharacteristic.Uuid);

this.GattCharacteristics.Add(gattCharacteristic);

this.btnOpt.Enabled = true;

// });

}

/// <summary>

/// 获取操作

/// </summary>

/// <param name="sender"></param>code>

/// <param name="e"></param>code>

private void BtnOpt_Click(object sender, EventArgs e)

{

if(this.cmbFeatures.SelectedItem == null)

{

MessageBox.Show("请选择蓝牙服务");

return;

}

var item = this.GattCharacteristics.Where(u => u.Uuid ==

new Guid(this.cmbFeatures.SelectedItem.ToString())).FirstOrDefault();

//获取操作

this.bleCore.SetOpteron(item);

if(item.CharacteristicProperties == (GattCharacteristicProperties.Read

| GattCharacteristicProperties.Write))

{

this.btnRead.Enabled = true;

this.btnWrite.Enabled = true;

}

}

/// <summary>

/// 读取

/// </summary>

/// <param name="sender"></param>code>

/// <param name="e"></param>code>

private void BtnRead_Click(object sender, EventArgs e)

{

string str = tbReadWriteInfo.Text;

string[] arr = str.Split(' ');

byte[] buffer = new byte[7];

for(int i=0; i<arr.Length; i++)

{

buffer[i] = Convert.ToByte(arr[i], 16);

}

//CRC校验

ushort crc = CRC(buffer, 5);

buffer[5] = (byte)((crc & 0xFF00) >> 8);

buffer[6] = (byte)((crc & 0x00FF));

this.bleCore.Write(buffer);

}

/// <summary>

/// 写入

/// </summary>

/// <param name="sender"></param>code>

/// <param name="e"></param>code>

private void BtnWrite_Click(object sender, EventArgs e)

{

string str = tbReadWriteInfo.Text;

string[] arr = str.Split(' ');

byte[] buffer = new byte[9];

for (int i = 0; i < arr.Length; i++)

{

buffer[i] = Convert.ToByte(arr[i], 16);

}

//CRC校验

ushort crc = CRC(buffer, 7);

buffer[7] = (byte)((crc & 0xFF00) >> 8);

buffer[8] = (byte)((crc & 0x00FF));

this.bleCore.Write(buffer);

}

/// <summary>

/// CRC校验

/// </summary>

/// <param name="data"></param>code>

/// <param name="length"></param>code>

/// <returns></returns>

public static ushort CRC(byte[] data, int length)

{

ushort tempCrcResult = 0xffff;

for (int i = 0; i < length; i++)

{

tempCrcResult = (ushort)(tempCrcResult ^ data[i]);

for (int j = 0; j < 8; j++)

{

if ((tempCrcResult & 0x0001) == 1)

tempCrcResult = (ushort)((tempCrcResult >> 1) ^ 0xa001);

else tempCrcResult = (ushort)(tempCrcResult >> 1);

}

}

return (tempCrcResult = (ushort)(((tempCrcResult & 0xff) << 8) | (tempCrcResult >> 8)));

}

}

}

以上是我项目中用到的Demo, 一些全局变量就不贴了。

结尾

至此,低功耗蓝牙的搜索和通讯就完成了,感谢大神们贴的博文。

如何正常的关闭(断开)蓝牙连接

因工作上的进一步需要,我也遇到了这个问题,今天调试了一个上午,终于可以正常断开了。特此记录一下,

想直接在评论区写的,奈何写的比较啰嗦,给的字数不够用,就加在后文了。

也是根据这篇“The BLE Device Disconnection not working on UWP C#” 问答的思路写的,总的来说就是将一切引用到当前BluetoothLEDevice对象都要释放掉,否则是不能正常断开蓝牙的。

我因为BluetoothLEDevice对象在三个地方都存着有,一个是BluetoothLEDevice对象列表(List),还有两个类(BleCore,BlueForm)之中也有引用这个对象,所以找起来还是有点费劲的。GATT通道也务必要关闭哈。

BLE蓝牙正常断开的方法,请执行以下步骤,不分先后

1 关闭GATT服务通道:

//关闭该蓝牙的所有服务

foreach(var sev in GattDeviceServices)

{

sev.Dispose();

}

2 移除事件监听器:(非必要,不移除也行,但可能会有内存泄漏啥的)

if(CurrentDevice != null)

CurrentDevice.ConnectionStatusChanged -= CurrentDevice_ConnectionStatusChanged;

3 释放BluetoothLEDevice对象:(包括存储在 List DeviceList中的,或者指向当前BluetoothLEDevice对象的,都要释放)。

//找到在蓝牙列表里面执行当前蓝牙的对象

string deviceName = this.listboxBleDevice.SelectedItem.ToString();

Windows.Devices.Bluetooth.BluetoothLEDevice bluetoothLEDevice =

this.DeviceList.Where(u => u.Name == deviceName).FirstOrDefault();

if (bluetoothLEDevice != null)

{ //从列表中移除

this.DeviceList.Remove(bluetoothLEDevice);

}

if (bluetoothLEDevice != null)

{ //关闭列表中的蓝牙

bluetoothLEDevice.Dispose();

}

bluetoothLEDevice = null;

//蓝牙类的关闭

this.bleCore.DisConnect();

完整断开蓝牙连接代码,如下:

//断开

private void BtnDisConent_Click(object sender, EventArgs e)

{

//找到在蓝牙列表里面执行当前蓝牙的对象

string deviceName = this.listboxBleDevice.SelectedItem.ToString();

Windows.Devices.Bluetooth.BluetoothLEDevice bluetoothLEDevice =

this.DeviceList.Where(u => u.Name == deviceName).FirstOrDefault();

if (bluetoothLEDevice != null)

{ //从列表中移除

this.DeviceList.Remove(bluetoothLEDevice);

}

//关闭该蓝牙的所有服务

foreach(var sev in GattDeviceServices)

{

sev.Dispose();

}

//并清空

GattDeviceServices.Clear();

GattCharacteristics.Clear();

if (bluetoothLEDevice != null)

{ //关闭列表中的蓝牙

bluetoothLEDevice.Dispose();

}

bluetoothLEDevice = null;

//蓝牙类的关闭

this.bleCore.DisConnect();

//释放内存

GC.Collect();

}

bleCore.DisConnect方法:

/// <summary>

/// 主动断开连接

/// </summary>

public void Dispose()

{

IsConnect = false;

CurrentDeviceMAC = null;

//使用到的服务 (我这里仅仅使用了一个服务)

CurrentService?.Dispose();

//蓝牙

if(CurrentDevice != null)

CurrentDevice.ConnectionStatusChanged -= CurrentDevice_ConnectionStatusChanged;

//关闭

CurrentDevice?.Dispose();

CurrentDevice = null;

CurrentService = null;

//特征值

CurrentNameCharacteristic = null;

CurrentWriteCharacteristic = null;

CurrentNotifyCharacteristic = null;

}



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。