本文以环境传感器为例说明如何使用zephyr gatt来实现一个BLE Peripheral
硬件
包含DHT11,可以测试温度和湿度
Spec
实现第一步先了解BLE环境传感器的spec,BLE中基于GATT的spec有4个层次:
- Profile: 配置文件:定义一个实际的应用场景,由一组Service组成
- Service:服务:由一组Characteristic组成
- Characteristic:有自己的读写Nodify属性, 并由一组Descriptor描述其它属性
- Descriptor:用于描述Characteristic的属性
Profile
读Profile找到要实现的service
环境传感器的profile是ESP:ENVIRONMENTAL SENSING PROFILE,找到包含的services
可以看到Environmental Sensing Service必须实现
Service
我们只实现Environmental Sensing Service,读ESS的spec,并下载 org.bluetooth.service.environmental_sensing可以得到如下信息:
Declare
推荐为Primary service
Characteristic
ESS可选的Characteristic有很多种,ESS spec要求至少实现一种(Page 10, Table 3.1),这里选择温度和湿度两种。
Descriptor
温度和湿度支援的描述,ESS spec未强制要求一定要实现
实现
一个Profile可以包含多个service,在Zephyr的实现中Profile是个逻辑概念,分别对Service进行定义即可。对于ESP我只实现了ESS,如下:
1 | Profile:ESP |
Service定义
Service
下面代码是ess service的定义
1 | //定义attr数组 |
在BLE协议简述一文中说明了GATT Service就是一组Attribute组成,这些Attribute分为Declare/Characteristic/Descriptor,从上也可以看到确实是先定义了一个bt_gatt_attr数组,再以这个数组来定义service,从下面代码可以看到一个service就是存放了attr数组和attr的个数
1 | #define BT_GATT_SERVICE(_attrs) \ |
Declare/Characteristic/Descriptor
将前面代码的几个宏展开,可以看到Declare/Characteristic/Descriptor最后都是Attr, Zephyr为方便使用将一些有共性的Descriptor进行重新封装,例如BT_GATT_CUD,BT_GATT_CCC
1 | #define BT_GATT_PRIMARY_SERVICE(_service) \ |
Attribute
Service所有的元素最后都落脚于Attribute,这里也看下Attribute的定义
1 | #define BT_GATT_ATTRIBUTE(_uuid, _perm, _read, _write, _value) \ |
启动BLE Serivce
1 | static void bt_ready(int err) |
GATT操作
CHARACTERISTIC读写
在使用宏BT_GATT_CHARACTERISTIC定义Characteristic时会将Read和Write的函数注册进入,当Central端进行读GATT时Peripheral调用Read函数,响应的Central端进行GATT写的时候就调用Write函数,例如
1 | BT_GATT_CHARACTERISTIC(BT_UUID_TEMPERATURE, |
只定义了读函数也就是支持gatt读温度,当Central需要读温度时,Peripheral主动调用该函数
1 | static ssize_t read_u16(struct bt_conn *conn, const struct bt_gatt_attr *attr, |
写类似,本文可参考Descriptor的写
Descriptor读写
在使用宏BT_GATT_DESCRIPTOR定义Descriptor时会将Read和Write的函数注册进入,当Central端进行读GATT时Peripheral调用Read函数,响应的Central端进行GATT写的时候就调用Write函数,例如
1 | BT_GATT_DESCRIPTOR(BT_UUID_ES_TRIGGER_SETTING, |
上面的read_temp_trigger_setting和write_temp_trigger_setting分别用于读取和设置温度传感器的触发方式
特殊Descriptor操作
对于一些特殊的Descriptor操作是固定的,所以读写函数Zephyr已经帮我们将其写好了,只用在定义的时候给出Value即可例如
1 | #define BT_GATT_CUD(_value, _perm) |
也有一些Descriptor操作是特定的,Zephyr将其操作接口抽象出来由用户实现既可以,例如:
1 | #define BT_GATT_CCC(_cfg, _cfg_changed) |
传感器Poll
在主thread中每1s poll一次温湿度
1 | static void ess_poll(void) |
演示
终端上直接看温度和湿度
通过手机蓝牙看温度和湿度
参考
https://docs.zephyrproject.org/latest/samples/bluetooth/peripheral_esp/README.html
https://www.bluetooth.com/specifications/gatt