文章内容
基于bluetooth->gattc_multi_connect示例创建工程,读懂程序,配合已有蓝牙从机设备,实现蓝牙主机一对多通信。
补充篇:之前调试只实现了连接一台蓝牙从机设备通信,补充篇实现一对多通信;同时开启多个notfiy
硬件
● 一款 ESP32 开发板(本专辑中均使用ESP32-LyraT-Mini V1.2开发板)
● USB 数据线 (A 转 Micro-B)
● 电脑(Windows)
一个服务下同时开启多个特征notify
用手机蓝牙调试助手app观察蓝牙从机模块服务及特征表
gattc_multi_connect示例程序中,针对notify的操作如下:
● 指定一个含notify属性的特征UUID(REMOTE_NOTIFY_CHAR_UUID)
● 通过esp_ble_gattc_get_char_by_uuid获取指定UUID的特征。其中count表示获取到特征的数量,char_elem_result_a用于存放获取到的特征元素(char_handle特征句柄、properties属性、UUID)
● 判断特征数量>0,char_elem_result_a[0]的特征其属性是否包含notify,若是包含notify则注册服务通知,对应特征值写入1开启notify
通过实验查找并验证问题,当REMOTE_NOTIFY_CHAR_UUID分别设为上图中的0xffe2和0xffe4时,确认esp_ble_gattc_get_char_by_uuid执行后返回的count值以及char_handle
增加LOGE语句,便于debug观察。
#define REMOTE_NOTIFY_CHAR_UUID 0xffe2
执行结果如下:
#define REMOTE_NOTIFY_CHAR_UUID 0xffe4
执行结果如下:
尝试将esp_ble_gattc_get_char_by_uuid参数由char_UUID改成service_UUID,实测会报错。
基本结论:若是需要实现多个notify只能改动程序,需要指定多个uuid并进行多次esp_ble_gattc_get_char_by_uuid获取。
修改程序如下:
● 指定多个含notify属性的特征UUID,增加一个宏定义,用于后续动态分配空间时使用,同步修改定义多个remote_filter_char_uuid。(本实验中设定为2个notify)
#define REMOTE_SERVICE_UUID 0xffe0 // 0x00FF //对应蓝牙从机设备(gatt_server)服务UUID
#define REMOTE_NOTIFY_CHAR_UUID1 0xffe2 // 0xFF01 //对应服务UUID为0x00FF下的特征UUID
#define REMOTE_NOTIFY_CHAR_UUID2 0xffe4
#define REMOTE_NOTIFY_CHART_COUNT 2 //需要开启两个特征的notify,增加一个宏定义,后续动态开辟空间时用
//其保存的是需要获取的特征UUID。主要esp_ble_gattc_get_char_by_uuid中使用,传递参数。通过uuid获取指定特征
static esp_bt_uuid_t remote_filter_char_uuid1 = {
.len = ESP_UUID_LEN_16,
.uuid = {.uuid16 = REMOTE_NOTIFY_CHAR_UUID1,},
};
static esp_bt_uuid_t remote_filter_char_uuid2 = {
.len = ESP_UUID_LEN_16,
.uuid = {.uuid16 = REMOTE_NOTIFY_CHAR_UUID2,},
};
● ESP_GATTC_SEARCH_CMPL_EVT事件处理中,通过调用esp_ble_gattc_get_char_by_uuid两次,获取指定UUID的特征,注册两个服务通知。(调用两次共用同一个count变量,逻辑上没那么严谨,但无伤大雅,后续可自行优化)
● ESP_GATTC_REG_FOR_NOTIFY_EVT事件处理中,相关操作均执行两次,传递不同的特征句柄,实现使能notify操作。
● ESP_GATTC_WRITE_DESCR_EVT事件处理中,因采购的蓝牙从机模块未开放相关特征说明,不做多余写入操作,注释掉写特征值部分程序。
● gattc_profile_b_event_handler和gattc_profile_c_event_handler事件处理程序中导入上述相同的修改。编译运行结果如下:
至此,一个服务下同时开启多个特征notify功能实现。
实现一对多通信
gattc_multi_connect示例为一主多从程序,实际测试只能连接一个蓝牙从机设备。从程序debug上看,连接了第一台从机设备后就不再进行扫描了。
搜索“esp_ble_gap_stop_scanning",查看程序中哪些条件成立会导致停止扫描。导致停止扫描的条件如下:
● esp_gap_cb回调函数的ESP_GAP_BLE_SCAN_RESULT_EVT事件处理中,已连接3台设备
● esp_gap_cb回调函数的ESP_GAP_BLE_SCAN_RESULT_EVT事件处理中,每次搜索到指定设备名称(remote_device_name)的设备时会停止扫描。
尝试修改:只保留条件1,注释掉上述条件2里的“esp_ble_gap_stop_scanning();”
编译运行结果:一台设备都无法连接,内部看门狗溢出导致任务重启。
转换思路,搜索“start_scan”,查看程序中哪些条件成立会启动扫描。启动扫描的条件如下:
● ESP_GATTC_WRITE_CHAR_EVT(GATT特征写入操作完成事件)中不管是否成功写入特征值,均会调用start_scan()启动蓝牙扫描;
● ESP_GATTC_DISCONNECT_EVT(ble物理连接断开事件)中调用start_scan()启动蓝牙扫描;
从程序上看,程序开机后会扫描广播,扫描到指定设备名称的设备后进行连接,连接时暂停扫描,写特征值完成后再重新启动扫描。查看程序基本确定是因为ESP_GATTC_WRITE_DESCR_EVT事件处理中,注释掉了写特征值部分程序,未进行写特征值操作,因此程序未能进入ESP_GATTC_WRITE_CHAR_EVT事件处理程序,也就未能重新开启扫描。
尝试修改:考虑到当前配合测试的是采购的现成蓝牙从机设备,未提供相关特征值操作说明,因此无法确定自行写入特征值是否会影响其正常工作,因此还是决定注释掉ESP_GATTC_WRITE_DESCR_EVT事件处理中的写特征值部分程序,然后将start_scan()从ESP_GATTC_WRITE_CHAR_EVT事件处理中挪到ESP_GATTC_WRITE_DESCR_EVT事件处理中。
编译运行结果:第一个蓝牙从机设备开机后显示连接成功,第二个蓝牙从机设备无法连接成功,debug信息中出现了多次异常信息“ task_wdt: Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:”,如下图所示:
发现每次发生的位置基本在两个log附近,查找资料,在两个log之前加入vTaskDelay(1);运行则不再出现task_wdt异常提示。
虽然不再出现task_wdt异常提示,但是第二个蓝牙从机设备依旧无法连接。查看debug信息,比对能正常连接和无法正常连接的搜索部分信息,发现区别如下图所示。
对应到程序部分,发现问题。实验中配合测试的蓝牙从机模块设备名称均相同,因此“static const char remote_device_name[3][20] = {“YX-BLE-Tag”, “YX-BLE-Tag”, “YX-BLE-Tag”};”写入的指定设备名称均相同,导致下方程序在做判断时,第一个条件永远符合,当第一个从机设备连接成功conn_device_==true时,直接跳出了该部分程序。
调整条件判断逻辑如下:
if (adv_name != NULL) {
//当前获取的设备名称及长度与连接并记录的第一个server比对相同
if (conn_device_a == false) {
if (strlen(remote_device_name[0]) == adv_name_len && strncmp((char *)adv_name, remote_device_name[0], adv_name_len) == 0) {
conn_device_a=true;
ESP_LOGI(GATTC_TAG, "Searched device %s", remote_device_name[0]);
esp_ble_gap_stop_scanning();//停止扫描广播
//打开一个直接连接或添加一个后台自动连接
esp_ble_gattc_open(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, scan_result->scan_rst.bda, scan_result->scan_rst.ble_addr_type, true);
Isconnecting = true;
}
break;
}
else if (conn_device_b == false) {
if (strlen(remote_device_name[1]) == adv_name_len && strncmp((char *)adv_name, remote_device_name[1], adv_name_len) == 0) {
conn_device_b=true;
ESP_LOGI(GATTC_TAG, "Searched device %s", remote_device_name[1]);
esp_ble_gap_stop_scanning();
esp_ble_gattc_open(gl_profile_tab[PROFILE_B_APP_ID].gattc_if, scan_result->scan_rst.bda, scan_result->scan_rst.ble_addr_type, true);
Isconnecting = true;
}
break;
}
else if (conn_device_c == false) {
if (strlen(remote_device_name[2]) == adv_name_len && strncmp((char *)adv_name, remote_device_name[2], adv_name_len) == 0) {
conn_device_c=true;
ESP_LOGI(GATTC_TAG, "Searched device %s", remote_device_name[2]);
esp_ble_gap_stop_scanning();
esp_ble_gattc_open(gl_profile_tab[PROFILE_C_APP_ID].gattc_if, scan_result->scan_rst.bda, scan_result->scan_rst.ble_addr_type, true);
Isconnecting = true;
}
break;
}
}
ESP_GATTC_NOTIFY_EVT事件中增加一个log打印mac地址,便于debug时观察上报的数据来源。
测试结果:3台蓝牙从机设备均连接成功。且notify上报成功。
基本结论:
1)原始的gattc_multi_connect示例只适用于连接3个设备名称不同的蓝牙从机设备。若要连接3个设备名称相同的设备则需要调整gap回调函数中ESP_GAP_BLE_SCAN_RESULT_EVT事件处理程序中的条件判断逻辑;
2)原始的gattc_multi_connect示例在启动扫描部分逻辑上是程序开机后会扫描广播,扫描到指定设备名称的设备后进行连接,连接时暂停扫描,写特征值完成后再重新启动扫描。因配合测试的蓝牙从机设备是现成采购的,未提供特征值相关操作说明,因此不对其进行特征值写入,程序针对这块逻辑上需要略作调整;
3) 原始的gattc_multi_connect示例其扫描广播周期是30s,30s之后就会停止扫描。实际应用中应该是若是没有连满3个从机设备则一直处于扫描状态。此问题暂不做修改。
最后补充:gattc_multi_connect示例程序中最多可连接3个蓝牙从机设备,分别写了3个事件处理程序,gattc_profile_a_event_handler/gattc_profile_b_event_handler/gattc_profile_c_event_handler。所有相关修改都需要3个处理程序同步修改。实际上,当连接的蓝牙从机设备为同一类型设备,且所有处理程序均相同时,可以合并成同一个事件处理程序以实现优化程序及代码的目的。
下一篇《ESP32从0到1》之MQTT与阿里iot通信,记得关注哦!
欢迎关注并留言
文章同步发表于公众号"IT搬砖客",欢迎关注并留言。