目录
3.2:scan for unprovisioned dev beacons
简单理一下泰凌微电子BLE SigMesh SDK V2.8.2通过手机对设备节点进行配网的过程,为今后的开发工作做一个参考的文档。因为SDK并不是开源的,因此有些部分可能出现谬误,欢迎指正;未完成部分等出现问题再看吧,目前实在是太懒得去看了。
配网(provision)功能是将一个未分配的节点加入到Mesh网络使之成为某个Mesh网络节点的功能。根据SIG Mesh spec的定义,配网过程会将Mesh网络的Network Key,IV Index以及分配给节点的unicast addr传输给节点。
1:准备工作
1.1:抓包工具
采用韦东山安卓视频蓝牙教程部分推荐的wireshark + nRF sniffer
1.2:telink BLE SIG Mesh SDK
泰凌微电子BLE SIG Mesh SDK下载地址:http://www.telink-semi.cn/viewtopic.php?f=7&t=100
包含固件SDK与android app sdk,其中有两个相对重要的参考文档:
<1>《AN_17090701-C1_Usage and Development Guide for Android APP TelinkSigMesh》
<2>《AN_17120401-C2_Telink SIG Mesh SDK Developer Handbook.pdf》
1.3:BLE SIG Mesh Spec
BLE SIG Mesh协议文档可以到官方网站下载,这里主要涉及第5章provisioning下的PB-GATT相关内容
1.4:调试设备
telink 8269 usb dongle + vivo
telink 8269 usb dongle MAC:AB:CD:04:0A:93:89
tlsr8269 usb dongle烧录BLE SIG Mesh SDK V2.8.2 release的固件
vivo手机通过android app对未配网的usb dongle进行扫描、连接、配网的操作
2:配网过程概述
本节主要参考BLE SIG Mesh Spec V1.0以及telink固件开发手册以下部分:
整理telink SIG Mesh SDK GATT配网模式流程如下:
3:设备发现
app为设备进行配网,首先要发现周围的未配网设备,设备发现主要分为以下三步:
<1>未配网的设备开机后会一直向外发送unprovisioned beacon(可连接类型)广播包;
<2>用户打开APP扫描周围的未配网设备;
<3>app扫描到设备的unprovisioned beacon,并向该设备发出SCAN_REQ;设备接收到SCAN_REQ后回复SCAN_RSP;
SCAN_RSP内附带有必要的自定义信息,可帮助app识别设备。
3.1:unprovisioned dev beacon
3.1.1:设备端beacon/广播初始化
设备端上电后在user_init函数内有对设备的广播进行初始化,主要步骤:
bls_ll_setAdvParam:设置广播间隔10ms,非定向广播类型,37,38,39三个信道
blc_ll_setAdvCustomedChannel:自定义广播使用的信道。SIG Mesh要求同时使用37,38,39三个广播信道,调试时为了抓包方便可以修改。
bls_ll_setAdvEnable:使能广播
rf_set_power_level_index:RF功率设置
3.1.2:设备端beacon数据解析
未配网的设备上电后开始不停向外发送unprovisioned dev beacon;
unprovisioned beacon是可连接类型广播包(ADV_IND),且已经被BLE SIG Mesh定义完毕全部字段,对应固件SDK代码内的结构体类型PB_GATT_ADV_DAT:
未配网的设备通过unprovision dev beacon向外广播mesh provisioning GATT service的数据service_data,service_data内包含设备的MAC地址及其它数据。Provisioner(APP)可以通过这些数据唯一地识别到发送该beacon的设备;
unprovision beacon必要时会带有oob_info;oob_info用于提示provisoner在进行下一步配网动作之前需要收集oob(附带数据);uuid 0x1827为16位mesh provisioning GATT service UUID
3.2:scan for unprovisioned dev beacons
3.2.1:app扫描unprovisioned dev beacon
Telink BLE SIG Mesh android APP 对设备是扫描发现一个配网一个的,其扫描与配网相关的代码实现均在DeviceProvisionActivity方法,本节只关注扫描设备相关:
<1>DeviceProvisionActivity.onCreate监听ScanEvent.DEVICE_FOUND与SCAN_TIMEOUT事件;
<2>DeviceProvisionActivity.onCreate调用startScan方法启动设备扫描;
<3>DeviceProvisionActivity.startScan首先生成默认扫描配置参数ScanParameters ,然后将已经配网的设备排除,最后调用MeshService.getInstance().startScan(parameters)启动Mesh服务扫描蓝牙设备;
<4>DeviceProvisionActivity.perfomed内对ScanEvent.DEVICE_FOUND事件进行监听,将扫描到的设备数据AdvertisingDevice device传给onDeviceFound处理(进行下一步配网)
<5>至此可知,APP扫描unprovisioned beacon获得的数据被缓存在了AdvertisingDevice device:
上图蓝色部分与3.1.2设备端unprovisioned beacon数据一致。
3.2.2:app获取设备信息
AdvertisingDevice内除了unprovisoned dev beacon还有一包数据,是app接收到设备端的unprovisoned dev beacon包后发给设备一个SCAN_REQ再由设备在SCAN_RSP返回的。设备端SCAN_RSP的内容在mesh_scan_rsp_t结构体内定义,抓包数据如下:
用户可以(或者说必须)在这个SCAN_RSP附带自定义的数据回复给app,例如用以区分识别厂家设备的信息(不是自家的设备不进行连接)。至此,app已发现并获取了周围某个未配网设备的必要信息,下一步将建立与设备的BLE连接。
4:设备连接与服务发现
app通过unprovisioned dev beacon扫描到未配网设备后,在正式配网之前,app需与设备建立BLE连接、扫描设备服务(属性表)、向设备写入CCC数据。
4.1:app发起BLE连接与获取服务
4.1.1:app发起BLE连接
app通过unprovisioned dev beacon获取到AdvertisingDevice 数据后会在onDeviceFound内继续调用直到MeshService.getInstance().startProvision(...);
startProvision调用this.device.connectGatt(...)向设备发起BLE连接请求。配网若出现失败,手机app的CONNECT_REQ没有收到回复是常见原因,因此CONNECT_REQ几个参数标出备查:
(参考:https://www.cnblogs.com/iini/p/8972635.html)
4.1.2:app获取设备服务
connectGatt接口的状态回调是onConnectionStateChange,app根据回调状态判断BLE连接建立是否成功;成功连接后app再通过gatt.discoverServices()获取设备支持的attribute table。抓包工具显示,master(手机app)递增attributes handle查询slave(BLE Mesh设备)支持的attribute table:
(本节参考:https://www.cnblogs.com/hzl6255/p/4141505.html)
discoverServices()的结果回调是onServicesDiscovered,onServicesDiscovered内app会向设备端写入CCC数据,CCC的用途是打开设备端的特征值nodify功能;见writeCCCForPv。
app使能设备端的数据上报后还需要调用enableNotifications使能app对设备PB_OUT_CHARACTERISTIC_UUID/PROXY_OUT_CHARACTERISTIC_UUID/CHARACTERISTIC_UUID_ONLINE_STATUS的nodify接收;当有数据上报时,onCharacteristicChanged接口被回调:
PB_OUT_CHARACTERISTIC_UUID:配网功能GATT Service数据输出特征值
PROXY_OUT_CHARACTERISTIC_UUID:代理功能GATT Service数据输出特征值CHARACTERISTIC_UUID_ONLINE_STATUS:Mesh网络状态上报功能GATT Service
CHARACTERISTIC_UUID_ONLINE_STATUS相关功能是telink为了实际产品开发状态同步问题加的,需要相关版本与宏打开;配网功能与代理功能的GATT services则是必须的,只是根据设备端是否已配网加载不同的属性表,详见4.2.2。
app会请求更新BLE传输的最大传输单元MTU(一个协议数据单元中能够传输的最大数据量),MTU默认最大23个字节,后续配网期间传输的数据远不止此。MTU实际变更的数值会在onMtuChanged回调内返回:
4.2:设备端BLE连接的处理
4.2.1:设备端BLE连接状态回调
<1>设备端代码app.c app_event_handler下HCI_SUB_EVT_LE_CONNECTION_COMPLETE是app与设备端建立BLE连接时的处理,结构体event_connection_complete_t对应4.1.1 app端发送的连接请求指令CONNECT_REQ参数;
<2>bls_app_registerEventCallback接口用于为各类BLE连接事件注册回调函数,SDK代码暴露出来的回调是mesh_ble_connect_cb/mesh_ble_disconnect_cb/mesh_conn_param_update_req;
<3>需要说明的是在mesh_ble_connect_cb与mesh_conn_param_update_req内有一些处理:手机app与设备端的BLE连接建立后会设置need_conn_update_flag与att_service_discover_tick,两者共同作用在BLE连接建立的500ms后由设备端发起告知手机更新BLE连接参数bls_l2cap_requestConnParamUpdate(16, 32, 0, 200)。根据telink工程师的解释:手机处于扫描设备阶段与建立BLE连接后进行配网/通讯时宜使用的连接参数不同。建立BLE连接500ms后再更新此参数的原因是需要留给手机app读取全部attribute table的时间。这就有可能涉及到时序问题:更新参数的请求与配网流程中的指令发生碰撞。不过也可改为配网完毕再进行参数更新,具体还要视SDK版本迭代情况。
4.2.2:设备端BLE attribute table
如4.1.2节所述app有获取设备端BLE attribute table(实际上是发现GATT services)的操作,设备端相关代码在app_attr.c下。user_init时会在my_att_init加载bls_att_setAttributeTable,用户在此添加自定义的GATT service。设备端SDK默认的GATT服务示意如下:
手机app与设备建立BLE连接时,手机作为BLE master设备端作为BLE slave;发现设备端GATT server时,则是手机app作为GATT client,设备端作为GATT server。GATT server与client之间通过ATT PDUs进行通讯。GATT client访问GATT server的characterists值通过READ/WRITE操作;GATT server可以把自己的characterists值通过nodify或者indicate的方式告知client;indicate方式相对于nodify有ack回复,所以较为可靠。
GATT client(对应手机app)使能/禁用GATT server(对应设备端)nodify/indicate功能,是通过对characteristic descriptors的Client Characterist Configuration(CCC,对应UUID 0x2902,上图浅蓝色部分)进行写操作实现的。从设备端代码pb_gatt_Write与proxy_gatt_Write看,CCC数据若写入失败,手机app向设备端写入的配网数据/控制指令均不会生效。
4.3:异常情况备忘
实际开发过程中,对于telink SIG Mesh的GATT配网方式,发现出现失败情况在主要发生本节所述的设备连接与服务发现阶段,这里稍微罗列下一些出现的异常情况:
<1>如4.1.1所述,手机发送给设备端的CONNECT_REQ没有响应。对应app端一般是出现BLE disconnect的回调发生。造成的原因各种各样,处理主要还是app重连。
<2>手机app对一个设备配网完毕后再配下一个,这个时序需要处理好,否则可能出现上一个设备配网的确认信息才处理,当前已连接的又是新的一个设备,误判新的设备配网情况。
<3>如4.1.2所述,手机app需要获取设备的attribute table。比较吊诡的是,android与iOS在底层获取atrribute table的具体顺序是不同的,因此对于attribute table何时获取完毕的判断条件是不同。当然这个情况是app可以处理的。具体抓包TBD。
<4>从获取attribute table到使能设备端GATT service的nodify(写入CCC数据)再到向设备端发送provision invite,三者之间需要满足一定的时序要求。具体原因与时序要求请请教telink原厂。
以上仅供参考备忘,应该随着SDK迭代都能fix这些情况。
5:配网
当手机APP与设备建立BLE & GATT连接成功,接下来就要开始对设备进行配网。
配网的详细过程参考BLE SIG Mesh Spec 5.4 Provisioning protocol
5.1:provisioning invite
app向设备端写入CCC后,再满足一定的时间间隔后,会向设备端发起provisioning invite,随即设备端回复provisioning capabilities数据,这些在SIG Mesh Spec有详细定义。
provisioning invite参考BLE SIG Mesh Spec 5.4.1.1
app通过startProvisionInvite向设备端发送provisioning invite,实际上就是3个字节的数据。
5.2:provisioning capabilities
provisioning capabilities参考BLE SIG Mesh Spec 5.4.1.2
设备端对于provisioning capabilities的定义是结构体:pro_trans_capa
对应BLE SIG Mesh Spec 5.4.1.2。绿框部分依次是写入CCC,app发起provisioning invite以及设备返回provisioning capabilities的抓包情况:
5.3:provisioning start
啥时候想写再说吧
6:绑定app key
啥时候想写再说吧