ESP32作为BLE客户端gatt-client教程

一、介绍

该代码实现了一个低功耗蓝牙 (BLE) 通用属性 (GATT) 客户端,该客户端扫描附近的外围服务器并连接到预定义的服务。然后,客户端搜索可用特征并订阅已知特征,以便接收通知或指示。该示例可以注册应用程序配置文件并初始化一系列事件,这些事件可用于配置通用访问配置文件 (GAP) 参数以及处理扫描、连接到外围设备以及读写特性等事件

二、include

gattc_demo.c 中包含的头文件是:

#include <stdint.h>

#include <string.h>

#include <stdbool.h>

#include <stdio.h>

#include "nvs.h"

#include "nvs_flash.h"

#include "controller.h"

这些是 FreeRTOS 和底层系统组件运行所必需的,包括日志记录功能和将数据存储在非易失性闪存中的库。

#include "bt.h"

#include "esp_gap_ble_api.h"

#include "esp_gattc_api.h"

#include "esp_gatt_defs.h"

#include "esp_bt_main.h"

#include "esp_gatt_common_api.h"

  • bt.h:从主机侧配置 BT 控制器和 VHCI。
  • esp_bt_main.h:初始化并启用 Bluedroid 堆栈。
  • esp_gap_ble_api.h:实现 GAP 配置,如广告、连接参数等。
  • esp_gattc_api.h:实现 GATT Client 配置,如连接外设、搜索服务等。

三、入口函数

main 函数首先初始化非易失性 storage library。该库允许在闪存中保存键值对,并被一些组件(如 Wi-Fi 库)用于保存 SSID 和密码:

esp_err_t ret = nvs_flash_init();

if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {

    ESP_ERROR_CHECK(nvs_flash_erase());

    ret = nvs_flash_init();

}

ESP_ERROR_CHECK( ret );

四、BT 控制器和堆栈初始化

  1. 首先创建一个 BT 控制器配置结构来初始化 BT 控制器,该结构使用宏生成的默认设置命名。BT 控制器在控制器端实现主机控制器接口 (HCI)、链路层 (LL) 和物理层 (PHY)。BT 控制器对用户应用程序不可见,并处理 BLE 堆栈的较低层。控制器配置包括设置 BT 控制器堆栈大小、优先级和 HCI 波特率。创建设置后,BT 控制器将初始化并使用以下功能启用:

esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();

ret = esp_bt_controller_init(&bt_cfg);

  1. 接下来,在 BLE 模式下启用控制器。

ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);

支持四种蓝牙模式:

ESP_BT_MODE_IDLE:蓝牙未运行

ESP_BT_MODE_BLE:BLE 模式

ESP_BT_MODE_CLASSIC_BT:BT Classic 模式

ESP_BT_MODE_BTDM:双模式(BLE + BT Classic)

  1. 初始化 BT 控制器后,Bluedroid 堆栈(包括 BT Classic 和 BLE 的通用定义和 API)将使用以下方法初始化和启用:

ret = esp_bluedroid_init();

ret = esp_bluedroid_enable();

  1. main 函数通过注册 GAP 和 GATT 事件处理程序以及应用程序配置文件来结束,并设置支持的最大 MTU 大小。

    //register the  callback function to the gap module

    ret = esp_ble_gap_register_callback(esp_gap_cb);

    //register the callback function to the gattc module

    ret = esp_ble_gattc_register_callback(esp_gattc_cb);

    ret = esp_ble_gattc_app_register(PROFILE_A_APP_ID);

    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);

    }

  1. GAP 和 GATT 事件处理程序是用于捕获 BLE 堆栈生成的事件并执行函数来配置应用程序参数的函数。此外,事件处理程序还用于处理来自中央的读取和写入事件。GAP 事件处理程序负责扫描和连接到服务器,而 GATT 处理程序管理客户端连接到服务器后发生的事件,例如搜索服务以及写入和读取数据。GAP 和 GATT 事件处理程序使用以下方法注册:

esp_ble_gap_register_callback();

esp_ble_gattc_register_callback();

五、应用程序配置文件

  1. Application Profiles 是一种对专为一个或多个 Server 应用程序设计的功能进行分组的方法。例如,您可以将一个应用程序配置文件连接到心率传感器,将另一个应用程序配置文件连接到温度传感器。每个应用程序配置文件都会创建一个 GATT 接口以连接到其他设备。
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;

};

在此示例中,有一个 Application Profile,其 ID 定义为:

#define PROFILE_NUM 1

#define PROFILE_A_APP_ID 0

Application Profile 存储在结构体数组中,该数组初始化为:gl_profile_tab

/* 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 */

    },

};

Application Profile 表数组的初始化包括定义 Profile 的回调函数。是的。此外,GATT 接口初始化为默认值 。稍后,当注册 Application Profile 时,BLE 堆栈会返回一个 GATT 接口实例,用于该 Application Profile。

 配置文件注册会触发一个事件,该事件由事件处理程序处理。处理程序获取事件返回的 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;

        }

    }

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

/* 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);

}

六、设置 Scan 参数

GATT 客户端通常会扫描附近的服务器,并尝试连接到它们(如果感兴趣)。但是,为了执行扫描,首先需要设置配置参数。这是在注册 Application Profiles 后完成的,因为注册完成后会触发事件。第一次触发此事件时,GATT 事件处理程序会捕获该事件并将其分配给配置文件 A,然后将该事件转发到配置文件 A 的 GATT 事件处理程序。在此事件处理程序中,该事件用于调用函数,该函数将结构实例作为参数。此结构定义为:ESP_GATTC_REG_EVT esp_ble_gap_set_scan_params() ble_scan_params

/// Ble scan parameters

typedef struct {

    esp_ble_scan_type_t     scan_type;              /*!< Scan type */

**    esp_ble_addr_type_t     own_addr_type;          /***!< Owner address type */

**    esp_ble_scan_filter_t   scan_filter_policy;     /***!< Scan filter policy */

**    uint16_t                scan_interval;          /***!< Scan interval. This is defined as the time interval from when the Controller started its last LE scan until it begins the subsequent LE scan.*/

    //Range: 0x0004 to 0x4000

    //Default: 0x0010 (10 ms)

    //Time = N * 0.625 msec

    //Time Range: 2.5 msec to 10.24        seconds

    uint16_t                scan_window;            /*!< Scan window. The duration of the LE scan. LE_Scan_Window shall be less than or equal to LE_Scan_Interval*/

    //Range: 0x0004 to 0x4000                                                            //Default: 0x0010 (10 ms)

    //Time = N * 0.625 msec

    //Time Range: 2.5 msec to 10240 msec

} esp_ble_scan_params_t;

它初始化为:

static esp_ble_scan_params_t ble_scan_params = {

    .scan_type              = BLE_SCAN_TYPE_ACTIVE,

    .own_addr_type          = BLE_ADDR_TYPE_PUBLIC,

    .scan_filter_policy     = BLE_SCAN_FILTER_ALLOW_ALL,

    .scan_interval          = 0x50,

    .scan_window            = 0x30

};

对 BLE 扫描参数进行配置,以便扫描类型处于活动状态(包括读取扫描响应),它是公共类型,允许读取任何广告设备,并且扫描间隔为 100 毫秒(1.25 毫秒 * 0x50)和 60 毫秒(1.25 毫秒 * 0x30)。

扫描值使用以下函数设置:esp_ble_gap_set_scan_params()

case ESP_GATTC_REG_EVT:

        ESP_LOGI(GATTC_TAG, "REG_EVT");

        esp_err_t scan_ret = esp_ble_gap_set_scan_params(&ble_scan_params);

        if (scan_ret){

            ESP_LOGE(GATTC_TAG, "set scan params error, error code = %x", scan_ret);

        }

        break;

七、开始扫描

设置扫描参数后,将触发一个事件,该事件由 GAP 事件处理程序处理。此事件用于开始扫描附近的 GATT 服务器:ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT esp_gap_cb()

    case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: {

        //the unit of the duration is second

        uint32_t duration = 30;

        esp_ble_gap_start_scanning(duration);

        break;

        }

使用函数启动扫描,该函数采用表示连续扫描持续时间(以秒为单位)的参数。扫描期结束后,将触发事件。esp_ble_gap_start_scanning() ESP_GAP_SEARCH_INQ_CMPL_EVT

八、获取扫描结果

扫描结果在与事件一起到达后立即显示,其中包括以下参数:ESP_GAP_BLE_SCAN_RESULT_EVT

我们对事件感兴趣,每次找到新设备时都会调用该事件。我们还对 感兴趣,它在扫描持续时间完成时触发,可用于重新启动扫描过程:ESP_GAP_SEARCH_INQ_RES_EVT  ESP_GAP_SEARCH_INQ_CMPL_EVT

首先,解析设备名称并将其与 中定义的设备名称进行比较。如果它等于我们感兴趣的 GATT 服务器的设备名称,则停止扫描。remote_device_name

九、连接到 GATT 服务器

每次我们从事件收到结果时,代码首先打印远程设备的地址:

ESP_GAP_SEARCH_INQ_RES_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);

为了获取设备名称,我们使用函数,该函数获取存储在 中的广告数据、广告数据的类型和长度,以便从广告数据包帧中提取值。然后打印设备名称。esp_ble_resolve_adv_data() scan_result->scan_rst.ble_adv

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);

最后,如果远程设备名称与我们上面定义的相同,则本地设备将停止扫描并尝试使用该函数打开与远程设备的连接。此函数将应用程序配置文件 GATT 接口、远程服务器地址和布尔值作为参数。

十、配置 MTU 大小

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

十一、发现服务

MTU 配置事件还用于开始发现客户端刚刚连接到的服务器中可用的服务。为了发现服务,使用了该函数。该函数的参数是 GATT 接口、Application Profile 连接 ID 和客户端感兴趣的服务应用程序的 UUID。我们正在寻找的服务定义为:esp_ble_gattc_search_service()

十二、获取特性

此示例实现从预定义服务获取特征数据。我们想要从中获取特征的服务具有 0x00FF 的 UUID,我们感兴趣的特征的 UUID 为 0xFF01:

十三、注册通知

客户端可以注册以在每次特征值更改时从服务器接收通知。在此示例中,我们希望注册使用 UUID 0xff01标识的特征的通知。获取所有特征后,我们检查接收到的特征的属性,然后使用该函数注册通知。函数参数是 GATT 接口、远程服务器的地址以及我们要注册通知的句柄。

### ESP32作为BLE客户端的示例代码及教程 #### 初始化和配置环境 为了使ESP32能够作为一个BLE客户端工作,需要先安装必要的库和支持包。这通常通过Arduino IDE或其他开发平台完成。 ```cpp #include <WiFi.h> #include <BluetoothSerial.h> #if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it. #endif #include <esp_bt.h> #include <esp_gap_ble_api.h> #include <esp_gatts_api.h> #include <esp_gattc_api.h> #include <esp_bt_main.h> #include <esp_bt_device.h> ``` 这段代码展示了如何导入必需的头文件来支持BLE功能[^1]。 #### 设置BLE客户端模式 接下来,在程序启动时设定ESP32进入GATT客户机角色: ```cpp void setup() { Serial.begin(115200); esp_err_t ret; // Initialize NVS — if you wish to use an external flash chip, change this line accordingly. ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); // BLE初始化 esp_bluedroid_status_t bt_stat; bt_stat = esp_bluedroid_get_status(); if(bt_stat==ESP_BLUEDROID_STATUS_UNINITIALIZED){ ret = esp_bluedroid_init(); if (ret != ESP_OK) { Serial.printf("%s initialize bluedroid error\n", __func__); return; } } if(bt_stat!=ESP_BLUEDROID_STATUS_ENABLED){ ret = esp_bluedroid_enable(); if (ret != ESP_OK) { Serial.printf("%s enable bluedroid error\n",__func__); return; } } // 注册GAP回调函数 ret = esp_ble_gap_register_callback(gap_event_handler); if (ret){ Serial.printf("gap register failed, error code = %x\n", ret); return; } // 注册GATT Client回调函数 ret = esp_ble_gattc_register_callback(gattc_event_handler); if (ret){ Serial.printf("gattc register failed, error code = %x\n", ret); return; } } ``` 此部分负责初始化蓝牙协议栈,并注册用于处理各种事件(如发现新的BLE设备、连接状态变化等)的回调函数。 #### 执行扫描操作寻找目标服务 一旦完成了上述准备工作,则可以通过调用API来进行周边设备扫描: ```cpp static void start_scan(void){ uint32_t duration = 3; // Scan for three seconds. /* Start a scan; the application is responsible for stopping it */ esp_ble_gap_start_scanning(duration); } // 在适当的地方调用start_scan() ``` 当找到匹配的目标后,就可以尝试与其建立连接并获取所需的服务/特征信息了。 #### 连接至远程服务器并与之交互 成功建立了与某个特定外设之间的链接之后,便可以进一步探索其提供的服务列表及其下的各个特性。对于感兴趣的数据项还可以发起订阅请求以实时监听来自对方的通知消息。 ```cpp static void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param){ switch(event){ case ESP_GATTC_CONNECT_EVT:{ // 处理连接成功的逻辑... break; } case ESP_GATTC_SEARCH_CMPL_EVT:{ // 查找完成后遍历所有service... break; } default: break; } } ``` 以上就是基于ESP32构建一个简单的BLE GATT客户的概览介绍。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值