背景情况:

有一个BLE从机,可以把它理解为一个传感器,我需要通过ESP32做一个主机去与它建立BLE连接,并订阅他的notify.订阅notify后传感器会周期性的给ESP32推送数据.

这个BLE从机的主控用的NRF52系列的芯片,我可以修改固件,可以查看固件log,可以临时性的增减调试代码,但最终使用时候的固件已经确定了,无法修改.

ESP32这边是可以随意修改调试的.

问题现象:

正常参考ESP-IDF的GATT_Client例程来写的固件.先实现了搜索广播,连接设备,发现服务以及订阅notify(对于ESP32指的是esp_ble_gattc_register_for_notify()和通过esp_ble_gattc_write_char_descr()对notify的CCCD写0x0001)的功能.上面这些流程操作都正常,但是ESP32这边收不到ESP_GATTC_NOTIFY_EVT事件,而且连接上以后过几秒或者十几秒钟的时间,两个设备之间的连接就断开了.

开始Debug:

经过一些自我怀疑的无用debug后,最开始的比较值得去做的测试是用嗅探器去抓包.因为这个BLE传感器的notify通过其他工具,例如手机APP之类的是可以正常使用的.所以当初想的就是去抓手机连接BLE传感器的空口包与ESP32去连接BLE传感器的空口包来看看有什么差别,因为这个BLE传感器不止一个notify的char,还有一个write and indicate的char,这个char在write后马上就会触发indicate,比较好调试.所以就按这个char来分析问题.

先看正常的手机的抓包数据:

在涂黑的条目上:

  • 第一条是写这个char的CCCD,因为是indicate,所以写的是0x0002:
  • 第二条是BLE传感器针对上一包发回应包.
  • 第三条是手机写这个char.
  • 第四条是BLE传感器针对上一包发的回应包.
  • 第五条是BLE传感器主动发的indicate包.
  • 第六条是手机针对上一包发的回应包.

可以看到整个write操作跟indicate操作都是OK的.

然后我们再看下ESP32这边:

首先是先写CCCD:

跟上面手机一样,都是发一个写0x0002的包,然后BLE传感器回复.这个没问题.

然后是写这个char:

可以看到也是ESP3发写char的包,然后BLE传感器回复.也是正常的(虽然不知道为啥ESP32有时候会连发多个包,但是目前看这个不影响,从机端固件那边还是只会收到一个).

但是,在这上面,BLE传感器回复写char的包以后并没有发出indicate的包,这也是问题.

而且根据我这边测试的情况,如果char是read属性的,那ESP32这边也是可以正常读取的.write属性的char,ESP32可以正常写入的,BLE传感器那边也能正常收到写入的数据.但是BLE传感器那边发送的notify跟indicate.在ESP32这边是收不到的,而且在BLE传感器那边,发送notify跟indicate的时候协议栈也会报错.具体是这样的:

在BLE传感器那边,发送notify跟indicate的时候会调sd_ble_gatts_hvx()这个API.执行后会返回一个错误码,如果是手机去连接,正常收到notify或indicate后这个API会返回NRF_SUCCESS,但是如果是ESP32去连的时候调用这个API就会返回8,也就是NRF_ERROR_INVALID_STATE.

查了一下这个API的说明,如果返回NRF_ERROR_INVALID_STATE的话有以下几个情况:

  • BLE连接状态不对.
  • notify或indicate的CCCD没使能.
  • MTU交换正在进行中.

所以只能一个一个排除这三个可能性.

  • 第一个连接状态这个肯定是OK的,在NRF52那边当初就有打印BLE_GAP_EVT_CONNECTED事件跟BLE_GAP_EVT_DISCONNECTED事件的log.确定在sd_ble_gatts_hvx()的期间是BLE_GAP_EVT_CONNECTED之后,BLE_GAP_EVT_DISCONNECTED之前.
  • 第二个notify或indicate的CCCD没使能这个刚刚已经通过抓包排除了,但是以防万一我还是在NRF52这边加了相关的log,在BLE_GATTS_EVT_WRITE事件处理时增加关于事件的UUID,Handle,以及写入数据内容的log.这个可能性算是排除了.
  • 第三个MTU交换进行中这个我这边尝试在NRF52的ble_evt_handler的callback函数中打印事件号,发现相对于ESP32,手机在连接过程中,建立连接完成后有一个event为58的事件产生.而ESP32连接的时候没有这个事件.

顺着这个线索查下去,发现这个事件是BLE_GATTC_EVT_EXCHANGE_MTU_RSP,看上去是手机给NRF52提出的MTU交换请求做了回复.MTU交换是由Client发起请求,再由Server回复的一个机制.但是我在ESP32上只有GATT Client,并没有GATT Server呀.感觉问题已经初步显现了.因为连接建立时,NRF52会主动发起MTU交换请求,而ESP32并不支持GATT Server,导致ESP32不会回复NRF52的MTU交换,导致MTU交换一直处于进行中,而在NRF52发notify或indicate时,NRF52的协议栈检测到MTU交换还未完成,于是中止了notify或indicate的发送,并返回了错误码.

根据上面的结论,开始修改ESP32的固件.首先先通过idf.py menuconfig给工程配置GATT Server的支持.然后在代码上,根据ESP-IDF中GATT Server的例程代码,增加esp_ble_gatts_app_register()与esp_ble_gatts_register_callback()以及对应的gatts_event_handler()函数.gatts_event_handler()中不需要做什么处理,留空就行.

使用上面修改过的ESP32固件重新烧录,测试.bingo~问题解决.可以正常收到notify跟indicate了.问题解决!~


一个电子工程师的自我修养