手机找到的服务和特征,怎样变成代码
手机能扫描并连接,只说明无线连接已经建立。接下来还要找到“往哪里写、从哪里收”。项目用一个服务和两个特征,把这两个方向交代给手机。
1 · 先给手机一个能发现的入口
广播让手机知道附近有设备,扫描响应可以补充名称。本项目名称是 SmartWatch,写在 scanRspData 中。名称方便人识别,但同名设备可能不止一台;连接后仍需发现服务和特征。
主线已经介绍低功耗蓝牙,这里只追踪应用接口。底层无线时隙、连接和链路收发由协议库承担,我们在库上登记手机能访问的数据入口。
2 · 服务分组,特征说明读写位置和能力
GATT 是 Generic Attribute Profile,即通用属性配置文件。它规定如何组织和访问这些数据项。服务把相关功能放在一起;特征包含一个值以及访问方式。UUID 是识别服务或特征的标识,不是本次连接的临时编号。
| 入口 | 完整 UUID | 本项目用途 |
|---|---|---|
| 服务 | 0000FFE0-0000-1000-8000-00805F9B34FB | 组织串口桥接的两个方向 |
| 写入特征 | 0000FFE2-0000-1000-8000-00805F9B34FB | 手机 → CH → STM32 |
| 通知特征 | 0000FFE3-0000-1000-8000-00805F9B34FB | STM32 → CH → 手机 |
FFE0 等是本项目采用的接口约定,不表示所有蓝牙串口设备都使用它们。CH 固件与 App 必须对应相同 UUID 和访问能力。
3 · 先看两个属性位,不抄整张表
ble_uart_service.c 中的属性设置
static uint8 ble_uart_RxCharProps =
GATT_PROP_WRITE_NO_RSP | GATT_PROP_WRITE;
static uint8 ble_uart_TxCharProps = GATT_PROP_NOTIFY;第一项允许手机写数据,并支持有响应与无响应两种写法;第二项允许设备主动通知手机。属性表把这些能力、UUID 和实际值关联起来。表中 handle 是协议库注册后使用的访问编号,手机按 UUID 发现后会获得对应 handle,不应把源码数组下标当成固定无线地址。
4 · 怎么让库知道数据来了该叫谁
Peripheral_Init 中登记服务与应用回调
ble_uart_add_service(on_bleuartServiceEvt);服务内部把属性表和读写回调交给 GATT 库。手机写入时,库调用 ble_uart_WriteAttrCB;它再构造一个事件,调用这里登记的 on_bleuartServiceEvt。函数指针在这里解决的实际问题,就是“库不知道我们的业务函数名字,初始化时把它告诉库”。
打开 服务实现,先找属性值的 UUID,再看 ble_uart_ProfileCBs 和服务注册,最后沿写入回调向上走。这样每个表项都有用途,不需要一次记住整张属性表。
5 · 连接成功以后,还缺一次订阅
手机希望持续接收 FFE3 通知,需要写入该特征的客户端配置描述符。源码写作 CCCD:它保存这次连接是否开启通知。订阅不是向 FFE2 发送一条业务命令,而是设置蓝牙接口的接收方式。
ble_uart_notify_is_ready 根据连接读取这份配置;断开后服务会清理相应配置。重连时手机应重新发现并订阅,不能只凭设备名称判断接收已经就绪。
用蓝牙调试工具连接,记录服务下两个特征的能力。先不订阅,观察收不到持续通知;开启订阅后再发送测试业务消息。把“连接”“找到接口”“订阅”分别记录。