unity 使用蓝牙通讯(PC版,非安卓)

BlueTooth in pc with unity

最近接到的需求是在unity里面开发蓝牙功能,其实一开始我并不慌,因为据我所知,unity有丰富的插件可以使用,但是问题随之而来
1.unity里面无法直接与蓝牙通讯(后来找到了开启runtime一类的东西,但是我找了半天也没找到在哪里可以打开)
2.引入dll通过dll与蓝牙通讯,包括去微软的官网下载c++编译,但是编译过程中种种问题.而我对于c++这一套也不精通.也是放弃了
3.github上找到一个封装好的BLE的一个 这是网址 但是在我的测试中发现 会有搜不到我的蓝牙设备的问题.并且即使连上了,发出的消息也没有回应.所以也是放弃了

于是只能通过一个比较笨的办法,将蓝牙通讯在winform中或者是wpf中,然后将unity程序放在winform或者是wpf的容器中,二者通过udp协议连接传输数据.最后的效果如下:

运行界面

那么如果是这种办法,那就变得简单的多了.winform开发不在话下.unity也是我熟悉的.其中winform支持原生开发,也就是说不用下载任何dll与插件,用到的只是windows.winmd,而这个windows系统是自带的.

将winmd类型文件引入后,编写开发工具类:

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.Networking;
using Windows.Networking.Proximity;
using Windows.Networking.Sockets;
using Windows.Security.Cryptography;
using Windows.Storage.Streams;

namespace Bluetooth
{
    internal class BleCore
    {
        private Boolean asyncLock = false;

        /// <summary>
        /// 搜索蓝牙设备对象
        /// </summary>
        private BluetoothLEAdvertisementWatcher watcher;

        /// <summary>
        /// 当前连接的蓝牙设备
        /// </summary>
        public BluetoothLEDevice CurrentDevice { 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 DeviceScanEvent(BluetoothLEDevice bluetoothLEDevice);

        /// <summary>
        /// 搜索蓝牙事件
        /// </summary>
        public event DeviceScanEvent DeviceScan;

        /// <summary>
        /// 提示信息委托
        /// </summary>
        public delegate void MessAgeLogEvent(MsgType type, string message, byte[] data = null);

        /// <summary>
        /// 提示信息事件
        /// </summary>
        public event MessAgeLogEvent MessAgeLog;

        /// <summary>
        /// 接收通知委托
        /// </summary>
        public delegate void ReceiveNotificationEvent(GattCharacteristic sender, byte[] data);

        /// <summary>
        /// 接收通知事件
        /// </summary>
        public event ReceiveNotificationEvent ReceiveNotification;

        /// <summary>
        /// 获取服务委托
        /// </summary>
        public delegate void DeviceFindServiceEvent(DeviceService deviceService);

        /// <summary>
        /// 获取服务事件
        /// </summary>
        public event DeviceFindServiceEvent DeviceFindService;

        /// <summary>
        /// 蓝牙状态委托
        /// </summary>
        public delegate void DeviceConnectionStatusEvent(BluetoothConnectionStatus status);

        /// <summary>
        /// 蓝牙状态事件
        /// </summary>
        public event DeviceConnectionStatusEvent DeviceConnectionStatus;

        /// <summary>
        /// 当前连接的蓝牙Mac
        /// </summary>
        private string CurrentDeviceMAC { get; set; }

        public BleCore()
        {
        }

        /// <summary>
        /// 搜索蓝牙设备
        /// </summary>
        public void StartBleDeviceWatcher()
        {
            watcher = new BluetoothLEAdvertisementWatcher();

            watcher.ScanningMode = BluetoothLEScanningMode.Active;

            //只有在接收到数值大于等于 -80 的情况下才激活监视器
            watcher.SignalStrengthFilter.InRangeThresholdInDBm = -80;

            // 如果数值低于 -90(用户离开),则停止监测
            watcher.SignalStrengthFilter.OutOfRangeThresholdInDBm = -90;

            // 注册回调函数,以便在我们看到广播时触发(执行)相关操作
            watcher.Received += OnAdvertisementReceived;

            // 等待 5 秒钟以确保设备确实超出范围
            watcher.SignalStrengthFilter.OutOfRangeTimeout = TimeSpan.FromMilliseconds(5000);
            watcher.SignalStrengthFilter.SamplingInterval = TimeSpan.FromMilliseconds(2000);

            // starting watching for advertisements
            this.DeviceList.Clear();
            watcher.Start();
            string msg = "开始搜索蓝牙设备...";

            this.MessAgeLog(MsgType.NotifyTxt, msg);
        }

        /// <summary>
        /// 停止搜索蓝牙
        /// </summary>
        public void StopBleDeviceWatcher()
        {
            this.watcher.Stop();
        }

        private void OnAdvertisementReceived(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs eventArgs)
        {
            //this.MessAgeChanged(MsgType.NotifyTxt, "发现设备FR_NAME:"+ eventArgs.Advertisement.LocalName + "BT_ADDR: " + eventArgs.BluetoothAddress);
            BluetoothLEDevice.FromBluetoothAddressAsync(eventArgs.BluetoothAddress).Completed = (asyncInfo, asyncStatus) =>
            {
                if (asyncStatus == AsyncStatus.Completed)
                {
                    if (asyncInfo.GetResults() != null)
                    {
                        BluetoothLEDevice currentDevice = asyncInfo.GetResults();

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

                            if (currentDevice.DeviceInformation.Name != "")
                            {
                                str = "发现设备:" + currentDevice.DeviceInformation.Name + " - MAC: \r\n" +
                                BitConverter.ToString(_Bytes1, 2, 6).Replace('-', ':').ToUpper() + " - ID: /r/n" + currentDevice.DeviceInformation.Id;
                            }
                            //舍弃没有名字的设备(匿名设备)
                            else
                            {
                                //    str = "发现设备:" + BitConverter.ToString(_Bytes1, 2, 6).Replace('-', ':').ToUpper();
                                return;
                            }
                            this.MessAgeLog(MsgType.NotifyTxt, str);
                            this.DeviceScan(currentDevice);
                        }
                    }
                }
            };
        }

        /// <summary>
        /// 匹配
        /// </summary>
        /// <param name="Device"></param>
        public void StartMatching(BluetoothLEDevice Device)
        {
            this.CurrentDevice?.Dispose();
            this.CurrentDevice = Device;
            Connect();
            FindService();
        }

        /// <summary>
        /// 获取蓝牙服务
        /// </summary>
        public async void FindService()
        {
            this.MessAgeLog(MsgType.NotifyTxt, "开始获取服务列表");
            this.CurrentDevice.GetGattServicesAsync().Completed = async (asyncInfo, asyncStatus) =>
            {
                if (asyncStatus == AsyncStatus.Completed)
                {
                    var services = asyncInfo.GetResults().Services;
                    //this.MessAgeChanged(MsgType.NotifyTxt, "GattServices size=" + services.Count);
                    foreach (GattDeviceService ser in services)
                    {
                        FindCharacteristic(ser);
                    }
                }
            };
        }

        /// <summary>
        /// 获取特性
        /// </summary>
        public async void FindCharacteristic(GattDeviceService gattDeviceService)
        {
            IAsyncOperation<GattCharacteristicsResult> result = gattDeviceService.GetCharacteristicsAsync();
            result.Completed = async (asyncInfo, asyncStatus) =>
            {
                if (asyncStatus == AsyncStatus.Completed)
                {
                    var characters = asyncInfo.GetResults().Characteristics;

                    List<GattCharacteristic> characteristics = new List<GattCharacteristic>();
                    foreach (GattCharacteristic characteristic in characters)
                    {
                        characteristics.Add(characteristic);
                        this.MessAgeLog(MsgType.NotifyTxt, "服务UUID:" + gattDeviceService.Uuid.ToString() + " --- 特征UUID:" + characteristic.Uuid.ToString());
                    }

                    DeviceService deviceService = new DeviceService();
                    deviceService.gattDeviceService = gattDeviceService;
                    deviceService.gattCharacteristic = characteristics;

                    this.DeviceFindService(deviceService);
                }
            };
        }

        /// <summary>
        /// 获取操作
        /// </summary>
        /// <returns></returns>
        public async Task SetNotify(GattCharacteristic gattCharacteristic)
        {
            GattCharacteristic CurrentNotifyCharacteristic;
            if ((gattCharacteristic.CharacteristicProperties & GattCharacteristicProperties.Notify) != 0)
            {
                CurrentNotifyCharacteristic = gattCharacteristic;
                CurrentNotifyCharacteristic.ProtectionLevel = GattProtectionLevel.Plain;
                CurrentNotifyCharacteristic.ValueChanged += Characteristic_ValueChanged;
                await this.EnableNotifications(CurrentNotifyCharacteristic);
            }
        }

        /// <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.ToUpper() + " ...";
            this.MessAgeLog(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.DeviceScan(bleDevice);
                        this.CurrentDevice = bleDevice;
                        FindService();
                    }
                };
            }
            catch (Exception e)
            {
                string msg = "没有发现设备" + e.ToString();
                this.MessAgeLog(MsgType.NotifyTxt, msg);
                this.StartBleDeviceWatcher();
            }
        }

        /// <summary>
        /// 主动断开连接
        /// </summary>
        /// <returns></returns>
        public void Dispose()
        {
            CurrentDeviceMAC = null;
            CurrentDevice?.Dispose();
            CurrentDevice = null;

            MessAgeLog(MsgType.NotifyTxt, "主动断开连接");
        }

        private void CurrentDevice_ConnectionStatusChanged(BluetoothLEDevice sender, object args)
        {
            this.DeviceConnectionStatus(sender.ConnectionStatus);
            if (sender.ConnectionStatus == BluetoothConnectionStatus.Disconnected && CurrentDeviceMAC != null)
            {
                string msg = "设备已断开,自动重连...";
                MessAgeLog(MsgType.NotifyTxt, msg);
                if (!asyncLock)
                {
                    asyncLock = true;
                    this.CurrentDevice.Dispose();
                    this.CurrentDevice = null;
                    SelectDeviceFromIdAsync(CurrentDeviceMAC);
                }
            }
            else
            {
                string msg = "设备已连接!";
                MessAgeLog(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();
                    byte[] _Bytes1 = BitConverter.GetBytes(mBluetoothAdapter.BluetoothAddress);//ulong转换为byte数组
                    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)
        {
            if (CurrentDevice.ConnectionStatus != BluetoothConnectionStatus.Connected)
            {
                this.MessAgeLog(MsgType.NotifyTxt, "蓝牙未连接!");
                return;
            }

            characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(CHARACTERISTIC_NOTIFICATION_TYPE).Completed = async (asyncInfo, asyncStatus) =>
            {
                Console.WriteLine("asyncStatus:" + asyncStatus);
                if (asyncStatus == AsyncStatus.Completed)
                {
                    GattCommunicationStatus status = asyncInfo.GetResults();

                    asyncLock = false;
                    string msg = "Notify(" + characteristic.Uuid.ToString() + "):" + status;
                    this.MessAgeLog(MsgType.NotifyTxt, msg);
                }
                else
                {
                    Console.WriteLine(asyncInfo.ErrorCode.ToString());
                }
            };
        }

        /// <summary>
        /// 接受到蓝牙数据
        /// </summary>
        private void Characteristic_ValueChanged(GattCharacteristic characteristic, GattValueChangedEventArgs args)
        {
            byte[] data;
            CryptographicBuffer.CopyToByteArray(args.CharacteristicValue, out data);
            string str = System.Text.Encoding.UTF8.GetString(data);
            this.ReceiveNotification(characteristic, data);

            this.MessAgeLog(MsgType.BleData, "收到数据(" + characteristic.Uuid.ToString() + "):" + str);
        }

        /// <summary>
        /// 发送数据接口
        /// </summary>
        /// <returns></returns>
        public async Task Write(GattCharacteristic writeCharacteristic, byte[] data)
        {
            if (writeCharacteristic != null)
            {
                string str = "发送数据(" + writeCharacteristic.Uuid.ToString() + "):" + BitConverter.ToString(data);
                this.MessAgeLog(MsgType.BleData, str, data);
                IAsyncOperation<GattCommunicationStatus> async = writeCharacteristic.WriteValueAsync(CryptographicBuffer.CreateFromByteArray(data), GattWriteOption.WriteWithResponse);
                async.Completed = async (asyncInfo, asyncStatus) =>
                {
                    if (asyncStatus == AsyncStatus.Completed)
                    {
                        this.MessAgeLog(MsgType.BleData, "数据发送成功!");
                    }
                    else
                    {
                        this.MessAgeLog(MsgType.BleData, "数据发送失败:" + asyncInfo.ErrorCode.ToString());
                    }
                };
            }
        }
    }

    public enum MsgType
    {
        NotifyTxt,
        BleData,
        BleDevice
    }

    public class DeviceService
    {
        public GattDeviceService gattDeviceService;
        public List<GattCharacteristic> gattCharacteristic;
    }
}

然后对于页面的按钮进行赋值,并将unity开发的exe放在合适的位置,其中通讯部分只需要简单的通讯即可,不需要考虑什么分包问题.因为本机通讯很稳定.
winform部分的udp:

using System;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace VincentUDP
{
    public class UdpServer
    {
        private UdpClient udpClient;
        private IPEndPoint remoteEndPoint;

        public void Init()
        {
            int port = 8888; // 选择一个端口
            udpClient = new UdpClient();
            remoteEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), port); // 目标IP和端口
        }

        public void Send(byte[] message)
        {
            // byte[] data = Encoding.UTF8.GetBytes(message);
            udpClient.Send(message, message.Length, remoteEndPoint);
        }

        public void Close()
        {
            udpClient.Close();
        }
    }
}

unity接收部分:

using System;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using UnityEngine;

public class RecvUDPMsg : MonoBehaviour
{
    private UdpClient udpClient;

    //消息队列
    private Queue queue = new Queue();

    private void Start()
    {
        // 初始化UDP客户端
        udpClient = new UdpClient(8888); // 监听的端口
        StartReceiving();
    }

    private void StartReceiving()
    {
        udpClient.BeginReceive(ReceiveCallback, null);
    }

    private void ReceiveCallback(IAsyncResult ar)
    {
        IPEndPoint remoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
        byte[] receivedBytes = udpClient.EndReceive(ar, ref remoteIpEndPoint);
        // string receivedText = Encoding.UTF8.GetString(receivedBytes);

        //Debug.Log("Received: " + receivedText);
        queue.Enqueue(receivedBytes);
        // 继续监听
        StartReceiving();
    }

    private void OnDestroy()
    {
        // 关闭UDP客户端
        udpClient.Close();
    }
}

那么最后的运行如下:
最终效果
最后附上下载地址: 点击这里
PS:如果下载不了(非csdn会员)请私信我.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Unity-Plane

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值