4. 连接远程设备
任务说明
本案例实现与指定蓝牙设备的异步连接(ACL)建立过程。给定目标设备地址(BD_ADDR),通过HCI命令完成从查询到连接建立的完整流程,并处理连接完成事件。核心交付物为一个可独立运行的Python脚本,模拟主机控制器接口(HCI)层的关键命令交互,验证连接建立的状态机转换。
HCI连接建立流程
ACL连接建立涉及以下HCI命令与事件的时序配合:
- HCI_Inquiry:发现周围设备(本例直接指定BD_ADDR,跳过查询)
- HCI_Create_Connection:向链路管理器发起ACL连接请求
- HCI_Connection_Complete:异步事件,报告连接建立成功或失败
核心难点在于异步性:Create_Connection命令立即返回状态,而连接结果通过后续事件通知。实现需维护命令与事件的对应关系,并处理超时与重试。
代码实现
import struct
import time
import threading
from enum import Enum
class HCI_Event_Code(Enum):
CONNECTION_COMPLETE = 0x03
COMMAND_COMPLETE = 0x0E
COMMAND_STATUS = 0x0F
class HCI_Command_Opcode(Enum):
CREATE_CONNECTION = 0x0405
INQUIRY = 0x0401
class ACL_Connection_State(Enum):
IDLE = 0
PENDING = 1
CONNECTED = 2
FAILED = 3
class HCI_Connection_Manager:
def __init__(self, timeout_sec=5.0):
self.state = ACL_Connection_State.IDLE
self.timeout_sec = timeout_sec
self.connection_handle = None
self._event_queue = []
self._lock = threading.Lock()
self._pending_opcode = None
def _send_command(self, opcode, params):
"""构造并发送HCI命令包(模拟发送到控制器)"""
# 参数格式: opcode(2字节) + 参数长度(1字节) + 参数
pkt = struct.pack('<HB', opcode.value, len(params)) + params
print(f"[TX] HCI Command: opcode=0x{opcode.value:04X}, params={params.hex()}")
# 实际实现中此处通过UART/USB发送给蓝牙控制器
return pkt
def _receive_event(self, event_code, params):
"""接收并处理来自控制器的事件(模拟异步回调)"""
with self._lock:
self._event_queue.append((event_code, params))
print(f"[RX] HCI Event: code=0x{event_code.value:02X}, params={params.hex()}")
def _process_events(self):
"""处理所有待处理事件(由事件分发线程调用)"""
while True:
with self._lock:
if not self._event_queue:
break
event_code, params = self._event_queue.pop(0)
if event_code == HCI_Event_Code.COMMAND_STATUS:
# 命令已被接受,等待后续事件
status = params[0]
if status != 0x00:
self.state = ACL_Connection_State.FAILED
print(f"[ERR] Command rejected, status=0x{status:02X}")
elif event_code == HCI_Event_Code.CONNECTION_COMPLETE:
status = params[0]
handle = struct.unpack('<H', params[1:3])[0]
if status == 0x00:
self.state = ACL_Connection_State.CONNECTED
self.connection_handle = handle
print(f"[OK] Connection established, handle=0x{handle:04X}")
else:
self.state = ACL_Connection_State.FAILED
print(f"[ERR] Connection failed, status=0x{status:02X}")
def connect(self, bd_addr):
"""发起ACL连接(异步)"""
if self.state not in (ACL_Connection_State.IDLE, ACL_Connection_State.FAILED):
raise RuntimeError(f"Invalid state for connect: {self.state}")
self.state = ACL_Connection_State.PENDING
# 参数: BD_ADDR(6字节) + packet_type(2字节) + page_scan_mode(1字节)
bd_addr_bytes = bytes.fromhex(bd_addr.replace(':', ''))
packet_type = struct.pack('<H', 0xCC18) # DM1/DH1/DM3/DH3/DM5/DH5
page_scan_mode = bytes([0x00])
params = bd_addr_bytes + packet_type + page_scan_mode
self._send_command(HCI_Command_Opcode.CREATE_CONNECTION, params)
# 模拟控制器异步响应(实际由硬件中断触发)
def _simulate_controller():
time.sleep(0.1) # 模拟链路建立耗时
# 模拟Command Status事件
self._receive_event(HCI_Event_Code.COMMAND_STATUS, bytes([0x00, 0x01, 0x00]))
# 模拟Connection Complete事件(成功)
self._receive_event(HCI_Event_Code.CONNECTION_COMPLETE,
bytes([0x00]) + struct.pack('<H', 0x0042) + bd_addr_bytes + bytes([0x00]))
threading.Thread(target=_simulate_controller, daemon=True).start()
# 等待连接完成或超时
start = time.time()
while self.state == ACL_Connection_State.PENDING:
self._process_events()
if time.time() - start > self.timeout_sec:
self.state = ACL_Connection_State.FAILED
print("[TIMEOUT] Connection attempt timed out")
break
time.sleep(0.01)
return self.state
def disconnect(self):
"""断开当前连接"""
if self.state != ACL_Connection_State.CONNECTED:
return
# 实际发送HCI_Disconnect命令
print(f"[TX] HCI Disconnect: handle=0x{self.connection_handle:04X}")
self.state = ACL_Connection_State.IDLE
self.connection_handle = None
# 使用示例
if __name__ == "__main__":
mgr = HCI_Connection_Manager()
target_addr = "AA:BB:CC:DD:EE:FF"
print(f"=== 连接远程设备 {target_addr} ===")
result = mgr.connect(target_addr)
if result == ACL_Connection_State.CONNECTED:
print(f"连接成功,句柄: 0x{mgr.connection_handle:04X}")
mgr.disconnect()
else:
print("连接失败")
关键机制说明
代码实现了以下核心逻辑:
- 状态机管理:IDLE→PENDING→CONNECTED/FAILED,确保连接流程状态可追踪
- 异步事件处理:通过事件队列和独立线程模拟控制器异步响应,符合真实蓝牙协议栈行为
- 超时保护:5秒超时防止无限等待
- 命令构造:严格按蓝牙Core 5.3规范构造HCI_Create_Connection命令参数
验证方法
运行脚本后观察输出:
- 应依次看到
[TX] HCI Command(Create_Connection)和两条[RX] HCI Event(Command Status、Connection Complete) - 最终状态为
CONNECTED,连接句柄为0x0042 - 若修改模拟事件中的状态字节为
0x01(失败),则状态应变为FAILED
判定标准:脚本输出包含完整的命令/事件交互序列,且最终状态与模拟事件内容一致,即验证通过。
HCI连接时序
上图展示了ACL连接建立过程中主机与控制器之间的HCI命令/事件交互时序。主机发送Create_Connection后进入PENDING状态,控制器先回复Command Status表示已接受命令,待链路建立完成后异步上报Connection Complete事件,主机据此更新连接状态。