Bläddra i källkod

整合windows版本蓝牙进来

slambb 1 månad sedan
förälder
incheckning
d9755dc546

+ 101 - 0
SmartBowSDK/BleApi.cs

@@ -0,0 +1,101 @@
+using System.Runtime.InteropServices;
+namespace SmartBowSDK
+{
+    public class BleApi
+    {
+        // dll calls
+        public enum ScanStatus { PROCESSING, AVAILABLE, FINISHED };
+
+        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+        public struct DeviceUpdate
+        {
+            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
+            public string id;
+            [MarshalAs(UnmanagedType.I1)]
+            public bool isConnectable;
+            [MarshalAs(UnmanagedType.I1)]
+            public bool isConnectableUpdated;
+            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
+            public string name;
+            [MarshalAs(UnmanagedType.I1)]
+            public bool nameUpdated;
+        }
+
+        [DllImport("BleWinrtDll.dll", EntryPoint = "StartDeviceScan")]
+        public static extern void StartDeviceScan();
+
+        [DllImport("BleWinrtDll.dll", EntryPoint = "PollDevice")]
+        public static extern ScanStatus PollDevice(ref DeviceUpdate device, bool block);
+
+        [DllImport("BleWinrtDll.dll", EntryPoint = "StopDeviceScan")]
+        public static extern void StopDeviceScan();
+
+        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+        public struct Service
+        {
+            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
+            public string uuid;
+        };
+
+        [DllImport("BleWinrtDll.dll", EntryPoint = "ScanServices", CharSet = CharSet.Unicode)]
+        public static extern void ScanServices(string deviceId);
+
+        [DllImport("BleWinrtDll.dll", EntryPoint = "PollService")]
+        public static extern ScanStatus PollService(out Service service, bool block);
+
+        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+        public struct Characteristic
+        {
+            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
+            public string uuid;
+            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
+            public string userDescription;
+        };
+
+        [DllImport("BleWinrtDll.dll", EntryPoint = "ScanCharacteristics", CharSet = CharSet.Unicode)]
+        public static extern void ScanCharacteristics(string deviceId, string serviceId);
+
+        [DllImport("BleWinrtDll.dll", EntryPoint = "PollCharacteristic")]
+        public static extern ScanStatus PollCharacteristic(out Characteristic characteristic, bool block);
+
+        [DllImport("BleWinrtDll.dll", EntryPoint = "SubscribeCharacteristic", CharSet = CharSet.Unicode)]
+        public static extern bool SubscribeCharacteristic(string deviceId, string serviceId, string characteristicId, bool block);
+
+        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+        public struct BLEData
+        {
+            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)]
+            public byte[] buf;
+            [MarshalAs(UnmanagedType.I2)]
+            public short size;
+            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
+            public string deviceId;
+            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
+            public string serviceUuid;
+            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
+            public string characteristicUuid;
+        };
+
+        [DllImport("BleWinrtDll.dll", EntryPoint = "PollData")]
+        public static extern bool PollData(out BLEData data, bool block);
+
+        [DllImport("BleWinrtDll.dll", EntryPoint = "SendData")]
+        public static extern bool SendData(in BLEData data, bool block);
+
+        [DllImport("BleWinrtDll.dll", EntryPoint = "Quit")]
+        public static extern void Quit();
+
+        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+        public struct ErrorMessage
+        {
+            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)]
+            public string msg;
+        };
+
+        [DllImport("BleWinrtDll.dll", EntryPoint = "GetError")]
+        public static extern void GetError(out ErrorMessage buf);
+
+        [DllImport("BleWinrtDll.dll", EntryPoint = "Disconnect", CharSet = CharSet.Unicode)]
+        public static extern void Disconnect(string deviceId);
+    }
+}

+ 538 - 0
SmartBowSDK/BleWinHelper.cs

@@ -0,0 +1,538 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using UnityEngine;
+using System.Runtime.InteropServices;
+using System.Linq;
+
+namespace SmartBowSDK
+{
+    /// <summary>
+    /// Windows连接BluetoothLE
+    /// 我的扫描逻辑默认了读写特征都在同一服务下
+    /// </summary>
+    public class BleWinHelper : MonoBehaviour
+    {
+        public string LogTag = "[BleWinHelper]";
+        private  void Log(string text)
+        {
+            //Debug.Log(LogTag + text);
+            SmartBowLogger.Log(this, LogTag + text);
+        }
+        private void Warn(string text)
+        {
+            //Debug.LogWarning(LogTag + text);
+            SmartBowLogger.Log(this, LogTag + text);
+        }
+        private  void Error(string text)
+        {
+            //Debug.Log(LogTag + text);
+            SmartBowLogger.Log(this, LogTag + text);
+        }
+
+        //private string targetDeviceName = "Bbow_20210501 | ARTEMIS Pro | HOUYI Pro | Pistol | Pistol M9 | BGBox_202012";
+        //private string targetDeviceNameHOUYIPro = "HOUYI Pro";
+        //private string targetDeviceNameGun = "Pistol | Pistol M9 | Bbow_20210501";
+        private string targetService = "{0000fff0-0000-1000-8000-00805f9b34fb}";
+        private string targetCharacteristicsNotify = "{0000fff1-0000-1000-8000-00805f9b34fb}";
+        private string targetCharacteristicsWrite = "{0000fff2-0000-1000-8000-00805f9b34fb}";
+
+        private bool isConnectLocking = false;
+        private bool isScanningDevices = false;
+        private bool isScanningServices = false;
+        private bool isScanningCharacteristics = false;
+        private bool isSubscribed = false;
+        private bool isSubscribing = false;
+
+        private string lastError = null;
+        [SerializeField]
+        private string selectedDeviceId = null;
+
+        private Dictionary<string, Dictionary<string, string>> deviceList = new Dictionary<string, Dictionary<string, string>>();
+        private List<string> serviceList = new List<string>();
+        private List<string> characteristicsList = new List<string>();
+
+        private float _connectedTime = 0;
+        private float _receiveDataTime = 0;
+        private float _heartBeatInterval = 0;
+
+        private Action<bool> OnScanEnded;
+        private Action OnConnected;
+        /// <summary>
+        /// 主动调用Disconnect()不会触发该委托
+        /// </summary>
+        private Action OnConnectionFailed;
+        private static Action<string, byte[]> OnCharacteristicChanged;
+
+        public SmartBowHelper smartBowHelper;
+        public BluetoothAim_SDK bluetoothAim;
+        /// <summary>
+        /// 注册window对象
+        /// </summary>
+        /// <param name="o">挂载对象</param>
+        /// <param name="bluetoothWindows">关联的BluetoothWindows</param>
+        /// <param name="logTip">提示的log标签</param>
+        /// <returns></returns>
+        public static BleWinHelper RegisterTo(SmartBowHelper _smartBowHelper, BluetoothWindows bluetoothWindows,string logTip = "first")
+        {
+            //if (_Instance)
+            //{
+            //    Error("Register fail, because only one can be registered.");
+            //    return null;
+            //}
+            string bleWinName = "BleWinHelper" + logTip;
+            GameObject obj = new GameObject(bleWinName);
+            obj.transform.SetParent(_smartBowHelper.transform);
+         
+            BleWinHelper bleWinHelper = obj.AddComponent<BleWinHelper>();
+            bleWinHelper.smartBowHelper = _smartBowHelper;
+            bleWinHelper.bluetoothAim = _smartBowHelper.bluetoothAim;
+            //日志名字
+            bleWinHelper.LogTag = logTip + ": ";
+
+            bluetoothWindows.Connect = bleWinHelper.Connect;
+            bluetoothWindows.Disconnect = bleWinHelper.Disconnect;
+            bluetoothWindows.Write = bleWinHelper.Write;
+            bluetoothWindows.WriteByte = bleWinHelper.WriteByte;
+            // windos 通知 bluetoothAim 
+            bleWinHelper.OnScanEnded = (bool bSelectedDeviceId) => bluetoothWindows.OnScanEnded?.Invoke(bSelectedDeviceId);
+            bleWinHelper.OnConnected = () => bluetoothWindows.OnConnected?.Invoke();
+            bleWinHelper.OnConnectionFailed = () => bluetoothWindows.OnConnectionFailed?.Invoke();
+            //多个定义共用一个OnCharacteristicChanged
+            OnCharacteristicChanged += (deviceID,bytes) => {
+                if (deviceID == bleWinHelper.selectedDeviceId) { 
+                
+                    bluetoothWindows.OnCharacteristicChanged?.Invoke(deviceID, bytes);
+                }
+            };
+
+            return bleWinHelper;
+        }
+
+        /// <summary>
+        /// 设置心跳检测
+        /// 1.每次收到的蓝牙数据都视为心跳
+        /// 2.帮助触发蓝牙断开监听
+        /// </summary>
+        /// <param name="interval">心跳检测间隔</param>
+        public void SetHeartBeat(float interval)
+        {
+            _heartBeatInterval = interval;
+        }
+
+        //private static BleWinHelper _Instance;
+
+        void Awake()
+        {
+           // _Instance = this;
+        }
+
+        void OnDestroy()
+        {
+          //  if (_Instance == this) _Instance = null;
+            BleApi.Quit();
+        }
+
+        void Update()
+        {
+            BleApi.ScanStatus status;
+            if (isScanningDevices)
+            {
+                BleApi.DeviceUpdate res = new BleApi.DeviceUpdate();
+                do
+                {
+                    status = BleApi.PollDevice(ref res, false);
+                    if (status == BleApi.ScanStatus.AVAILABLE)
+                    {
+                        // 如果存在 selectedDeviceId,不要重复
+                        if (selectedDeviceId != null) {
+                            //Log($"当前的 selectedDeviceId: {selectedDeviceId}");
+                            continue; // 跳过后续 AVAILABLE 的处理,等待 FINISHED
+                        }
+
+                        if (!deviceList.ContainsKey(res.id))
+                        {
+                            deviceList[res.id] = new Dictionary<string, string>() {
+                                { "name", "" },
+                                { "isConnectable", "False" }
+                            };
+                            //这里参考手机蓝牙模式
+                            if (smartBowHelper.GetIsConnectName())
+                            {
+                                //后续匹配名字 可以是多个设备
+                                Log($"发现设备{res.name},is fileters empty:{ string.IsNullOrEmpty(smartBowHelper.GetFilters())},name:{smartBowHelper.GetFilters()}");
+                            }
+                            else
+                            {
+                                // 统一格式化:去掉非十六进制字符,转大写
+                                string FormatMac(string mac) =>
+                                    string.Concat(mac.Where(c => Uri.IsHexDigit(c))).ToUpper();
+
+                                string searchMac = FormatMac(ExtractMacFromDeviceId(res.id));
+                                string connectMac = FormatMac(smartBowHelper.GetConnectMacStr());
+
+                                Log($"发现设备 {searchMac},is connectMacStr:{connectMac },DeviceAddress:{searchMac}");
+
+                            }
+                        }  
+
+                        if (res.nameUpdated)
+                            deviceList[res.id]["name"] = res.name;
+                        if (res.isConnectableUpdated)
+                            deviceList[res.id]["isConnectable"] = res.isConnectable.ToString();
+
+                        //这里参考手机蓝牙模式
+                        if (smartBowHelper.GetIsConnectName())
+                        {
+                            //后续匹配名字 可以是多个设备
+                            //Log( $"发现设备{res.name},is fileters empty:{ string.IsNullOrEmpty(smartBowHelper.GetFilters())},name:{smartBowHelper.GetFilters()}");
+                            string _filters = string.IsNullOrEmpty(smartBowHelper.GetFilters()) 
+                                ? bluetoothAim.deviceConfig.deviceName 
+                                : smartBowHelper.GetFilters();
+
+                            string[] filterArray = _filters.Split('|'); // 支持多个名字
+                            foreach (var f in filterArray)
+                            {
+                                string trimmedFilter = f.Trim();
+                                if (string.Equals(trimmedFilter, deviceList[res.id]["name"].Trim(), StringComparison.OrdinalIgnoreCase)
+                                    && res.isConnectable)
+                                {
+                                    selectedDeviceId = res.id;
+                                    StopDeviceScan();
+                                    Log($"Name匹配设备 {trimmedFilter}");
+                                    break;
+                                }
+                            }
+                        }
+                        else 
+                        {
+                            // 统一格式化:去掉非十六进制字符,转大写
+                            string FormatMac(string mac) =>
+                                string.Concat(mac.Where(c => Uri.IsHexDigit(c))).ToUpper();
+
+                            string searchMac = FormatMac(ExtractMacFromDeviceId(res.id));
+                            string connectMac = FormatMac(smartBowHelper.GetConnectMacStr());
+
+                            //Log($"发现设备 {searchMac},is connectMacStr:{connectMac },DeviceAddress:{searchMac}");
+                            //按mac地址匹配
+                            if (!string.IsNullOrEmpty(connectMac) && connectMac.Contains(searchMac) && res.isConnectable)
+                            {
+                                selectedDeviceId = res.id;
+                                StopDeviceScan();
+                                Log($"MAC匹配设备 {searchMac}");
+                            }
+                        }
+
+                    }
+                    else if (status == BleApi.ScanStatus.FINISHED)
+                    {
+                        StartCoroutine(ScanServiceAndCharacteristicsToSubscribe());
+
+                        Log($" ScanStatus FINISHED !");
+                        //停止扫描后通知?
+                        OnScanEnded?.Invoke(selectedDeviceId != null);
+                    }
+                } while (status == BleApi.ScanStatus.AVAILABLE);
+            }
+            if (isScanningServices)
+            {
+                BleApi.Service res;
+                do
+                {
+                    status = BleApi.PollService(out res, false);
+                    if (status == BleApi.ScanStatus.AVAILABLE)
+                    {
+                        serviceList.Add(res.uuid);
+                    }
+                    else if (status == BleApi.ScanStatus.FINISHED)
+                    {
+                        isScanningServices = false;
+                    }
+                } while (status == BleApi.ScanStatus.AVAILABLE);
+            }
+            if (isScanningCharacteristics)
+            {
+                BleApi.Characteristic res;
+                do
+                {
+                    status = BleApi.PollCharacteristic(out res, false);
+                    if (status == BleApi.ScanStatus.AVAILABLE)
+                    {
+                        Log("res.userDescription:"+ res.userDescription+ ",res.uuid:" + res.uuid);
+                        if (res.userDescription == "no description available" ||  //旧设备
+                            res.userDescription == "SPP Write Channel" ||   //新设备
+                            res.userDescription == "SPP Read Channel")
+                        {
+                           
+                            characteristicsList.Add(res.uuid);
+                        }
+                        else {
+                            //跟着之前代码
+                            characteristicsList.Add(res.userDescription);
+                        }
+                        //string name = res.userDescription != "no description available" ? res.userDescription : res.uuid;
+                        //characteristicsList.Add(name);
+                    }
+                    else if (status == BleApi.ScanStatus.FINISHED)
+                    {
+                        isScanningCharacteristics = false;
+                    }
+                } while (status == BleApi.ScanStatus.AVAILABLE);
+            }
+            if (isSubscribed)
+            {
+                BleApi.BLEData res;
+                while (BleApi.PollData(out res, false))
+                {
+                    //string text = BitConverter.ToString(res.buf, 0, res.size);
+                    // string text = Encoding.ASCII.GetString(res.buf, 0, res.size);
+                    byte[] bytes = new byte[res.size];
+                    Array.Copy(res.buf, bytes, res.size);
+                    _receiveDataTime = Time.realtimeSinceStartup;
+                    OnCharacteristicChanged?.Invoke(res.deviceId, bytes);
+
+                }
+            }
+            {
+                BleApi.ErrorMessage res;
+                BleApi.GetError(out res);
+                if (lastError != res.msg)
+                {
+                    Error(res.msg);
+                    lastError = res.msg;
+                    //对应设备才断开
+                    if (lastError.Contains("SendDataAsync") && lastError.Contains(selectedDeviceId) && isSubscribed)
+                    {
+                        HandleConnectFail();
+                    }
+                }
+            }
+        }
+
+        void LateUpdate()
+        {
+            if (
+                _heartBeatInterval > 0 &&
+                isSubscribed &&
+                Time.realtimeSinceStartup - _connectedTime >= _heartBeatInterval &&
+                Time.realtimeSinceStartup - _receiveDataTime >= _heartBeatInterval
+            )
+            {
+                HandleConnectFail();
+            }
+              
+        }
+
+        private bool Connect()
+        {
+            if (isConnectLocking || isScanningDevices)
+            {
+                Warn("Connect Invalid, because is in connect.");
+                return false;
+            }
+            //开始连接时候,重置一下参数
+            ReinitAfterConnectFail();
+
+            BleApi.StartDeviceScan();
+            isConnectLocking = true;
+            isScanningDevices = true;
+            Log("Start Connect!");
+            return true;
+        }
+
+        private void StopDeviceScan()
+        {
+            if (!isScanningDevices) return;
+            BleApi.StopDeviceScan();
+            isScanningDevices = false;
+            Log($"Stop DeviceScan!");
+        }
+
+        private IEnumerator ScanServiceAndCharacteristicsToSubscribe()
+        {
+            isSubscribing = true;
+
+            if (selectedDeviceId == null)
+            {
+                HandleConnectFail();
+                smartBowHelper.InvokeOnBluetoothError(BluetoothError.Unknown, "选择设备 DeviceId 不存在!");
+                yield break;
+            }
+            Log("SelectedDeviceId OK");
+
+            BleApi.ScanServices(selectedDeviceId);
+            isScanningServices = true;
+            serviceList.Clear();
+
+            while (isScanningServices) yield return null;
+
+            bool findTargetService = false;
+
+            foreach (string service in serviceList)
+            {
+                if (service == targetService)
+                {
+                    findTargetService = true;
+                    Log("FindTargetService OK");
+                    break;
+                }
+            }
+
+            if (!findTargetService)
+            {
+                HandleConnectFail();
+                //一般都不会出现这个问题,直接使用Unknown
+                smartBowHelper.InvokeOnBluetoothError(BluetoothError.Unknown, "目标设备服务 service 不存在!");
+                yield break;
+            }
+
+            BleApi.ScanCharacteristics(selectedDeviceId, targetService);
+            isScanningCharacteristics = true;
+            characteristicsList.Clear();
+
+            while (isScanningCharacteristics) yield return null;
+
+            bool findTargetCharacteristicsNotify = false;
+            bool findTargetCharacteristicsWrite = false;
+
+            foreach (string characteristics in characteristicsList)
+            {
+                if (characteristics == targetCharacteristicsNotify)
+                {
+                    findTargetCharacteristicsNotify = true;
+                    Log("FindTargetCharacteristicsNotify OK");
+                }
+                else if (characteristics == targetCharacteristicsWrite)
+                {
+                    findTargetCharacteristicsWrite = true;
+                    Log("FindTargetCharacteristicsWrite OK");
+                }
+            }
+
+            if (!findTargetCharacteristicsNotify || !findTargetCharacteristicsWrite)
+            {
+                HandleConnectFail();
+                //一般都不会出现这个问题,直接使用Unknown
+                smartBowHelper.InvokeOnBluetoothError(BluetoothError.Unknown, "目标设备特征值 Characteristics 不存在!");
+                yield break;
+            }
+
+            BleApi.SubscribeCharacteristic(selectedDeviceId, targetService, targetCharacteristicsNotify, false);
+            isSubscribed = true;
+            isSubscribing = false;
+            Log("SubscribeCharacteristicNotify OK");
+
+            _connectedTime = Time.realtimeSinceStartup;
+            OnConnected?.Invoke();
+        }
+
+        /// <summary>
+        /// 连接错误
+        /// </summary>
+        private void HandleConnectFail()
+        {
+            if (selectedDeviceId != null) BleApi.Disconnect(selectedDeviceId);
+            bool isLockBefore = isConnectLocking;
+            ReinitAfterConnectFail();
+            if (isLockBefore) OnConnectionFailed?.Invoke();
+        }
+
+        private void ReinitAfterConnectFail()
+        {
+            isConnectLocking = false;
+            isScanningDevices = false;
+            isScanningServices = false;
+            isScanningCharacteristics = false;
+            isSubscribed = false;
+            isSubscribing = false;
+            selectedDeviceId = null;
+            deviceList.Clear();
+            serviceList.Clear();
+            characteristicsList.Clear();
+        }
+
+        private bool Write(string text)
+        {
+            if (!isSubscribed) return false;
+            byte[] payload = Encoding.ASCII.GetBytes(text);
+            BleApi.BLEData data = new BleApi.BLEData();
+            data.buf = new byte[512];
+            data.size = (short)payload.Length;
+            data.deviceId = selectedDeviceId;
+            data.serviceUuid = targetService;
+            data.characteristicUuid = targetCharacteristicsWrite;
+            for (int i = 0; i < payload.Length; i++)
+                data.buf[i] = payload[i];
+            // no error code available in non-blocking mode
+            BleApi.SendData(in data, false);
+            Log("Write(" + text + ")");
+            return true;
+        }
+
+
+        private bool WriteByte(byte[] payload)
+        {
+            if (!isSubscribed) return false;
+
+            BleApi.BLEData data = new BleApi.BLEData();
+            data.buf = new byte[512];
+            data.size = (short)payload.Length;
+            data.deviceId = selectedDeviceId;
+            data.serviceUuid = targetService;
+            data.characteristicUuid = targetCharacteristicsWrite;
+
+            for (int i = 0; i < payload.Length; i++)
+                data.buf[i] = payload[i];
+
+            // no error code available in non-blocking mode
+            BleApi.SendData(in data, false);
+
+            Log("Write(byte[])");
+            return true;
+        }
+
+        private bool Disconnect()
+        {
+            if (!isConnectLocking)
+            {
+                Warn("Disconnect Invalid, because not in connect.");
+                return false;
+            }
+            if (isSubscribing)
+            {
+                Warn("Disconnect Invalid, because is subscribing.");
+                return false;
+            }
+            if (isScanningDevices) StopDeviceScan();
+            if (selectedDeviceId != null) BleApi.Disconnect(selectedDeviceId);
+            ReinitAfterConnectFail();
+            Log("Disconnect OK");
+            return true;
+        }
+
+        /// <summary>
+        /// 其实是 WinRT BLE API 的设备 ID 格式,它包含两部分:
+        /// 前面:BluetoothLE#BluetoothLE00:e0:4c:2a:12:97 → 设备的内部标识(适配器 + 随机 ID)。
+        /// 后面:d3:5c:89:57:68:de → 真实的 MAC 地址。
+        /// 微软官方文档也说明了: WinRT 不直接给 BluetoothAddress,而是拼接在 Id 的末尾。
+        /// </summary>
+        /// <param name="deviceId">参考 BluetoothLE#BluetoothLE00:e0:4c:2a:12:97-d3:5c:89:57:68:de</param>
+        /// <returns></returns>
+        string ExtractMacFromDeviceId(string deviceId)
+        {
+            if (string.IsNullOrEmpty(deviceId))
+                return null;
+
+            // WinRT 格式一般是:BluetoothLE#BluetoothLEXX:XX:XX:XX:XX:XX-YY:YY:YY:YY:YY:YY
+            int lastDash = deviceId.LastIndexOf('-');
+            if (lastDash >= 0 && lastDash < deviceId.Length - 1)
+            {
+                return deviceId.Substring(lastDash + 1); // 提取最后一段 MAC
+            }
+            return deviceId;
+        }
+
+    }
+
+}

+ 50 - 9
SmartBowSDK/BluetoothAim_SDK.cs

@@ -3,6 +3,7 @@ using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 using ArduinoBluetoothAPI;
+using System.Linq;
 
 namespace SmartBowSDK
 {
@@ -58,7 +59,7 @@ namespace SmartBowSDK
             BluetoothStatusEnum oldStatus = bluetoothStatus;
             if (oldStatus == newStatus) return;
             bluetoothStatus = newStatus;
-            SmartBowLogger.Log(this, $"oldStatus[{oldStatus}]=>newStatus[{newStatus}]");
+            SmartBowLogger.Log(this, $" oldStatus:[{oldStatus}]=> newStatus:[{newStatus}]");
             if (newStatus == BluetoothStatusEnum.None)
             {
                 _bluetoothHelper = null;
@@ -93,6 +94,7 @@ namespace SmartBowSDK
             if (BluetoothWindows.IsWindows())
             {
                 _bluetoothWindows = new BluetoothWindows();
+                _bluetoothWindows.OnScanEnded = OnScanEnded_windows;
                 _bluetoothWindows.OnConnected = OnConnected_windows;
                 _bluetoothWindows.OnConnectionFailed = OnConnectionFailed_windows;
                 _bluetoothWindows.OnCharacteristicChanged = OnCharacteristicChanged_windows;
@@ -108,11 +110,12 @@ namespace SmartBowSDK
             {
                 if (bluetoothStatus == BluetoothStatusEnum.None)
                 {
+                    //windos连接触发蓝牙
                     _bluetoothWindows.Connect();
                     SetStatus(BluetoothStatusEnum.Connecting);
 
                     //初始化一次六轴解析
-                    if (sixAxisFusion == null)
+                    if (sensorAxisType == SensorAxisType.SixAxisSensor && sixAxisFusion == null)
                     {
                         Vector3 accelOffset = Vector3.zero;
                         Vector3 gyroOffset = Vector3.zero;
@@ -372,19 +375,38 @@ namespace SmartBowSDK
                     //if (device.DeviceName == deviceConfig.deviceName)
                     //后续匹配名字 可以是多个设备
                     string _filters = string.IsNullOrEmpty(filters) ? deviceConfig.deviceName : filters;
-                    if (_filters.Contains(device.DeviceName))
+                    //if (_filters.Contains(device.DeviceName))
+                    //{
+                    //    _bluetoothHelper.setDeviceName(device.DeviceName);
+                    //    _bluetoothHelper.Connect();
+                    //    SmartBowLogger.Log(this, $"Name匹配设备{device.DeviceName}");
+                    //    return;
+                    //}
+                    // 精确匹配,大小写敏感,多设备名字用 " | " 分隔
+                    string[] filterArray = _filters.Split('|'); // 支持多个名字
+                    foreach (var f in filterArray)
                     {
-                        _bluetoothHelper.setDeviceName(device.DeviceName);
-                        _bluetoothHelper.Connect();
-                        SmartBowLogger.Log(this, $"Name匹配设备{device.DeviceName}");
-                        return;
+                        string trimmedFilter = f.Trim();
+                        if (string.Equals(trimmedFilter, device.DeviceName.Trim(), StringComparison.OrdinalIgnoreCase))
+                        {
+                            _bluetoothHelper.setDeviceName(device.DeviceName);
+                            _bluetoothHelper.Connect();
+                            SmartBowLogger.Log(this, $"Name匹配设备 {device.DeviceName}");
+                            return;
+                        }
                     }
                 }
                 else
                 {
                     SmartBowLogger.Log(this, $"发现设备 {device.DeviceAddress},is connectMacStr:{ connectMacStr },DeviceAddress:{device.DeviceAddress}");
-                    //按mac地址匹配
-                    if (connectMacStr.Contains(device.DeviceAddress))
+
+                    // 统一格式化:去掉非十六进制字符,转大写
+                    string FormatMac(string mac) =>
+                        string.Concat(mac.Where(c => Uri.IsHexDigit(c))).ToUpper();
+
+                    string deviceMac = FormatMac(device.DeviceAddress);
+                    string targetMac = FormatMac(connectMacStr);
+                    if (targetMac.Contains(deviceMac))
                     {
                         _bluetoothHelper.setDeviceName(device.DeviceName);
                         _bluetoothHelper.setDeviceAddress(device.DeviceAddress);
@@ -392,12 +414,31 @@ namespace SmartBowSDK
                         SmartBowLogger.Log(this, $"Mac匹配设备{device.DeviceAddress}");
                         return;
                     }
+                    //按mac地址匹配
+                    //if (connectMacStr.Contains(device.DeviceAddress))
+                    //{
+                    //    _bluetoothHelper.setDeviceName(device.DeviceName);
+                    //    _bluetoothHelper.setDeviceAddress(device.DeviceAddress);
+                    //    _bluetoothHelper.Connect();
+                    //    SmartBowLogger.Log(this, $"Mac匹配设备{device.DeviceAddress}");
+                    //    return;
+                    //}
                 }
             }
             SetStatus(BluetoothStatusEnum.None);
             smartBowHelper.InvokeOnBluetoothError(BluetoothError.ScanNotFoundTargetDevice, "扫描结束后未发现目标设备");
         }
 
+        void OnScanEnded_windows(bool bSelectedDeviceId) {
+            SmartBowLogger.Log(this, $"ScanEnded bSelectedDeviceId:" + bSelectedDeviceId);
+            if (!bSelectedDeviceId) 
+            {
+                SetStatus(BluetoothStatusEnum.None);
+                smartBowHelper.InvokeOnBluetoothError(BluetoothError.ScanNotFoundTargetDevice, "扫描结束后未发现目标设备");
+            }
+        }
+
+
         private float _lastWriteDataTime;
         bool WriteData(string data)
         {

+ 5 - 0
SmartBowSDK/BluetoothWindows.cs

@@ -3,12 +3,17 @@ using UnityEngine;
 
 namespace SmartBowSDK
 {
+    /// <summary>
+    /// 此类用于手机适配windos蓝牙的中转,相当于还是使用手机蓝牙的api来去兼容windos的操作,
+    /// 尽量还原原本手机代码风格操作。
+    /// </summary>
     public class BluetoothWindows
     {
         public  Func<bool> Connect;
         public  Func<bool> Disconnect;
         public  Func<string, bool> Write;
         public  Func<byte[], bool> WriteByte;
+        public  Action<bool> OnScanEnded;
         public  Action OnConnected;
         public  Action OnConnectionFailed;
         public  Action<string,byte[]> OnCharacteristicChanged;

+ 29 - 0
SmartBowSDK/SmartBowHelper.cs

@@ -24,6 +24,12 @@ namespace SmartBowSDK
             bluetoothAim.smartBowHelper = this;
             aimHandler.smartBowHelper = this;
             shootChecker.smartBowHelper = this;
+
+            //如果是编辑器运行。自动注册一个windos蓝牙关联
+            if (BluetoothWindows.IsWindows())
+            {
+                BleWinHelper.RegisterTo(this, CreateBluetoothWindows());
+            }
         }
 
         /// <summary>
@@ -272,6 +278,14 @@ namespace SmartBowSDK
             }
         }
 
+        /// <summary>
+        /// 当前过滤的设备名称
+        /// </summary>
+        /// <returns></returns>
+        public string GetFilters() {
+            return bluetoothAim.filters;
+        }
+
         /// <summary>
         /// 设置是否使用名称连接
         /// </summary>
@@ -281,6 +295,14 @@ namespace SmartBowSDK
             bluetoothAim.isConnectName = useName;
         }
 
+        /// <summary>
+        /// 获取使用名称连接
+        /// </summary>
+        /// <returns>是否使用名称连接</returns>
+        public bool GetIsConnectName() {
+            return bluetoothAim.isConnectName;
+        }
+
         /// <summary>
         /// 设置连接的MAC地址
         /// </summary>
@@ -292,6 +314,13 @@ namespace SmartBowSDK
                 bluetoothAim.connectMacStr = macStr;
             }
         }
+        /// <summary>
+        /// 获取需要连接的MAC地址
+        /// </summary>
+        /// <returns>MAC地址字符串</returns>
+        public string GetConnectMacStr() {
+            return bluetoothAim.connectMacStr;
+        }
 
         /// <summary>
         /// 设置传感器类型,用来区分解析

+ 3 - 3
SmartBowSDK/SmartBowLogger.cs

@@ -8,19 +8,19 @@ namespace SmartBowSDK
         public static void Log(object context, string message)
         {
             if (!isDebug) return;
-            Debug.Log(string.Format("[{0}]{1}", context.GetType().Name, message));
+            Debug.Log(string.Format("[{0}] {1}", context.GetType().Name, message));
         }
 
         public static void LogWarning(object context, string message)
         {
             if (!isDebug) return;
-            Debug.LogWarning(string.Format("[{0}]{1}", context.GetType().Name, message));
+            Debug.LogWarning(string.Format("[{0}] {1}", context.GetType().Name, message));
         }
 
         public static void LogError(object context, string message)
         {
             if (!isDebug) return;
-            Debug.LogError(string.Format("[{0}]{1}", context.GetType().Name, message));
+            Debug.LogError(string.Format("[{0}] {1}", context.GetType().Name, message));
         }
     }
 }