BLE学习笔记BLE协议中的16位UUID和128位UUID有啥区别?

Posted 架构师李肯

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BLE学习笔记BLE协议中的16位UUID和128位UUID有啥区别?相关的知识,希望对你有一定的参考价值。

【BLE学习笔记】BLE协议中的16位UUID和128位UUID有啥区别?

BLE的UUID有16位和128位之分,你了解过吗?你知道它们的转换吗?

文章目录

1 写在前面

熟悉BLE开发的技术朋友可能都一定知道UUID这个东西,它表示的一种身份标识;可能用于标识某一个服务厂商,也可能用于标识某一个具体的属性服务。

本文通过一个简短的介绍,带你了解下UUID的那些事儿,顺带给大家如何将16位的UUID与128位的UUID互相转换。

2 UUID的简要介绍

蓝牙核心规范制定了两种不同的UUID,一种是基本的128位UUID,一种是代替基本UUID的16位UUID。 所有的蓝牙技术联盟定义UUID共用了一个基本的UUID:
0x0000xxxx-0000-1000-8000-00805F9B34FB
为了进一步简化基本UUID,每一个蓝牙技术联盟定义的属性有一个唯一的16位UUID,以代替上面的基本UUID的‘x’部分。例如,心率测量特性使用0X2A37作为它的16位UUID,因此它完整的128位UUID为:
0x00002A37-0000-1000-8000-00805F9B34FB
虽然蓝牙技术联盟使用相同的基本UUID,但是16位的UUID足够唯一地识别蓝牙技术联盟所定义的各种属性。蓝牙技术联盟所用的基本UUID不能用于任何定制的属性、服务和特性。对于定制的属性,必须使用另外完整的128位UUID。

SoftDevice 根据蓝牙技术联盟定义UUID类似的方式定义UUID:先增加一个特定的基本UUID,再定义一个16位的UUID(类似于一个别名),再加载在基本UUID之上。这种采用为所有的定制属性定义一个共用的基本UUID的方式使得应用变为更加简单,至少在同一服务中更是如此。

3 UUID位数的转换

在实战编程中,我们常常会遇到16位UUID与128位UUID的互相转换,下面将为大家以源码的形式呈现。

注意,下面的代码中,默认代码运行的主机的存储结构是 小端存储

1)16位UUID转128位UUID

#define ATT_UUID_128_LEN 16

void ble_uuid_128_to_16(uint8_t *uuid128, uint16_t *uuid16)

    uint8_t cursor = 12;

    /* get the UUID on 12th to 13th location of UUID */
	*uuid16 = uuid128_base[cursor + 1] << 8 | uuid128_base[cursor];

很简单,其实就是从UUID128的第12-13字节中抽出UUID16,然后再通过出参返回。

2)128位UUID转16位UUID

#define ATT_UUID_128_LEN 16

void ble_uuid_16_to_128(uint8_t *uuid128_base, uint8_t *uuid128, uint16_t uuid16)

    uint8_t cursor = 12;

    /* place the UUID on 12th to 13th location of UUID */
    uuid128_base[cursor] = (uint8_t)(uuid16 & 0xFF);
    uuid128_base[cursor + 1] = (uint8_t)((uuid16 >> 8) & 0xFF);

    /* update value */
    memcpy(&uuid128[0], &uuid128_base[0], ATT_UUID_128_LEN);

很简单,其实就是将UUID16填充到BASE_UUID128的第12-13字节中,然后再通过出参uuid12返回。

4 知识点总结

UUID这个知识点还是比较重要的,我们主要掌握以下几点:

  • UUID的基本含义
  • 128位UUID与16位UUID的转换
  • BLE规范定义的base UUID
  • UUID的表示方法(大、小端存储格式)

[注]:本文部分描述来源于博客:https://www.cnblogs.com/yanye0xff/p/15872646.html

5 更多分享

架构师李肯

架构师李肯全网同名),一个专注于嵌入式IoT领域的架构师。有着近10年的嵌入式一线开发经验,深耕IoT领域多年,熟知IoT领域的业务发展,深度掌握IoT领域的相关技术栈,包括但不限于主流RTOS内核的实现及其移植、硬件驱动移植开发、网络通讯协议开发、编译构建原理及其实现、底层汇编及编译原理、编译优化及代码重构、主流IoT云平台的对接、嵌入式IoT系统的架构设计等等。拥有多项IoT领域的发明专利,热衷于技术分享,有多年撰写技术博客的经验积累,连续多月获得RT-Thread官方技术社区原创技术博文优秀奖,荣获CSDN博客专家CSDN物联网领域优质创作者2021年度CSDN&RT-Thread技术社区之星2022年RT-Thread全球技术大会讲师RT-Thread官方嵌入式开源社区认证专家RT-Thread 2021年度论坛之星TOP4华为云云享专家(嵌入式物联网架构设计师)等荣誉。坚信【知识改变命运,技术改变世界】!

ESP32学习笔记(32)——BLE GAP主机端连接

一、背景

1.1 低功耗蓝牙(BLE)协议栈


链路层(LL) 控制设备的射频状态,有五个设备状态:待机、广播、扫描、初始化和连接。

广播 为广播数据包,而 扫描 则是监听广播。

GAP通信中角色,中心设备(Central - 主机) 用来扫描和连接 外围设备(Peripheral - 从机)

1.2 BLE从初始化到建立连接的过程

  1. 外围设备开始广播,发送完一个广播包后T_IFS,开启射频Rx窗口接收来自中心设备的数据包
  2. 中心设备扫描到广播,在收取此广播T_IFS后如果开启了中心设备的扫描回复,中心设备将向外设发送回复
  3. 外设收取到中心设备的回复,做好接收准备,并返回ACK包
  4. 如果ACK包未被中心设备接收到,中心设备将一直发送回复直到超时,此期间内只要外设返回过一次ACK包就算连接成功
  5. 开始建立通信,后续中心设备将以收取到外设广播的时间为原点,以Connection Interval为周期向外设发送数据包,数据包将具有两个作用:同步两设备时钟建立主从模式通信

外设每收到中心设备的一个包,就会把自己的时序原点重新设置,以和中心设备同步(Service向Client同步)

BLE通信在建立成功后变为主从模式,中心设备Central变为Master,外设Peripheral变为Slave,Slave只能在Master向它发送了一个包以后才能在规定的时间内把自己的数据回传给Master

  1. 连接建立成功
  2. 外设自动停止广播,其他设备无法再查找到该外设
  3. 按照以下时序进行通信,在中心设备发送包的间隔内,外设可以发送多个包

  1. 需要连接断开时只需要中心设备停止连接(停止发送包)即可
  2. 中心设备可以将外设的addr写入Flash或SRAM等存储器件,保持监听此addr,当再次收到外设广播时就可以建立通信。BLE Server设备为了省电,当一段时间内没有数据要发送时,可以不再发送包,双方就会因为连接超时(connection timeout)断开,这时需要中心设备启动监听,这样,当BLE Server设备需要发送数据时,就可以再次连接

1.3 ESP32蓝牙应用结构

蓝牙是⼀种短距通信系统,其关键特性包括鲁棒性、低功耗、低成本等。蓝牙系统分为两种不同的技术:经典蓝牙 (Classic Bluetooth) 和蓝牙低功耗 (Bluetooth Low Energy)。
ESP32 支持双模蓝牙,即同时支持经典蓝牙和蓝牙低功耗。

从整体结构上,蓝牙可分为控制器 (Controller) 和主机 (Host) 两⼤部分:控制器包括了 PHY、Baseband、Link Controller、Link Manager、Device Manager、HCI 等模块,用于硬件接⼝管理、链路管理等等;主机则包括了 L2CAP、SMP、SDP、ATT、GATT、GAP 以及各种规范,构建了向应用层提供接口的基础,方便应用层对蓝牙系统的访问。主机可以与控制器运行在同⼀个宿主上,也可以分布在不同的宿主上。ESP32 可以支持上述两种方式。

1.4 Bluedroid主机架构

在 ESP-IDF 中,使用经过大量修改后的 BLUEDROID 作为蓝牙主机 (Classic BT + BLE)。BLUEDROID 拥有较为完善的功能,⽀持常用的规范和架构设计,同时也较为复杂。经过大量修改后,BLUEDROID 保留了大多数 BTA 层以下的代码,几乎完全删去了 BTIF 层的代码,使用了较为精简的 BTC 层作为内置规范及 Misc 控制层。修改后的 BLUEDROID 及其与控制器之间的关系如下图:

二、API说明

以下控制器和虚拟 HCI 接口位于 bt/include/esp32/include/esp_bt.h

2.1 esp_bt_controller_mem_release

2.2 esp_bt_controller_init

2.3 esp_bt_controller_enable


以下 GAP 接口位于 bt/host/bluedroid/api/include/api/esp_bt_main.hbt/host/bluedroid/api/include/api/esp_gap_ble_api.h

2.4 esp_bluedroid_init

2.5 esp_bluedroid_enable

2.6 esp_ble_gap_register_callback

2.7 esp_ble_gap_set_scan_params

2.8 esp_ble_gap_start_scanning

2.9 esp_ble_gap_stop_scanning

2.10 esp_ble_resolve_adv_data

2.11 esp_ble_gap_disconnect


以下 GATT 接口位于 bt/host/bluedroid/api/include/api/esp_gattc_api.h

2.12 esp_ble_gattc_open

2.13 esp_ble_gattc_close

三、BT控制器和协议栈初始化

使用 esp-idf\\examples\\bluetooth\\bluedroid\\ble\\gatt_client 中的例程

.........
//esp_bt_controller_config_t是蓝牙控制器配置结构体,这里使用了一个默认的参数
    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
    //初始化蓝牙控制器,此函数只能被调用一次,且必须在其他蓝牙功能被调用之前调用
    ret = esp_bt_controller_init(&bt_cfg);
    if (ret) {
        ESP_LOGE(GATTC_TAG, "%s initialize controller failed: %s\\n", __func__, esp_err_to_name(ret));
        return;
    }

    //使能蓝牙控制器,mode是蓝牙模式,如果想要动态改变蓝牙模式不能直接调用该函数,
    //应该先用disable关闭蓝牙再使用该API来改变蓝牙模式
    ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
    if (ret) {
        ESP_LOGE(GATTC_TAG, "%s enable controller failed: %s\\n", __func__, esp_err_to_name(ret));
        return;
    }
    //初始化蓝牙并分配系统资源,它应该被第一个调用
    /*
    蓝牙栈bluedroid stack包括了BT和BLE使用的基本的define和API
    初始化蓝牙栈以后并不能直接使用蓝牙功能,
    还需要用FSM管理蓝牙连接情况
    */
    ret = esp_bluedroid_init();
    if (ret) {
        ESP_LOGE(GATTC_TAG, "%s init bluetooth failed: %s\\n", __func__, esp_err_to_name(ret));
        return;
    }
    //使能蓝牙栈
    ret = esp_bluedroid_enable();
    if (ret) {
        ESP_LOGE(GATTC_TAG, "%s enable bluetooth failed: %s\\n", __func__, esp_err_to_name(ret));
        return;
    }

    //建立蓝牙的FSM(有限状态机)
    //这里使用回调函数来控制每个状态下的响应,需要将其在GATT和GAP层的回调函数注册
    /*esp_gattc_cb和esp_gap_cb处理蓝牙栈可能发生的所有情况,达到FSM的效果*/
    ret = esp_ble_gap_register_callback(esp_gap_cb);
    if (ret){
        ESP_LOGE(GATTC_TAG, "%s gap register failed, error code = %x\\n", __func__, ret);
        return;
    }
    ret = esp_ble_gattc_register_callback(esp_gattc_cb);
    if(ret){
        ESP_LOGE(GATTC_TAG, "%s gattc register failed, error code = %x\\n", __func__, ret);
        return;
    }

    //下面创建了BLE GATT服务A,相当于1个独立的应用程序
    ret = esp_ble_gattc_app_register(PROFILE_A_APP_ID);
    if (ret){
        ESP_LOGE(GATTC_TAG, "%s gattc app register failed, error code = %x\\n", __func__, ret);
    }
    /*
    设置了MTU的值(经过MTU交换,从而设置一个PDU中最大能够交换的数据量)。
    例如:主设备发出一个1000字节的MTU请求,但是从设备回应的MTU是500字节,那么今后双方要以较小的值500字节作为以后的MTU。
    即主从双方每次在做数据传输时不超过这个最大数据单元。
    */
    esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(500);
    if (local_mtu_ret){
        ESP_LOGE(GATTC_TAG, "set local  MTU failed, error code = %x", local_mtu_ret);
    }
.......

四、应用程序配置文件

应用程序配置文件是为一个或多个服务器应用程序设计的功能进行分组的一种方法。例如,可以将应用程序配置文件连接到心率传感器,并将另一个应用程序配置文件连接到温度传感器。每个应用程序配置文件创建一个GATT接口以连接到其他设备。代码中的应用程序配置文件是gattc_profile_inst结构的实例,其定义如下:

struct gattc_profile_inst {
    esp_gattc_cb_t gattc_cb;
    uint16_t gattc_if;
    uint16_t app_id;
    uint16_t conn_id;
    uint16_t service_start_handle;
    uint16_t service_end_handle;
    uint16_t char_handle;
    esp_bd_addr_t remote_bda;
};

结构包括:

  • gattc_cb:GATT客户端回调函数
  • gattc_if:此配置文件的GATT客户端接口号
  • app_id:应用程序配置文件ID号
  • conn_id:连接ID号
  • service_start_handle:服务头部句柄
  • service_end_handle:服务末尾句柄
  • char_handle:特征句柄
  • remote_bda:连接到此客户端的远程设备地址

本例中有一个应用程序配置文件,其ID定义为:

#define PROFILE_NUM 1
#define PROFILE_A_APP_ID 0

应用程序配置文件存储在gl_profile_tab数组中,并分配相应的回调函数gattc_profile_a_event_handler()。GATT 客户端上的不同应用程序使用不同的接口,由 gattc_if 参数表示。对于初始化,此参数设置为ESP_GATT_IF_NONE,这意味着应用程序配置文件尚未链接到任何服务端。

/* One gatt-based profile one app_id and one gattc_if, this array will store the gattc_if returned by ESP_GATTS_REG_EVT */
static struct gattc_profile_inst gl_profile_tab[PROFILE_NUM] = {
		[PROFILE_A_APP_ID] = {.gattc_cb = gattc_profile_event_handler,
								  .gattc_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
    },
};

应用程序配置文件注册触发ESP_GATTC_REG_EVT事件,该事件由esp_gattc_cb()事件处理程序处理。处理程序获取事件返回的GATT接口,并将其存储在profile表中:

static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
{
    ESP_LOGI(GATTC_TAG, "EVT %d, gattc if %d", event, gattc_if);

    /* If event is register event, store the gattc_if for each profile */
    if (event == ESP_GATTC_REG_EVT) {
        if (param->reg.status == ESP_GATT_OK) {
            gl_profile_tab[param->reg.app_id].gattc_if = gattc_if;
        } else {
            ESP_LOGI(GATTC_TAG, "reg app failed, app_id %04x, status %d",
                    param->reg.app_id,
                    param->reg.status);
            return;
        }
    }

最后,回调函数调用gl_profile_tab表中每个配置文件的相应事件处理程序。

/* If the gattc_if equal to profile A, call profile A cb handler,
     * so here call each profile's callback */
    do {
        int idx;
        for (idx = 0; idx < PROFILE_NUM; idx++) {
            if (gattc_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */
                    gattc_if == gl_profile_tab[idx].gattc_if) {
                if (gl_profile_tab[idx].gattc_cb) {
                    gl_profile_tab[idx].gattc_cb(event, gattc_if, param);
                }
            }
        }
    } while (0);
}

五、获取扫描结果

启动扫描吼,扫描结果在到达ESP_GAP_BLE_SCAN_RESULT_EVT事件时立即显示,该事件包括以下参数:

    /**
     * @brief ESP_GAP_BLE_SCAN_RESULT_EVT
     */
    struct ble_scan_result_evt_param {
        esp_gap_search_evt_t search_evt;            /*!< Search event type */
        esp_bd_addr_t bda;                          /*!< Bluetooth device address which has been searched */
        esp_bt_dev_type_t dev_type;                 /*!< Device type */
        esp_ble_addr_type_t ble_addr_type;          /*!< Ble device address type */
        esp_ble_evt_type_t ble_evt_type;            /*!< Ble scan result event type */
        int rssi;                                   /*!< Searched device's RSSI */
        uint8_t  ble_adv[ESP_BLE_ADV_DATA_LEN_MAX + ESP_BLE_SCAN_RSP_DATA_LEN_MAX]; /*!< Received EIR */
        int flag;                                   /*!< Advertising data flag bit */
        int num_resps;                              /*!< Scan result number */
        uint8_t adv_data_len;                       /*!< Adv data length */
        uint8_t scan_rsp_len;                       /*!< Scan response length */
    } scan_rst;                                     /*!< Event parameter of ESP_GAP_BLE_SCAN_RESULT_EVT */

该事件还包括如下所示的子事件列表:

/// Sub Event of ESP_GAP_BLE_SCAN_RESULT_EVT
typedef enum {
    ESP_GAP_SEARCH_INQ_RES_EVT             = 0,      /*!< Inquiry result for a peer device. */
    ESP_GAP_SEARCH_INQ_CMPL_EVT            = 1,      /*!< Inquiry complete. */
    ESP_GAP_SEARCH_DISC_RES_EVT            = 2,      /*!< Discovery result for a peer device. */
    ESP_GAP_SEARCH_DISC_BLE_RES_EVT        = 3,      /*!< Discovery result for BLE GATT based service on a peer device. */
    ESP_GAP_SEARCH_DISC_CMPL_EVT           = 4,      /*!< Discovery complete. */
    ESP_GAP_SEARCH_DI_DISC_CMPL_EVT        = 5,      /*!< Discovery complete. */
    ESP_GAP_SEARCH_SEARCH_CANCEL_CMPL_EVT  = 6,      /*!< Search cancelled */
} esp_gap_search_evt_t;

ESP_GAP_SEARCH_INQ_RES_EVT事件,它在每次找到新设备时都会被调用。ESP_GAP_SEARCH_INQ_CMPL_EVT事件在扫描完成时触发,可以用来重新启动扫描过程:

      case ESP_GAP_BLE_SCAN_RESULT_EVT: {
        esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param;
        switch (scan_result->scan_rst.search_evt) {
	        case ESP_GAP_SEARCH_INQ_RES_EVT:
		        esp_log_buffer_hex(GATTC_TAG, scan_result->scan_rst.bda, 6);
		        ESP_LOGI(GATTC_TAG, "searched Adv Data Len %d, Scan Response Len %d", scan_result->scan_rst.adv_data_len, scan_result->scan_rst.scan_rsp_len);
		        adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len);
		        ESP_LOGI(GATTC_TAG, "searched Device Name Len %d", adv_name_len);
		        esp_log_buffer_char(GATTC_TAG, adv_name, adv_name_len);
		        ESP_LOGI(GATTC_TAG, "\\n");
		        if (adv_name != NULL) {
			        if (strlen(remote_device_name) == adv_name_len && strncmp((char *)adv_name, remote_device_name, adv_name_len) == 0) {
                    ESP_LOGI(GATTC_TAG, "searched device %s\\n", remote_device_name);
                    if (connect == false) {
                        connect = true;
                        ESP_LOGI(GATTC_TAG, "connect to the remote device.");
                        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);
                    }
                }
            }
            break;

首先解析设备名并与remote_device_name中定义的设备名进行比较。如果设备名相符,那么扫描就会停止,并发起连接。

六、发起连接

使用``函数打开到远程设备的连接。该函数将应用程序配置文件GATT接口、远程设备地址和一个布尔值作为参数。布尔值用于指示连接是直接完成的还是后台完成的(自动连接),此时必须将布尔值设置为true以建立连接。请注意,客户端打开到服务端的虚拟连接。虚拟连接返回一个连接ID。虚拟连接是应用程序配置文件和远程服务端之间的连接。由于许多应用程序配置文件可以在一个ESP32上运行,所以可能会有许多虚拟连接打开到同一个远程服务端。还有物理连接,即客户端和服务端之间的实际BLE连接。因此,如果物理连接被esp_ble_gap_disconnect()函数断开,那么所有其他的虚拟连接也会被关闭。在本例中,每个应用程序配置文件使用esp_ble_gattc_open()函数创建到同一服务端的虚拟连接,因此当调用close函数时,只有来自应用程序配置文件的连接是关闭的,而如果调用gap_disconnect函数,则两个连接都将关闭。此外,连接事件被传播到所有的应用程序配置文件,因为它与物理连接相关,而打开的事件只传播到创建虚拟连接的应用程序配置文件。

七、连接事件,配置MTU大小

ATT_MTU定义为客户端与服务端之间发送的任何数据包的最大大小。当客户端连接到服务端时,它通过交换MTU请求和响应协议数据单元(PDU)通知服务端使用哪个MTU大小。这是在连接打开之后完成的。打开连接后,ESP_GATTC_CONNECT_EVT事件被触发。

     case ESP_GATTC_CONNECT_EVT:
        //p_data->connect.status always be ESP_GATT_OK
        ESP_LOGI(GATTC_TAG, "ESP_GATTC_CONNECT_EVT conn_id %d, if %d, status %d", conn_id, gattc_if, p_data->connect.status);
        conn_id = p_data->connect.conn_id;
        gl_profile_tab[PROFILE_A_APP_ID].conn_id = p_data->connect.conn_id;
        memcpy(gl_profile_tab[PROFILE_A_APP_ID].remote_bda, p_data->connect.remote_bda, sizeof(esp_bd_addr_t));
        ESP_LOGI(GATTC_TAG, "REMOTE BDA:");
        esp_log_buffer_hex(GATTC_TAG, gl_profile_tab[PROFILE_A_APP_ID].remote_bda, sizeof(esp_bd_addr_t));
        esp_err_t mtu_ret = esp_ble_gattc_send_mtu_req (gattc_if, conn_id);
        if (mtu_ret){
            ESP_LOGE(GATTC_TAG, "config MTU error, error code = %x", mtu_ret);
        }
        break;

远程设备(服务端)的连接ID和地址存储在Application Profile表中,并打印:

conn_id = p_data->connect.conn_id;
gl_profile_tab[PROFILE_A_APP_ID].conn_id = p_data->connect.conn_id;
memcpy(gl_profile_tab[PROFILE_A_APP_ID].remote_bda, p_data->connect.remote_bda, 
		sizeof(esp_bd_addr_t));
ESP_LOGI(GATTC_TAG, "REMOTE BDA:");
esp_log_buffer_hex(GATTC_TAG, gl_profile_tab[PROFILE_A_APP_ID].remote_bda, 
		sizeof(esp_bd_addr_t));

蓝牙4.0连接的典型MTU大小是23字节。客户端可以使用esp_ble_gattc_send_mtu_req()函数更改MTU的大小,该函数接受GATT接口和连接ID。请求的MTU大小由esp_ble_gatt_set_local_mtu()定义。然后服务端可以接受或者拒绝请求。ESP32支持高达517字节的MTU大小,这是由esp_gattc_api.h中的ESP_GATT_MAX_MTU_SIZE定义的。在本例中,MTU的大小设置为500字节。如果配置失败,返回的错误被打印出来:

esp_err_t mtu_ret = esp_ble_gattc_send_mtu_req (gattc_if, conn_id);
if (mtu_ret){
	ESP_LOGE(GATTC_TAG, "config MTU error, error code = %x", mtu_ret);
}
break;

打开连接会触发ESP_GATTC_OPEN_EVT事件,它用于检查连接的打开是否成功,否则打印错误并退出。

case ESP_GATTC_OPEN_EVT:
        if (param->open.status != ESP_GATT_OK){
            ESP_LOGE(GATTC_TAG, "open failed, status %d", p_data->open.status);
            break;
        }
ESP_LOGI(GATTC_TAG, "open success");

当交换MTU时,ESP_GATTC_CFG_MTU_EVT被触发,在本例中,它用于打印新的MTU大小。

case ESP_GATTC_CFG_MTU_EVT:
        if (param->cfg_mtu.status != ESP_GATT_OK){
            ESP_LOGE(GATTC_TAG,"config mtu failed, error status = %x", param->cfg_mtu.status);
        }
        ESP_LOGI(GATTC_TAG, "ESP_GATTC_CFG_MTU_EVT, Status %d, MTU %d, conn_id %d", param->cfg_mtu.status, param->cfg_mtu.mtu, param->cfg_mtu.conn_id);

八、断开连接

由于许多应用程序配置文件可以在一个ESP32上运行,所以可能会有许多虚拟连接打开到同一个远程服务端。还有物理连接,即客户端和服务端之间的实际BLE连接。因此,如果物理连接被esp_ble_gap_disconnect()函数断开,那么所有其他的虚拟连接也会被关闭。在本例中,每个应用程序配置文件使用esp_ble_gattc_open()函数创建到同一服务端的虚拟连接,因此当调用esp_ble_gattc_close()函数时,只有来自应用程序配置文件的连接是关闭的,而如果调用esp_ble_gap_disconnect()函数,则两个连接都将关闭。


• 由 Leung 写于 2021 年 7 月 9 日

• 参考:ESPIDF开发ESP32学习笔记【经典蓝牙与BLE】
    GATT 客户端示例演练

以上是关于BLE学习笔记BLE协议中的16位UUID和128位UUID有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

蓝牙 BLE 三种 UUID 格式转换

具有 128 位 UUID 的 startLeScan 不适用于本机 Android BLE 实现

如何确保 UUID 对于我的 BLE 服务或特性是唯一的?

Arduino101学习笔记—— 蓝牙BLE

为啥 BLE 服务 UUid 在 android 和 iphone 中不同?

ESP32学习笔记(33)——BLE GATT客户端发现服务和读写特征值