ESP-AT 系列: BLE 命令介绍和使用

目录

一: 概述

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-ATBLE 的命令做的比较细, 对 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 如果已经提前知道了 serverMAC 地址, 可以不扫描, 直接发起连接的命令;
如果不知道 serverMAC 地址, 可以先扫描一下.

连接与通信
一般的 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> 参数, 具体的介绍与用法, 会在下面的实例章节详细介绍, 这里仅需了解下大概的流程.

查询好了服务相关的参数, 就可以数据通信了, clientserver 发送数据需要通过写的方式:

  • char 或者 desc: AT+BLEGATTCWR
  • char 或者 desc: AT+BLEGATTCRD

serverclient 发送数据需要通过 notifyindicate 的方式.

  • 发送 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 包.

除了AppleiBeacon, 还有谷歌的 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 datascan 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+BLECONNAT+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 的数据通信都是基于属性协议的, servicesserver 端, 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.

还可以查询服务下面的 characteristicsdescription:

+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, 他的 prop0x10. 这个 characteristic 下面还跟着一个 description, UUID0x2902.

其实看到这个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 个服务, 请直接忽略 0x18010x1800, 这两个是 GAPGATT, 最下面那个第三个服务 0xA002 才是刚才我们创建的服务.

在例子中, 我们进一步的查询了第三个服务下面的有哪些 characteristicdescripton, 用户可以对比下这里查询到的服务是不是和刚才在 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 端的读和写

clientserver 发送数据可以用 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

BLEcharacteristic 的属性除了读写, 还有 notifyindicate, 这两种都是 serverclient 发送数据的方式. 但是要想真的发送成功, 需要 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>

cccserver 可以发送 notifyindicate 的前提条件

Tips

  • 其实如果客户向这些 description 写错误的值, ESP-AT 内部也会转成正确的.

3.4.6 server 主动向 client 发送数据

在上一个环节, 我们已经做好了发送 notifyindicate 的前提条件, 我们现在就可以直接发送数据了

// 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 CAPNoInputNoOutput, 那么肯定就不是 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 端来讲, 用来 txcharacteristic 必须是可以被 write 或者 write without rsp. 用来 rxcharacteristic 是第七个 characteristic, 他的属性是 indicate, 是用来接收 server 发送数据的.

然后就可以进入 SPP 模式了:

// client:

AT+BLESPP

// server:

AT+BLESPP
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章