【WPF、UWP】搜索蓝牙设备

1 篇文章 0 订阅

基于Inthehand开源库开发

搜了一下网上的代码,基本上都是采用同步方式,即蓝牙固定扫描一段时间,然后返回这段时间内扫描到的所有蓝牙设备;缺点是耗时太长;下边给出另一种解决方法,采用了事件方式,每发现一个设备,就触发一次事件,实测耗时大大缩短;

准备工作

在nuget中搜索,32feet,我使用的是3.5版本

代码如下

Test.cs

using InTheHand.Net.Bluetooth;
using InTheHand.Net.Sockets;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Test.Ble
{
    class BleDiscover
    {
        bool findBleDevice = false;

        string tagDeviceAddress = null;
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="x">需要搜索的蓝牙mac地址</param>
        public BleDiscover(string x)
        {
            tagDeviceAddress = x;

            BluetoothClient bluetoothClient = new BluetoothClient();

            //bluetoothClient.DiscoverDevices();

            BluetoothRadio bluetoothRadio = BluetoothRadio.PrimaryRadio;
            bluetoothRadio.Mode = RadioMode.Connectable;

            BluetoothComponent bluetoothComponent = new BluetoothComponent(bluetoothClient);

            bluetoothComponent.DiscoverDevicesAsync(10, false, false, false, true, bluetoothComponent);

            bluetoothComponent.DiscoverDevicesProgress += BluetoothComponent_DiscoverDevicesProgress;

            Thread findBleDeviceThread = new Thread(findBleDeviceThreadFun);
            findBleDeviceThread.Start();
        }

        private void findBleDeviceThreadFun()
        {
            //throw new NotImplementedException();
            while (true)
            {
                if (findDeviceEventArgsHandler != null)
                {
                    FindDeviceEventArgs findDeviceEventArgs = new FindDeviceEventArgs();
                    findDeviceEventArgs.isFinded = findBleDevice;

                    findDeviceEventArgsHandler(this, findDeviceEventArgs);                            
                }

                Thread.Sleep(500);
            }
        }

        private void BluetoothComponent_DiscoverDevicesProgress(object sender, DiscoverDevicesEventArgs e)
        {
            //throw new NotImplementedException();            

            if (e.Devices[0].DeviceAddress.ToString() == tagDeviceAddress && findBleDevice == false)
            {
                //MessageBox.Show("Find my MI9");
                findBleDevice = true;
            }
        }

        /// <summary>
        /// 声明事件
        /// </summary>
        public event FindDeviceEventArgsHandler findDeviceEventArgsHandler;      
    }

    #region 找到设备事件
    /// <summary>
    /// 1、声明参数类型
    /// </summary>
    public class FindDeviceEventArgs : EventArgs
    {
        public bool isFinded;
    }

    /// <summary>
    /// 2、声明委托类型
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public delegate void FindDeviceEventArgsHandler(object sender, FindDeviceEventArgs e);

    //事件源头是 BleDiscover 这个class
    #endregion
}

MainWindows.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using InTheHand.Net.Bluetooth;
using InTheHand.Net.Sockets;

namespace WpfAppForBlueTooth
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {        
        string macAddress = "B4C9B92ccxxx";

        public MainWindow()
        {
            InitializeComponent();
            
            #region 方法1
            BluetoothClient bluetoothClient = new BluetoothClient();
            BluetoothRadio bluetoothRadio = BluetoothRadio.PrimaryRadio;
            bluetoothRadio.Mode = RadioMode.Connectable;

            Test.Ble.BleDiscover bleDiscover = new Test.Ble.BleDiscover(macAddress);

            bleDiscover.findDeviceEventArgsHandler += BleDiscover_findDeviceEventArgsHandler;
            #endregion

            //#region 方法2
            //BluetoothClient bluetoothClient = new BluetoothClient();
            //BluetoothRadio bluetoothRadio = BluetoothRadio.PrimaryRadio;
            //bluetoothRadio.Mode = RadioMode.Connectable;
            //while (true)
            //{                               
            //    BluetoothDeviceInfo[] bluetoothDeviceInfos = bluetoothClient.DiscoverDevices();  //固定10s

            //    Thread.Sleep(1000);
            //}            
            //#endregion
        }

        private void BleDiscover_findDeviceEventArgsHandler(object sender, Test.Ble.FindDeviceEventArgs e)
        {
            //throw new C();
            MessageBox.Show(e.isFinded.ToString());
        }
    }
}

一个挺大的问题,就是Inthehand这个库不支持BLE,实际上这个开源库也只是将windows的api封装了一下,而win8之前,windows并不支持BLE,所以…

如果需要BLE的话,一种方法是使用WIN10的API,用UWP开发,可以获得最好的支持;可参考GitHub的开源项目Windows-universal-samples

测试程序

基本是使用了GitHub上的代码,通过断点调试可以发现能够找到BLE的蓝牙设备;

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.Devices.Bluetooth;
using System.ServiceModel.Channels;
using Windows.UI.Popups;
using Windows.ApplicationModel;
using Windows.Devices.Bluetooth.Advertisement;
using System.Collections.ObjectModel;
using Windows.Devices.Enumeration;
using Windows.UI.Core;
using System.Diagnostics;

// https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x804 上介绍了“空白页”项模板

namespace UwpForBle
{
    /// <summary>
    /// 可用于自身或导航至 Frame 内部的空白页。
    /// </summary>
    public sealed partial class MainPage : Page
    {     

        private DeviceWatcher deviceWatcher;

        private List<DeviceInformation> devices = new List<DeviceInformation>();
        
        public MainPage()
        {
            InitializeComponent();
        }


        
        private async void onButtonClick(object sender, RoutedEventArgs e)
        {
            var dialog = new MessageDialog("当前设置尚未保存,你确认要退出该页面吗?", "消息提示");

            dialog.Commands.Add(new UICommand("确定", cmd => { }, commandId: 0));
            dialog.Commands.Add(new UICommand("取消", cmd => { }, commandId: 1));

            //设置默认按钮,不设置的话默认的确认按钮是第一个按钮
            dialog.DefaultCommandIndex = 0;
            dialog.CancelCommandIndex = 1;

            //获取返回值
            var result = await dialog.ShowAsync();
        }      

        #region Device discovery
        /// <summary>
        /// Starts a device watcher that looks for all nearby Bluetooth devices (paired or unpaired). 
        /// Attaches event handlers to populate the device collection.
        /// </summary>
        private void StartBleDeviceWatcher()
        {
            // Additional properties we would like about the device.
            // Property strings are documented here https://msdn.microsoft.com/en-us/library/windows/desktop/ff521659(v=vs.85).aspx
            string[] requestedProperties = { "System.Devices.Aep.DeviceAddress", "System.Devices.Aep.IsConnected", "System.Devices.Aep.Bluetooth.Le.IsConnectable" };

            // BT_Code: Example showing paired and non-paired in a single query.
            string aqsAllBluetoothLEDevices = "(System.Devices.Aep.ProtocolId:=\"{bb7bb05e-5972-42b5-94fc-76eaa7084d49}\")";

            deviceWatcher =
                    DeviceInformation.CreateWatcher(
                        aqsAllBluetoothLEDevices,
                        requestedProperties,
                        DeviceInformationKind.AssociationEndpoint);

            // Register event handlers before starting the watcher.
            deviceWatcher.Added += DeviceWatcher_Added;
            //deviceWatcher.Updated += DeviceWatcher_Updated;
            //deviceWatcher.Removed += DeviceWatcher_Removed;
            //deviceWatcher.EnumerationCompleted += DeviceWatcher_EnumerationCompleted;
            //deviceWatcher.Stopped += DeviceWatcher_Stopped;

            // Start over with an empty collection.
            devices.Clear();

            // Start the watcher. Active enumeration is limited to approximately 30 seconds.
            // This limits power usage and reduces interference with other Bluetooth activities.
            // To monitor for the presence of Bluetooth LE devices for an extended period,
            // use the BluetoothLEAdvertisementWatcher runtime class. See the BluetoothAdvertisement
            // sample for an example.
            deviceWatcher.Start();
        }

        /// <summary>
        /// Stops watching for all nearby Bluetooth devices.
        /// </summary>
        private void StopBleDeviceWatcher()
        {
            if (deviceWatcher != null)
            {
                // Unregister the event handlers.
                deviceWatcher.Added -= DeviceWatcher_Added;
                //deviceWatcher.Updated -= DeviceWatcher_Updated;
                //deviceWatcher.Removed -= DeviceWatcher_Removed;
                //deviceWatcher.EnumerationCompleted -= DeviceWatcher_EnumerationCompleted;
                //deviceWatcher.Stopped -= DeviceWatcher_Stopped;

                // Stop the watcher.
                deviceWatcher.Stop();
                deviceWatcher = null;
            }
        }

        private async void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation deviceInfo)
        {
            // We must update the collection on the UI thread because the collection is databound to a UI element.
            await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                lock (this)
                {
                    Debug.WriteLine(String.Format("Added {0}{1}", deviceInfo.Id, deviceInfo.Name));

                    // Protect against race condition if the task runs after the app stopped the deviceWatcher.
                    if (sender == deviceWatcher)
                    {
                        // Make sure device isn't already present in the list.
                        if (deviceInfo.Id == null)
                        {
                            if (deviceInfo.Name != string.Empty)
                            {
                                // If device has a friendly name display it immediately.
                                devices.Add(deviceInfo);
                            }
                            else
                            {
                                // Add it to a list in case the name gets updated later. 
                                devices.Add(deviceInfo);
                            }
                        }

                    }
                }
            });
        }
        #endregion

        private void EnumerateButton_Click(object sender, RoutedEventArgs e)
        {
            if (deviceWatcher == null)
            {
                StartBleDeviceWatcher();
                EnumerateButton.Content = "Stop enumerating";                
            }
            else
            {
                StopBleDeviceWatcher();
                EnumerateButton.Content = "Start enumerating";                
            }
        }
    }
}

总结如下:

Windows蓝牙开发

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的 WPF 应用程序,它可以利用蓝牙设备接收心电波形数据并实时绘制心电图。在本示例中,我们将使用 WPF 的 SerialPort 类来模拟蓝牙设备,生成随机的心电波形数据并将其发送到电脑上。在实际的应用中,您需要使用适合您的蓝牙设备的 API 来实现数据传输。 首先,我们需要在 XAML 中创建一个 Canvas 控件来绘制心电图: ```xml <Canvas x:Name="canvas" Width="500" Height="200"> <Path Stroke="Red" StrokeThickness="2"> <Path.Data> <PathGeometry> <PathFigure StartPoint="0,100"> <PolyLineSegment Points="0,100"/> </PathFigure> </PathGeometry> </Path.Data> </Path> </Canvas> ``` 在代码中,我们将起点设置为 (0,100),表示心电波形的起始位置。然后,我们将 PolyLineSegment 的 Points 属性设置为一个空的字符串,这样我们可以在后面的代码中动态地添加点。 接下来,我们需要在代码中创建一个 SerialPort 对象,并在窗口加载时打开它: ```csharp private SerialPort serialPort; private void Window_Loaded(object sender, RoutedEventArgs e) { // 创建 SerialPort 对象 serialPort = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One); serialPort.Open(); } ``` 在本示例中,我们使用 COM1 端口来模拟蓝牙设备。您需要根据实际情况更改端口号和其他参数。 然后,我们需要使用一个计时器来定期读取蓝牙设备的数据,并将其添加到心电波形中: ```csharp private DispatcherTimer timer; private void Window_Loaded(object sender, RoutedEventArgs e) { // 创建计时器 timer = new DispatcherTimer(); timer.Interval = TimeSpan.FromSeconds(0.1); timer.Tick += Timer_Tick; timer.Start(); } private void Timer_Tick(object sender, EventArgs e) { // 读取数据并添加到心电波形中 if (serialPort.BytesToRead > 0) { double[] data = ReadData(); AddData(data); } } private double[] ReadData() { // 从 SerialPort 中读取数据 byte[] buffer = new byte[1024]; int count = serialPort.Read(buffer, 0, buffer.Length); string text = Encoding.ASCII.GetString(buffer, 0, count); // 将数据转换为 double 类型的数组 string[] parts = text.Split(','); double[] data = new double[parts.Length]; for (int i = 0; i < parts.Length; i++) { data[i] = double.Parse(parts[i]); } return data; } private void AddData(double[] data) { // 获取 Path 对象和 PathGeometry 对象 Path path = canvas.Children.OfType<Path>().First(); PathGeometry geometry = path.Data as PathGeometry; // 获取 PolyLineSegment 对象并添加新的数据点 PolyLineSegment segment = geometry.Figures.First().Segments.OfType<PolyLineSegment>().First(); for (int i = 0; i < data.Length; i++) { segment.Points.Add(new Point(i, 100 - data[i])); } // 重新绘制心电波形 path.Data = geometry; } ``` 在代码中,我们首先创建一个 DispatcherTimer 对象,用于定期读取蓝牙设备的数据。在定时器的 Tick 事件中,我们首先检查 SerialPort 的 BytesToRead 属性是否大于 0,如果是,则读取数据并将其添加到心电波形中。在本示例中,我们假设数据以逗号分隔的字符串形式发送到电脑上,您需要根据实际情况更改读取数据的代码。 然后,我们将读取的数据添加到心电波形中。在 AddData 方法中,我们获取 Path 对象和 PathGeometry 对象,然后获取 PolyLineSegment 对象并将新的数据点添加到它的 Points 属性中。最后,我们重新将 PathGeometry 对象设置为 Path 的 Data 属性,以便重新绘制心电波形。 这就是一个简单的 WPF 应用程序,它可以利用蓝牙设备接收心电波形数据并实时绘制心电图。在实际的应用中,您需要使用适合您的蓝牙设备的 API 来实现数据传输。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值