@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
踩过的坑
1 使用的dll
需要注意的是我们使用的低功耗蓝牙BLE,如果使用一些库32feet.NET
、InTheHand
等,是搜索不到低功耗蓝牙的,只能搜索到经典蓝牙。
我们需要用MSDN自带的库:System.Runtime.WindowsRuntime.dll
和 Windows.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)
这个函数,而不能照搬这个函数。
蓝牙包含哪些服务,服务下有含有哪些特征值,这个特征值的属性是可读,可写还是可订阅,这些可以通过蓝牙厂商获取,或如果是自己公司开发的话,那问相关的开发人员会更清楚。
效果图
蓝牙类的代码
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>
/// <param name="bluetoothLEDevice"></param>
public delegate void DevicewatcherChangedEvent(MsgType type, BluetoothLEDevice bluetoothLEDevice);
/// <summary>
/// 搜索蓝牙事件
/// </summary>
public event DevicewatcherChangedEvent DevicewatcherChanged;
/// <summary>
/// 获取服务委托
/// </summary>
/// <param name="gattDeviceService"></param>
public delegate void GattDeviceServiceAddedEvent(GattDeviceService gattDeviceService);
/// <summary>
/// 获取服务事件
/// </summary>
public event GattDeviceServiceAddedEvent GattDeviceServiceAdded;
/// <summary>
/// 获取特征委托
/// </summary>
/// <param name="gattCharacteristic"></param>
public delegate void CharacteristicAddedEvent(GattCharacteristic gattCharacteristic);
/// <summary>
/// 获取特征事件
/// </summary>
public event CharacteristicAddedEvent CharacteristicAdded;
/// <summary>
/// 提示信息委托
/// </summary>
/// <param name="type"></param>
/// <param name="message"></param>
/// <param name="data"></param>
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>
/// <param name="args"></param>
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>
/// <param name="args"></param>
private void DeviceWatcher_Stopped(DeviceWatcher sender, object args)
{
string msg = "自动发现设备停止";
this.MessageChanged(MsgType.NotifyTxt, msg);
}
/// <summary>
/// 匹配
/// </summary>
/// <param name="device"></param>
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>
/// <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>
/// <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>
/// <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>
/// <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>
/// <param name="e"></param>
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>
/// <param name="e"></param>
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>
/// <param name="e"></param>
private void BtnServes_Click(object sender, EventArgs e)
{
this.cmbServer.Items.Clear();
this.bleCore.FindService();
}
/// <summary>
/// 获取服务列表
/// </summary>
/// <param name="gattDeviceService"></param>
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>
/// <param name="e"></param>
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>
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>
/// <param name="e"></param>
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>
/// <param name="e"></param>
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>
/// <param name="e"></param>
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>
/// <param name="length"></param>
/// <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;
}