WinForm低功耗蓝牙通信BlueToothLE C#
工作中用到低功耗蓝牙,在网上找了一圈自己总结了一下
最终效果
开发环境:Visual Studio2019 Win10
参考文章
https://www.cnblogs.com/webtojs/p/9675956.html
https://blog.csdn.net/weixin_32673065/article/details/113578844?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0.pc_relevant_aa&spm=1001.2101.3001.4242.1&utm_relevant_index=3
自己实现的库
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Devices.Enumeration;
using Windows.Security.Cryptography;
public class LanBle
{
string Mac { get; set; }
string ServiceID { get; set; }
string WriteID { get; set; }
string ReadID { get; set; }
public BluetoothLEDevice Device { get; set; }
//存储检测到的主服务。
public GattDeviceService Service { get; set; }
//存储检测到的写特征对象。
public GattCharacteristic WriteCharacteristic { get; set; }
//存储检测到的通知特征对象。
public GattCharacteristic NotifyCharacteristic { get; set; }
public event BleRev OnRev;
public event BleScanResult OnScanResult;
List<ScanResult> ScanResults = new List<ScanResult>();
bool flag = false;
public LanBle()
{
}
public bool DisConnect()
{
NotifyCharacteristic = null;
WriteCharacteristic = null;
Service?.Dispose();
Service = null;
Device?.Dispose();
Device = null;
return true;
}
public void Stop()
{
flag = false;
}
private bool Find(string id)
{
var x = from a in ScanResults where a.ID == id select a;
if (x == null) { return false; }
return x.Count() == 1;
}
public void Scan(int timeOut = 10)
{
string[] requestedProperties = { "System.Devices.Aep.IsConnected","System.Devices.Aep.DeviceAddress", "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}\")";
DeviceWatcher deviceWatcher =
DeviceInformation.CreateWatcher(
aqsAllBluetoothLEDevices,
requestedProperties,
DeviceInformationKind.AssociationEndpoint);
deviceWatcher.Added += DeviceWatcher_Added;
deviceWatcher.Updated += DeviceWatcher_Updated;
deviceWatcher.Stopped += DeviceWatcher_Stopped;
flag = true;
long l1 = Environment.TickCount + 1 * timeOut * 1000;
Task.Factory.StartNew(() =>
{
while (flag)
{
if (Environment.TickCount > l1)
{
deviceWatcher.Stop();
return;
}
else
{
Thread.Sleep(50);
}
}
});
ScanResults.Clear();
deviceWatcher.Start();
}
private void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation args)
{
if (string.IsNullOrEmpty(args.Name)) { return; }
var rssi = args.Properties.Single(d => d.Key == "System.Devices.Aep.SignalStrength").Value;
if (rssi == null) { return; }
int x1 = int.Parse(rssi.ToString());
if (x1 < -80) { return; }
string s1 = $"[{x1}] { (args.Name).PadLeft(30, ' ')} {args.Id}";
Console.WriteLine(s1);
if (!Find(args.Id))
{
var x = args.Properties;
ScanResults.Add(new ScanResult(args.Name, args.Id));
}
}
private void DeviceWatcher_Stopped(DeviceWatcher sender, object args)
{
OnScanResult?.Invoke(sender, new BleScanResultEventArgs(ScanResults));
}
private void DeviceWatcher_Updated(DeviceWatcher sender, DeviceInformationUpdate args)
{
}
public async Task<int> Connect(string mac, string serviceId, string writeId, string readId)
{
Mac = mac.ToLower();
ServiceID = serviceId.ToLower();
WriteID = writeId.ToLower();
ReadID = readId.ToLower();
try
{
BluetoothAdapter bluetoothAdapter = await BluetoothAdapter.GetDefaultAsync();
if (bluetoothAdapter == null) { return -1; }
byte[] _Bytes1 = BitConverter.GetBytes(bluetoothAdapter.BluetoothAddress);//ulong转换为byte数组
Array.Reverse(_Bytes1);
string macAddress = BitConverter.ToString(_Bytes1, 2, 6).Replace('-', ':').ToLower();
string Id = "BluetoothLE#BluetoothLE" + macAddress + "-" + Mac;
Device = await BluetoothLEDevice.FromIdAsync(Id);
if (Device == null) { return -2; }
GattDeviceServicesResult result = await Device.GetGattServicesForUuidAsync(new Guid(ServiceID));
IEnumerable<GattDeviceService> lists = result.Services;
if (lists == null || lists.Count() == 0) { return -3; }
Service = result.Services[0];
GattCharacteristicsResult w1 = await Service.GetCharacteristicsForUuidAsync(new Guid(WriteID));
IEnumerable<GattCharacteristic> list2 = w1.Characteristics;
if (list2 == null || list2.Count() == 0) { return -4; }
WriteCharacteristic = w1.Characteristics[0];
GattCharacteristicsResult r1 = await Service.GetCharacteristicsForUuidAsync(new Guid(ReadID));
IEnumerable<GattCharacteristic> list3 = r1.Characteristics;
if (list3 == null || list3.Count() == 0) { return -5; }
NotifyCharacteristic = r1.Characteristics[0];
NotifyCharacteristic.ProtectionLevel = GattProtectionLevel.Plain;
NotifyCharacteristic.ValueChanged += Characteristic_ValueChanged;
await NotifyCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);
return 0;
}
catch
{
return -6;
}
}
/// <summary>
/// 发送数据接口
/// </summary>
/// <param name="characteristic"></param>
/// <param name="data"></param>
/// <returns></returns>
public async Task<bool> Write(byte[] data)
{
if (WriteCharacteristic == null) { return false; }
int count = 20;
int len = count;
try
{
for (int i = 0; i < data.Length; i += count)
{
if (i + count > data.Length) { len = data.Length - i; }
byte[] b1 = new byte[len];
Array.Copy(data, i, b1, 0, len);
Console.WriteLine($"发送 : " + ByteToHex(b1));
GattCommunicationStatus status = await WriteCharacteristic.WriteValueAsync(CryptographicBuffer.CreateFromByteArray(b1), GattWriteOption.WriteWithoutResponse);
if (status != GattCommunicationStatus.Success) { return false; }
else
{
Thread.Sleep(5);
}
}
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message.ToString());
return false;
}
}
private void Characteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
{
byte[] data;
CryptographicBuffer.CopyToByteArray(args.CharacteristicValue, out data);
string str = BitConverter.ToString(data);
OnRev?.Invoke(this, new BleEventArgs(data));
}
private string ByteToHex(byte[] b, bool space = true)
{
if (b == null) { return ""; }
StringBuilder sb = new StringBuilder();
for (int i = 0; i < b.Length; i++)
{
sb.Append(b[i].ToString("X2") + (space ? " " : ""));
}
return sb.ToString();
}
}
public delegate void BleRev(object sender, BleEventArgs e);
public delegate void BleScanResult(object sender, BleScanResultEventArgs e);
public class BleScanResultEventArgs : EventArgs
{
public List<ScanResult> ScanResults;
public BleScanResultEventArgs(List<ScanResult> scanResults)
{
this.ScanResults = scanResults;
}
}
public class ScanResult
{
public string Name { get; set; }
public string Mac { get; set; }
public string ID { get; set; }
public ScanResult(string name, string id)
{
this.Name = name;
if (string.IsNullOrEmpty(Name))
{
Name = "未知";
}
this.ID = id;
string[] s1 = id.Split('-');
if (s1.Length == 2)
{
this.Mac = s1[1];
}
else
{
this.Mac = "";
}
}
public override string ToString()
{
//return $"{Name}\t{Mac}\t{ID}";
return $"{Name}\t{Mac}";
}
}
public class BleEventArgs : EventArgs
{
public byte[] Data { get; set; }
public BleEventArgs(byte[] data)
{
this.Data = data;
}
}
调用方式
扫描:
lb = new LanBle();
lb.OnScanResult += Lb_OnScanResult;
lb.Scan();
private void Lb_OnScanResult(object sender, BleScanResultEventArgs e)
{
foreach (var item in e.ScanResults)
{
AppendText(item.ToString(), false);
}
}
连接:
LanBle lb = new LanBle();
lb.OnRev += Lb_OnRev;
int iRev = await lb.Connect(mac, _serviceGuid, _writeCharacteristicGuid, _notifyCharacteristicGuid);
bool flag = iRev == 0;
发送数据:
private async Task
{
bool flag = false;
string sSend = Fun.ByteToHex(bSend);
if (lb != null && lb.Service != null)
{
lTime = Environment.TickCount;
flag = await lb.Write(bSend);
AppendText($"Send : {sSend}");
Console.WriteLine(flag);
sRev = "";
}
Console.WriteLine($"发送【{(flag ? "成功" : "失败")}】:{sSend }");
return flag;
}
收到数据:
private void Lb_OnRev(object sender, BleEventArgs e)
{
byte[] data = e.Data;
}