蓝牙通讯协议学习
1. 传统蓝牙 (RFCOMM) VS BLE (低功耗蓝牙)
传统蓝牙 (RFCOMM) 和 BLE (低功耗蓝牙) 使用不同协议
BLE设备需要知道服务UUID和特征UUID才能正确通信
在主板(如树莓派、STM32等)通过 串口(UART) 连接蓝牙模块(如HC-05、JDY-31等)的场景中,UUID 仅适用于BLE(低功耗蓝牙),而传统蓝牙串口模块(基于SPP/RFCOMM协议)不需要UUID。以下是具体分析和解决方案:
1.1 明确蓝牙模块类型
情况1:传统蓝牙模块(如HC-05)
- 协议:使用 RFCOMM/SPP(串口端口协议),直接透传串口数据。
- UUID无关:数据通过固定的 通道(Channel) 传输,而非UUID。
- 通信流程:
情况2:BLE模块(如HM-10)
- 协议:基于 GATT(通用属性协议),需通过UUID标识服务/特征。
- 关键UUID:
- 串口数据服务:通常为
0000FFE0-0000-1000-8000-00805F9B34FB
- 写入特征(TX):
0000FFE1-...
(主板→蓝牙) - 通知特征(RX):
0000FFE2-...
(蓝牙→主板)
- 串口数据服务:通常为
1.2 如何确定数据传输方式
步骤1:确认模块型号及协议
- 查看模块手册或标签(如HC-05是传统蓝牙,CC2541是BLE)。
- 传统蓝牙:无需UUID,直接配对后映射为虚拟串口(如
/dev/rfcomm0
)。 - BLE模块:需查找UUID(常见于手册或AT指令响应)。
步骤2:获取BLE模块的UUID
-
方法1:AT指令查询(连接模块的UART,发送指令):
AT+UUID? # 查询服务UUID AT+CHAR? # 查询特征UUID
响应示例:
+UUID=FFE0 +CHAR=FFE1
-
方法2:使用BLE扫描工具(如nRF Connect):
- 扫描设备 → 查看服务列表 → 找到串口相关服务(如
FFE0
)。
- 扫描设备 → 查看服务列表 → 找到串口相关服务(如
步骤3:传统蓝牙的通道确认
- 固定为 RFCOMM Channel 1(多数模块默认值),无需配置UUID。
1.3 代码实现示例
传统蓝牙(如HC-05)通信
# 主板通过UART发送数据(Python示例)
import serialuart = serial.Serial("/dev/ttyS0", baudrate=9600) # 替换为实际串口
uart.write(b"Hello Bluetooth\n") # 数据直接透传到蓝牙模块
BLE模块(如HM-10)通信
# 通过UUID读写数据(需bleak库)
from bleak import BleakClientUART_SERVICE = "0000FFE0-0000-1000-8000-00805F9B34FB"
TX_CHAR = "0000FFE1-0000-1000-8000-00805F9B34FB" # 主板写入特征async def send_data(address, message):async with BleakClient(address) as client:await client.write_gatt_char(TX_CHAR, message.encode())print("数据已发送")# 使用
address = "80:6F:B0:12:34:56" # 模块蓝牙地址
asyncio.run(send_data(address, "Hello BLE"))
蓝牙设备的 MAC 地址,格式为 6组十六进制数,用冒号 : 或连字符 - 分隔。地址字母不区分大小写:(00:1A:7D 和 00:1a:7d 等效)
可以手动从手机/电脑蓝牙设置中查看:
在手机蓝牙设置中点击搜索到的蓝牙名称 → 查看详情 → 找到 MAC 地址(格式如 00:11:22:33:44:55)。
1.4 关键区别总结
特性 | 传统蓝牙(如HC-05) | BLE模块(如HM-10) |
---|---|---|
协议 | RFCOMM/SPP | GATT |
是否需要UUID | 否 | 是 |
数据传输方式 | 直接串口透传 | 通过特征(Characteristic)读写 |
典型应用 | 音频、文件传输 | 传感器数据、IoT设备 |
1.5 常见问题解决
Q1:如何确认数据是否通过蓝牙发出?
- 传统蓝牙:用手机APP(如"蓝牙串口")查看接收的数据。
- BLE:使用nRF Connect监听特征值变化。
Q2:发送后对方未收到数据?
- 检查波特率(确保主板与蓝牙模块的UART波特率一致)。
- 确认BLE模块的写特征UUID是否正确。
- 检查硬件连接
确认 TX/RX 线序正确(主机 TX → 模块 RX,主机 RX → 模块 TX) - 检查供电是否稳定(避免电压不足导致数据丢失)
Q3:如何自定义BLE模块的UUID?
- 通过AT指令修改(依模块型号而定):
AT+UUID=ABCD1234 # 设置自定义服务UUID AT+CHAR=5678 # 设置特征UUID
总结
- 传统蓝牙串口模块:数据直接透传,无需UUID,只需确保UART配置正确。
- BLE模块:需通过 服务/特征UUID 读写数据,UUID通常为
FFE0/FFE1
或由厂商指定。
2. 蓝牙模块的广播功能
蓝牙模块的广播功能是其核心特性之一,主要用于无连接的数据传输和设备发现。
广播的本质:单向主动发射
蓝牙模块(广播者):像“广播电台”一样,主动、周期性发送广播包(无需等待其他设备请求)。
其他设备(观察者/扫描者):如手机、网关等,只需开启扫描(Scan)功能,即可被动接收这些广播数据,无需提前配对或建立连接。
3. python建立BLE蓝牙通讯的示例代码
- 直接根据蓝牙模块的MAC建立通讯连接
from bleak import BleakClient
import asyncioasync def connect_ble(device_address):"""连接BLE设备"""try:async with BleakClient(device_address) as client:print(f"已连接到 {device_address}")# 获取服务services = await client.get_services()for service in services:print(f"服务: {service.uuid}")for char in service.characteristics:print(f" 特征: {char.uuid}")# 示例: 读取特征值 (需要替换为实际的特征UUID)# value = await client.read_gatt_char("00002a00-0000-1000-8000-00805f9b34fb")# print(f"读取的值: {value}")except Exception as e:print(f"错误: {e}")# 要运行的BLE设备地址
DEVICE_ADDRESS = "XX:XX:XX:XX:XX:XX" # 替换为你的设备地址if __name__ == "__main__":asyncio.run(connect_ble(DEVICE_ADDRESS))
- 通过扫描确定蓝牙模块的MAC建立通讯连接
from bleak import BleakScanner, BleakClient
import asyncioasync def scan_ble_devices():print("扫描BLE设备...")devices = await BleakScanner.discover()for d in devices:print(f"{d.name}: {d.address}")return devicesasync def connect_to_device(address):async with BleakClient(address) as client:print(f"已连接到 {address}")# 这里可以添加你的通信代码# 使用示例
async def main():devices = await scan_ble_devices()if devices:await connect_to_device(devices[0].address)asyncio.run(main())