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 advertisementsthis.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会员)请私信我.