7、低功耗蓝牙(BLE)基础:BLE与经典蓝牙区别、GATT协议架构、广播与扫描
各位同学,今天我们来聊聊低功耗蓝牙,也就是 BLE。说实话,我刚入行那会儿,蓝牙开发这块还挺让人头疼的。经典蓝牙和 BLE 傻傻分不清,GATT 协议栈更是看得云里雾里。但别担心,今天这一章,我会把我这些年踩过的坑、总结的经验,掰开了揉碎了讲给你听。
7.1 BLE 与经典蓝牙:它们到底差在哪?
很多人问我:“老师,BLE 和经典蓝牙不都是蓝牙吗?有啥区别?” 嗯,这个问题问得好。说白了,它们俩虽然都叫蓝牙,但设计思路完全不同。
经典蓝牙,你想想看,它设计出来是为了干啥?是为了传输大量数据,比如你连个蓝牙耳机听歌、传个文件。它追求的是高吞吐量,但代价就是功耗高。你手机开一天蓝牙,电量哗哗往下掉,这就是经典蓝牙的“功劳”。
而 BLE 呢?它从一开始就没打算干“重活”。它的核心思想就四个字:低功耗。它设计出来是为了传输小数据量,比如智能手环的心率、温度传感器的读数、门锁的开关状态。它大部分时间都在“睡觉”,只有需要传输数据时才“醒”一下,传完继续睡。所以,一颗纽扣电池能让 BLE 设备跑好几年。
我给大家整理了一个对比表,一目了然:
| 特性 | 经典蓝牙 (BR/EDR) | 低功耗蓝牙 (BLE) |
|---|---|---|
| 设计目标 | 高数据吞吐量 | 超低功耗、低成本 |
| 功耗 | 高(约 1W) | 极低(约 0.01W ~ 0.5W) |
| 数据传输速率 | 1-3 Mbps | 125 Kbps ~ 2 Mbps |
| 连接延迟 | 约 100ms | 约 3ms ~ 6ms |
| 典型应用 | 蓝牙耳机、音箱、文件传输 | 智能穿戴、传感器、信标 |
| 网络拓扑 | 点对点(Piconet) | 点对点、广播、Mesh |
核心区别一句话总结:经典蓝牙是“大力士”,适合搬重物(传大文件);BLE 是“小精灵”,适合跑腿送小纸条(传小数据),而且它很会偷懒(低功耗)。
我在项目中遇到过不少新手,上来就用 BLE 去传音频流,结果发现卡得要死。这就是没搞清楚 BLE 的定位。记住,BLE 不是用来替代经典蓝牙的,它们是互补的关系。
7.2 GATT 协议架构:BLE 世界的“摩斯密码”
好,现在我们知道了 BLE 是干啥的。那它到底是怎么工作的呢?这就不得不提 GATT 协议了。GATT,全称是 Generic Attribute Profile,通用属性协议。你可以把它理解成 BLE 设备之间沟通的“语言规范”。
我个人习惯把 GATT 架构想象成一个“俄罗斯套娃”,一层套一层。从大到小,分别是:
- Profile(配置文件):最外层的大套娃。它定义了一个完整的应用场景。比如“心率计 Profile”,它规定了心率计设备应该有哪些功能。
- Service(服务):中间层的套娃。一个 Profile 包含多个 Service。比如“心率计 Profile”里,就有一个“心率 Service”,专门负责心率数据。
- Characteristic(特征):里层的小套娃。一个 Service 包含多个 Characteristic。比如“心率 Service”里,就有一个“心率测量值 Characteristic”,用来存放具体的心率数值。
- Descriptor(描述符):最小的套娃。它用来描述 Characteristic 的额外信息。比如“心率测量值 Characteristic”的 Descriptor 可以描述这个值的单位是“bpm”(每分钟心跳次数)。
为了让你更直观地理解,我画了一张图:
你看,这个结构是不是很清晰?在 Android 开发中,我们就是通过这个层级关系,一步步找到我们想要的数据的。
小技巧:在调试 BLE 设备时,我习惯先用 nRF Connect 这类工具扫描一下设备,看看它暴露了哪些 Service 和 Characteristic。这能帮你快速了解设备的“能力”,避免在代码里瞎猜。
7.3 广播与扫描:BLE 设备的“打招呼”方式
好了,现在我们知道了 BLE 设备内部是怎么组织的。那问题来了:一个 BLE 设备怎么让别人发现它?另一个设备又怎么找到它?这就是广播和扫描要做的事。
广播(Advertising):你可以把它想象成 BLE 设备在“喊话”。它每隔一段时间(比如 100ms)就在特定的信道上喊一嗓子:“我在这里!我叫 XX 设备!我有这些功能!” 这个“喊话”的数据包,就叫广播包。
扫描(Scanning):另一个设备(比如你的手机)则在“监听”。它会在这些信道上“竖起耳朵”,听有没有设备在“喊话”。一旦听到,它就记录下来,然后可以发起连接。
这个过程,说白了就是“一个在喊,一个在听”。
我曾经在一个项目中,发现设备广播出去了,但手机就是扫描不到。排查了半天,发现是广播间隔设置得太长了。设备每 5 秒才广播一次,手机扫描窗口又短,正好错过了。嗯,这里要注意:广播间隔和扫描窗口的匹配,是个很容易踩的坑。
广播包的结构也很有意思。它里面可以包含一些关键信息:
- 设备名称:比如 “MyHeartRateMonitor”。
- UUID:用来标识设备支持的 Service。比如心率 Service 的 UUID 是 0x180D。
- 厂商自定义数据:一些厂商会在这里塞自己的私有数据。
在 Android 上,扫描 BLE 设备的代码大致是这样的:
// 获取 BluetoothAdapter
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// 创建扫描回调
ScanCallback scanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
BluetoothDevice device = result.getDevice();
String deviceName = device.getName();
String deviceAddress = device.getAddress();
// 这里你就可以拿到设备信息了
Log.d("BLE", "发现设备: " + deviceName + " [" + deviceAddress + "]");
}
};
// 开始扫描
BluetoothLeScanner scanner = bluetoothAdapter.getBluetoothLeScanner();
ScanFilter filter = new ScanFilter.Builder()
.setServiceUuid(new ParcelUuid(UUID.fromString("0000180d-0000-1000-8000-00805f9b34fb")))
.build();
ScanSettings settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) // 低延迟模式,扫描更快
.build();
scanner.startScan(Arrays.asList(filter), settings, scanCallback);
避坑指南:我曾经在 Android 6.0 以上的设备上,忘了申请位置权限,结果扫描回调死活不触发。记住,从 Android 6.0 开始,扫描 BLE 设备需要 ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION 权限。从 Android 10 开始,还需要 ACCESS_BACKGROUND_LOCATION 权限。权限问题,是 BLE 开发中最常见的“拦路虎”。
广播和扫描,是 BLE 通信的第一步。只有成功“对上眼”,才能进行后续的连接和数据交互。所以,把这一步搞扎实了,后面的路就好走了。
好了,这一章的内容就到这里。BLE 的基础概念,说白了就是这些:它和经典蓝牙的区别、GATT 的套娃结构、以及广播扫描的“喊话”机制。把这些搞懂了,你就已经一只脚踏进 BLE 开发的大门了。