killapp/Assets/Scripts/Base/BluetoothManager.cs
2026-03-30 16:25:00 +08:00

770 lines
24 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using UnityEngine;
using System;
using System.Collections.Generic;
/// <summary>
/// 蓝牙设备信息
/// </summary>
[Serializable]
public class BluetoothDevice
{
public string Address; // 设备地址
public string Name; // 设备名称
public int Rssi; // 信号强度
public BluetoothDevice(string address, string name, int rssi = 0)
{
Address = address;
Name = name;
Rssi = rssi;
}
}
/// <summary>
/// 蓝牙管理器 - 提供基础的蓝牙扫描、连接、断开功能
/// </summary>
public class BluetoothManager : MonoBehaviour
{
public static BluetoothManager Instance { get; private set; }
// 事件定义
public event Action<bool> OnInitialized; // 初始化完成事件
public event Action<BluetoothDevice> OnDeviceFound; // 发现设备事件
public event Action<string> OnConnected; // 连接成功事件 (参数: 设备地址)
public event Action<string> OnConnectFailed; // 连接失败事件 (参数: 设备地址)
public event Action<string> OnDisconnected; // 断开连接事件 (参数: 设备地址)
public event Action<string> OnError; // 错误事件
public event Action<string> OnLog; // 日志事件
public event Action OnConnectedSuccess; // 连接成功并可通信事件 (无参数用于通知BLECommunicationManager)
// 状态
public bool IsInitialized { get; private set; }
public bool IsScanning { get; private set; }
public bool IsConnected { get; private set; }
public string ConnectedDeviceAddress { get; private set; }
// 扫描到的设备列表
public List<BluetoothDevice> DiscoveredDevices { get; private set; } = new List<BluetoothDevice>();
// 上一次连接的设备地址
private const string LAST_DEVICE_ADDRESS_KEY = "LastConnectedDeviceAddress";
private const string LAST_DEVICE_NAME_KEY = "LastConnectedDeviceName";
// 是否自动连接
public bool AutoConnect = true;
// 连接超时设置
public float ConnectionTimeout = 10f; // 连接超时时间(秒)
private float _connectionTimer;
private bool _isConnecting;
private string _connectingAddress;
private void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
private void OnDestroy()
{
if (Instance == this)
{
Instance = null;
}
}
private void Update()
{
// 检查连接超时
if (_isConnecting)
{
_connectionTimer -= Time.deltaTime;
if (_connectionTimer <= 0)
{
// 连接超时
_isConnecting = false;
Log($"连接超时: {_connectingAddress}");
OnConnectFailed?.Invoke(_connectingAddress);
OnError?.Invoke($"连接超时: 无法连接到设备 {_connectingAddress}");
_connectingAddress = null;
}
}
}
/// <summary>
/// 初始化蓝牙
/// </summary>
public void Initialize()
{
if (IsInitialized)
{
Log("蓝牙已经初始化");
OnInitialized?.Invoke(true);
return;
}
Log("正在初始化蓝牙...");
BluetoothLEHardwareInterface.Initialize(true, false,
() =>
{
IsInitialized = true;
Log("蓝牙初始化成功");
OnInitialized?.Invoke(true);
// 初始化成功后,延迟尝试自动连接上一次设备
// 延迟是为了让用户有机会先进行手动扫描
if (AutoConnect)
{
Log("将在3秒后尝试自动连接上一次设备...");
Invoke(nameof(TryAutoConnectDelayed), 3f);
}
},
(error) =>
{
// 忽略 "Service not found" 错误,这是插件内部问题,不影响功能
if (error.Contains("Service not found"))
{
Log($"蓝牙初始化警告: {error}");
Log("继续初始化...");
IsInitialized = true;
OnInitialized?.Invoke(true);
// 初始化成功后,延迟尝试自动连接上一次设备
if (AutoConnect)
{
Log("将在3秒后尝试自动连接上一次设备...");
Invoke(nameof(TryAutoConnectDelayed), 3f);
}
}
else
{
Log($"蓝牙初始化失败: {error}");
OnError?.Invoke($"初始化失败: {error}");
OnInitialized?.Invoke(false);
}
}
);
}
/// <summary>
/// 延迟尝试自动连接(用于初始化后)
/// </summary>
private void TryAutoConnectDelayed()
{
// 如果用户已经开始扫描或连接,跳过自动连接
if (IsScanning || IsConnected || _isConnecting)
{
Log("用户正在进行扫描或连接,跳过自动连接");
return;
}
TryAutoConnect();
}
/// <summary>
/// 尝试自动连接上一次连接的设备
/// </summary>
private void TryAutoConnect()
{
string lastAddress = PlayerPrefs.GetString(LAST_DEVICE_ADDRESS_KEY, "");
string lastName = PlayerPrefs.GetString(LAST_DEVICE_NAME_KEY, "");
if (string.IsNullOrEmpty(lastAddress))
{
Log("没有上一次连接的设备记录");
return;
}
Log($"尝试自动连接上一次设备: {lastName} [{lastAddress}]");
// 先扫描设备,找到后再连接
StartScanForAutoConnect(lastAddress);
}
/// <summary>
/// 为自动连接扫描设备
/// </summary>
private void StartScanForAutoConnect(string targetAddress)
{
if (!IsInitialized)
{
Log("蓝牙未初始化,无法扫描");
return;
}
Log("[BLE-DEBUG] 自动连接: 开始扫描...");
// 清空设备列表
DiscoveredDevices.Clear();
// 扫描5秒
BluetoothLEHardwareInterface.ScanForPeripheralsWithServices(null,
(address, name) =>
{
// 先添加到设备列表
AddOrUpdateDevice(address, name, 0);
// 检查是否是要找的设备(统一使用大写比较)
if (address.ToUpper() == targetAddress.ToUpper())
{
Log($"[BLE-DEBUG] 自动连接: 找到设备 {name}");
BluetoothLEHardwareInterface.StopScan();
// 延迟100ms再连接确保扫描完全停止与手动连接流程一致
_pendingAutoConnectAddress = targetAddress;
Invoke(nameof(DelayedAutoConnect), 0.1f);
}
},
(address, name, rssi, bytes) =>
{
// 先添加到设备列表
AddOrUpdateDevice(address, name, rssi);
// 检查是否是要找的设备(统一使用大写比较)
if (address.ToUpper() == targetAddress.ToUpper())
{
Log($"[BLE-DEBUG] 自动连接: 找到设备 {name}, RSSI: {rssi}");
BluetoothLEHardwareInterface.StopScan();
// 延迟100ms再连接确保扫描完全停止与手动连接流程一致
_pendingAutoConnectAddress = targetAddress;
Invoke(nameof(DelayedAutoConnect), 0.1f);
}
},
true
);
// 5秒后停止扫描
Invoke(nameof(StopScan), 5f);
}
// 用于自动连接的临时地址存储
private string _pendingAutoConnectAddress;
/// <summary>
/// 延迟自动连接(确保扫描完全停止后再连接)
/// </summary>
private void DelayedAutoConnect()
{
if (!string.IsNullOrEmpty(_pendingAutoConnectAddress))
{
Log($"[BLE-DEBUG] 自动连接: 延迟连接 {_pendingAutoConnectAddress}");
Connect(_pendingAutoConnectAddress);
_pendingAutoConnectAddress = null;
}
}
/// <summary>
/// 保存连接的设备信息
/// </summary>
private void SaveLastConnectedDevice(string address, string name)
{
// 统一使用大写格式保存,确保地址格式一致
string normalizedAddress = address.ToUpper();
PlayerPrefs.SetString(LAST_DEVICE_ADDRESS_KEY, normalizedAddress);
PlayerPrefs.SetString(LAST_DEVICE_NAME_KEY, name);
PlayerPrefs.Save();
Log($"保存设备连接记录: {name} [{normalizedAddress}]");
}
/// <summary>
/// 清除设备连接记录
/// </summary>
public void ClearLastConnectedDevice()
{
PlayerPrefs.DeleteKey(LAST_DEVICE_ADDRESS_KEY);
PlayerPrefs.DeleteKey(LAST_DEVICE_NAME_KEY);
PlayerPrefs.Save();
Log("清除设备连接记录");
}
/// <summary>
/// 开始扫描蓝牙设备
/// </summary>
/// <param name="timeout">扫描超时时间默认10秒</param>
public void StartScan(float timeout = 10f)
{
if (!IsInitialized)
{
OnError?.Invoke("蓝牙未初始化请先调用Initialize()");
return;
}
// 取消自动连接,避免干扰手动扫描
CancelInvoke(nameof(TryAutoConnectDelayed));
if (IsScanning)
{
Log("已经在扫描中,先停止当前扫描...");
StopScan();
}
// 如果还在连接状态,先断开
if (IsConnected)
{
Log("警告: 扫描时仍处于连接状态,先断开连接...");
Disconnect();
// 延迟一点再开始扫描
Invoke(nameof(DelayedStartScan), 1f);
return;
}
// 清空之前的设备列表
DiscoveredDevices.Clear();
IsScanning = true;
Log($"开始扫描蓝牙设备 (超时: {timeout}秒)...");
// 开始扫描
BluetoothLEHardwareInterface.ScanForPeripheralsWithServices(null,
// 回调1基础设备信息地址和名称
(address, name) =>
{
AddOrUpdateDevice(address, name, 0);
},
// 回调2包含RSSI和制造商数据
(address, name, rssi, bytes) =>
{
AddOrUpdateDevice(address, name, rssi);
},
true // 允许不带有制造商数据的RSSI
);
// 设置超时自动停止
Invoke(nameof(StopScan), timeout);
}
/// <summary>
/// 延迟开始扫描(用于断开连接后)
/// </summary>
private void DelayedStartScan()
{
Log("延迟扫描开始...");
// 清空设备列表
DiscoveredDevices.Clear();
IsScanning = true;
BluetoothLEHardwareInterface.ScanForPeripheralsWithServices(null,
(address, name) =>
{
AddOrUpdateDevice(address, name, 0);
},
(address, name, rssi, bytes) =>
{
AddOrUpdateDevice(address, name, rssi);
},
true
);
Invoke(nameof(StopScan), 10f);
}
/// <summary>
/// 停止扫描
/// </summary>
public void StopScan()
{
if (!IsScanning) return;
IsScanning = false;
// 取消所有扫描相关的Invoke
CancelInvoke(nameof(StopScan));
CancelInvoke(nameof(DelayedStartScan));
CancelInvoke(nameof(TryAutoConnectDelayed));
BluetoothLEHardwareInterface.StopScan();
Log("停止扫描蓝牙设备");
}
/// <summary>
/// 连接指定设备
/// </summary>
/// <param name="deviceAddress">设备地址</param>
public void Connect(string deviceAddress)
{
if (!IsInitialized)
{
OnError?.Invoke("蓝牙未初始化");
return;
}
if (IsConnected)
{
OnError?.Invoke("已连接到设备,请先断开连接");
return;
}
// 如果正在连接其他设备,先取消
if (_isConnecting)
{
Log($"取消之前的连接尝试: {_connectingAddress}");
_isConnecting = false;
}
Log($"正在连接设备: {deviceAddress}");
// 启动连接超时计时
_isConnecting = true;
// 统一使用大写格式保存地址
_connectingAddress = deviceAddress.ToUpper();
_connectionTimer = ConnectionTimeout;
BluetoothLEHardwareInterface.ConnectToPeripheral(deviceAddress,
// 连接成功回调
(address) =>
{
Log($"设备连接成功: {address}");
},
// 服务发现回调
(address, serviceUUID) =>
{
Log($"[BLE-DEBUG] 发现服务: {serviceUUID}");
},
// 特征发现回调
(address, serviceUUID, characteristicUUID) =>
{
Log($"[BLE-DEBUG] 发现特征: Service={serviceUUID}, Char={characteristicUUID}");
// 首次发现特征时设置连接状态
if (!IsConnected)
{
// 连接成功,停止超时计时
_isConnecting = false;
_connectingAddress = null;
IsConnected = true;
// 统一使用大写格式保存地址,确保与断开时使用的格式一致
ConnectedDeviceAddress = address.ToUpper();
// 保存连接的设备信息
var device = DiscoveredDevices.Find(d => d.Address.ToUpper() == address.ToUpper());
string deviceName = device != null ? device.Name : "Unknown";
SaveLastConnectedDevice(address, deviceName);
OnConnected?.Invoke(address);
// 延迟通知连接成功并可通信
Invoke(nameof(NotifyConnectedSuccess), 0.5f);
}
},
// 断开连接回调
(disconnectAddress) =>
{
// 如果正在连接中就被断开,说明连接失败
if (_isConnecting && disconnectAddress.ToUpper() == _connectingAddress?.ToUpper())
{
_isConnecting = false;
Log($"连接失败: {disconnectAddress}");
OnConnectFailed?.Invoke(disconnectAddress);
_connectingAddress = null;
}
// 设备断开连接(使用不区分大小写的地址比较)
else if (IsConnected && disconnectAddress.ToUpper() == ConnectedDeviceAddress?.ToUpper())
{
IsConnected = false;
string disconnectedAddr = ConnectedDeviceAddress;
ConnectedDeviceAddress = null;
Log($"设备断开连接: {disconnectAddress}");
OnDisconnected?.Invoke(disconnectAddress);
// 执行清理
CleanupAfterDisconnect();
}
else
{
Log($"收到未处理的断开回调: {disconnectAddress}, 当前连接状态: IsConnected={IsConnected}");
}
}
);
}
/// <summary>
/// 按名称连接设备
/// </summary>
/// <param name="deviceName">设备名称</param>
public void ConnectByName(string deviceName)
{
var device = DiscoveredDevices.Find(d => d.Name == deviceName);
if (device != null)
{
Connect(device.Address);
}
else
{
OnError?.Invoke($"未找到设备: {deviceName}");
}
}
/// <summary>
/// 断开当前连接(带协议通知)
/// 先发送0x70断开命令给设备再断开BLE连接
/// </summary>
public void Disconnect()
{
if (!IsConnected)
{
Log("当前没有连接的设备");
return;
}
string addressToDisconnect = ConnectedDeviceAddress;
Log($"正在断开连接: {addressToDisconnect}");
// 先停止扫描,避免扫描干扰断开过程
if (IsScanning)
{
StopScan();
}
// 取消可能正在进行的连接尝试
if (_isConnecting)
{
_isConnecting = false;
_connectingAddress = null;
}
// 先通过BLE协议发送断开命令0x70
Log($"检查BLECommunicationManager: Instance={(BLECommunicationManager.Instance != null ? "" : "null")}");
if (BLECommunicationManager.Instance != null)
{
Log("发送断开命令(0x70)到设备...");
BLECommunicationManager.Instance.Disconnect((success) =>
{
Log($"断开命令发送结果: {(success ? "" : "")}");
// 无论命令是否成功都执行BLE断开
PerformBLEDisconnect(addressToDisconnect);
});
}
else
{
Log("BLECommunicationManager.Instance 为 null跳过0x70命令直接断开BLE");
// 如果没有BLE通信管理器直接断开BLE
PerformBLEDisconnect(addressToDisconnect);
}
}
/// <summary>
/// 执行BLE层断开连接
/// </summary>
private void PerformBLEDisconnect(string addressToDisconnect)
{
Log($"执行BLE断开: {addressToDisconnect}");
BluetoothLEHardwareInterface.DisconnectPeripheral(addressToDisconnect,
(address) =>
{
Log($"BLE断开连接回调: {address}");
}
);
// 立即清理状态
IsConnected = false;
ConnectedDeviceAddress = null;
OnDisconnected?.Invoke(addressToDisconnect);
// 延迟清理
Invoke(nameof(CleanupAfterDisconnect), 0.5f);
}
/// <summary>
/// 断开连接后的清理工作
/// </summary>
private void CleanupAfterDisconnect()
{
Log("执行断开连接后的清理...");
// 确保停止扫描
if (IsScanning)
{
StopScan();
}
// 取消可能冲突的Invoke
CancelInvoke(nameof(StopScan));
CancelInvoke(nameof(DelayedStartScan));
CancelInvoke(nameof(TryAutoConnectDelayed));
// 清理设备列表,为下次扫描做准备
DiscoveredDevices.Clear();
Log("断开连接清理完成,可以开始新的扫描");
}
/// <summary>
/// 完全重置蓝牙(断开连接并重新初始化)
/// 用于解决断开后无法扫描到设备的问题
/// </summary>
public void ResetBluetooth()
{
Log("开始完全重置蓝牙...");
// 保存上一次连接的设备信息
string lastAddress = PlayerPrefs.GetString(LAST_DEVICE_ADDRESS_KEY, "");
string lastName = PlayerPrefs.GetString(LAST_DEVICE_NAME_KEY, "");
// 停止扫描
if (IsScanning)
{
StopScan();
}
// 断开连接 - 先发送0x70断开命令再断开BLE连接
if (IsConnected)
{
string addressToDisconnect = ConnectedDeviceAddress;
Log($"重置蓝牙: 正在断开连接 {addressToDisconnect}");
// 先通过BLE协议发送断开命令0x70
if (BLECommunicationManager.Instance != null)
{
Log("重置蓝牙: 发送断开命令(0x70)到设备...");
BLECommunicationManager.Instance.Disconnect((success) =>
{
Log($"重置蓝牙: 断开命令发送结果: {(success ? "" : "")}");
// 无论命令是否成功都执行BLE断开
BluetoothLEHardwareInterface.DisconnectPeripheral(addressToDisconnect, (addr) => {});
});
}
else
{
// 如果没有BLE通信管理器直接断开BLE
BluetoothLEHardwareInterface.DisconnectPeripheral(addressToDisconnect, (addr) => {});
}
IsConnected = false;
ConnectedDeviceAddress = null;
}
// 取消所有Invoke
CancelInvoke();
// 清理设备列表
DiscoveredDevices.Clear();
// 反初始化蓝牙
BluetoothLEHardwareInterface.DeInitialize(() =>
{
IsInitialized = false;
Log("蓝牙已反初始化,准备重新初始化...");
// 延迟重新初始化
Loom.QueueOnMainThread(() =>
{
Invoke(nameof(ReinitializeBluetooth), 1f);
});
});
}
/// <summary>
/// 重新初始化蓝牙(断开连接后使用,不触发自动连接)
/// </summary>
private void ReinitializeBluetooth()
{
Log("重新初始化蓝牙...");
BluetoothLEHardwareInterface.Initialize(true, false,
() =>
{
IsInitialized = true;
Log("蓝牙重新初始化成功,现在可以扫描设备了");
OnInitialized?.Invoke(true);
// 注意:这里不触发自动连接,避免干扰用户操作
},
(error) =>
{
if (error.Contains("Service not found"))
{
Log("蓝牙重新初始化警告,继续...");
IsInitialized = true;
OnInitialized?.Invoke(true);
}
else
{
Log($"蓝牙重新初始化失败: {error}");
OnError?.Invoke($"重新初始化失败: {error}");
}
}
);
}
/// <summary>
/// 反初始化蓝牙
/// </summary>
public void DeInitialize()
{
if (IsConnected)
{
Disconnect();
}
if (IsScanning)
{
StopScan();
}
BluetoothLEHardwareInterface.DeInitialize(() =>
{
IsInitialized = false;
Log("蓝牙已反初始化");
});
}
/// <summary>
/// 添加或更新设备到列表
/// </summary>
private void AddOrUpdateDevice(string address, string name, int rssi)
{
// 调试日志:显示所有发现的设备(包括无名称的)
Log($"扫描到设备: Name='{name}', Address={address}, RSSI={rssi}");
// 查找是否已存在
var existingDevice = DiscoveredDevices.Find(d => d.Address.ToUpper() == address.ToUpper());
if (existingDevice != null)
{
// 更新RSSI
existingDevice.Rssi = rssi;
Log($"更新设备信号: {existingDevice.Name} [{address}] RSSI: {rssi}");
}
else
{
// 过滤掉没有名称的设备
if (string.IsNullOrEmpty(name) || name == "No Name" || name == "Unknown")
{
Log($"过滤无名称设备: [{address}]");
return;
}
// 添加新设备
var newDevice = new BluetoothDevice(address, name, rssi);
DiscoveredDevices.Add(newDevice);
Log($"发现新设备: {name} [{address}] RSSI: {rssi}");
OnDeviceFound?.Invoke(newDevice);
}
}
/// <summary>
/// 输出日志
/// </summary>
private void Log(string message)
{
Debug.Log($"[BluetoothManager] {message}");
OnLog?.Invoke(message);
}
/// <summary>
/// 通知连接成功并可通信
/// </summary>
private void NotifyConnectedSuccess()
{
Log("蓝牙连接已就绪,可以开始通信");
OnConnectedSuccess?.Invoke();
}
}