使用C#进行低功耗蓝牙BLE开发

  1. 打开VS->工具->NuGet包管理器->程序包管理器设置->常规->默认包管理格式(设置为PackageRefrence)

  1. 解决方案里引用处,右键选择管理NuGet程序包,浏览->搜索 Microsoft.Windows.SDK.Contracts并安装。需要几分钟时间。

  1. 代码实现基本大同小异,主要是UWP API的引入方式。

重点说明: 这一步中,很多博主的采用的方式如下:

1.修改工程文件.csproj, 在文件中增加

<TargetPlatformVersion>10.0</TargetPlatformVersion>

2.引用 Windows.Devices、Windows.Foundation、Windows.Security、Windows.Services、Windows.Storage等组件进行环境搭建的。

本人在Windows11+Vs2022环境中实测,编译时无法通过,会出现无法引用System.Runtime.dll组件、IDisposable 没有实现等错误。采用Nuget方式引用,解决上述问题。也遇到上述问题的码友可以参考本文的方式。

using System;
using System.Collections.Generic;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.Advertisement;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Foundation;
using Windows.Security.Cryptography;

namespace BleSolution
{
    public class BleCore:IDisposable
    {
        private bool asyncLock = false;

        /// <summary>
        ///  搜索蓝牙设备对象
        /// </summary>
        private 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 CHARACERISTIC_NOTIFICATION_TYPE = GattClientCharacteristicConfigurationDescriptorValue.Notify;

        /// <summary>
        /// 存储检测到的设备
        /// </summary>
        private List<BluetoothLEDevice> DeviceList = new List<BluetoothLEDevice>();

        /// <summary>
        /// 定义搜索蓝牙设备委托
        /// </summary>
        /// <param name="type"></param>
        /// <param name="bluetoothLEDevice"></param>
        public delegate void DeviceWatcherChangeEvent(MsgType type, BluetoothLEDevice bluetoothLEDevice);

        /// <summary>
        /// 搜索蓝牙事件
        /// </summary>
        public event DeviceWatcherChangeEvent DeviceWatcherChanged;

        /// <summary>
        /// 获取服务委托
        /// </summary>
        /// <param name="gattDeviceService"></param>
        public delegate void GattDeviceServiceAddEvent(GattDeviceService gattDeviceService);

        /// <summary>
        /// 获取服务事件
        /// </summary>
        public event GattDeviceServiceAddEvent GattDeviceServiceAdded;

        /// <summary>
        /// 获取特征委托
        /// </summary>
        /// <param name="characteristic"></param>
        public delegate void CharacteristicAddedEvent(GattCharacteristic characteristic);

        /// <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>
        /// 搜索蓝牙设备
        /// </summary>
        public void StartBleDeviceWatcher()
        {  
            deviceWatcher = new BluetoothLEAdvertisementWatcher();
            deviceWatcher.ScanningMode = BluetoothLEScanningMode.Active;
            deviceWatcher.SignalStrengthFilter.InRangeThresholdInDBm = -80;
            deviceWatcher.SignalStrengthFilter.OutOfRangeThresholdInDBm = -90;
            deviceWatcher.SignalStrengthFilter.OutOfRangeTimeout = TimeSpan.FromMilliseconds(5000);
            deviceWatcher.SignalStrengthFilter.SamplingInterval = TimeSpan.FromMilliseconds(2000);

            deviceWatcher.Received += DeviceWatcher_Received;
            deviceWatcher.Stopped += DeviceWatcher_Stopped;
            deviceWatcher.Start();
            string msg = "自动发现设备中...";
            MessageChanged(MsgType.NotifyTxt, msg);
        }

        private void DeviceWatcher_Stopped(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementWatcherStoppedEventArgs args)
        {
            
        }

        private void DeviceWatcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
        {
            BluetoothLEDevice.FromBluetoothAddressAsync(args.BluetoothAddress).Completed = (asyncInfo, asyncStatus) =>
            {
                if (asyncStatus == AsyncStatus.Completed)
                {
                    if (asyncInfo.GetResults() != null)
                    {
                        BluetoothLEDevice currentDevice = asyncInfo.GetResults();

                        bool contain = false;
                        foreach (BluetoothLEDevice device in DeviceList)//过滤重复的设备
                        {
                            if (device.DeviceId == currentDevice.DeviceId)
                            {
                                contain = true;
                            }
                        }
                        if (!contain)
                        {
                            byte[] _Bytes1 = BitConverter.GetBytes(currentDevice.BluetoothAddress);
                            Array.Reverse(_Bytes1);

                            DeviceList.Add(currentDevice);
                            MessageChanged(MsgType.NotifyTxt, "发现设备:" + currentDevice.Name + "  address:" + BitConverter.ToString(_Bytes1, 2, 6).Replace('-', ':').ToLower());
                            DeviceWatcherChanged(MsgType.BleDevice, currentDevice);
                        }
                    }
                    else
                    {
                        MessageChanged(MsgType.NotifyTxt, "没有得到结果集");
                    }
                }
            };

        }

        public void StopBleDeviceWathcer()
        {
            if (deviceWatcher != null)
                deviceWatcher.Stop();
        }

        public void StartMatching(BluetoothLEDevice device)
        {
            CurrentDevice = device;
        }


        public void FindService()
        {
            CurrentDevice.GetGattServicesAsync().Completed = (asyncInfo, asyncStatus) =>
            {
                if (asyncStatus == AsyncStatus.Completed)
                {
                    var services = asyncInfo.GetResults().Services;
                    MessageChanged(MsgType.NotifyTxt, "GattService size = " + services.Count);
                    foreach (var service in services)
                    {
                        this.GattDeviceServiceAdded(service);
                        FindCharacteristic(service);
                    }
                    MessageChanged(MsgType.NotifyTxt, "获取特征码完毕!");
                }
            };
        }

        public void FindCharacteristic(GattDeviceService gattDeviceService)
        {
            CurrentService = gattDeviceService;
            CurrentService.GetCharacteristicsAsync().Completed = (asyncInfo, asyncStatus) =>
            {
                if (asyncStatus == AsyncStatus.Completed)
                {
                    var services = asyncInfo.GetResults().Characteristics;
                    foreach (var c in services)
                    {
                        CharacteristicAdded?.Invoke(c);
                    }

                }
            };

        }

        public void SetOpertor(GattCharacteristic gattCharacteristic)
        {
            byte[] _Bytes1 = BitConverter.GetBytes(this.CurrentDevice.BluetoothAddress);
            Array.Reverse(_Bytes1);
            CurrentDeviceMAC = BitConverter.ToString(_Bytes1, 2, 6).Replace('-', ':').ToLower();

            string msg = "正在连接设备<" + this.CurrentDeviceMAC + ">..";
            MessageChanged(MsgType.NotifyTxt, msg);


            if (gattCharacteristic.CharacteristicProperties == GattCharacteristicProperties.Write)
            {
                CurrentWriteCharacteristic = gattCharacteristic;
            }

            if(gattCharacteristic.CharacteristicProperties == GattCharacteristicProperties.Notify)
            {
                CurrentNotifyCharacteristic = gattCharacteristic;
            }

            if((uint)gattCharacteristic.CharacteristicProperties == 26)
            {

            }

            if(gattCharacteristic.CharacteristicProperties == (GattCharacteristicProperties.Notify | 
                GattCharacteristicProperties.Read | GattCharacteristicProperties.Write))
            {
                CurrentWriteCharacteristic = gattCharacteristic;

                CurrentNotifyCharacteristic = gattCharacteristic;
                CurrentNotifyCharacteristic.ProtectionLevel = GattProtectionLevel.Plain;
                CurrentNotifyCharacteristic.ValueChanged += Characteristic_ValueChanged;
                EnableNotifications(CurrentNotifyCharacteristic);

            }
        }


        private void Matching(string Id)
        {
            try
            {
                BluetoothLEDevice.FromIdAsync(Id).Completed = (asyncInfo, asyncStatus) =>
                {
                    if (asyncStatus == AsyncStatus.Completed)
                    {
                        BluetoothLEDevice bleDevice = asyncInfo.GetResults();
                        DeviceList.Add(bleDevice);
                        DeviceWatcherChanged(MsgType.BleDevice, bleDevice);
                    }
                    if (asyncStatus == AsyncStatus.Started)
                    {
                        Console.WriteLine(asyncStatus.ToString());
                    }
                    if (asyncStatus == AsyncStatus.Canceled)
                    {
                        Console.WriteLine(asyncStatus.ToString());
                    }
                    if (asyncStatus == AsyncStatus.Error)
                    {
                        Console.WriteLine(asyncStatus.ToString());
                    }

                };
            }
            catch(Exception e)
            {
                string msg = "没有发现设备" + e.Message;
                MessageChanged(MsgType.NotifyTxt, msg);
                StartBleDeviceWatcher();
            }

        }

        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;
                    CurrentDevice.Dispose();
                    CurrentDevice = null;
                    CurrentService = null;
                    CurrentWriteCharacteristic = null;
                    CurrentNotifyCharacteristic = null;
                    SelectDeviceFromIdAsync(CurrentDeviceMAC);
                }
            }
            else
            {
                string msg = "设备已连接";
                MessageChanged(MsgType.NotifyTxt, msg);
            }
        }

        public void SelectDeviceFromIdAsync(string MAC)
        {
            CurrentDeviceMAC = MAC;
            CurrentDevice = null;
            BluetoothAdapter.GetDefaultAsync().Completed = (asyncInfo, asyncStatus) =>
            {
                if (asyncStatus == AsyncStatus.Completed)
                {
                    BluetoothAdapter adapter = asyncInfo.GetResults();
                    if (adapter != null)
                    {
                        byte[] _Bytes1 = BitConverter.GetBytes(adapter.BluetoothAddress);
                        Array.Reverse(_Bytes1);
                        string macAddress = BitConverter.ToString(_Bytes1, 2, 6).Replace('-', ':').ToLower();
                        string Id = "BluetoothLE#BluetoothLE" + macAddress + "-" + MAC;
                        Matching(Id);
                    }
                    else
                    {
                        string msg = "查找连接蓝牙设备异常!";
                        MessageChanged(MsgType.NotifyTxt, msg);
                    }
                }
            };
            
        }

        public void EnableNotifications(GattCharacteristic characteristic)
        {
            string msg = "收通知对象=" + CurrentDevice.ConnectionStatus;
            this.MessageChanged(MsgType.NotifyTxt, msg);

            characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(CHARACERISTIC_NOTIFICATION_TYPE).Completed =  (asyncInfo, asyncStatus) =>
            {
                if (asyncStatus == AsyncStatus.Completed)
                {
                    GattCommunicationStatus status = asyncInfo.GetResults();
                    if (status == GattCommunicationStatus.Unreachable)
                    {
                        msg = "设备不可用";
                        MessageChanged(MsgType.NotifyTxt, msg);
                        if (CurrentNotifyCharacteristic != null && !asyncLock)
                        {
                            EnableNotifications(CurrentNotifyCharacteristic);
                        }
                    }
                    asyncLock = false;
                    msg = "设备连接状态" + status;
                    MessageChanged(MsgType.NotifyTxt, msg);
                }
            };
        }

        private void Characteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
        {
            CryptographicBuffer.CopyToByteArray(args.CharacteristicValue, out byte[] data);
            string str = BitConverter.ToString(data);
            MessageChanged(MsgType.BleData, str, data);
        }

        public void Write(byte[] data)
        {
            if (CurrentWriteCharacteristic != null)
            {
                CurrentWriteCharacteristic.WriteValueAsync(CryptographicBuffer.CreateFromByteArray(data), GattWriteOption.WriteWithResponse).Completed = (asyncInfo, asyncStatus) =>
                {
                    if (asyncStatus == AsyncStatus.Completed)
                    {
                        GattCommunicationStatus a = asyncInfo.GetResults();
                        string str = "发送数据:" + BitConverter.ToString(data) + "State:" + a;
                        MessageChanged(MsgType.BleData, str, data);
                    }
                };

            }
            
        }
    }

    public enum MsgType
    {
        BleData,
        NotifyTxt,
        BleDevice
    }
}
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Windows.Forms;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.GenericAttributeProfile;

namespace BleSolution
{
    public partial class Form1 : Form
    {
        BleCore bleCore = new BleCore();
        List<BluetoothLEDevice> DeviceList = new List<BluetoothLEDevice>();
        List<GattDeviceService> GattDeviceServices = new List<GattDeviceService>();
        List<GattCharacteristic> GattCharacteristics  = new List<GattCharacteristic>();

        public Form1()
        {
            InitializeComponent();
            CheckForIllegalCrossThreadCalls = false;
            bleCore.MessageChanged += BleCore_MessageChanged;
            bleCore.DeviceWatcherChanged += BleCore_DeviceWatcherChanged;
            bleCore.GattDeviceServiceAdded += BleCore_GattDeviceServiceAdded;
            bleCore.CharacteristicAdded += BleCore_CharacteristicAdded;
            this.Init();
        }

        private void BleCore_GattDeviceServiceAdded(GattDeviceService gattDeviceService)
        {
            RunAsync(() =>
            {
                this.cmbServer.Items.Add(gattDeviceService.Uuid.ToString());
                this.GattDeviceServices.Add(gattDeviceService);
                this.btnFeatures.Enabled = true;
            });

        }

        public static void RunAsync(Action action)
        {
            ((Action)delegate ()
            {
                action.Invoke();
            }).BeginInvoke(null, null);
        }

        private void Init()
        {
            //throw new NotImplementedException();
        }

        private void BleCore_CharacteristicAdded(GattCharacteristic characteristic)
        {
            RunAsync(() =>
            {
                this.cmbFeatures.Items.Add(characteristic.Uuid);
                this.GattCharacteristics.Add(characteristic);
                this.btnOperator.Enabled = true;
            });


        }

        private void BleCore_DeviceWatcherChanged(MsgType type, BluetoothLEDevice bluetoothLEDevice)
        {
            RunAsync(() =>
            {
                //小米体重称
                //if (bluetoothLEDevice.Name.Equals("MI SCALE2") || bluetoothLEDevice.DeviceId.Contains("c8:47:8c:a0:2b:db"))
                //{
                //    this.listboxBleDevice.Items.Add(bluetoothLEDevice.Name);
                //    this.DeviceList.Add(bluetoothLEDevice);
                //    this.btnConnect.Enabled = true;
                //}

                this.listboxBleDevice.Items.Add(bluetoothLEDevice.Name);
                this.DeviceList.Add(bluetoothLEDevice);
                this.btnConnect.Enabled = true;
            });

        }

        private void BleCore_MessageChanged(MsgType type, string message, byte[] data = null)
        {
            RunAsync(() =>
            {
                this.listboxMessage.Items.Add(message);
                if(type == MsgType.BleData)
                {
                    //小米体重数据 第1-2位为体重数据,小端存储。单位为斤(磅)
                    //float weight_kg = Convert.ToInt32(BinaryConversion(date[2], date[1]), 16) / 100f / 2;
                }
            });

        }


        private void btnSearch_Click(object sender, EventArgs e)
        {
            if (this.btnSearch.Text == "搜索")
            {
                this.listboxBleDevice.Items.Clear();
                this.listboxMessage.Items.Clear();
                this.bleCore.StartBleDeviceWatcher();
                this.btnSearch.Text = "停止";
            }
            else
            {
                this.bleCore.StopBleDeviceWathcer();
                this.btnSearch.Text = "搜索";
            }
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            if(this.listboxBleDevice.SelectedItem != null)
            {
                string deviceName = this.listboxBleDevice.SelectedItem.ToString();
                var 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 btnOperator_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.SetOpertor(item);
                if(item.CharacteristicProperties == (GattCharacteristicProperties.Notify | 
                    GattCharacteristicProperties.Read | GattCharacteristicProperties.Write))
                {
                    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[6];
            dataBytes[0] = 0x23;
            dataBytes[1] = 0x00;
            dataBytes[2] = 0x02;
            dataBytes[3] = 0x01;
            dataBytes[4] = (byte)(dataBytes[1] + dataBytes[2] + dataBytes[3]);
            dataBytes[5] = 0x2A;
            this.bleCore.Write(dataBytes);

            //小米体重称
            //GattCharacteristic gattCharacteristic = GattCharacteristics.Find((x) => { return x.Uuid.Equals(new Guid("00002a2f-0000-3512-2118-0009af100700")); });
            //bleCore.SetOpertor(gattCharacteristic);
            //bleCore.Write(new byte[] { 0x01, 0xFF, 0xFF, 0xFF, 0xFF, });
            //bleCore.Write(new byte[] { 0x02 });

        }
    }
}

猜你喜欢

转载自blog.csdn.net/allexw/article/details/129127168