目录
- 一: 概述
- 二: 命令的分类
- 三: 如何最简洁的跑起来一个 BLE 应用
- 3.1 一个 iBeacon Demo
- 3.2 如何扫描周边的蓝牙设备
- 3.3 如何建立一个BLE连接
- 3.4 如何进行数据的收发
- 3.4.1 server 创建并开启服务
- 3.4.2 server 查询本地服务
- 3.4.3 client 做服务发现
- 3.4.4 client 端的读和写
- 3.4.5 register notification
- 3.4.6 server 主动向 client 发送数据
- 四: 如何处理 BLE 加密
- 4.1 加密参数该如何设置
- 4.2 不同的加密参数组合会出现什么样的结果
- 4.3 加密示例
- 五: 如何使用 BLE SPP
一: 概述
ESP-AT BLE
命令支持 BLE server
以及 BLE client
, 目前两种角色只能同一时刻使用一种.
做 BLE server
的时候, 可以同时被 3 个 client
相连, 做 BLE client
的时候, 可以同时连接 3 个 server
.
除了通用的 GATT
以及 GAP
指令, ESP-AT BLE
还支持 BLE HID
, 用来模拟鼠标键盘等等.
目前 ESP-AT BLE
的 命令基本能覆盖客户的所有需求, 包括自定义 sevices
, 自定义广播数据, 服务发现, 数据通信, 连接参数更新, MTU size
更新以及所有的加密参数相关的设置.
除此之外, 我们还自定义了一套私有的 BLE
透传协议, 如果客户不使用经典蓝牙的透传, 也可以使用 BLE
透传.
二: 命令的分类
因为我们 ESP-AT
的 BLE
的命令做的比较细, 对 BLE
协议本身不是很熟悉的人, 初次上手的时候可能会觉得有些困惑, 所以我们首先对这些命令进行一些分类, 这样理解以及用起来会更顺手一点.
2.1 角色划分
目前 ESP-AT BLE
的命令都是基于 GAP
, GATT
层实现的, 我们在初始化的时候, 首先需要决定是做 client
还是做 server
.
初始化为 client
:
AT+BLEINIT=1
OK
初始化为 server
:
AT+BLEINIT=2
OK
如果已经初始化为
server
, 想切换为client
, 这个时候需要将协议栈deinit
, 再重新初始化为想要的角色.
关闭蓝牙:
AT+BLEINIT=0
OK
不同的角色, 可以使用的命令也不同, 详细的划分请见下面, 看命令后面的注释可以先粗略了解这条命令是做什么的:
BLE client 特有的命令:
AT+BLESCANPARAM // 设置扫描参数
AT+BLESCAN // 开启或者关闭扫描
AT+BLECFGMTU // Exchange MTU size
AT+BLECONN // 连接远端设备
AT+BLEGATTCPRIMSRV // 做服务发现. 获取主要服务
AT+BLEGATTCINCLSRV // 获取主要服务下面的 include 服务
AT+BLEGATTCCHAR // 查询服务下面的 characteristics
AT+BLEGATTCWR // 对 characteristic 进行写的操作
AT+BLEGATTCRD // 对 characteristic 进行读的操作
AT+BLECONNPARAM // 更新连接参数
BLE server 特有的命令:
AT+BLEADVPARAM // 设置广播参数
AT+BLEADVDATA // 设置广播数据
AT+BLESCANRSPDATA // 设置扫描返回数据 scan rsp data
AT+BLEADVSTART // 开始广播
AT+BLEADVSTOP // 关闭广播
AT+BLEGATTSSRVCRE // 创建服务
AT+BLEGATTSSRVSTART // 开启服务
AT+BLEGATTSSRV // 查询本地的服务
AT+BLEGATTSCHAR // 查询本地服务下面的 characteristic
AT+BLEGATTSNTFY // 向 client 发送 notify
AT+BLEGATTSIND // 向 client 发送 indicate
AT+BLEGATTSSETATTR // 设置本地 characteristic 的值
BLE client 与 BLE server 都可以使用的命令:
AT+BLEINIT // BLE 协议栈初始化
AT+BLEADDR // 查询 BLE MAC 地址
AT+BLENAME // 设置或者查询 BLE 名字
AT+BLEDISCONN // 主动断开与远端设备的连接
AT+BLEDATALEN // 设置 packet data length
AT+BLESPPCFG // 配置 BLE 透传参数
AT+BLESPP // 进入 BLE 透传模式
AT+BLESECPARAM // 设置 BLE 加密参数
AT+BLEENC // 发起 BLE 加密请求
AT+BLEENCRSP // 对 BLE 加密请求作出应答
AT+BLEKEYREPLY // 输入对端设备显示的密码
AT+BLEENCDEV // 查询本地已经 bonding 的设备信息
AT+BLEENCCLEAR // 清除本地 bonding 设备的信息
AT+BLESETKEY // 设置 BLE 静态密码
2.2 几个主要的功能
虽然命令数量繁多, 有 30 多个, 其实主要也就是这几个功能:
- 初始化
- 广播与扫描
- 连接与通信
- 参数更新
- 加密
初始化
初始化是第一步, 在这里首先需要使能 BLE
协议栈, 然后配置一些本地的参数, 比如:
- 扫描参数:
AT+BLESCANPARAM
- 广播参数:
AT+BLEADVPARAM
- 设备名称:
AT+BLENAME
- 加密参数:
AT+BLESECPARAM
- 广播数据:
AT+BLEADVDATA
,AT+BLESCANRSPDATA
如果不去主动设置这些参数, 会用默认的参数, 至于默认参数是多少, 这里就不花篇幅介绍了, 可以直接用查询指令去查询看看, 也可参照 ESP-AT
指令介绍文档.
广播与扫描
广播与扫描比较简单, 看命令的名字就能认出来.
- 广播:
AT+BLEADVSTART
,AT+BLEADVSTOP
- 扫描:
AT+BLESCAN
广播是建立连接的前提条件. client
如果想和 server
建立连接, server
必须是处于广播的状态 (可连接的广播).
client
如果已经提前知道了 server
的 MAC
地址, 可以不扫描, 直接发起连接的命令;
如果不知道 server
的 MAC
地址, 可以先扫描一下.
连接与通信
一般的 BLE
应用, 还是需要基于连接的, 除非是 beacon
类设备.
与连接相关的命令只有这两条:
- 发起连接:
AT+BLECONN
- 断开连接:
AT+BLEDISCONN
其中, 发起连接的命令, 只能 client
下发, 主动断开的命令, 两边都可以.
因为 BLE
的数据通信都是基于属性协议, 所以 BLE server
端需要在被连接之前把服务开启, 需要下发两条命令:
- 创建服务:
AT+BLEGATTSSRVCRE
- 开启服务:
AT+BLEGATTSSRVSTART
这两条命令的先后顺序不能颠倒, 只需要在 AT+BLEINIT=2
之后, 被 client
连接之前就可以, 中间或者前后都可以穿插别的命令, 一般我们建议是在 AT+BLEADVSTART
之前, 因为只要广播开启了, 就可能被远端设备连接上, 对端会发现不到服务.
设备连接成功之后, client
端需要做服务发现, 服务发现的命令有这几个:
- 获取对端主要服务:
AT+BLEGATTCPRIMSRV
- 获取
include
服务:AT+BLEGATTCINCLSRV
- 获取服务下面的
char
:AT+BLEGATTCCHAR
而 server
端查询本地的服务和 char
用到这两个命令:
- 查询服务:
AT+BLEGATTSSRV
- 查询服务下面的
char
:AT+BLEGATTSCHAR
查询服务是必须的操作, 因为 ESP-AT BLE
指令的读写操作都是依赖查询的得到的 \<index>
参数, 具体的介绍与用法, 会在下面的实例章节详细介绍, 这里仅需了解下大概的流程.
查询好了服务相关的参数, 就可以数据通信了, client
往 server
发送数据需要通过写的方式:
- 写
char
或者desc
:AT+BLEGATTCWR
- 读
char
或者desc
:AT+BLEGATTCRD
server
向 client
发送数据需要通过 notify
和 indicate
的方式.
- 发送
notify
:AT+BLEGATTSNTFY
- 发送
indicate
:AT+BLEGATTSIND
参数更新
有些时候, 针对一些应用场景, 可能需要更新参数, 最常用的两个就是 MTU
和连接参数:
- 更新
MTU size
:AT+BLECFGMTU
- 更新连接参数:
AT+BLECONNPARAM
MTU size
只能在 client 端发起, 目前 ESP-AT
这里最大能支持 517. 如果不更新的话, 默认是 23. 更新一个较大的 MTU 的好处就是单包可以发送更长的数据了, 一包最长是(MTU size - 3
), 也就是说, 默认情况下, 一包最多发送 20 字节的数据.
连接参数的更新可以在连接建议起来之后任意时刻开始, 不同的连接参数会有什么影响我们在下面章节详细说明.
加密
目前 ESP-AT
支持的 BLE
所有加密等级, 用户可以自由设置加密参数, 发起加密或者接受加密, 弹出密码, 输入密码, bonding
, 等等.
- 设置加密参数:
AT+BLESECPARAM
- 发起加密请求:
AT+BLEENC
- 接受或者拒绝加密:
AT+BLEENCRSP
- 设置静态的密码:
AT+BLESETKEY
- 输入KEY:
AT+BLEKEYREPLY
- 确认KEY:
AT+BLECONFREPLY
- 查询
bonding
的设备:AT+BLEENCDEV
- 清除
bonding
信息:AT+BLEENCCLEAR
3. 如何最简洁的跑起来一个 BLE 应用
不同的应用场景, 用的指令都有差异, 我们先从最简单的说起.
3.1 一个 iBeacon Demo
iBeacon
主要用于广告推送, 室内定位等等, 其实他的原理很简单, 就是发送一些特定格式的 BLE
广播包, 关于 ibeacon
, 本文不具体介绍, 用户可以自行参阅苹果官方文档 iBeacon.pdf.
我这这里举一个例子, ESP-AT
发送 iBeacon
广播包, 用微信摇一摇搜索周边设备:
首先初始化为 server
AT+BLEINIT=2
OK
然后设置广播参数
如果不做这一步, 直接配置广播包并开启广播也是可以被微信摇一摇周边搜索到的, 但是一般我们还是配置下广播类型为不可连接广播比较好, 避免被不可预知的设备连接上.
AT+BLEADVPARAM=64,64,3,0,7 //第三个参数 '3', 表示广播包的类型是 ADV_TYPE_NONCONN_IND
OK
其他参数的具体描述请参阅指令文档.
配置广播包
AT+BLEADVDATA="0201061AFF4C000215FDA50693A4E24FB1AFCFC6EB0764782527B7F206C5"
OK
这里只是举个例子, 客户可以参考 iBeacon
的格式定义, 自己重新定义.
设置好广播 data
之后, 就可以开启广播了.
AT+BLEADVSTART
OK
现在可以用手机微信的摇一摇功能, 发现我们的 ibeacon
包.
除了Apple
的 iBeacon
, 还有谷歌的 eddystone
, 都是同样的道理, 一些特定格式的广播包.
Tips:
- 如果不配置广播包, 也可以直接开启广播, 只是广播包的
payload
是空, 但是不影响被client
连接. - 广播包最大长度是 31 字节, 如果用户的数据大于这个长度, 可以配置
scan rsp data
, 不过scan rsp data
的最大长度也是 31 字节.
3.2 如何扫描周边的蓝牙设备
扫描是一个很常见的场景, 比如扫描周围的 beacon
包, 扫描周围想要连接的目标设备等等.
ESP-AT 中关于扫描是指令 AT+BLESCAN
.
开启扫描的前提是初始化角色为 client
:
AT+BLEINIT=1
OK
开启持续扫描:
AT+BLESCAN=1
OK
开启扫描一段时间, 比如3秒:
AT+BLESCAN=1,3
OK
停止扫描:
AT+BLESCAN=0
OK
扫描示例:
AT+BLESCAN=1
OK
+BLESCAN:c4:4f:33:16:f8:c3,-50,,,0
+BLESCAN:4d:71:ae:b0:72:08,-40,02011a0aff4c001005131c5fc0b3,,1
+BLESCAN:c4:4f:33:16:f8:c3,-55,,,0
+BLESCAN:58:81:89:b9:a2:de,-45,02010613ff4c000c0e0806040a6279452dfb65b171ddf9,,1
+BLESCAN:58:81:89:b9:a2:de,-47,02010613ff4c000c0e0806040a6279452dfb65b171ddf9,,1
+BLESCAN:ac:bc:32:c2:0e:da,-46,0201060aff4c0010050b1c7ac126,,0
+BLESCAN:ac:bc:32:c2:0e:da,-55,0201060aff4c0010050b1c7ac126,,0
+BLESCAN:4c:38:cc:bf:3f:64,-68,02010613ff4c000c0e009a8b60fd84a3fa91cb767de4a2,,1
+BLESCAN:c4:4f:33:16:f8:c3,-47,,,0
+BLESCAN:c4:4f:33:16:f8:c3,-56,,,0
+BLESCAN:77:64:1d:9a:46:ea,-44,0201060aff4c00100541187179a3,,1
+BLESCAN:c4:4f:33:16:f8:c3,-48,,,0
+BLESCAN:c4:4f:33:16:f8:c3,-57,,,0
AT+BLESCAN=0
OK
上面的 log 是开启扫描然后广播扫描的例子, 扫描结果的开头都是 +BLESCAN:
, 然后是 MAC
地址, 比如 "4d:71:ae:b0:72:08"
, 下面跟着 RSSI
值, 再紧接着的是 adv data
和 scan rsp data
. 最后一个参数是地址的类型, 0 表示 public
, 1 表示 random
.
Tips:
AT+BLESCAN
不会阻塞, 如果开启持续扫描的话, 会一刻不停的打印扫描信息, 除非周围没有任何BLE
广播包.- 如果用户觉得信息太多, 建议开始定时功能, 比如只扫描 3 秒或者 5 秒
- 目前扫描返回的数据, 仅支持符合
BLE
广播包格式的数据, 如果对端广播的是raw data
, 比如{0x00, 0x01, 0x02, 0x03}
这种, 扫描得到的数据内容以及长度一般都是错误的. - 在
ESP-AT
中, 限定只有client
才能发起扫描. - 有的时候, 如果发现
MAC
地址对的, 都是连接的时候就是连不上, 这个时候要注意地址的类型对不对, 下面的连接示例中对此会有说明.
3.3 如何建立一个 BLE 连接
关于连接的命令有两个, AT+BLECONN
和 AT+BLEDISCONN
.
发起连接:
+BLECONN:0,"c4:4f:33:16:f8:c3"
OK
发起连接有两个必须输入的参数:conn_index
, mac addr
conn_index
是由用户指定的, 用来区分不同的连接, 当多连接的时候, 只要每次输入的不一样就可以, 不能重复, 如果连接断开, 这个 index
就释放出来可以再次使用. 这个例子中是 0 , 当然客户也可以选择输入 9527, 这也是可以的, 只要不重复.
发起连接的第三个参数是可选参数, 表示对端设备的地址类型, 默认认为对端设备是 public
地址类型, 如果对端设备是 random
地址类型的话, 需要输入第三个可选参数为 1.
查询连接:
AT+BLECONN?
+BLECONN:0,c4:4f:33:16:f8:c3
OK
如果有连接存在, 就输出这样的 log, 第一个参数就是 conn_index
, 发起连接的时候输入多少, 这里查询出来就是多少. 基于连接的命令都需要输入这样的参数.
断开连接:
AT+BLEDISCONN=0
+BLEDISCONN:0,"c4:4f:33:16:f8:c3"
OK
这是主动断开连接的命令, 如果是被动断开, 也会有相应的 log 提示:
+BLEDISCONN:0,"xx:xx:xx:xx:xx:xx"
Tips:
- 连接只能
client
端发起, 断开连接两端都可以发起. - 如果对端设备的地址类型是
random
, 需要加上第三个可选参数 1.
3.4 如何进行数据的收发
BLE
的数据通信都是基于属性协议的, services
在 server
端, server
端初始化之后, 首先先要创建以及开启服务.
3.4.1 server 创建并开启服务
AT+BLEGATTSSRVCRE // 创建服务
OK
AT+BLEGATTSSTART // 开启服务
OK
创建服务需要在开启服务之前, 只有 server
端能下发这两个命令, 建议在开启广播之前把服务开启.
3.4.2 server 查询本地服务
ESP-AT BLE
很多指令都用到 index
这样的参数, 比如连接里面的 conn_index
, Services
这里也有很多 index
, 比如:
- service index
- characteristic index
- description index
在 ESP-AT
中, 所有关于 services
的操作都是基于 index
来操作的, ble_data.bin
一定需要烧录, 不然无法创建服务, 服务创建好之后就可以查询本地的服务了, 比如:
AT+BLEINIT=2
OK
AT+BLEGATTSSRVCRE
OK
AT+BLEGATTSSRV?
+BLEGATTSSRV:1,0,0xA002,1
OK
AT+BLEGATTSCHAR?
+BLEGATTSCHAR:"char",1,1,0xC300,0x02
+BLEGATTSCHAR:"desc",1,1,1,0x2901
+BLEGATTSCHAR:"char",1,2,0xC301,0x02
+BLEGATTSCHAR:"desc",1,2,1,0x2901
+BLEGATTSCHAR:"char",1,3,0xC302,0x08
+BLEGATTSCHAR:"desc",1,3,1,0x2901
+BLEGATTSCHAR:"char",1,4,0xC303,0x04
+BLEGATTSCHAR:"desc",1,4,1,0x2901
+BLEGATTSCHAR:"char",1,5,0xC304,0x08
+BLEGATTSCHAR:"char",1,6,0xC305,0x10
+BLEGATTSCHAR:"desc",1,6,1,0x2902
+BLEGATTSCHAR:"char",1,7,0xC306,0x20
+BLEGATTSCHAR:"desc",1,7,1,0x2902
+BLEGATTSCHAR:"char",1,8,0xC307,0x02
+BLEGATTSCHAR:"desc",1,8,1,0x2901
OK
当创建了服务之后, 我们就可以查询:
+BLEGATTSSRV:<srv_index>,<start>,<srv_uuid>,<srv_type>
对比 log 和参数说明, 我们可以看到, server
上面只有一个 service
, 它的 index
是 1, 第二个参数表示这个服务还没有被开启, 第三个参数是 UUID
, 第四个参数表示这个服务是 primary service
.
还可以查询服务下面的 characteristics
和 description
:
+BLEGATTSCHAR:"char",<srv_index>,<char_index>,<char_uuid>,<char_prop>
+BLEGATTSCHAR:"desc",<srv_index>,<char_index>,<desc_index>
对着这些格式说明看上面的 log, 可以发现, index
为 1 的这个服务(0xA002
)下面有8个 characteristics
, 每个 characteristic
下面还有相应的 description
.
例如:
+BLEGATTSCHAR:"char",1,6,0xC305,0x10
+BLEGATTSCHAR:"desc",1,6,1,0x2902
表示UUID
(0xC305
) 这个 characteristic
是第一个 service
(0xA002
)下面的第 6 个 characteristic
, 他的 prop
是 0x10
. 这个 characteristic
下面还跟着一个 description,
UUID
是 0x2902
.
其实看到这个UUID
0x2902
, 就应该知道这个是 ccc
(client Characteristic Configuration Descriptor), 这个 characteristic
肯定是可以 notification
的, client
端使能 notify/indicate
就是需要写这个 ccc
.
建议对属性协议不熟悉的客户, 可以先翻阅一下协议的 ATT/GATT
部分, 有助于理解.
3.4.3 client 做服务发现
当连接建立之后, client
就可以做服务发现, 与服务发现相关的命令有 3 条:
- AT+BLEGATTCPRIMSRV
- AT+BLEGATTCINCLSRV
- AT+BLEGATTCCHAR
AT+BLEGATTCPRIMSRV
用来发现对端设备上面的 primary service
, 它需要带一条参数 conn_index
, 也就是下面连接示例里提到的 conn_index
.
为什么
server
端查询服务不需要带参数, 而client
端需要带参数呢?
因为server
即使被多个client
相连, 也是共用服务的, 不区分几个连接, 而client
不一样, 如果连接两个server
, 他们上面的服务是相互独立的, 所以需要带个连接参数来指定发现哪个server
上面的服务.
AT+BLEINIT=1
OK
AT+BLECONN=0,"c4:4f:33:16:f8:c3"
+BLECONN:0,"c4:4f:33:16:f8:c3"
OK
AT+BLEGATTCPRIMSRV=0
+BLEGATTCPRIMSRV:0,1,0x1801,1
+BLEGATTCPRIMSRV:0,2,0x1800,1
+BLEGATTCPRIMSRV:0,3,0xA002,1
OK
AT+BLEGATTCINCLSRV=0,3
OK
AT+BLEGATTCCHAR=0,3
+BLEGATTCCHAR:"char",0,3,1,0xC300,0x02
+BLEGATTCCHAR:"desc",0,3,1,1,0x2901
+BLEGATTCCHAR:"char",0,3,2,0xC301,0x02
+BLEGATTCCHAR:"desc",0,3,2,1,0x2901
+BLEGATTCCHAR:"char",0,3,3,0xC302,0x08
+BLEGATTCCHAR:"desc",0,3,3,1,0x2901
+BLEGATTCCHAR:"char",0,3,4,0xC303,0x04
+BLEGATTCCHAR:"desc",0,3,4,1,0x2901
+BLEGATTCCHAR:"char",0,3,5,0xC304,0x08
+BLEGATTCCHAR:"char",0,3,6,0xC305,0x10
+BLEGATTCCHAR:"desc",0,3,6,1,0x2902
+BLEGATTCCHAR:"char",0,3,7,0xC306,0x20
+BLEGATTCCHAR:"desc",0,3,7,1,0x2902
+BLEGATTCCHAR:"char",0,3,8,0xC307,0x02
+BLEGATTCCHAR:"desc",0,3,8,1,0x2901
OK
测试 log 是 client
初始化之后发起连接请求, 然后做服务发现, 服务发现的结果是:
- +BLEGATTCPRIMSRV:0,1,0x1801,1
- +BLEGATTCPRIMSRV:0,2,0x1800,1
- +BLEGATTCPRIMSRV:0,3,0xA002,1
对端 server
上面一共有 3 个服务, 请直接忽略 0x1801
和 0x1800
, 这两个是 GAP
和 GATT
, 最下面那个第三个服务 0xA002
才是刚才我们创建的服务.
在例子中, 我们进一步的查询了第三个服务下面的有哪些 characteristic
和 descripton
, 用户可以对比下这里查询到的服务是不是和刚才在 server
上创建的一样.
// server
AT+BLEGATTSCHAR?
+BLEGATTSCHAR:"char",1,1,0xC300,0x02
+BLEGATTSCHAR:"desc",1,1,1,0x2901
+BLEGATTSCHAR:"char",1,2,0xC301,0x02
+BLEGATTSCHAR:"desc",1,2,1,0x2901
+BLEGATTSCHAR:"char",1,3,0xC302,0x08
+BLEGATTSCHAR:"desc",1,3,1,0x2901
+BLEGATTSCHAR:"char",1,4,0xC303,0x04
+BLEGATTSCHAR:"desc",1,4,1,0x2901
+BLEGATTSCHAR:"char",1,5,0xC304,0x08
+BLEGATTSCHAR:"char",1,6,0xC305,0x10
+BLEGATTSCHAR:"desc",1,6,1,0x2902
+BLEGATTSCHAR:"char",1,7,0xC306,0x20
+BLEGATTSCHAR:"desc",1,7,1,0x2902
+BLEGATTSCHAR:"char",1,8,0xC307,0x02
+BLEGATTSCHAR:"desc",1,8,1,0x2901
OK
// client
AT+BLEGATTCCHAR=0,3
+BLEGATTCCHAR:"char",0,3,1,0xC300,0x02
+BLEGATTCCHAR:"desc",0,3,1,1,0x2901
+BLEGATTCCHAR:"char",0,3,2,0xC301,0x02
+BLEGATTCCHAR:"desc",0,3,2,1,0x2901
+BLEGATTCCHAR:"char",0,3,3,0xC302,0x08
+BLEGATTCCHAR:"desc",0,3,3,1,0x2901
+BLEGATTCCHAR:"char",0,3,4,0xC303,0x04
+BLEGATTCCHAR:"desc",0,3,4,1,0x2901
+BLEGATTCCHAR:"char",0,3,5,0xC304,0x08
+BLEGATTCCHAR:"char",0,3,6,0xC305,0x10
+BLEGATTCCHAR:"desc",0,3,6,1,0x2902
+BLEGATTCCHAR:"char",0,3,7,0xC306,0x20
+BLEGATTCCHAR:"desc",0,3,7,1,0x2902
+BLEGATTCCHAR:"char",0,3,8,0xC307,0x02
+BLEGATTCCHAR:"desc",0,3,8,1,0x2901
OK
除了 index
不一样, 其他都是一样的, index
只在本地有效, 方便用户精准的操作任意一个属性值.
3.4.4 client 端的读和写
client
向 server
发送数据可以用 AT+BLEGATTCWR
, 主动读取可以用 AT+BLEGATTCRD
.
例如去读第一个 characteristic
0xC300
:
AT+BLEGATTCRD=0,3,1
+BLEGATTCRD:0,1,0 //长度为 1, 数值为 0
OK
现在我们去写第二个 characteristic
0xC301
:
AT+BLEGATTCWR=0,3,4,10
> // 这里我们输入了10个字符 abcdefghij
OK
在 server
端可以看到写的 log:
+WRITE:0,1,4,,10,abcdefghij
这里面的参数很多, 第一个都是 conn_index
, 后面跟的依次是 serv_index
, char_index
, desr_index
, length
以及 data
.
如果写的是 characteristic
的值, 那么 desc_index
就会被省略.
Tips
- 如果指定输入字符的长度, 真实输入的字符超过了这个长度, 会提示
ERROR
- 如果写了不可写的
characteristic
, 或者读了不可读的characteristic
,会报ERROR
3.4.5 register notification
BLE
的 characteristic
的属性除了读写, 还有 notify
和 indicate
, 这两种都是 server
向 client
发送数据的方式. 但是要想真的发送成功, 需要 client
提前 register notification
, 也就是写 ccc
的值.
如果是可 notify
, 需要写 0x1
, 如果是可 indicate
的, 需要写 0x2
(写 0x2902
这个 description
).
比如我们 ESP-AT
里面默认的服务中, 0xC305
是可 notify
的, 0xC306
是可 indicate
的, 我们分别写这两个 characteristics
下面的 0x2902
description
:
// client
AT+BLEGATTCWR=0,3,6,1,2
> // 写 0x01
OK
// server
+WRITE:0,1,6,1,2,<0x01>,<0x00>
AT+BLEGATTCWR=0,3,7,1,2
> // 写 0x02
OK
// server
+WRITE:0,1,6,1,2,<0x02>,<0x00>
写 ccc
是 server
可以发送 notify
和 indicate
的前提条件
Tips
- 其实如果客户向这些
description
写错误的值,ESP-AT
内部也会转成正确的.
3.4.6 server 主动向 client 发送数据
在上一个环节, 我们已经做好了发送 notify
和 indicate
的前提条件, 我们现在就可以直接发送数据了
// server
AT+BLEGATTSNTFY=0,1,6,8
> //输入 123456789
OK
// client
+NOTIFY:0,3,6,8,12345678
// server
AT+BLEGATTSIND=0,1,7,9
> //输入 aaabbbccc
OK
// client
+INDICATE:0,3,7,9,aaabbbccc
每个参数定义其实和上面都类似, 用户可以参考指令文档详细了解每个参数的意义.
四: 如何处理 BLE 加密
上一章节介绍了 BLE
数据通信的示例, 本节介绍 BLE
加密相关的内容.
BLE
加密相关的指令主要就是做这些事情:
- 设置加密参数
- 发起加密请求
- 响应加密请求
KEY
的交互, 比如弹出密码或者输入密码- 查询
bonding
信息或者清楚bonding
信息
Tips:
- 加密和
bonding
是两码事,bonding
只是加密成功之后本地存了一个长期的key
4.1 加密参数该如何设置
BLE
加密参数可以在初始化之后, 发起加密之前的任意时刻下发, 也可以多次下发, 但是仅仅最近的那次下发有效, 会覆盖前面的设置.
AT+BLESECPARAM=<auth_req>,<iocap>,<key_size>,<init_key>,<rsp_key>[,<auth_option>]
OK
这里面的参数, 客户仅仅需要关注最前面的两个就可以了, 后面的几个参数可以参照 example
里面默认设置.
第一个参数是 auth_req:
- 0 : NO_BOND
- 1 : BOND
- 4 : MITM
- 8 : SC_ONLY
- 9 : SC_BOND
- 12 : SC_MITM
- 13 : SC_MITM_BOND
这个参数主要影响加密等级, 表达自身的加密诉求. 比如是想要 MITM
, 还是 security connection
, 还是bonding
.
第二个参数是 IO CAP:
- 0 : DisplayOnly
- 1 : DisplayYesNo
- 2 : KeyboardOnly
- 3 : NoInputNoOutput
- 4 : Keyboard displa
这个参数就是用来设置自身的输入输入能力的, 表示自己是否可输入可输出.
注意:
如果
auth_req
设置的是MITM
, 但是IO CAP
是NoInputNoOutput
, 那么肯定就不是MITM
了, 只能是just work
, 有人会说, 这不是矛盾了嘛, 我设置的是MITM
啊, 其实并不矛盾, 通俗的讲,MITM
是期望,IO
是能力, 能力不足, 再高的期望也是达不到的. 同理, 如果自己是可输入可输出, 但是对方设备是不能输入不能输出, 那么也只能是just work
, 队友能力不足也不行.
4.2 不同的加密参数组合会出现什么样的结果
不同的 IO
设置组合, 会出不同的现象, 比如:
- 一端可输入另一端可输出
- 两端都可输入输出
这里的组合实在太多, 用户可以参阅core spec
根据不同的组合, 大致上最常见的就是两种情况, 一个是弹出密码给对端输入, 另一种就是输入对端给的密码
4.3 加密示例
我们现在举个例子, 用两块 ESP-AT
来加密并 bonding
, 前面建立连接的步骤这里不重复了, 假设我们现在连接已经建立.
首先设置两端的加密参数
// client
AT+BLESECPARAM=13,4,16,3,3 // IO是 Keyboard displa
// server
AT+BLESECPARAM=13,2,16,3,3 // IO是 KeyboardOnly
一个是可输出一个是可输入, 而且要求 MITM
, 这个时候肯定是 client
弹出密码, server
输入密码.
第二部是发起加密请求, 这个请求可以 client
发起, 也可以 server
发起, 都一样的, 这里的例子中, 我们用 client
发起
AT+BLEENC=0,3 // 具体参数描述请参阅指令文档
OK
这个时候, server
端会弹出加密有加密请求的 log:
+BLESECREQ:0 // 表示第一个连接的设备请求加密
这个时候需要对加密请求作出相应, 决定是同意加密, 还是拒绝. 这里我们选择同意
AT+BLEENCRSP=0,1 // 表示同意加密
OK
下一步就是 KEY
交互的过程了, 如我们上面分析一样, 是 client
弹出密码
+BLESECNTFYKEY:0,344804 // 密码是 344804
然后我们在 server
端输入密码
AT+BLEKEYREPLY=0,344804
如果密码输入正确, 那么加密就会成功, log 如下:
// client
+BLESECKEYTYPE:0,2
+BLESECKEYTYPE:0,16
+BLESECKEYTYPE:0,1
+BLESECKEYTYPE:0,32
+BLEAUTHCMPL:0,0
// server
+BLESECKEYTYPE:0,16
+BLESECKEYTYPE:0,1
+BLESECKEYTYPE:0,32
+BLESECKEYTYPE:0,2
+BLEAUTHCMPL:0,0
关于 KEYTYPE
的 log 都可以忽略, 仅仅关注 +BLEAUTHCMPL:0,0
就可以, 第二个参数为 0, 表示成功, 不为 0 表示失败, 第一个参数是 conn_index
, 取决于当初连接的时候设置的是多少.
上面的例子仅仅只是一种情况, 如果我们当时将 IO
颠倒下, 那么结果又是不一样的, 用户可以自由尝试, 其实无非是就是两端配合着输入输出密码, 或者直接 just work
, 不需要人工参与交互.
五: 如何使用 BLE SPP
因为 BLE SPP
没有标准的协议, 这里的 BLE SPP
是我们自己定义的, 与别的厂商不兼容. 如果客户需要使用, 可以使用两块 ESP32
的模组, 或者按照我们的方法在手机端或者别的厂商芯片上面实现.
BLE SPP
的原理就是利用两个 characteristic
的来进行数据收发, 一个需要可以被写, 另一个需要可以 notify
或者 indicate
.
我们用两个 ESP32
来举例, 首先我们分别初始化两块板子:
// client:
AT+BLEINIT=1
OK
// server:
AT+BLEINIT=2
OK
然后我们在 server
上面创建并开启服务, 开启广播, 等待被连接
AT+BLEGATTSSRVCRE
OK
AT+BLEGATTSSRVSTART
OK
AT+BLEADVSTART
OK
client
发起连接请求, 如果不知道对端的地址, 可以先扫描一下
AT+BLECONN=0,”XX:XX:XX:XX:XX:XX”
OK
如果连接成功, server
会有 log 打印
+BLECONN:0,”XX:XX:XX:XX:XX:XX”
client
做服务发现, server
也查询下自身的服务, 并设置 SPP
参数
// client:
AT+BLEGATTCPRIMSRV=0
AT+BLEGATTCCHAR=0,3
AT+BLESPPCFG=1,3,3,3,7
// server:
AT+BLEGATTSSRV?
AT+BLEGATTSCHAR?
AT+BLESPPCFG=1,1,7,1,3
SPP
参数其实就是指定用那两个 characteristic
来收发数据
AT+BLESCANPARAM=<option>[,<tx_service_index>,<tx_char_index>,<rx_service_index>,<rx_char_index>]
第一个参数在设置的时候必须设置为 1, 如果是 0, 表示清除配置
后面的四个参数分别是:
- tx_service_index
- tx_char_index
- rx_service_index
- rx_char_index
分别指定用哪个 service
下面的哪个 characteristic
来收发数据.
在例子中, client
端设置是的第三个服务下面的第三个 characteristic
来发送数据, 这个 characteristic
的属性是 write
, 是可写的, 对于 client
端来讲, 用来 tx
的 characteristic
必须是可以被 write
或者 write without rsp
. 用来 rx
的 characteristic
是第七个 characteristic
, 他的属性是 indicate
, 是用来接收 server
发送数据的.
然后就可以进入 SPP
模式了:
// client:
AT+BLESPP
// server:
AT+BLESPP