ESP32学习笔记(30)——BLE GATT服务端自定义服务和特征

发布者:PositiveEnergy最新更新时间:2025-02-27 来源: jianshu关键字:ESP32  BLE 手机看文章 扫描二维码
随时随地手机看文章

一、简介

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



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


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

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

大部分情况下外围设备通过广播自己来让中心设备发现自己,并建立 GATT 连接,从而进行更多的数据交换。

也有些情况是不需要连接的,只要外设广播自己的数据即可,用这种方式主要目的是让外围设备,把自己的信息发送给多个中心设备。

1.2 通用属性协议(GATT)

GATT是用Attribute Protocal(属性协议)定义的一个service(服务)框架。这个框架定义了Services以及它们的Characteristics的格式和规程。规程就是定义了包括发现、读、写、通知、指示以及配置广播的characteristics。

为实现配置文件(Profile)的设备定义了两种角色:Client(客户端)、Server(服务器)。esp32的ble一般就处于Server模式。

一旦两个设备建立了连接,GATT就开始发挥效用,同时意味着GAP协议管理的广播过程结束了。

1.2.1 Profile(规范)

profile 可以理解为一种规范,建立的蓝牙应用任务,蓝牙任务实际上分为两类:标准蓝牙任务规范 profile(公有任务),非标准蓝牙任务规范 profile(私有任务)。

  • 标准蓝牙任务规范 profile:指的是从蓝牙特别兴趣小组 SIG 的官网上已经发布的 GATT 规范列表,包括警告通知(alert notification),血压测量(blood pressure),心率(heart rate),电池(battery)等等。它们都是针对具体的低功耗蓝牙的应用实例来设计的。目前蓝牙技术联盟还在不断的制定新的规范,并且发布。

  • 非标准蓝牙任务规范 profile:指的是供应商自定义的任务,在蓝牙 SIG 小组内未定义的任务规范。

1.2.2 Service(服务)

service 可以理解为一个服务,在 BLE 从机中有多个服务,例如:电量信息服务、系统信息服务等;
每个 service 中又包含多个 characteristic 特征值;
每个具体的 characteristic 特征值才是 BLE 通信的主题,比如当前的电量是 80%,电量的 characteristic 特征值存在从机的 profile 里,这样主机就可以通过这个 characteristic 来读取 80% 这个数据。
GATT 服务一般包含几个具有相关的功能,比如特定传感器的读取和设置,人机接口的输入输出。组织具有相关的特性到服务中既实用又有效,因为它使得逻辑上和用户数据上的边界变得更加清晰,同时它也有助于不同应用程序间代码的重用。

1.2.3 Characteristic(特征)

characteristic 特征,BLE 主从机的通信均是通过 characteristic 来实现,可以理解为一个标签,通过这个标签可以获取或者写入想要的内容。

1.2.4 UUID(通用唯一识别码)

uuid 通用唯一识别码,我们刚才提到的 service 和 characteristic 都需要一个唯一的 uuid 来标识;
每个从机都会有一个 profile,不管是自定义的 simpleprofile,还是标准的防丢器 profile,他们都是由一些 service 组成,每个 service 又包含了多个 characteristic,主机和从机之间的通信,均是通过characteristic来实现。

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 及其与控制器之间的关系如下图:

1.5 ESP32的GATT服务器服务表示例

使用类似表格的数据结构来定义服务器服务和特性,因此,它展示了一种定义服务器的实用方法功能集中在一处,而不是一一添加服务和特性。


二、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


以下 GATT 接口位于 bt/host/bluedroid/api/include/api/esp_bt_main.h 和 bt/host/bluedroid/api/include/api/esp_gatts_api.h。


2.4 esp_bluedroid_init

2.5 esp_bluedroid_enable

2.6 esp_ble_gatts_register_callback

2.7 esp_ble_gatts_app_register

2.8 esp_ble_gatts_create_service

2.9 esp_ble_gatts_add_char

2.10 esp_ble_gatts_add_char_descr

2.11 esp_ble_gatts_start_service

2.12 esp_ble_gatts_send_indicate

2.13 esp_ble_gatts_send_response

2.14 esp_ble_gatts_get_attr_value

三、蓝牙4.0通信实现过程

扫描蓝牙BLE终端设备,对应esp32就是广播给大家供扫描


连接蓝牙BLE终端设备,pad扫描到后去连接


启动服务发现,连接到esp32后获取相应的服务。

连接成功后,我们就要去寻找我们所需要的服务,这里需要先启动服务发现。


获取Characteristic

之前我们说过,我们的最终目的就是获取Characteristic来进行通信,正常情况下,我们可以从硬件工程师那边得到serviceUUID和characteristicUUID,也就是我们所比喻的班级号和学号,以此来获得我们的characteristic。


开始通信

我们在得到Characteristic后,就可以开始读写操作进行通信了。

a. 对于读操作来说,读取BLE终端设备返回的数据会通过回调方法mGattCallback中的onCharacteristicChanged函数返回。

b. 对于写操作来说,可以通过向Characteristic写入指令以此来达到控制BLE终端设备的目的


四、Demo程序GATT启动流程

使用 esp-idfexamplesbluetoothbluedroidblegatt_server_service_table 中的例程


.........//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(GATTS_TABLE_TAG, '%s enable controller failed: %s', __func__, esp_err_to_name(ret));

        return;

    }


    //使能蓝牙控制器,mode是蓝牙模式,如果想要动态改变蓝牙模式不能直接调用该函数,

    //应该先用disable关闭蓝牙再使用该API来改变蓝牙模式

    ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);

    if (ret) {

        ESP_LOGE(GATTS_TABLE_TAG, '%s enable controller failed: %s', __func__, esp_err_to_name(ret));

        return;

    }

    //初始化蓝牙并分配系统资源,它应该被第一个调用

    /*

    蓝牙栈bluedroid stack包括了BT和BLE使用的基本的define和API

    初始化蓝牙栈以后并不能直接使用蓝牙功能,

    还需要用FSM管理蓝牙连接情况

    */

    ret = esp_bluedroid_init();

    if (ret) {

        ESP_LOGE(GATTS_TABLE_TAG, '%s init bluetooth failed: %s', __func__, esp_err_to_name(ret));

        return;

    }

    //使能蓝牙栈

    ret = esp_bluedroid_enable();

    if (ret) {

        ESP_LOGE(GATTS_TABLE_TAG, '%s enable bluetooth failed: %s', __func__, esp_err_to_name(ret));

        return;

    }


    //建立蓝牙的FSM(有限状态机

    //这里使用回调函数来控制每个状态下的响应,需要将其在GATT和GAP层的回调函数注册

    /*gatts_event_handler和gap_event_handler处理蓝牙栈可能发生的所有情况,达到FSM的效果*/

    ret = esp_ble_gatts_register_callback(gatts_event_handler);

    if (ret){

        ESP_LOGE(GATTS_TAG, 'gatts register error, error code = %x', ret);

        return;

    }

    ret = esp_ble_gap_register_callback(gap_event_handler);

    if (ret){

        ESP_LOGE(GATTS_TAG, 'gap register error, error code = %x', ret);

        return;

    }


    //下面创建了BLE GATT服务A,相当于1个独立的应用程序

    ret = esp_ble_gatts_app_register(PROFILE_A_APP_ID);

    if (ret){

        ESP_LOGE(GATTS_TAG, 'gatts app register error, error code = %x', ret);

        return;

    }

    //下面创建了BLE GATT服务B,相当于1个独立的应用程序

    ret = esp_ble_gatts_app_register(PROFILE_B_APP_ID);

    if (ret){

        ESP_LOGE(GATTS_TAG, 'gatts app register error, error code = %x', ret);

        return;

    }

    /*

    设置了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(GATTS_TAG, 'set local  MTU failed, error code = %x', local_mtu_ret);

    }.......


五、服务数据结构体设置

一个GATT 服务器应用程序架构(由Application Profiles组织起来)如下:

每个Profile定义为一个结构体,结构体成员依赖于该Application Profile 实现的services服务和characteristic特征。结构体成员还包括GATT interface(GATT 接口)、Application ID(应用程序ID)和处理profile事件的回调函数。


每个profile包括GATT interface(GATT 接口)、Application ID(应用程序ID)、 Connection ID(连接ID)、Service Handle(服务句柄)、Service ID(服务ID)、Characteristic handle(特征句柄)、Characteristic UUID(特征UUID)、ATT权限、Characteristic Properties、描述符句柄、描述符UUID。


如果Characteristic支持通知(notifications)或指示(indicatons),它就必须是实现CCCD(Client Characteristic Configuration Descriptor)----这是额外的ATT。描述符有一个句柄和UUID。如:


struct gatts_profile_inst {

    esp_gatts_cb_t gatts_cb;       //GATT的回调函数

    uint16_t gatts_if;             //GATT的接口

    uint16_t app_id;               //应用的ID

    uint16_t conn_id;              //连接的ID

    uint16_t service_handle;       //服务Service句柄

    esp_gatt_srvc_id_t service_id; //服务Service ID

    uint16_t char_handle;          //特征Characteristic句柄

    esp_bt_uuid_t char_uuid;       //特征Characteristic的UUID

    esp_gatt_perm_t perm;          //特征属性Attribute 授权

    esp_gatt_char_prop_t property; //特征Characteristic的特性

    uint16_t descr_handle;         //描述descriptor句柄

    esp_bt_uuid_t descr_uuid;      //描述descriptorUUID    };

配置文件Application Profile存储在heart_rate_profile_tab数组中,由于本示例中只有一个配置文件,因此一个元素存储在数组中,索引为零,如HEART_PROFILE_APP_IDX。此外,还初始化了配置文件事件处理程序回调函数gatts_profile_event_handler。GATT 服务端上的不同应用程序使用不同的接口,由 gatts_if 参数表示。对于初始化,此参数设置为ESP_GATT_IF_NONE,这意味着应用程序配置文件尚未链接到任何客户端。


/* One gatt-based profile one app_id and one gatts_if, this array will store the gatts_if returned by ESP_GATTS_REG_EVT */static struct gatts_profile_inst gl_profile_tab[PROFILE_NUM] = {

    [PROFILE_A_APP_ID] = {

        .gatts_cb = gatts_profile_a_event_handler,

        .gatts_if = ESP_GATT_IF_NONE,       /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */

    },

    [PROFILE_B_APP_ID] = {

        .gatts_cb = gatts_profile_b_event_handler,                   /* This demo does not implement, similar as profile A */

        .gatts_if = ESP_GATT_IF_NONE,       /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */

    },};

这是两个元素的数组。可以用Application ID来注册Application Profiles,Application ID是由应用程序分配的用来标识每个Profile。 通过这种方法,可以在一个Server中拥有多个Application Profile。


esp_ble_gatts_app_register (PROFILE_A_APP_ID);esp_ble_gatts_app_register (PROFILE_B_APP_ID);


六、GATT事件处理程序

其作用就是建立了蓝牙GATT的FSM(有限状态机),callback回调函数处理从BLE堆栈推送到应用程序的所有事件。


回调函数的参数:


event: esp_gatts_cb_event_t 这是一个枚举类型,表示调用该回调函数时的事件(或蓝牙的状态)


gatts_if: esp_gatt_if_t (uint8_t) 这是GATT访问接口类型,通常在GATT客户端上不同的应用程序用不同的gatt_if(不同的Application profile对应不同的gatts_if) ,调用esp_ble_gatts_app_register()时,注册Application profile 就会有一个gatts_if。


param: esp_ble_gatts_cb_param_t 指向回调函数的参数,是个联合体类型,不同的事件类型采用联合体内不同的成员结构体。


static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param){

    /*如果事件是注册事件,则为每个配置文件存储 gatts_if */

    if (event == ESP_GATTS_REG_EVT) {

        if (param->reg.status == ESP_GATT_OK) {

[1] [2] [3] [4] [5]
关键字:ESP32  BLE 引用地址:ESP32学习笔记(30)——BLE GATT服务端自定义服务和特征

上一篇:ESP32学习笔记(31)——BLE带有属性表的GATT服务
下一篇:ESP32学习笔记(29)——BLE iBeacon广播

推荐阅读最新更新时间:2026-03-23 11:21

ESP32学习笔记(26)——BLE GAP从机端广播
一、背景 1.1 低功耗蓝牙(BLE)协议栈 链路层(LL) 控制设备的射频状态,有五个设备状态:待机、广播、扫描、初始化和连接。 广播 为广播数据包,而 扫描 则是监听广播。 GAP通信中角色,中心设备(Central - 主机) 用来扫描和连接 外围设备(Peripheral - 从机)。 大部分情况下外围设备通过广播自己来让中心设备发现自己,并建立 GATT 连接,从而进行更多的数据交换。 也有些情况是不需要连接的,只要外设广播自己的数据即可,用这种方式主要目的是让外围设备,把自己的信息发送给多个中心设备。 1.2 从机广播 从机(外围设备)要被主机连接,那么它就必须先被主机发现。这个时候,从机设备把自身信息以广
[单片机]
ESP32学习笔记(31)——BLE带有属性表的GATT服务
一、简介 1.1 通用属性协议(GATT) GATT是用Attribute Protocal(属性协议)定义的一个service(服务)框架。这个框架定义了Services以及它们的Characteristics的格式和规程。规程就是定义了包括发现、读、写、通知、指示以及配置广播的characteristics。 为实现配置文件(Profile)的设备定义了两种角色:Client(客户端)、Server(服务器)。esp32的ble一般就处于Server模式。 一旦两个设备建立了连接,GATT就开始发挥效用,同时意味着GAP协议管理的广播过程结束了。 1.1.1 Profile(规范) profile 可以理解为一种规范,建立的蓝
[单片机]
ESP32学习笔记(32)——BLE GAP主机端连接
一、背景 1.1 低功耗蓝牙(BLE)协议栈 链路层(LL) 控制设备的射频状态,有五个设备状态:待机、广播、扫描、初始化和连接。 广播 为广播数据包,而 扫描 则是监听广播。 GAP通信中角色,中心设备(Central - 主机) 用来扫描和连接 外围设备(Peripheral - 从机)。 1.2 BLE从初始化到建立连接的过程 外围设备开始广播,发送完一个广播包后T_IFS,开启射频Rx窗口接收来自中心设备的数据包 中心设备扫描到广播,在收取此广播T_IFS后如果开启了中心设备的扫描回复,中心设备将向外设发送回复 外设收取到中心设备的回复,做好接收准备,并返回ACK包 如果ACK包未被中心设备接收到,中心
[单片机]
ESP32学习笔记(33)——BLE GATT客户端发现服务和读写特征值
一、背景 1.1 GATT协议 GATT(Generic Attributes Profile)的缩写,中文是通用属性协议,是已连接的低功耗蓝牙设备之间进行通信的协议。 一旦两个设备建立起了连接,GATT 就开始起作用了,这也意味着,你必需完成前面的GAP协议。 GATT使用了 ATT(Attribute Protocol)协议,ATT 协议把 Service,Characteristic 对应的数据保存在一个查找表中,查找表使用 16bit ID 作为每一项的索引。 GATT定义的多层数据结构简要概括起来就是 服务(Service) 可以包含多个 特征(Characteristic),每个特征包含 属性(Properties)
[单片机]
使用BLE 4.2的系统设计:更快、更安全、更节能
提到家庭和工业自动化、物联网(IoT)、可穿戴设备、人机接口设备(HID)众多应用的无线连接协议时,蓝牙一定是首选。为满足各种应用的需求,蓝牙技术联盟(SIG)对蓝牙规格进行了持续改进。发布4.1版大约一年后, SIG在2014年12月蓝牙发布了蓝牙规范4.2版。新的4.2主要包括三项更新 - 低功耗(LE)数据长度扩展(DLE)、链路层(LL)隐私保护以及安全性加强。这些功能提高了BLE数据带宽、隐私保护和安全性,同时还有助于降低功耗。本系列文章将详细讨论这些功能以及它们如何影响系统性能。 蓝牙低功耗(BLE)协议栈可以分成三个部分: 控制器:协议栈控制器对数据包进行了加密,转换为无线信号发送。在接收时,控制器将对无线信号解
[嵌入式]
使用<font color='red'>BLE</font> 4.2的系统设计:更快、更安全、更节能
车规级BLE方案如何应用在智能汽车上
在汽车上都有哪些成熟应用的无线技术?我们能想到的:收音机,RKE,经典蓝牙、PKE, WIFI,4G等;BLE为何适合在车上应用,一方面BLE很适合汽车ECU的低待机功耗应用场合;另一方面BLE功能基本是手机的标准配置,目前甚至于300多的手机都是标配BLE功能。 一、BLE在汽车上如何应用 采用BLE技术,可以为汽车消费者提供更多的便利。通过智能手机来控制诸多功能,例如解锁汽车、与朋友或家人远程共享钥匙、个性化调整座椅位置以及温度和信息娱乐设置、控制车辆内外照明等。 图1 蓝牙控车应用 采用BLE+硬件加密+软件后台的组合,可以为汽车安全进入提供补充,可在当前被动无钥匙门禁和启动系统之外,增添智能手机汽车门禁选项,增强汽
[嵌入式]
车规级<font color='red'>BLE</font>方案如何应用在智能汽车上
贸泽供应适用于Matter IoT应用的 Espressif Systems ESP32-H2-MINI-1x BLE + IEEE 802.15.4模组
2024年2月22日 – 提供超丰富半导体和电子元器件™的业界知名新品引入 (NPI) 代理商贸泽电子 (Mouser Electronics) 即日起供货Espressif Systems的ESP32-H2-MINI-1x模组 。ESP32-H2-MINI-1x模组是功能强大的通用低功耗蓝牙和IEEE 802.15.4组合模组,经优化兼容Matter。Matter是一种基于IP的行业统一连接协议,可简化IoT应用的开发,还能无缝集成到智能家居、工业自动化、消费电子、智慧农业、医疗保健等各种生态系统中。 贸泽供应的Espressif Systems ESP32-H2-MINI-1x模组采用ESP32-H2片上系统 (So
[物联网]
贸泽供应适用于Matter IoT应用的  Espressif Systems ESP32-H2-MINI-1x <font color='red'>BLE</font> + IEEE 802.15.4模组
使用STM32WB设计BLE应用时影响功耗的内容
功耗是物联网应用当中非常关键的因素,在开发的早期都会对功耗进行评估和测试。那么,如何使用ST提供的工具对动态功耗进行测量呢?针对BLE应用应当如何进行低功耗的设计呢?本篇跟大家一起聊聊该话题。 测量工具 在本文中使用CubeMonitor-Power + Power Shield的组合工具对STM32Nucleo开发板的动态功耗进行测量。 CubeMonitor-Power是ST提供的上位机软件,主要帮助用户动态的测量功耗。用户可以设定采样频率,采样时间,输入电压等。其界面如下: X-NUCLEO-LPM01A是ST提供的一块功耗测量的评估板,它可以配合CubeMonitor-Power上位机软件,对目标板的动态功耗进行测量,
[单片机]
使用STM32WB设计<font color='red'>BLE</font>应用时影响功耗的内容
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved