查看: 7917|回复: 6

【CurieNano教程1】教你查询GATT并编写相关BLE服务

[复制链接]
  • TA的每日心情
    开心
    2017-5-15 14:59
  • 签到天数: 8 天

    连续签到: 1 天

    [LV.3]偶尔看看II

    发表于 2017-5-4 20:50:23 | 显示全部楼层 |阅读模式
    分享到:
    概述:

    要进行BLE开发,GATT协议是必须了解的,因为思路与传统蓝牙不同,导致刚接触BLE的人产生很大困惑。
    本文章教你如何在 https://www.bluetooth.com/specifications/gatt 里查询你需要的Service并编写相关的应用。




    目的:
    教大家了解GATT协议、如何查询GATT服务并用CurieNano编写GATT服务





    BLE中的连接关系和服务关系:
    连接关系:主动连接其他设备的设备称为Central(中心设备),接受其他设备连接的设备称为Peripheral(外围设备)。Central与Peripheral的关系是连接关系。
    服务关系:提供服务的一方是Server(服务端),被提供服务的一方是Client(客户端)。Server与Client的关系是服务关系。
    两种关系是共同存在的,比如本帖要讲解的例子中,CurieNano是Peripheral同时也是Server,手机是Central同时也是Client。




    GATT协议描述:

    GATT协议规定了BLE提供的标准服务,编写应用最好遵循这些标准。比如心率服务就负责发送心率数据,血压服务就负责发送血压数据,最好不要乱用。


    上图是GATT Profile的逻辑结构。解释一下上图的几个名词:

    • Profile,翻译为规范或配置文件,个人认为这个翻译容易引起误导。实际上,Profile就是在BLE通信中,由若干服务构成,实现某个特定功能的实体。
    • Service,翻译为服务,是由数据及其关联的行为构成的集合,由一个或多个特征组成
    • Characteristic,翻译为特征,通常由若干字段组成。
    • UUID:统一唯一标识符,用于标识唯一的服务或特征,比如心率服务的UUID是0x180D,温度测量值特征的UUID是0x2A1C。UUID的作用是:通信双方互相告知UUID,就能知道对方提供的是哪些服务。


    这种解释不够直观。那么我们举一个例子:
    • 心率服务的UUID是0x180D,它只包含一个特征,叫心率测量值特征,UUID是0x2A37。
    • 该特征的第一个字节是控制字节,它的各个比特位规定了心率值是1字节还是2字节,传感器是否接触人体,是否有补充字段等。
    • 第二个字节开始就是心率值数据了,CurieNano要做的就是不断更新这个值,在手机上就能看到心率值的变化了。

    这些字节的格式都是GATT规定好的,不能乱改,也不要混用!比如不要把心率服务用来传输电池服务数据。因为不同服务直接是不能互相连接的!一个提供心率服务的
    CurieNano不能与接受电池服务的手机连接。
    这种强制的GATT规定看似多余,实际上非常有利于物联网的分工开发。比如开发蓝牙体重秤,负责手机APP和负责下位机的两个小组不需要商定数据格式,只需要共同遵守GATT的体重服务规定即可。






    在哪查询GATT定义?


    那么,在哪查看GATT规定了哪些Service和Characteristic,以及他们对应的UUID呢?当然是蓝牙官网:
    https://www.bluetooth.com/specifications/gatt
    点击GATT Services,可以查看各个Service的UUID,以及他们包含哪些Characteristic。下图给出了部分GATT Services
    点击GATT Characteristic,可以查看各个Characteristic,以及他们包含哪些字段。

    GATTServices.png

    图:部分GATTSerivce条目








    实战一:通过查询GATT定义,编写心率服务:

    CurieNano心率服务是Arduino.cc提供的官方BLE例程之一(链接点此),我在这里把它的编程思路为大家梳理一下。注:我们并不是编写真正心率测量的应用,为了突出蓝牙编程的细节,本示例不会真正地去使用心率传感器。

    首先,查询GATT定义:
    • 进入GATT Servies查询页面:https://www.bluetooth.com/specifications/gatt/services
    • 找到Heart Rate(心率服务),它的UUID是0x180D。
    • Service Characteristics 表格里,看到该服务包括好几个特征,但只有一个特征 Heart Rate Measurement 标记为Mandatory(强制的),其它特征都标记为Optional(可选的)。为了简化编程,我们只考虑这一个特征。
    • 注意到Heart Rate Measurement的Properties栏里,只有Notify标记为Mandatory(强制的),也就是说该特征必须有Notify属性,其他属性都是可选的。所谓Notify,就是当CurieNano(Server方)改变特征的值时,手机(Client方)能立即检测到。
    • 点进Heart Rate Measurement(心率测量值特征),看到它的UUID是0x2A37,它包括字段如下表。我们可以令Flags恒为0,改变Heart Rate Measurement Value即可

    字段大小意义备注
    Flags1Byte控制字节,指出其他字段的大小,以及可选字段是否存在必须存在
    Heart Rate Measurement Value1~2Byte心率值,当Flags的第一bit为0时该字段1Byte,为1时该字段2Byte必须存在
    其他字段(本程序不考虑)--当Flags相应的bit都为0时,这些字段都不存在可选


    好,让我们理一下思路:我们的程序首先要建立一个UUID为0x180D的心率服务。然后建立一个UUID为0x2A37的心率测量值特征,该特征包括2字节:前1字节是Flags字段,我们让它恒为0;后1字节是心率值,不断改变该值,手机上就能收到不断变化的心率值了。

    下面我们按此思路编写代码,注意注释中每个语句与我们的思路如何联系。
    1. #include <CurieBLE.h>

    2. BLEPeripheral blePeripheral;
    3. // 创建UUID为0x180D的服务(心率服务)
    4. BLEService Service("180D");
    5. // 创建UUID为0x2A37的特征(心率测量值特征),它的属性为BLENotify,字段总长度2Byte
    6. BLECharacteristic c("2A37", BLENotify, 2);

    7. void setup() {
    8.   blePeripheral.setLocalName("Arduino 101");
    9.   blePeripheral.setAdvertisedServiceUuid(Service.uuid());
    10.   blePeripheral.addAttribute(Service);
    11.   blePeripheral.addAttribute(c);
    12.   blePeripheral.begin();
    13. }

    14. // 待发送的2Byte心率特征的值。其中即data[0]是控制字节,令它恒为0。data[1]是心率值
    15. uint8_t data[] = {0,0};

    16. void loop() {
    17.   // 将心率值+1
    18.   data[1] ++;
    19.   // 发送心率值
    20.   c.setValue(data,2);
    21.   delay(500);
    22. }
    复制代码
    程序测试:
    将以上代码上传到CurieNano。我们使用NRFToolBox和NRFConnect两款手机APP进行测试。NRFToolBox可以用于一些简单的蓝牙应用,NRFConnect可以用于调试蓝牙。建议学BLE的同学都安装上。
    下载这些工具请前往奈何大大的帖子:http://www.arduino.cn/thread-22901-1-1.html

    测试方法一:打开NRFToolBox,点击HRM(HeartRateMonitor),连接CurieNano后可以看到心率值一直在线性增加。
    测试方法二:打开NRFConnect,点SCAN搜索设备,搜到CurieNano后点击Heart Rate,再点击Heart Rate Measurement右边的三个向下箭头,可以看到Value不断地变化,每次+1

    HRMtest.jpeg

    图:心率服务测试结果



    通过以上例子,你应该掌握了如何“如何查询GATT条目,编写自己的BLE物联网应用”。下面我们按照这个套路再举一个例子。















    实战二:通过查询GATT定义,编写骑行计程服务:

    首先,查询GATT定义:
    • 进入GATT Servies查询页面:https://www.bluetooth.com/specifications/gatt/services
    • 找到Cycling Speed and Cadence(自行车速度和节奏),它的UUID是0x1816。
    • Service Characteristics 表格里,看到该服务包括好几个特征,只有两个特征CSC Measurement和CSC Feature是Mandatory(强制的)。我们只考虑这两个特征。
    • 注意到CSC Feature特征的Properties栏里,只有Read是Mandatory(强制的)。所谓Read,指的是手机(Client方)有权读取该特征的值。
    • 注意到CSC Measurement特征的Properties栏里,只有Notify是Mandatory(强制的)。所谓Notify,就是当CurieNano(Server方)改变特征的值时,手机(Client方)能立即检测到。
    • 点开CSC Feature特征,看到它的UUID是0x2A5C,它仅仅包含一个2Byte的字段。看起来这个字段没什么重要的,我们令它为0即可
    • 点开CSC Measurement特征,看到它的UUID是0x2A5B,它包含的字段如下表。为了让Cumulative Wheel Revolutions和Last Wheel Event Time字段存在,我们令Flags的第一bit为1,其余bit为0,即Flags=1。Cumulative Wheel Revolutions是用来指示骑行里程的,Last Wheel Event Time是用来指示骑行速度的。

    字段大小意义备注
    Flags1Byte控制字节,指出其他字段是否存在必须存在
    Cumulative Wheel Revolutions4Byte车轮总转数计数,当Flags的第一bit为1时,该字段存在可选
    Last Wheel Event Time2Byte上一次车轮转满一圈时的时间值,当Flags的第一bit为1时,该字段存在可选
    其他字段--当Flags相应的bit都为0时,这些字段都不存在可选



    好,让我们理一下思路:我们的程序要建立一个UUID为0x1816的Cycling Speed and Cadence服务、一个UUID为0x2A5B的CSC Measurement特征、一个UUID为0x2A5C的CSC Feature特征。其中CSC Feature特征包括2字节,我们让它恒为0。CSC Measurement特征包括7字节:第一字节为控制字符Flags,我们让它恒为1;中间4字节是车轮总转数计数,我们让它不断+1;后2字节是上一次车轮转满一圈的时间值,我们让它不断增加。

    代码如下:
    1. #include <CurieBLE.h>

    2. BLEPeripheral blePeripheral;
    3. BLEService CSC("1816");
    4. BLEShortCharacteristic CSCFeature("2A5C", BLERead);
    5. BLECharacteristic CSCMeasure("2A5B", BLENotify, 7);

    6. void setup() {
    7.   blePeripheral.setLocalName("Arduino 101");
    8.   blePeripheral.setAdvertisedServiceUuid(CSC.uuid());
    9.   blePeripheral.addAttribute(CSC);
    10.   blePeripheral.addAttribute(CSCFeature);
    11.   blePeripheral.addAttribute(CSCMeasure);
    12.   blePeripheral.begin();
    13.   CSCFeature.setValue(0);
    14. }

    15. uint8_t data[7] = {1,0,0,0,0,0,0};

    16. void loop() {
    17.   data[1]++;
    18.   data[6]++;
    19.   CSCMeasure.setValue(data,7);
    20.   delay(1000);
    21. }
    复制代码
    程序测试:
    将以上代码上传到CurieNano。
    测试方法一:打开NRFToolBox,点击CSC(Cycling Speed and Cadence),连接CurieNano后可以看到左下图的效果。
    测试方法二:打开NRF
    Connect,点SCAN搜索设备,搜到CurieNano后点击Cycling Speed and Cadence,再点击右边的三个向下箭头,可以看到Value不断地变化
    CSCtest.jpeg
    图:骑行服务测试结果


    回复

    使用道具 举报

  • TA的每日心情
    开心
    2021-12-10 15:56
  • 签到天数: 2675 天

    连续签到: 1 天

    [LV.Master]伴坛终老

    发表于 2017-5-4 22:03:28 | 显示全部楼层
    好教程,感谢分享。
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    无聊
    2024-9-27 09:31
  • 签到天数: 3153 天

    连续签到: 1 天

    [LV.Master]伴坛终老

    发表于 2017-5-5 09:58:55 | 显示全部楼层
    教程确实好,有板能验证更佳。
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    发表于 2017-5-5 10:20:26 | 显示全部楼层
    楼主的帖子后面还有要补充的吧
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2017-5-15 14:59
  • 签到天数: 8 天

    连续签到: 1 天

    [LV.3]偶尔看看II

     楼主| 发表于 2017-5-5 13:55:32 | 显示全部楼层
    噗噗熊 发表于 2017-5-5 10:20
    楼主的帖子后面还有要补充的吧

    没了,就是讲解一下GATT service的结构,然后展示两个例子,就没了
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2017-5-15 14:59
  • 签到天数: 8 天

    连续签到: 1 天

    [LV.3]偶尔看看II

     楼主| 发表于 2017-5-5 14:03:41 | 显示全部楼层
    xyz.543 发表于 2017-5-5 09:58
    教程确实好,有板能验证更佳。

    验证了啊,两个例子都有上传程序并展示测试结果啊
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    难过
    2021-2-27 22:16
  • 签到天数: 1568 天

    连续签到: 1 天

    [LV.Master]伴坛终老

    发表于 2017-12-22 10:14:10 | 显示全部楼层
    每个人都可以顺利验证吧
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /5 下一条



    手机版|小黑屋|与非网

    GMT+8, 2024-12-21 03:53 , Processed in 0.165778 second(s), 30 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.