[分享] 手把手教你开发BLE数据透传应用程序(一)
2465 查看
7 回复
 楼主 | 发布于 2019-04-29 | 只看楼主
分享到:

如何开发BLE数据透传应用程序?什么是BLE service和characteristic?如何开发自己的service和characteristic?如何区分ATT和GATT?有没有什么工具可以对BLE设备进行压力测试?如何提高BLE设备的数据上传速度?本文将对以上问题进行解答。

在很多应用场合,BLE只是作为一个数据透传模块,即将设备端数据上传给手机,同时接收手机端下发的数据。本文将和大家一起,一步一步演示如何开发一个BLE透传应用程序。按照本文的说明,大家可以很快就实现一个BLE透传应用,BLE透传应用已经是BLE应用中比较复杂的一种,一旦大家掌握了BLE透传应用,其他BLE应用开发就更不在话下了。本文还会以BLE透传为例子,来解释BLE service和characteristic等概念,以帮助大家理解如何定义和开发自己的BLE service和characteristic等,从而彻底理解BLE协议栈中的ATT和GATT的运行原理。然后,本文还将手把手教大家如何提高BLE数据传输速度(蓝牙4.2的理论吞吐率大概为100kB/s,而我们实际达到了80kB/s,已经非常接近理论值)。最后,我们将告诉大家如何使用安卓版nRF Connect来对你的BLE设备进行压力测试,以测试设备的稳定性和可靠性。当然,文章的最后也会告诉大家如何找到安卓和iOS手机app开发参考代码。

 

1. 开发准备

1)     Nordic nRF52或者nRF51开发板1块。请参考“Nordic nRF51/nRF52开发流程说明”,购买相应开发板(DK)。

2)     开发环境搭建。简述如下(详细说明请参考“Nordic nRF51/nRF52开发环境搭建”):

  1. 安装Keil5 MDK
  2. 安装SDK。如果你使用的是nRF52开发板,请安装nRF5 SDK15.0.0,下载链接:https://www.nordicsemi.com/eng/nordic/download_resource/59012/70/52858981/116085。如果你手上是nRF51开发板,请下载nRF5 SDK12.3.0:https://www.nordicsemi.com/eng/nordic/download_resource/54280/56/38442131/32925 (nRF51最高SDK版本只能到12.3.0,后续SDK就不再支持nRF51
  3. 安装ARM CMSIS4.5.0,下载链接:https://github.com/ARM-software/CMSIS/releases/download/v4.5.0/ARM.CMSIS.4.5.0.pack
  4. 安装Keil5 Device Family Pack,下载链接:https://www.nordicsemi.com/eng/nordic/download_resource/58865/28/26535159/87790
  5. 安装nRF5 Command Line Tools,下载链接(Windows版):https://www.nordicsemi.com/eng/nordic/download_resource/58850/47/60411125/53210
  6. 安装安卓版或者iOS版nRF connect。iOS版nRF connect请到苹果app store下载,搜索“nRF”即可以找到。安卓版nRF connect可以到Nordic Github官网上下载,下载链接为:https://github.com/NordicSemiconductor/Android-nRF-Connect/releases
  7. 安装PC版nRF connect或者nRFgo studio,两个选其一即可。PC版nRF connect下载链接(Windows版):https://www.nordicsemi.com/eng/nordic/download_resource/58847/15/21277021/108233

注:如果你使用的是Linux系统/Mac系统,或者你使用的不是Keil5-MDK,请参考“Nordic nRF51/nRF52开发环境搭建”来搭建你的开发环境。

2. 运行Nordic ble_app_uart应用程序

Nordic SDK已经提供了一个直接就可以编译和运行的数据透传应用程序:ble_app_uart,Nordic将BLE透传服务称为Nordic UART Service(NUS),所以在Nordic SDK中,NUS就是BLE透传服务。请按照如下步骤运行SDK自带的ble_app_uart程序:

1)     确认自己的芯片型号或者开发板。如果采用Nordic官方开发板的话,芯片型号和开发板编号对应关系如下:

  • nRF51系列对应开发板编号为PCA10028
  • nRF52832和nRF52810对应开发板编号为PCA10040。虽然52832和52810共用同一块开发板,但是他们在SDK中的项目编号是不一样的,52832对应PCA10040目录,52810对应PCA10040e目录,由于52810和52832 PIN to PIN兼容,软件也是完全兼容的,因此SDK很多项目只有PCA10040的目录,而没有PCA10040e目录,此时需要你自己来建立PCA10040e对应的目录和工程,具体说明可参考:http://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v15.0.0%2Fnrf52810_user_guide.html&cp=4_0_0_5_0
  • nRF52840对应开发板编号为PCA10056
  • nRF52840 dongle编号为PCA10059

这里我会以nRF52832开发板PCA10040为例来阐述整个开发过程,其他开发板与之类似,大家自己可以举一反三来开始自己的开发之旅。

2)     将开发板与PC机通过USB线相连,同时打开开发板电源(将左下角的拨位开关打到“ON”位置),打开桌面版nRF Connect,选择启动“Programmer”应用,由于驱动之前已经安装好了,设备可以立即识别成功。执行“full erase”操作,以擦除芯片原始内容。 

 

 

3)     打开SDK中的ble_app_uart程序。如果是52832开发板,请打开:nRF5_SDK_15.0.0_a53641a\examples\ble_peripheral\ble_app_uart\pca10040\s132\arm5_no_packs;如果是51822开发板,请打开:nRF5_SDK_12.3.0_d7731ad\examples\ble_peripheral\ble_app_uart\pca10028\s130\arm5_no_packs

后续将以52832开发板为例来阐述,51822与之类似就不再阐述了。

 

注:Nordic SDK例程目录结构为:SDK版本/ examples /协议角色/例子名称/开发板型号/协议栈型号/工具链类型/具体工程,比如下面例子:

 

 

Nordic每一个例子都支持5种工具链:Keil5/Keil4/IAR/GCC/SES,如下所示:

 

 

4)     编译程序。如果你已经按照之前的说明配置好了开发环境,那么这里编译是不会报任何错的。(如果你遇到了编译错误,请重新按照前面说明去搭建你的开发环境,不要怀疑SDK例子代码有问题哦)

5)     下载程序。程序下载包括2步:一先下载softdevice,二再下载应用。Softdevice是Nordic蓝牙协议栈的名称,整个开发过程中只需下载一次。应用就是我们这里的ble_app_uart程序。如果你的开发板已经下载了其他代码,那么最好先把开发板全擦一次,然后再下载softdevice和应用。

  • 芯片全擦(可选)。你可以使用nRFgo studio,或者nRF connect桌面版,或者nrfjprog,三者选其一来执行擦除操作。
    • 使用nRFgo studio执行全擦操作

 

  • 使用nRF connect桌面版执行全擦操作

  

 

  • 使用nrfjprog执行全擦操作

 

 

  • 蓝牙协议栈下载(整个开发周期只需下载一次)。在Keil ‘select target’下拉列表中,默认选择的是Keil工程对应的Target,即‘nrf52832_xxaa’。我们还可以选择另一个target ‘flash_s132_nrf52_6.0.0_softdevice’,即softdevice对应的target,然后点击“下载download”(不需要编译哦!),此时会把softdevice下载到开发板中。

 

 

  • 应用下载。重新选择Target:‘nrf52832_xxaa’,点击“下载Download”,此时会把ble_app_uart应用程序下载到开发板中。此时开发板的LED1闪烁,表示程序运行正常。

6)     连接手机。打开手机蓝牙和手机版nRF connect。在nRF connect中,你将看到一个广播设备:Nordic_UART,这个就是开发板的广播名字。点击“CONNECT”,手机将与设备建立连接,并开始服务发现过程,连接成功后,LED1熄灭,LED2点亮,最后将得到如下界面。

 

上图的Nordic UART Service(NUS)就是我们的数据透传服务, NUS具体包括两个characteristic:TX和RX,由于NUS是由设备提供的,所以TX表示设备发送数据给手机,RX表示设备接收手机发过来的数据。

7)     测试NUS服务。ble_app_uart使用串口与上位机交互,选择一款串口助手软件,比如Putty,打开该串口软件,并做如下设置:

  • Baud rate: 115.200
  • 8 data bits
  • 1 stop bit
  • No parity
  • HW flow control: None

复位开发板,你会发现串口助手会打印如下信息:

 

 

按照第6)步,重新将开发板连上手机,然后点击右上角的“Enable CCCDs”以使能notification,如下所示:

 

 

设备接收数据: 点击RX characteristic旁边的向上箭头,通过手机蓝牙往设备发送:12345678,如下所示:

 

     此时设备通过串口打印出刚才接收到的数据,如下所示:

 

设备发送数据:在串口助手中输入“abcdefgh”并输入“\n”(注:在Putty中,先按“CTRL”再按“J”就会发出“\n”换行符)作为结束符,设备将把串口收到的数据通过蓝牙发送给手机,手机的TX characteristic将显示上述字符串,如下所示:

 

 

                注:如果你的串口助手发不出“\n”换行符,那么你需要最少输入MTU-3个字符,设备才会把收到的全部字符通过蓝牙发出去

 

通过上面的测试,大家可以发现Nordic SDK已经把蓝牙数据透传服务做好了,大家可以直接拿过来使用,下面将对其工作原理进行阐述,最后在Nordic蓝牙透传例子ble_app_uart上进行二次开发,以增加一些其他有用功能。如果大家觉得Nordic ble_app_uart已经可以满足自己的需求,而且也不想花时间去研究里面的原理,那么章节3/4/5/6/7.1可以略过不看。

3. BLE client/server(C/S) 架构

        BLE采用了client/server (C/S)架构来进行数据交互,C/S架构是一种非常常见的架构,在我们身边随处可见,比如我们经常用到的浏览器和服务器也是一种C/S架构,这其中浏览器是客户端client,服务器是服务端server,server比如淘宝服务器,提供商品信息,广告,社交等服务,而浏览器,比如微软的IE,就可以用来请求这些服务,并使用server提供的服务。BLE与此类似,一般而言设备提供服务,因此设备是server,手机使用设备提供的服务,因此手机是client。比如蓝牙体温计,它可以提供“体温”数据服务,因此是一个server,而手机则可以请求“体温”数据以显示在手机上,因此手机是一个client。

        服务是以数据为载体的,所以说server提供服务其实就是提供各种有价值的数据。

 

上图所示的Request和Response其实就是我们经常说的ATT命令(ATT PDU),也就是说Client和Server之间通过ATT PDU进行交互。另外,一个数据“37”,有可能是说体温“37度”,也有可能是说心率“37次”或者湿度“37%”,因此Server需要将数据进行包装和分类,在BLE中,数据是通过characteristic进行包装的,而且多个characteristic组成一个service,service是一个独立的服务单元,或者说service是一个基本的BLE应用。因此我们可以把上图细化为:

 

如果某个service是一个蓝牙联盟定义的标准服务,也可以称其为profile,比如HID/心率计/体温计/血糖仪等,都是标准蓝牙服务,因此都有相应的profile规格书。

 

4. BLE service, characteristic以及CCCD

如文章“深入浅出低功耗蓝牙(BLE)协议栈”所讲,BLE协议栈架构如下所示:

 

        如上图所示,用户开发应用程序或者说service的时候,调用的都是GATT API,而GATT又调用了ATT API,前面也讲过,BLE数据最终都是通过ATT PDU来传输的,那么为什么还需要GATT层?直接操作ATT层不也可以达到同样的目的吗?

        前面也提过,Server是通过characteristic来表示数据的,虽然一条数据最有价值的部分是它的值(value),但是仅有value是不够,比如27,到底是表示27°温度还是27%湿度;如果表示的是温度,那么它的单位是摄氏度还是华氏度。同时每个value还有相应的读写属性以及权限属性,因此一个characteristic包含三种条目:characteristic声明,characteristic的值以及characteristic的描述符(可以有多个描述符),如下所示:

 

        由于一个service可以包含多个characteristic,characteristic declaration就是每个characteristic的分界符,解析时一旦遇到characteristic declaration,就可以认为接下来又是一个新的characteristic了,同时characteristic declaration还将包含value的读写属性等。Characteristic value就是数据的值了,这个比较好理解就不再说了。Characteristic descriptor就是数据的额外信息,比如温度的单位是什么,数据是用小数表示还是百分比表示等之类的数据描述信息。CCCD是一种特殊的characteristic descriptor,一般而言,都是client来访问server的characteristic,我们把这种操作称为读或者写。另外,server可以直接把自己的characteristic的值告诉client,我们称其为notify或者indicate,跟read操作相比,只有需要传输数据的时候或者说只有当数据有效时,server才开始notify或者indicate数据到client,因此这种操作方式可以大大节省server的功耗。有时候client不想监听characteristic notify或者indicate过来的数据,那么就可以使用CCCD来关闭characteristic的notify或者indicate功能;如果client又需要监听characteristic的notify或者indicate,那么它可以重新使能CCCD来打开相关操作。总结一下,当characteristic具有notify或者indicate操作功能时,那么必须为其添加相应CCCD,以方便client来使能或者禁止notify或者indicate功能。

        不管是characteristic declaration,characteristic value还是characteristic descriptor,实现的时候,我们都是用attribute来表达的,也就是说,他们每一个都是一个attribute,attribute可以用下图来表示:

 

  • Attribute handle,Attribute句柄,16-bit长度。Client要访问Server的Attribute,都是通过这个句柄来访问的,也就是说ATT PDU一般都包含handle的值。用户在软件代码添加characteristic的时候,系统会自动按顺序地为相关attribute生成句柄。
  • Attribute type,Attribute类型,2字节或者16字节长。在BLE中我们使用UUID来定义数据的类型,UUID是128 bit的,所以我们有足够的UUID来表达万事万物。其中有一个UUID非常特殊,它被蓝牙联盟采用为官方UUID,这个UUID如下所示:

 

 由于这个UUID众所周知,蓝牙联盟将自己定义的attribute或者数据只用16bit UUID来表示,比如0x1234,其实它也是128bit,完整表示为:

 

Attribute type一般是由service和characteristic规格来定义,站在蓝牙协议栈角度来看,ATT层定义了一个通信的基本框架,数据的基本结构,以及通信的指令,而GATT层就是前文所述的service和characteristic,GATT层用来赋予每个数据一个具体的内涵,让数据变得有结构和意义。换句话说,没有GATT层,低功耗蓝牙也可以通信起来,但会产生兼容性问题以及通信的低效率。

  • Attribute value,就是数据真正的值,0到512字节长。
  • Attribute permissions,Attribute的权限属性,权限属性不会直接在空中包中体现,而是隐含在ATT命令的操作结果中。假设一个attribute read属性设为open(即读操作不需要任何权限),那么client去读这个attribute时server将直接返回attribute的值;如果这个attribute read属性设为authentication(即需要配对才能访问),如果client没有与server配对而直接去访问这个attribute,那么server会返回一个错误码:告诉client你的权限不够,此时client会对server发起配对请求,以满足这个attribute的读属性要求。目前主要有如下四种权限属性:
    • Open,直接可以读或者写
    • No Access,禁止读或者写
    •  Authentication,需要配对才能读或者写,由于配对有多种类型,因此authentication又衍生多种子类型,比如带不带MITM,有没有LESC
    • Authorization,跟open一样,不过server返回attribute的值之前需要应用先授权,也就是说应用可以在回调函数里面去修改读或者写的原始值。
    • Signed,签名后才能读或者写,这个用得比较少。

         大家还记不记得设备与手机nRF connect连接成功后呈现的界面,我这里再贴一下:

 

         可以看到手机呈现的就是上文讲的service和characteristic,nRF Connect为了让整个界面变得更美观,将访问属性,UUID,handle都分列来表示了,以致于很多初学者会把理论和现实二者对应不起来。Nordic之前推出过一款Master Control Panel(MCP),MCP现在已经不推荐使用了,不过MCP有一个好处,它对service和characteristic的组织方式更接近底层实现方式,对大家理解service和characteristic是非常有帮助的。还是这个设备,我用MCP跟它连接并进行服务发现,你会发现它呈现的界面如下所示:

 

这个图就跟上面讲的理论知识可以一一对应起来了,NUS包含2个characteristic:RX和TX,每一个条目都是一个attribute,NUS服务本身就是一个attribute,而RX characteristic本身又包含2条attribute:一条是declaration attribute,一条是value本身attribute。由于TX支持notify,所以它包含3条attribute,另外一条attribute是CCCD。每个attribute都有一个handle和UUID,handle用来访问该attribute,UUID用来指明该attribute的类型。可以说,server提供数据,而数据是由attribute来表达,所有attribute组成一个attribute table,设备支持的服务不同,attribute table就不同。这里说明一下,当你在Nordic已有例程基础上再去添加新的服务或者删除已有的服务,记得一定要去修改ATTR_TAB_SIZE那个宏,否则协议栈初始化会有问题。

       

(3 ) (1 )
回复 举报

回复于 2019-04-29 沙发

谢谢分享
(0 )
评论 (0) 举报

回复于 2019-04-30 2#

谢谢分享!!!!
(0 )
评论 (0) 举报

回复于 2019-05-21 3#

里面的图在哪里找?急人!

(0 )
评论 (0) 举报

回复于 2019-05-25 4#

感谢分享!
(0 )
评论 (0) 举报

回复于 2020-02-18 5#

感谢分享
(0 )
评论 (0) 举报

回复于 2020-02-18 6#

感谢分享,欢迎关注我,资料持续更新中。有需要机械臂,电源,硬件电路设计,软件编程,开发板等各种定制的可以私聊我哦,相互学习,共同进步。
(0 )
评论 (0) 举报

回复于 2020-03-21 7#

(0 )
评论 (0) 举报
  • 发表回复
    0/3000





    举报

    请选择举报类别

    • 广告垃圾
    • 违规内容
    • 恶意灌水
    • 重复发帖

    全部板块

    返回顶部