我的初衷是直接用Unity实现PC端连接蓝牙,接收蓝牙发送的数据。但是能力有限,在网上也找了很久,一直没有找到解决方案。就只能退而求其次通过Unity+WindowsForms两个平台实现这一功能。
项目实例:
https://download.csdn.net/download/qq_34421469/14988605
1.使用WindowsForms连接到蓝牙,接收蓝牙信息后,通过UDP发送(我这里指定了蓝牙,也可以通过查找蓝牙,获取到蓝牙信息连接接收数据)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Devices.Enumeration;
using Windows.Storage.Streams;
namespace BlueTooth
{
public partial class Form1 : Form
{
private static BluetoothLEDevice bluetoothLeDevice = null;
static DeviceWatcher deviceWatcher = null;
static DeviceInformation mydeviceInformation = null;
static List<DeviceInformation> deviceInformation = null;
static GattCharacteristic gatt_notify, gatt_write;
static Thread threadMain;
static Mutex _mutex;
private string pointIP = "d2:67:9d:ce:b7:ad";// 目标蓝牙mac地址
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
deviceInformation = new List<DeviceInformation>();
threadMain = new Thread(new ThreadStart(StartThread));
threadMain.Start();
FirstStep();
UdpPeer(1234);
}
void StartThread()
{
while (true)
{
if (queueRec.Count > 0)
{
string str = queueRec.Dequeue();
string[] data = str.Split(':');
switch (data[0])
{
case "10003":
if (data[1].CompareTo("Start") == 0)
{
ss = "";
//开启
FirstStep();
}
else if (data[1].CompareTo("Close") == 0)
{
MethodInvoker action_Close = delegate
{
textBox1.Text += "Close+";
};
textBox1.BeginInvoke(action_Close);
CloseDevice("StartThread");
Application.Exit();
}
break;
default:break;
}
}
}
}
/// <summary>
/// 第一步
/// </summary>
void FirstStep()
{
while (true)
{
if (bluetoothLeDevice != null)
{
bluetoothLeDevice.Dispose();
bluetoothLeDevice = null;
MethodInvoker action_s = delegate
{
textBox1.Text = "正在断开蓝牙连接.......";
};
textBox1.BeginInvoke(action_s);
Thread.Sleep(1000);
}
else
{
//第一步
FindDevive();
break;
}
}
}
/// <summary>
/// 查找设备
/// </summary>
void FindDevive()
{
deviceInformation = new List<DeviceInformation>();
MethodInvoker action_s = delegate
{
textBox1.Text += "正在搜素设备,请稍后\r\n";
};
textBox1.BeginInvoke(action_s);
string[] requestedProperties = { "System.Devices.Aep.DeviceAddress", "System.Devices.Aep.IsConnected" };
deviceWatcher = DeviceInformation.CreateWatcher(
BluetoothLEDevice.GetDeviceSelectorFromPairingState(false),//查找未配对蓝牙?
requestedProperties,
DeviceInformationKind.AssociationEndpoint);
deviceWatcher.Added += DeviceWatcher_Added;
deviceWatcher.Updated += DeviceWatcher_Updated;
deviceWatcher.Removed += DeviceWatcher_Removed;
deviceWatcher.EnumerationCompleted += DeviceWatcher_EnumerationCompleted;
deviceWatcher.Stopped += DeviceWatcher_Stopped;
// Start the watcher.
deviceWatcher.Start();
}
private void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation args)
{
if (getBB(args.Id))
{
MethodInvoker action = delegate
{
deviceInformation.Add(args);
string mac = "" + args.Id.Split('-')[1];
string blenum = "" + mac.Replace(":", "").Substring(2);
blenum = "" + Convert.ToInt64(blenum, 16);
if (string.Compare(args.Id.Split('-')[1], pointIP) == 0)
{
//第二步:发现目标设备,开始匹配
LinkingDevice();
}
};
textBox1.BeginInvoke(action);
}
}
/// <summary>
/// 连接设备
/// </summary>
void LinkingDevice()
{
MethodInvoker action_s = delegate
{
textBox1.Text += "连接设备,请稍后----\r\n";
};
textBox1.BeginInvoke(action_s);
foreach (var deviceinfo in deviceInformation)
{
MethodInvoker action_deviceinfo = delegate
{
// textBox1.Text += "deviceinfo.Id+" + deviceinfo.Id + "---pointIP----+" + pointIP + "\r\n";
};
textBox1.BeginInvoke(action_deviceinfo);
if (deviceinfo.Id.Contains("" + pointIP))
{
MethodInvoker action = delegate
{
//3
textBox1.Text += "匹配IP蓝牙:" + deviceinfo.Id + ",请稍后\r\n\r\n";
};
textBox1.BeginInvoke(action);
mydeviceInformation = deviceinfo;
}
}
if (null != mydeviceInformation)
{
ConnectDevice(mydeviceInformation);
}
else
{
MethodInvoker action_1 = delegate
{
textBox1.Text += "连接失败,未搜索到该设备。\r\n";
};
textBox1.BeginInvoke(action_1);
}
deviceWatcher.Stop();
deviceWatcher = null;
}
/// <summary>
/// 写入信息
/// </summary>
/// <param name="hexstring"></param>
async void BLE_HexString_Write(string hexstring)
{
var writer = new DataWriter();
writer.WriteBytes(HexStringToByteArray(hexstring));
GattCommunicationStatus sta1 = await gatt_write.WriteValueAsync(writer.DetachBuffer());
if (sta1 == GattCommunicationStatus.Success)
{
//Console.WriteLine("Send :" + hexstring);
MethodInvoker invoker = delegate {
textBox1.Text += (hexstring + "\r\n");
};
textBox1.BeginInvoke(invoker);
}
}
/// <summary>
/// 关闭设备
/// </summary>
void CloseDevice(string message)
{
ClearBluetoothLEDevice();
gatt_write = null;
mydeviceInformation = null;
deviceInformation = new List<DeviceInformation>();
ss = "";
if (deviceWatcher!=null)
{
deviceWatcher = null;
}
MethodInvoker invoker = delegate {
textBox1.Text += (message + ":已断开设备连接。\r\n");
};
textBox1.BeginInvoke(invoker);
}
private void DeviceWatcher_Stopped(DeviceWatcher sender, object args)
{
//throw new NotImplementedException();
// CloseDevice("DeviceWatcher_Stopped");
}
private void DeviceWatcher_EnumerationCompleted(DeviceWatcher sender, object args)
{
//throw new NotImplementedException();
}
private void DeviceWatcher_Removed(DeviceWatcher sender, DeviceInformationUpdate args)
{
//throw new NotImplementedException();
// CloseDevice("DeviceWatcher_Removed");
}
private void DeviceWatcher_Updated(DeviceWatcher sender, DeviceInformationUpdate args)
{
//throw new NotImplementedException();
}
private bool ClearBluetoothLEDevice()
{
try
{
if (gatt_notify != null)
gatt_notify.ValueChanged -= S1_ValueChanged;
if (bluetoothLeDevice != null)
{
bluetoothLeDevice.Dispose();
bluetoothLeDevice = null;
}
return true;
}
catch (Exception ep1)
{
MethodInvoker invoker = delegate {
textBox1.Text += ep1.ToString();
};
textBox1.BeginInvoke(invoker);
}
return false;
}
string ss = "";
private bool getBB(string s)
{
if (ss.Contains(s))
return false;
else
{
ss += s;
return true;
}
}
private delegate void InvokeCallback(string msg);
async void ConnectDevice(DeviceInformation deviceInfo)
{
bluetoothLeDevice = await BluetoothLEDevice.FromIdAsync(deviceInfo.Id);
GattDeviceServicesResult result = await bluetoothLeDevice.GetGattServicesAsync();
if (result.Status == GattCommunicationStatus.Success)
{
MethodInvoker invoker_Status = delegate
{
textBox1.Text += "result.Status:" + result.Status + "\r\n";
};
textBox1.BeginInvoke(invoker_Status);
IReadOnlyList<GattDeviceService> ses = result.Services;
GattDeviceService service0 = result.Services[0];
GattDeviceService service1 = result.Services[1];
GattDeviceService service2 = result.Services[2];
string svf = service2.Uuid.ToString();
GattCharacteristicsResult result2 = await service2.GetCharacteristicsAsync();
if (result2.Status == GattCommunicationStatus.Success)
{
var characteristics = result2.Characteristics;
gatt_notify = characteristics[0];
gatt_write = characteristics[1];
string notiy = gatt_notify.CharacteristicProperties.ToString();//Notify
string write = gatt_write.CharacteristicProperties.ToString();//WriteWithoutResponse
string notify_uuid = gatt_notify.Uuid.ToString();
string write_uuid = gatt_write.Uuid.ToString();
GattCommunicationStatus status = await gatt_notify.WriteClientCharacteristicConfigurationDescriptorAsync(
GattClientCharacteristicConfigurationDescriptorValue.Notify);
gatt_notify.ValueChanged += S1_ValueChanged;
MethodInvoker invoker = delegate
{
textBox1.Text += "连接成功\r\n";
};
textBox1.BeginInvoke(invoker);
}
else
{
MethodInvoker invoker = delegate
{
textBox1.Text += "连接失败\r\n";
};
textBox1.BeginInvoke(invoker);
if (gatt_notify != null)
gatt_notify.ValueChanged -= S1_ValueChanged;
if (bluetoothLeDevice != null)
{
bluetoothLeDevice.Dispose();
bluetoothLeDevice = null;
}
MessageBox.Show("不能重复运行软件,请关闭重新启动所有体验软件!", "系统消息");
Application.Exit();
}
}
}
/// <summary>
/// 重启
/// </summary>
private void Restart()
{
Thread thtmp = new Thread(new ParameterizedThreadStart(run));
object appName = Application.ExecutablePath;
Thread.Sleep(3000);
thtmp.Start(appName);
}
private void run(Object obj)
{
Process ps = new Process();
ps.StartInfo.FileName = obj.ToString();
ps.Start();
}
/// <summary>
/// 获取到蓝牙数据后发送
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
private void S1_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
{
var reader = DataReader.FromBuffer(args.CharacteristicValue);
byte[] vs = new byte[args.CharacteristicValue.Length];
reader.ReadBytes(vs);
string strData = ByteArrayToHexString(vs);
MethodInvoker invoker = delegate
{
textBox1.Text += (strData + "\r\n");
//之前程序运行到10分钟左右会崩溃 加入下面代码解决
if (textBox1.Text.Length >= textBox1.MaxLength)
{
textBox1.Text = "";
}
//发送接收到的数据到2D模拟灭火(本机)
SendM("10004:" + strData);
};
textBox1.BeginInvoke(invoker);
}
/// <summary>
/// 字符串转字节数组
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static byte[] HexStringToByteArray(string s)
{
s = s.Replace(" ", "");
byte[] buffer = new byte[s.Length / 2];
for (int i = 0; i < s.Length; i += 2)
{
buffer[i / 2] = (byte)Convert.ToByte(s.Substring(i, 2), 16);
}
return buffer;
}
/// <summary>
/// 字节数组转字符串
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static string ByteArrayToHexString(byte[] data)
{
if (data == null)
return "";
StringBuilder sb = new StringBuilder(data.Length * 3);
foreach (byte b in data)
{
sb.Append(Convert.ToString(b, 16).PadLeft(2, '0'));
}
return sb.ToString().ToUpper();
}
/*----------UDP------------*/
public string EncodingMOD = "UTF8";
public static Encoding ecd;
int recv;
byte[] data = new byte[4096];
public static EndPoint Remote;
public static Socket newsock;
public static string localip;
Thread rcv;
Thread threadSend;
public string recivestring = "";
public void UdpPeer(int port)
{
uint IOC_IN = 0x80000000;
uint IOC_VENDOR = 0x18000000;
uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
if (EncodingMOD == "UTF8" || EncodingMOD == "utf8")
{
ecd = Encoding.UTF8;
}
else if (EncodingMOD == "ASCII" || EncodingMOD == "ascii")
{
ecd = Encoding.ASCII;
}
else
{
ecd = Encoding.Default;
}
string hostName = Dns.GetHostName();//本机名
IPAddress[] addressList = Dns.GetHostAddresses(hostName);//会返回所有地址,包括IPv4和IPv6
localip = addressList[0].ToString();
localip = "127.0.0.1";// textBox4.Text.Trim();
MethodInvoker action_s = delegate
{
textBox1.Text = ("连接的主机IP地址:" + localip + "\r\n");
};
textBox1.BeginInvoke(action_s);
//得到本机IP,设置UDP端口号
IPEndPoint ip = new IPEndPoint(IPAddress.Any, port);
newsock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
newsock.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
newsock.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
//绑定网络地址
newsock.Bind(ip);
IPEndPoint sender = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2345);
Remote = (EndPoint)(sender);
rcv = new Thread(new ThreadStart(RecTread));
rcv.Start();
}
void SendTread()
{
while (true)
{
SendM("");
Thread.Sleep(50000);
}
}
/// <summary>
/// 接收数据
/// </summary>
Queue<string> queueRec = new Queue<string>();
public void RecTread()
{
while (true)
{
//if (isStop) return;
data = new byte[4096];
if (EncodingMOD == "UTF8" || EncodingMOD == "utf8")
{
ecd = Encoding.UTF8;
}
else if (EncodingMOD == "ASCII" || EncodingMOD == "ascii")
{
ecd = Encoding.ASCII;
}
else
{
ecd = Encoding.Default;
}
try
{
//发送接收信息
recv = newsock.ReceiveFrom(data, ref Remote);
recivestring = ecd.GetString(data, 0, recv);
}
catch (Exception ex)
{
MethodInvoker action_ex = delegate
{
textBox1.Text = ("ex:" + ex.Message + "\r\n");
};
textBox1.BeginInvoke(action_ex);
}
MethodInvoker action_s = delegate
{
textBox1.Text = ("recivestring:" + recivestring + "\r\n");
};
textBox1.BeginInvoke(action_s);
if (!queueRec.Contains(recivestring))
{
queueRec.Enqueue(recivestring);
}
}
}
public static void mSendMessage(string ip, int port, string Mes)
{
byte[] senddata = ecd.GetBytes(Mes);
IPEndPoint sender = new IPEndPoint(IPAddress.Parse(ip), port);
EndPoint mRemote = (EndPoint)(sender);
newsock.SendTo(senddata, senddata.Length, SocketFlags.None, mRemote);
}
/// <summary>
/// 本机发送消息
/// </summary>
/// <param name="Mes"></param>
public static void SendM(string Mes)
{
byte[] senddata = ecd.GetBytes(Mes);
if (newsock != null)
{
try
{
newsock.SendTo(senddata, senddata.Length, SocketFlags.None, Remote);
}
catch
{
}
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (MessageBox.Show("真的要退出程序吗?", "退出程序", MessageBoxButtons.OKCancel) == DialogResult.OK)
{
QuiteUDP();
Application.Exit();
}
else
{
e.Cancel = true;
}
}
/// <summary>
/// 结束UDP
/// </summary>
public void QuiteUDP()
{
if (newsock != null)
{
newsock.Close();
newsock = null;
rcv.Abort();
rcv = null;
threadSend.Abort();
threadSend = null;
threadMain.Abort();
threadMain = null;
}
}
}
}
2.unity实现接收WindowsForms发过来的数据
using UnityEngine;
using System.Collections;
using System.Net.Sockets;
using System.Net;
using System.Collections.Generic;
using System;
using System.Text;
using System.Threading;
public class UdpClient
{
public static string EncodingMOD = "UTF8";
public static Encoding ecd;
public static int recv;
public static byte[] data = new byte[4096];
public static EndPoint Remote;
public static Socket newsock;
public static string localip;
public static Thread rcv;
// bool recivebyte = false;
public static string recivestring = "";
public static void UdpPeer(int port)
{
uint IOC_IN = 0x80000000;
uint IOC_VENDOR = 0x18000000;
uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
if (EncodingMOD == "UTF8" || EncodingMOD == "utf8")
{
ecd = Encoding.UTF8;
}
else if (EncodingMOD == "ASCII" || EncodingMOD == "ascii")
{
ecd = Encoding.ASCII;
}
else
{
ecd = Encoding.Default;
}
string hostName = Dns.GetHostName();//本机名
IPAddress[] addressList = Dns.GetHostAddresses(hostName);//会返回所有地址,包括IPv4和IPv6
localip = addressList[0].ToString();
//得到本机IP,设置UDP端口号
IPEndPoint ip = new IPEndPoint(IPAddress.Any, port);
newsock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
newsock.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
newsock.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
//绑定网络地址
newsock.Bind(ip);
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
Remote = (EndPoint)(sender);
rcv = new Thread(new ThreadStart(RecTread));
rcv.Start();
}
public static void RecTread()
{
while (true)
{
data = new byte[4096];
if (EncodingMOD == "UTF8" || EncodingMOD == "utf8")
{
ecd = Encoding.UTF8;
}
else if (EncodingMOD == "ASCII" || EncodingMOD == "ascii")
{
ecd = Encoding.ASCII;
}
else
{
ecd = Encoding.Default;
}
//接收信息
recv = newsock.ReceiveFrom(data, ref Remote);
recivestring = ecd.GetString(data, 0, recv);
}
}
public static void QuiteUDP()
{
if (newsock != null)
{
newsock.Close();
rcv.Abort();
}
}
public void OnApplicationQuit()
{
if (newsock != null)
{
newsock.Close();
rcv.Abort();
}
}
/// <summary>
/// 发送数据
/// </summary>
/// <param name="ip">127.0.0.1</param>
/// <param name="port">2345</param>
/// <param name="Mes">发送的数据内容</param>
public static void mSendMessage(string ip, int port, string Mes)
{
byte[] senddata = ecd.GetBytes(Mes);
IPEndPoint sender = new IPEndPoint(IPAddress.Parse(ip), port);
EndPoint mRemote = sender;
newsock.SendTo(senddata, senddata.Length, SocketFlags.None, mRemote);
}
public static void SendM(string Mes)
{
byte[] senddata = ecd.GetBytes(Mes);
if (newsock != null)
{
try
{
newsock.SendTo(senddata, senddata.Length, SocketFlags.None, Remote);
}
catch
{
}
}
}
}