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會員)請私信我.