目录
前言
因为项目需要上位机软件与下位机的蓝牙模块进行通讯,所以上网查阅了很多关于蓝牙通讯的资料。刚开始以为使用现成的库就可以了,像InTheHand,发现无法搜索到蓝牙设备。后来查询资料知道,由于我们的下位机使用的是低功耗蓝牙,很多蓝牙通讯的库不适用低功耗蓝牙。再后来发现,我们的上位机软件是使用WinForm开发的,网上关于WinForm开发低功耗蓝牙的资料很少。整个开发过程挺不容易的,也踩了很多坑,终于可以正常与蓝牙模块进行通讯了。
一、添加引用
需要引用Windows.winmd,不过我发现按照网上说的路径添加引用,程序编译不通过,所以我把Windows.winmd文件和源代码一块打包,在Debug目录下,代码地址见文末。
二、使用步骤
1.蓝牙通讯类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Devices.Enumeration;
using Windows.Foundation;
using Windows.Networking;
using Windows.Networking.Proximity;
using Windows.Networking.Sockets;
using Windows.Security.Cryptography;
using Windows.Storage.Streams;
namespace Test
{
public class BleCore
{
public enum MsgType
{
NotifyTxt,
BleData,
BleDevice
}
private Boolean asyncLock = false;
/// <summary>
/// 搜索蓝牙设备对象
/// </summary>
private Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementWatcher 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 CurrentNotifyCharacteristic { get; set; }
/// <summary>
/// 特性通知类型通知启用
/// </summary>
private const GattClientCharacteristicConfigurationDescriptorValue CHARACTERISTIC_NOTIFICATION_TYPE = GattClientCharacteristicConfigurationDescriptorValue.Notify;
/// <summary>
/// 存储检测到的设备
/// </summary>
private List<BluetoothLEDevice> DeviceList = new List<BluetoothLEDevice>();
/// <summary>
/// 定义搜索蓝牙设备委托
/// </summary>
public delegate void DeviceWatcherChangedEvent(MsgType type, BluetoothLEDevice bluetoothLEDevice);
/// <summary>
/// 搜索蓝牙事件
/// </summary>
public event DeviceWatcherChangedEvent DeviceWatcherChanged;
/// <summary>
/// 获取服务委托
/// </summary>
public delegate void GattDeviceServiceAddedEvent(GattDeviceService gattDeviceService);
/// <summary>
/// 获取服务事件
/// </summary>
public event GattDeviceServiceAddedEvent GattDeviceServiceAdded;
/// <summary>
/// 获取特征委托
/// </summary>
public delegate void CharacteristicAddedEvent(GattCharacteristic gattCharacteristic);
/// <summary>
/// 获取特征事件
/// </summary>
public event CharacteristicAddedEvent CharacteristicAdded;
/// <summary>
/// 提示信息委托
/// </summary>
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>
/// 搜索蓝牙设备
/// </summary>
public void StartBleDeviceWatcher()
{
this.deviceWatcher = new Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementWatcher();
this.deviceWatcher.ScanningMode = Windows.Devices.Bluetooth.Advertisement.BluetoothLEScanningMode.Active;
this.deviceWatcher.SignalStrengthFilter.InRangeThresholdInDBm = -80;
this.deviceWatcher.SignalStrengthFilter.OutOfRangeThresholdInDBm = -90;
this.deviceWatcher.SignalStrengthFilter.OutOfRangeTimeout = TimeSpan.FromMilliseconds(5000);
this.deviceWatcher.SignalStrengthFilter.SamplingInterval = TimeSpan.FromMilliseconds(2000);
this.deviceWatcher.Received += DeviceWatcher_Received;
this.deviceWatcher.Start();
}
private void DeviceWatcher_Received(Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementWatcher sender, Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementReceivedEventArgs args)
{
BluetoothLEDevice.FromBluetoothAddressAsync(args.BluetoothAddress).Completed = async (asyncInfo, asyncStatus) =>
{
if (asyncStatus == AsyncStatus.Completed)
{
if (asyncInfo.GetResults() != null)
{
BluetoothLEDevice currentDevice = asyncInfo.GetResults();
Boolean contain = false;
foreach (BluetoothLEDevice device in DeviceList)//过滤重复的设备
{
if (device.DeviceId == currentDevice.DeviceId)
{
contain = true;
}
}
if (!contain)
{
this.DeviceList.Add(currentDevice);
this.DeviceWatcherChanged(MsgType.BleDevice, currentDevice);
}
}
}
};
}
/// <summary>
/// 停止搜索蓝牙
/// </summary>
public void StopBleDeviceWatcher()
{
this.deviceWatcher.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);
}
/// <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()
{
this.CurrentDevice.GetGattServicesAsync().Completed = async (asyncInfo, asyncStatus) =>
{
if (asyncStatus == AsyncStatus.Completed)
{
var services = asyncInfo.GetResults().Services;
foreach (GattDeviceService ser in services)
{
this.GattDeviceServiceAdded(ser);
}
}
};
}
/// <summary>
/// 获取特性
/// </summary>
public async void FindCharacteristic(GattDeviceService gattDeviceService)
{
this.CurrentService = gattDeviceService;
this.CurrentService.GetCharacteristicsAsync().Completed = async (asyncInfo, asyncStatus) =>
{
if (asyncStatus == AsyncStatus.Completed)
{
var characteristics = asyncInfo.GetResults().Characteristics;
foreach (GattCharacteristic characteristic in characteristics)
{
this.CharacteristicAdded(characteristic);
}
}
};
}
/// <summary>
/// 获取操作
/// </summary>
/// <returns></returns>
public async Task SetOpteron(GattCharacteristic gattCharacteristic)
{
if (gattCharacteristic.CharacteristicProperties == (GattCharacteristicProperties.Write | GattCharacteristicProperties.WriteWithoutResponse))
{
this.CurrentWriteCharacteristic = gattCharacteristic;
}
if (gattCharacteristic.CharacteristicProperties == GattCharacteristicProperties.Notify)
{
this.CurrentNotifyCharacteristic = gattCharacteristic;
}
if ((uint)gattCharacteristic.CharacteristicProperties == 26)
{ }
if (gattCharacteristic.CharacteristicProperties == (GattCharacteristicProperties.Notify | GattCharacteristicProperties.Read | GattCharacteristicProperties.Write))
{
this.CurrentWriteCharacteristic = gattCharacteristic;
this.CurrentNotifyCharacteristic = gattCharacteristic;
this.CurrentNotifyCharacteristic.ProtectionLevel = GattProtectionLevel.Plain;
this.CurrentNotifyCharacteristic.ValueChanged += Characteristic_ValueChanged;
await this.EnableNotifications(CurrentNotifyCharacteristic);
}
this.Connect();
}
/// <summary>
/// 连接蓝牙
/// </summary>
/// <returns></returns>
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 += this.CurrentDevice_ConnectionStatusChanged;
}
/// <summary>
/// 搜索到的蓝牙设备
/// </summary>
/// <returns></returns>
private async Task Matching(string Id)
{
try
{
BluetoothLEDevice.FromIdAsync(Id).Completed = async (asyncInfo, asyncStatus) =>
{
if (asyncStatus == AsyncStatus.Completed)
{
BluetoothLEDevice bleDevice = asyncInfo.GetResults();
this.DeviceList.Add(bleDevice);
this.DeviceWatcherChanged(MsgType.BleDevice, bleDevice);
}
};
}
catch (Exception e)
{
string msg = "没有发现设备" + e.ToString();
this.MessAgeChanged(MsgType.NotifyTxt, msg);
this.StartBleDeviceWatcher();
}
}
/// <summary>
/// 主动断开连接
/// </summary>
/// <returns></returns>
public void Dispose()
{
CurrentDeviceMAC = null;
CurrentService?.Dispose();
CurrentDevice?.Dispose();
CurrentDevice = null;
CurrentService = null;
CurrentWriteCharacteristic = null;
CurrentNotifyCharacteristic = null;
MessAgeChanged(MsgType.NotifyTxt, "主动断开连接");
}
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;
CurrentService = null;
CurrentWriteCharacteristic = null;
CurrentNotifyCharacteristic = null;
SelectDeviceFromIdAsync(CurrentDeviceMAC);
}
}
else
{
string msg = "设备已连接";
MessAgeChanged(MsgType.NotifyTxt, msg);
}
}
/// <summary>
/// 按MAC地址直接组装设备ID查找设备
/// </summary>
public async Task SelectDeviceFromIdAsync(string MAC)
{
CurrentDeviceMAC = MAC;
CurrentDevice = null;
BluetoothAdapter.GetDefaultAsync().Completed = async (asyncInfo, asyncStatus) =>
{
if (asyncStatus == AsyncStatus.Completed)
{
BluetoothAdapter mBluetoothAdapter = asyncInfo.GetResults();
if (mBluetoothAdapter != null)
{
await Matching(MAC);
}
else
{
string msg = "查找连接蓝牙设备异常.";
}
}
};
}
/// <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(CHARACTERISTIC_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.BleData, 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.BleData, str, data);
}
}
}
}
2.测试界面
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;
namespace Test
{
public partial class FormTest : 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 FormTest()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
}
private void FormTest_Load(object sender, EventArgs e)
{
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.btnConnect.Enabled = false;
this.btnServer.Enabled = false;
this.btnFeatures.Enabled = false;
this.btnOpteron.Enabled = false;
this.btnReader.Enabled = false;
this.btnSend.Enabled = false;
}
// 异步线程
public static void RunAsync(Action action)
{
((Action)(delegate ()
{
action.Invoke();
})).BeginInvoke(null, null);
}
/// <summary>
/// 提示消息
/// </summary>
private void BleCore_MessAgeChanged(BleCore.MsgType type, string message, byte[] data)
{
RunAsync(() =>
{
this.listboxMessage.Items.Add(message);
});
}
/// <summary>
/// 搜索蓝牙设备列表
/// </summary>
private void BleCore_DeviceWatcherChanged(BleCore.MsgType type, Windows.Devices.Bluetooth.BluetoothLEDevice bluetoothLEDevice)
{
RunAsync(() =>
{
this.listboxBleDevice.Items.Add(bluetoothLEDevice.Name);
this.DeviceList.Add(bluetoothLEDevice);
this.btnConnect.Enabled = true;
});
}
/// <summary>
/// 获取蓝牙服务列表
/// </summary>
private void BleCore_GattDeviceServiceAdded(Windows.Devices.Bluetooth.GenericAttributeProfile.GattDeviceService gattDeviceService)
{
RunAsync(() =>
{
this.cmbServer.Items.Add(gattDeviceService.Uuid.ToString());
this.GattDeviceServices.Add(gattDeviceService);
this.btnFeatures.Enabled = true;
});
}
/// <summary>
/// 获取特征列表
/// </summary>
private void BleCore_CharacteristicAdded(Windows.Devices.Bluetooth.GenericAttributeProfile.GattCharacteristic gattCharacteristic)
{
RunAsync(() =>
{
this.cmbFeatures.Items.Add(gattCharacteristic.Uuid);
this.GattCharacteristics.Add(gattCharacteristic);
this.btnOpteron.Enabled = true;
});
}
private void buttonSearch_Click(object sender, EventArgs e)
{
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 = "搜索";
}
}
private void btnConnect_Click(object sender, EventArgs e)
{
if (this.listboxBleDevice.SelectedItem != null)
{
string DeviceName = this.listboxBleDevice.SelectedItem.ToString();
Windows.Devices.Bluetooth.BluetoothLEDevice bluetoothLEDevice = this.DeviceList.Where(u => u.Name == DeviceName).FirstOrDefault();
if (bluetoothLEDevice != null)
{
bleCore.StartMatching(bluetoothLEDevice);
this.btnServer.Enabled = true;
}
else
{
MessageBox.Show("没有发现此蓝牙,请重新搜索.");
this.btnServer.Enabled = false;
}
}
else
{
MessageBox.Show("请选择连接的蓝牙.");
this.btnServer.Enabled = false;
}
}
private void btnServer_Click(object sender, EventArgs e)
{
this.cmbServer.Items.Clear();
this.bleCore.FindService();
}
private void btnFeatures_Click(object sender, EventArgs e)
{
this.cmbFeatures.Items.Clear();
if (this.cmbServer.SelectedItem != null)
{
var item = this.GattDeviceServices.Where(u => u.Uuid == new Guid(this.cmbServer.SelectedItem.ToString())).FirstOrDefault();
this.bleCore.FindCharacteristic(item);
}
else
{
MessageBox.Show("选择蓝牙服务.");
}
}
private void btnOpteron_Click(object sender, EventArgs e)
{
if (this.cmbFeatures.SelectedItem != null)
{
var item = this.GattCharacteristics.Where(u => u.Uuid == new Guid(this.cmbFeatures.SelectedItem.ToString())).FirstOrDefault();
this.bleCore.SetOpteron(item);
if (item.CharacteristicProperties == (Windows.Devices.Bluetooth.GenericAttributeProfile.GattCharacteristicProperties.Write | Windows.Devices.Bluetooth.GenericAttributeProfile.GattCharacteristicProperties.WriteWithoutResponse))
{
this.btnReader.Enabled = true;
this.btnSend.Enabled = true;
}
}
else
{
MessageBox.Show("选择蓝牙服务.");
}
}
private void btnReader_Click(object sender, EventArgs e)
{
}
private void btnSend_Click(object sender, EventArgs e)
{
byte[] dataBytes = new byte[8];
dataBytes[0] = 0xAB;
dataBytes[1] = 0x00;
dataBytes[2] = 0x01;
dataBytes[3] = 0xA2;
dataBytes[4] = 0x02;
dataBytes[5] = 0x0D;
dataBytes[6] = 0xAC;
dataBytes[7] = 0xBA;
this.bleCore.Write(dataBytes);
}
private void FormTest_FormClosed(object sender, FormClosedEventArgs e)
{
this.bleCore.Dispose();
}
}
}
3.界面截图
操作步骤:搜索》选择列表框中的某个蓝牙》匹配》获取服务》获取特征》获取操作》发送
三、注意事项
在获取操作的时候,需要根据使用的蓝牙设备的特征修改BleCore类的SetOpteron()方法,单步调试程序来查看gattCharacteristic.CharacteristicProperties属性的值,修改成匹配的GattCharacteristicProperties的组合。否则,写特征CurrentWriteCharacteristic为空,无法发送数据。同时,需要修改“获取操作”按钮的响应函数btnOpteron_Click()。