unity 使用蓝牙通讯(PC版,非安卓)_unity蓝牙通信
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; /// /// 搜索蓝牙设备对象 /// private BluetoothLEAdvertisementWatcher watcher; /// /// 当前连接的蓝牙设备 /// public BluetoothLEDevice CurrentDevice { get; set; } /// /// 特性通知类型通知启用 /// private const GattClientCharacteristicConfigurationDescriptorValue CHARACTERISTIC_NOTIFICATION_TYPE = GattClientCharacteristicConfigurationDescriptorValue.Notify; /// /// 存储检测到的设备 /// private List<BluetoothLEDevice> DeviceList = new List<BluetoothLEDevice>(); /// /// 搜索蓝牙设备委托 /// public delegate void DeviceScanEvent(BluetoothLEDevice bluetoothLEDevice); /// /// 搜索蓝牙事件 /// public event DeviceScanEvent DeviceScan; /// /// 提示信息委托 /// public delegate void MessAgeLogEvent(MsgType type, string message, byte[] data = null); /// /// 提示信息事件 /// public event MessAgeLogEvent MessAgeLog; /// /// 接收通知委托 /// public delegate void ReceiveNotificationEvent(GattCharacteristic sender, byte[] data); /// /// 接收通知事件 /// public event ReceiveNotificationEvent ReceiveNotification; /// /// 获取服务委托 /// public delegate void DeviceFindServiceEvent(DeviceService deviceService); /// /// 获取服务事件 /// public event DeviceFindServiceEvent DeviceFindService; /// /// 蓝牙状态委托 /// public delegate void DeviceConnectionStatusEvent(BluetoothConnectionStatus status); /// /// 蓝牙状态事件 /// public event DeviceConnectionStatusEvent DeviceConnectionStatus; /// /// 当前连接的蓝牙Mac /// private string CurrentDeviceMAC { get; set; } public BleCore() { } /// /// 搜索蓝牙设备 /// 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); } /// /// 停止搜索蓝牙 /// 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); } } } }; } /// /// 匹配 /// /// public void StartMatching(BluetoothLEDevice Device) { this.CurrentDevice?.Dispose(); this.CurrentDevice = Device; Connect(); FindService(); } /// /// 获取蓝牙服务 /// 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); } } }; } /// /// 获取特性 /// 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); } }; } /// /// 获取操作 /// /// 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); } } /// /// 连接蓝牙 /// /// 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; } /// /// 搜索到的蓝牙设备 /// /// 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(); } } /// /// 主动断开连接 /// /// 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); } } /// /// 按MAC地址直接组装设备ID查找设备 /// 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); } }; } /// /// 设置特征对象为接收通知对象 /// /// /// 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()); } }; } /// /// 接受到蓝牙数据 /// 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); } /// /// 发送数据接口 /// /// 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会员)请私信我.