Windows 上 Python 扫描经典蓝牙设备完整解决方案
·
最近在 Windows 系统上使用 Python 进行经典蓝牙设备扫描时,遇到了 PyBluez 库安装失败的问题。经过多次尝试,终于找到了一个可靠的解决方案。本文将详细介绍解决过程,并提供可直接运行的代码。
最终解决方案
1. 安装 PyBluez(Windows 适配版本)
# 步骤 1:设置合适的 setuptools 版本
pip install setuptools==68.0.0
# 步骤 2:从 GitHub 安装修复版 PyBluez
pip install git+https://github.com/pybluez/pybluez.git
# 步骤 3:恢复最新版 setuptools
pip install --upgrade setuptools
2. 扫描经典蓝牙设备的完整代码
import bluetooth
import time
def scan_classic_bluetooth(duration=8):
"""
扫描经典蓝牙设备
:param duration: 扫描持续时间(秒)
:return: 设备列表,每个设备为 (地址, 名称) 元组
"""
print(f"开始扫描经典蓝牙设备,持续时间 {duration} 秒...")
print("-" * 50)
try:
# discover_devices 参数说明:
# lookup_names=True: 查找设备名称
# flush_cache=True: 清除缓存,重新扫描
# lookup_class=False: 不查找设备类
# duration: 扫描持续时间
nearby_devices = bluetooth.discover_devices(
lookup_names=True,
flush_cache=True,
duration=duration
)
if not nearby_devices:
print("未发现任何蓝牙设备")
return []
print(f"发现 {len(nearby_devices)} 个设备:\n")
for i, (addr, name) in enumerate(nearby_devices, 1):
# 设备名称可能为 None
device_name = name if name else "未知设备"
print(f"{i:2d}. 地址: {addr}")
print(f" 名称: {device_name}")
# 可选:获取设备更多信息
try:
# 获取设备类(需要额外查询)
device_class = bluetooth.lookup_class(addr)
if device_class:
print(f" 类别: 0x{device_class:06x}")
except:
pass
print()
return nearby_devices
except Exception as e:
print(f"扫描失败: {e}")
return []
def get_device_services(device_address):
"""
获取设备的服务信息
:param device_address: 蓝牙设备地址
"""
print(f"\n查询设备 {device_address} 的服务...")
try:
services = bluetooth.find_service(address=device_address)
if not services:
print("未发现服务")
return []
print(f"发现 {len(services)} 个服务:")
for i, service in enumerate(services, 1):
print(f"\n服务 {i}:")
print(f" 名称: {service.get('name', '未知')}")
print(f" 协议: {service.get('protocol', '未知')}")
print(f" 端口: {service.get('port', '未知')}")
print(f" 服务ID: {service.get('service-id', '未知')}")
return services
except Exception as e:
print(f"查询服务失败: {e}")
return []
if __name__ == "__main__":
# 扫描设备
devices = scan_classic_bluetooth(duration=10)
# 输出结果
print("扫描结果:", devices)
问题解决全流程记录
1. 初始尝试:直接安装失败
一开始尝试使用 pip install pybluez 安装,遇到了以下错误:
error in PyBluez setup command: use_2to3 is invalid.
这个错误是因为 PyBluez-0.23 使用了已弃用的 use_2to3 参数,与新版本的 setuptools 不兼容。
2. 尝试 PyBluez2 也失败
转向 PyBluez2 分支版本:
pip install pybluez2
结果出现另一个错误:
error: package directory 'bluetooth\windows' does not exist
这表明 Windows 平台支持文件缺失。
3. 降级 setuptools 的尝试
根据网络上的建议,尝试降级 setuptools:
pip install setuptools==58.2.0
pip install pybluez
这虽然绕过了 use_2to3 错误,但又出现了新问题:
ModuleNotFoundError: No module named 'distutils'
这是因为旧版 setuptools 与当前 Python 环境的兼容性问题。
4. 从 GitHub 直接安装的成功方案
经过多次尝试,最终找到的解决方案是从 GitHub 仓库直接安装,配合特定版本的 setuptools:
# 1. 使用兼容的 setuptools 版本
pip install setuptools==68.0.0
# 2. 从 GitHub 安装修复版
pip install git+https://github.com/pybluez/pybluez.git
# 3. 恢复 setuptools
pip install --upgrade setuptools
这个方案的原理是:
- 使用 68.0.0 版本的 setuptools 作为中间版本,既兼容 PyBluez 又不会导致 distutils 缺失
- 从 GitHub 安装包含 Windows 支持文件的版本
- 安装完成后恢复最新的 setuptools,避免影响其他包的依赖
5. 验证安装成功
安装完成后,可以运行以下代码验证:
import bluetooth
print("PyBluez 版本:", bluetooth.__version__)
print("本地蓝牙地址:", bluetooth.read_local_bd_addr())
代码使用说明
基本功能
# 1. 扫描附近蓝牙设备
devices = scan_classic_bluetooth(duration=10)
# 2. 扫描结果格式
# 返回列表,每个元素是 (MAC地址, 设备名称) 的元组
# 例如: [('AA:BB:CC:DD:EE:FF', 'MyDevice'), ...]
# 3. 查询设备服务
if devices:
addr, name = devices[0] # 取第一个设备
services = get_device_services(addr)
增强功能
如果需要更详细的扫描信息,可以添加以下功能:
def enhanced_scan(duration=12):
"""
增强版蓝牙扫描
"""
devices_found = []
start_time = time.time()
print(f"【蓝牙扫描】开始时间: {time.strftime('%H:%M:%S')}")
print("=" * 60)
# 执行扫描
devices = bluetooth.discover_devices(
lookup_names=True,
lookup_class=True, # 获取设备类别
flush_cache=True,
duration=duration
)
scan_time = time.time() - start_time
for addr, name, device_class in devices:
# 解析设备类别
major_class = (device_class >> 8) & 0xFF
minor_class = (device_class >> 2) & 0x3F
device_info = {
'address': addr,
'name': name or "未知设备",
'class': device_class,
'major_class': major_class,
'minor_class': minor_class,
'scan_time': scan_time
}
devices_found.append(device_info)
# 设备类别说明
class_names = {
0x01: "计算机",
0x02: "手机",
0x03: "网络设备",
0x04: "音频/视频设备",
0x05: "外设",
0x06: "成像设备",
0x07: "可穿戴设备",
0x08: "玩具",
0x1F: "未分类"
}
class_name = class_names.get(major_class, "未知类型")
print(f"设备: {name or '未知设备'}")
print(f"地址: {addr}")
print(f"类别: {class_name} (0x{device_class:06x})")
print("-" * 40)
print(f"【扫描完成】共发现 {len(devices_found)} 个设备")
print(f"扫描耗时: {scan_time:.2f} 秒")
return devices_found
常见问题与解决方案
1. 扫描不到设备
- 检查蓝牙适配器:确保 Windows 蓝牙已开启
- 设备可见性:目标设备需处于可发现模式
- 管理员权限:尝试以管理员身份运行
- 延长扫描时间:增加到 10-15 秒
2. 权限错误
# 以管理员身份运行
# 或在代码开始时请求权限提升
import ctypes
import sys
def is_admin():
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
if not is_admin():
ctypes.windll.shell32.ShellExecuteW(
None, "runas", sys.executable, " ".join(sys.argv), None, 1
)
sys.exit()
3. 连接特定设备
def connect_to_device(address, port=1):
"""
连接蓝牙设备
"""
try:
# 创建蓝牙套接字
sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
# 连接设备
sock.connect((address, port))
print(f"成功连接到 {address}:{port}")
# 发送数据示例
sock.send(b"Hello Bluetooth!")
# 接收数据
data = sock.recv(1024)
print(f"收到数据: {data}")
# 关闭连接
sock.close()
return True
except Exception as e:
print(f"连接失败: {e}")
return False
替代方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 本文方案 | 支持经典蓝牙,代码简单 | Windows 安装复杂 | Windows 经典蓝牙 |
| Bleak 库 | 安装简单,支持 BLE | 仅 BLE,不兼容经典蓝牙 | 低功耗蓝牙设备 |
| PowerShell | 无需额外安装 | 依赖 Windows 系统 | 简单扫描和信息获取 |
| PySerial | 通过虚拟串口,兼容性好 | 需设备配对为串口 | 蓝牙串口设备 |
总结
在 Windows 上使用 Python 进行蓝牙开发确实存在一些挑战,特别是对于经典蓝牙设备。通过从 GitHub 安装修复版的 PyBluez 库,配合特定版本的 setuptools,我们可以成功地在 Windows 上实现蓝牙设备扫描。
关键点总结:
- PyBluez 官方版本在 Windows 上支持有限
- 需要从 GitHub 安装包含 Windows 支持文件的版本
- 使用 setuptools==68.0.0 作为过渡版本可解决兼容性问题
- 安装完成后恢复最新版 setuptools 避免依赖冲突
这个方案为在 Windows 上进行经典蓝牙开发提供了一个稳定的基础,可以在此基础上实现设备连接、数据传输等更复杂的功能。
注意:如果主要使用蓝牙低功耗(BLE)设备,建议直接使用 bleak 库,它在 Windows 上安装简单且功能完整。对于经典蓝牙设备,本文提供的方案是当前可用的最佳方案之一。
更多推荐



所有评论(0)