【CurieNano教程1】教你查询GATT并编写相关BLE服务
概述:要进行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提供的标准服务,编写应用最好遵循这些标准。比如心率服务就负责发送心率数据,血压服务就负责发送血压数据,最好不要乱用。
http://www.arduino.cn/data/attachment/forum/201703/15/225922j82db0td5s0d9p58.png
上图是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,以及他们包含哪些字段。
图:部分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字节是心率值,不断改变该值,手机上就能收到不断变化的心率值了。
下面我们按此思路编写代码,注意注释中每个语句与我们的思路如何联系。
#include <CurieBLE.h>
BLEPeripheral blePeripheral;
// 创建UUID为0x180D的服务(心率服务)
BLEService Service("180D");
// 创建UUID为0x2A37的特征(心率测量值特征),它的属性为BLENotify,字段总长度2Byte
BLECharacteristic c("2A37", BLENotify, 2);
void setup() {
blePeripheral.setLocalName("Arduino 101");
blePeripheral.setAdvertisedServiceUuid(Service.uuid());
blePeripheral.addAttribute(Service);
blePeripheral.addAttribute(c);
blePeripheral.begin();
}
// 待发送的2Byte心率特征的值。其中即data是控制字节,令它恒为0。data是心率值
uint8_t data[] = {0,0};
void loop() {
// 将心率值+1
data ++;
// 发送心率值
c.setValue(data,2);
delay(500);
}程序测试:
将以上代码上传到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
图:心率服务测试结果
通过以上例子,你应该掌握了如何“如何查询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字节是上一次车轮转满一圈的时间值,我们让它不断增加。
代码如下:#include <CurieBLE.h>
BLEPeripheral blePeripheral;
BLEService CSC("1816");
BLEShortCharacteristic CSCFeature("2A5C", BLERead);
BLECharacteristic CSCMeasure("2A5B", BLENotify, 7);
void setup() {
blePeripheral.setLocalName("Arduino 101");
blePeripheral.setAdvertisedServiceUuid(CSC.uuid());
blePeripheral.addAttribute(CSC);
blePeripheral.addAttribute(CSCFeature);
blePeripheral.addAttribute(CSCMeasure);
blePeripheral.begin();
CSCFeature.setValue(0);
}
uint8_t data = {1,0,0,0,0,0,0};
void loop() {
data++;
data++;
CSCMeasure.setValue(data,7);
delay(1000);
}程序测试:
将以上代码上传到CurieNano。
测试方法一:打开NRFToolBox,点击CSC(Cycling Speed and Cadence),连接CurieNano后可以看到左下图的效果。
测试方法二:打开NRFConnect,点SCAN搜索设备,搜到CurieNano后点击Cycling Speed and Cadence,再点击右边的三个向下箭头,可以看到Value不断地变化
图:骑行服务测试结果
好教程,感谢分享。 教程确实好,有板能验证更佳。
楼主的帖子后面还有要补充的吧
噗噗熊 发表于 2017-5-5 10:20 static/image/common/back.gif
楼主的帖子后面还有要补充的吧
没了,就是讲解一下GATT service的结构,然后展示两个例子,就没了 xyz.543 发表于 2017-5-5 09:58 static/image/common/back.gif
教程确实好,有板能验证更佳。
验证了啊,两个例子都有上传程序并展示测试结果啊 每个人都可以顺利验证吧
页:
[1]