概念
<p><a href="https://webbluetoothcg.github.io/web-bluetooth/">https://webbluetoothcg.github.io/web-bluetooth/</a></p>
<p><a href="http://www.wowotech.net/bluetooth/ble_ll_privacy.html">http://www.wowotech.net/bluetooth/ble_ll_privacy.html</a></p>
<p>Bluetooth 4.0推出了一个新的低功耗模式:蓝牙Smart,BLE或LE,它的速度最高为1Mbps。BLE通过键值对提供各种功能。</p>
<h3>角色</h3>
<ul>
<li>广播者(发送数据)</li>
<li>观察者(接收数据)</li>
<li><code>Peripheral</code>外设(接收连接)</li>
<li>中心(连接到外设)</li>
</ul>
<p>充当外围或中心角色的设备可以承载 GATT 服务器,尽管<code>GATT</code>协议设计用于支持 BLE 传输,但关贸总协定协议也可以通过 <code>BR/EDR</code> 传输运行。</p>
<p>每个蓝牙<code>BR/EDR</code>设备有唯一的48位地址<code>BD_ADDR</code>,每个BLE设备至少有一个公共设备地址和静态设备地址。公共设备地址是<code>MAC</code>地址,而静态设备地址可以在每次重启时生成。<code>BR/EDR/LE</code>设备使用相同的<code>BD_ADDR</code>和公共设备地址。
一个<code>BLE</code>可以有一个128位的身份解析秘钥(<code>Identity Resolving Key</code>),在绑定阶段发送到受信设备。为了避免泄露持久标识,LE设备可以使用随机可解析或不可解析的私有地址代替公共设备地址或静态设备地址。</p>
<h3>协议栈</h3>
<p><a href="https://www.cnblogs.com/blogs-of-lxl/p/7010061.html">https://www.cnblogs.com/blogs-of-lxl/p/7010061.html</a></p>
<h2>一. 引言</h2>
<p>现在低功耗蓝牙(BLE)连接都是建立在GATT(Generic Attribute Profile)协议之上。GATT是一个在蓝牙连接之上的发送和接收很短的数据段的通用规范,这些很短的数据段被称为属性(<code>Attribute</code>)。</p>
<h2>二. GAP</h2>
<p>详细介绍GATT之前,需要了解<code>GAP</code>(<code>Generic Access Profile</code>),它在用来控制设备和广播。GAP使你的设备被其他设备可见,并决定了你的设备是否可以或者怎样与合同设备进行交互。例如<code>Beacon</code>设备就只是向外广播,不支持连接,小米手环就等设备就可以与中心设备连接。</p>
<h3>1. 设备角色</h3>
<p>GAP给设备定义了若干角色,其中主要的两个是:外围设备(Peripheral)和中心设备(Central)。</p>
<p>外围设备:这一般就是非常小或者简单的低功耗设备,用来提供数据,并连接到一个更加相对强大的中心设备。例如小米手环。
中心设备:中心设备相对比较强大,用来连接其他外围设备。例如手机等。</p>
<h3>2. 广播数据</h3>
<p>在GAP中外围设备通过两种方式向外广播数据:<code>Advertising Data Payload</code>(广播数据)和<code>Scan Response Data</code>(扫描回复),这里包含一些设备额外的信息,例如设备的名字。</p>
<h3>3. 广播流程</h3>
<p>GAP的广播工作流程如下图所示。</p>
<p><img src="https://www.showdoc.cc/server/api/common/visitfile/sign/69e16520cb0aa402f6877455b26d70e2?showdoc=.jpg" alt="" /></p>
<p>从图中我们可以清晰看出广播数据和扫描回复数据是怎么工作的。外围设备会设定一个广播间隔,每个广播间隔中,它会重新发送自己的广播数据。广播间隔越长,越省电,同时也不太容易扫描到。</p>
<h3>4. 广播的网络拓扑结构</h3>
<p>大部分情况下,外设通过广播自己来让中心设备发现自己,并建立GATT连接,从而进行更多的数据交换。也有些情况是不需要连接的,只要外设广播自己的数据即可。用这种方式主要目的是让外围设备,把自己的信息发送给多个中心设备。因为基于GATT连接的方式的,只能是一个外设连接一个中心设备。使用广播这种方式最典型的应用就是苹果的<code>iBeacon</code>。广播工作模式下的网络拓扑图如下:</p>
<p><img src="https://www.showdoc.cc/server/api/common/visitfile/sign/0f1c954b5b0a9217a0896385c636d069?showdoc=.jpg" alt="" /></p>
<h2>GATT</h2>
<p><code>GATT</code>的全名是<code>Generic Attribute Profile</code>,他定义两个BLE设备通过叫做<code>Service</code>和<code>Characteristic</code>的东西进行通信。GATT就是使用了<code>ATT(Attribute Protocol)</code>协议,<code>ATT</code>协议把<code>Service,Characteristic</code>遗迹对应的数据保存在一个查找表中,次查找表使用<code>16bit ID</code>作为每一项的索引。
一旦两个设备建立起了连接,GATT就开始起作用了,这也 意味着,你必需完成前面的GAP协议。<strong><font color=red>这里需要说明的是,GATT连接,必须先经过GAP协议。实际上,我们在Android开发中,可以直接使用设备的MAC地址,发起连接,可以不经过扫描的步骤。这并不意味不需要经过GAP,实际上在芯片级别已经给你做好了,蓝牙芯片发起连接,总是先扫描设备,扫描到了才会发起连接。</font></strong>
GATT连接需要特别注意的是:GATT连接是独占的。也就是一个BLE外设同时只能被一个中心设备连接。一旦外设被连接,它就会马上停止广播,这样它就是一个BLE外设同时只能被一个中心设备连接。一旦外设被连接,它就会马上停止广播,这样它就对其他设备不可见了。当设备断开,它又开始广播。
<strong>中心设备和外设需要双向通信的话,唯一的方式就是建立GATT连接。</strong></p>
<h3>1. GATT连接的网络拓扑</h3>
<p>下图展示了GTT连接网络拓扑结构。这里很清楚的显示,一个外设只能连接一个中心设备,而一个中心设备可以连接多个外设。
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/ee2b378b03988adca75c4e406330d803?showdoc=.jpg" alt="" /></p>
<p>一旦建立了连接,通信就是双向的了,对比前面的GAP广播的网络塔扑,GAP通信是单向的。<font color=red>如果你要让两个设备外设能通信,就只能通过中心设备中转.</font></p>
<h3>2. GATT通信事务</h3>
<p>GATT通信的双方是C/S关系。外设作为GATT服务端(Server),它维持了ATT的查找表以及service和characteristic的定义。中心设备是GATT客户端(Client),他向Server发起请求。需要注意的是,所有的通信事件,都是由客户端(也叫主设备,Master)发起,并且接收服务端(也叫从设备,Slava)的响应。
一旦连接建立,外设将会给中心设备建议一个连接间隔<code>(Connection Interval)</code>,这样,中心设备就会在每个连接间隔尝试去重新连接,检查是否有新的数据。但是,这个连接间隔只是一个建议,你的中心设备可能并不会严格按照这个间隔来执行,例如你的中心设备正在忙于连接其他的外设,或者中心设备资源太忙。</p>
<p>下图展示一个外设(GATT服务端)和中心设备(GATT客户端)之间的数据交流流程,可以看到的是,每次都是主设备发起请求:
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/8ed545a9a28643b6add26b796ef4395f?showdoc=.jpg" alt="" /></p>
<h3>3. GATT结构</h3>
<p>GATT事务是建立在嵌套的Profiles,Services和Characteristics之上的,如下如所示:
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/b77674eb981eaa708a456ed7e22aa1b7?showdoc=.jpg" alt="" /></p>
<ul>
<li><code>Profile</code> Profile并不是实际存在于BLE外设上的,它只是一个被Bluetooth SIG或者外设设计者预先定义的Service的集合。例如心率<code>Profile(Heart Rate Profile)</code>就是结合了<code>Heart Rate Service</code>和<code>Device Information Sercvice</code>。所有官方通过<code>GATT Profile</code>的列表可以从这里找到。</li>
<li><code>Service</code> Service是把数据分成一个个的独立逻辑项,它包含一个或者多个Characteristic。每个Service有一个UUID唯一标识。UUID有16bit的,或者128bit的。16bit的UUID是官方通过认证的,需要花钱购买,128bit是自定义的,这个就可以自己随便设置。
官方通过了一些标准Service,完整列表在这里。以Heart Rate Service为例,可以看到它的官方通过16bitUUID是<code>0x180D</code>,包含3个<code>Characteristic:Heart Rate Measurement,Body Sensor Location和Heart Control Point</code>,并且定义了只有一个第一个必须的,它是可选实现的。</li>
<li><code>Characteristic</code> 在GATT事务中的最低界别的是Characteristic,Characteristic是最小的逻辑数据单元,当然它可能包含一个组关联的数据,例如加速度计的X/Y/Z三轴值。
与Service类似,每个Characteristic用16bit或者128bit的UUID唯一标识。你可以免费使用Bluetooth SIG官方定义的标准Characteristic,使用官方定义的,可以确保BLE的软件和硬件能相互理解。当然,你可以自定义Characteristic,这样的话,就只有你自己的软件和外设能够相互理解。
举个例子,<code>Heart Rate Measurement Characteristic</code>,这是上面提到的<code>Heart Rate Service</code>必需实现的Characteristic,它的UUID是0x2A37。它的数据结构是,开始8bit定义心率数据格式(是UINT8还是UINT16?),接下来就是对应格式的实际心率数据。
实际上,和BLE外设打交道,主要是通过Characteristic。你可以从Characteristic读取数据,也可以往Characteristic写数据。这样就实现了双向的通信。所以你可以自己实现一个类似串口(UART)的service,这个Service中包含两个Characteristic,一个被配置只读的通道(RX),另一个配置为只写的通道(TX)。</li>
</ul>
<h2>mesh组网</h2>
<p><a href="https://zhuanlan.zhihu.com/p/31543386">https://zhuanlan.zhihu.com/p/31543386</a>
<a href="https://zhuanlan.zhihu.com/bluetooth-mesh">https://zhuanlan.zhihu.com/bluetooth-mesh</a>
在蓝牙Mesh的使用场合,大部分的设备是基于嵌入式芯片或者模块的,所以如果每次通信都用非对称加密的话代价太大。所以蓝牙选择的是两种方式的结合。</p>
<ul>
<li>
<p><code>Asymmetric cryptography</code>不对称密码:椭圆曲线Diffie–Hellman
(ECDH)是一种匿名密钥协议协议,允许双方各有一个椭圆曲线公私密钥对,在不安全的通道上建立一个共享的秘密。在蓝牙网格配置中,ECDH的目的是允许创建配网者和未配网设备之间的安全链接。它使用公钥和私钥分发一个对称密钥,这两个设备可以用来加密和解密后续消息。</p>
</li>
<li><code>Symmetric cryptography</code>:对称密码:在蓝牙Mesh网络中传输的每条消息都使用AES - 128加密技术进行加密。AES - 128算法是一种通用的对称加密/解密引擎,常用于嵌入式平台。</li>
</ul>
<p>在交换公钥阶段,有两种方法可以交换ECDH公共密钥。它们可以通过蓝牙连接或通过OOB隧道进行交换。在配网邀请阶段,未配网设备已经报告是否支持通过OOB隧道发送其公钥。如果支持,配网者人可以继续使用它,并通过发送一个配网启动PDU通知未配网设备。</p>
<p>如果没有未配网设备支持通过OOB隧道的公钥交换,那么一个临时的公钥就从配网者传输到设备,未配网设备可以使用适当的OOB技术来读取一个静态公共密钥 (比如二维码),见图。如果不支持OOB隧道,那就通过一个BLE的连接通道交换,见图4
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/beb410974d88fb9ae14bde601d172da8?showdoc=.jpg" alt="" />
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/f5cf94e44d65242013707aa7b9bc6da1?showdoc=.jpg" alt="" /></p>
<ol>
<li>Authentication 身份认证
在这一步中,配网器使用所选择的认证方式来认证未配网设备。3种方式可选:输出OOB,输入OOB,静态的OOB和没有OOB。</li>
</ol>
<p>Output OOB输出OOB</p>
<p>如果选择了输出OOB的认证方式,未配网设备就会产生一个随机数并通过一个和它的能力相兼容的方式来输出。举个例子:如果未配网设备是一个灯,它可以通过闪烁给定的次数来表示这个随机数。如果设备有一个LCD的屏幕,它就可以显示一个多位的随机数。配网器的使用者把观察到的数字输入来认证未配网设备。输出OOB的认证流程见图5.</p>