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