本人小白,纯学习记录,在公司做BMS研发,业余学习一下ESP32IDF框架:
首先注意:
- ESP32S3没有SPP协议(SPP协议是串口蓝牙协议)也不支持经典蓝牙,所以说在看IDF例子的时候要特别注意支持设备(通过阅读Readme文档,查看例子支持案例),只要ESP32支持这个功能,但是可以使用服务和特性完成这个功能。
-
低功耗蓝牙通信基于特性和服务,这也是GATT最重要的概念。
-
GATT 是处理数据交互的关系。
-
包括:服务、特征、值、描述。-----牢记
-
- GAP:如何发现设备、建立连接、管理连接、一般是广播的时候使用。
- 包括:属性、UUID、值、权限。----牢记
- GATT协议和ATT协议的基本数据结构是条目:条目由属性、UUID、值、权限构成,GATT 在这个基础上又有了服务、特征、值、描述。
- 如下是5个条目:分别是属性----UUID------属性值------权限
学习ESP32IDF例子-GATT Server Service Table Example
文件路径在:
本文档详细介绍了适用于 ESP32 的 GATT 服务器服务表示例代码的使用方法。 此示例使用表格实现了蓝牙低功耗(BLE)通用属性(GATT)服务器。 例如,使用数据结构来定义服务器服务及其相关特性,比如如图所示的那种情况。 如下的图表所示:因此,它展示了一种在单一位置定义服务器功能的实用方法。 而不是一项一项地增加服务和特性。
意思是直接使用数据表来注册服务,手机APP是属于GATT客户端,ESP32S3作为服务端,客户端是主机只负责扫描和发起扫描请求,ESP32是从机负责广播和扫描响应。
心率服务:
一个心率服务有3个特征;服务和特征都有UUID,UUID可以自定义也可以参考SIG小组的规定(查文档就行了!!)
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "bt.h"
#include "bta_api.h"
#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_bt_defs.h"
#include "esp_bt_main.h"
#include "esp_bt_main.h"
#include “gatts_table_creat_demo.h"
头文件:
这些内容对于 FreeRTOS 及其底层系统组件的正常运行是必需的,包括日志功能以及用于将数据存储在非易失性闪存中的库。我们对 bt.h、esp_bt_main.h、esp_gap_ble_api.h 和 _esp_gatts_api.h_ 这些文件感兴趣,因为它们公开了实现此示例所需的 BLE 应用程序接口。
- bt.h:从主机端实现了蓝牙控制器和 HCI 配置程序。
- esp_bt_main.h:实现了蓝牙堆栈的初始化和启用操作。
- esp_gap_ble_api.h:实现了诸如广告和连接参数等 GAP(通用连接协议)的配置功能。
- esp_gatts_api.h:实现了 GATT 服务器的配置功能,例如创建服务和特征。
enum
{
HRS_IDX_SVC,
HRS_IDX_HR_MEAS_CHAR,
HRS_IDX_HR_MEAS_VAL,
HRS_IDX_HR_MEAS_NTF_CFG,
HRS_IDX_BOBY_SENSOR_LOC_CHAR,
HRS_IDX_BOBY_SENSOR_LOC_VAL,
HRS_IDX_HR_CTNL_PT_CHAR,
HRS_IDX_HR_CTNL_PT_VAL,
HRS_IDX_NB,
};
枚举元素的设置顺序与Heart Rate Profile属性相同,从服务开始,然后是该服务的特征。此外,心率测量特性有一个客户端特性配置(CCC)描述符,该描述符是一个附加属性,用于描述该特性是否启用了通知。稍后在创建实际属性表时,可以使用枚举索引来标识每个元素
- HRS_IDX_SVC:心率服务索引
- HRS_IDX_HR_MEAS_CHAR:心率测量特征索引
- HRS_IDX_HR_MEAS_VAL:心率测量特征值索引
- HRS IDX_HR_MEAS_NTF_CFG:心率测量通知配置(CCC)索引
- HRS_IDX_BOBY_SENSOR_LOC_CHAR:心率体传感器位置特征索引
- HRS_IDX_BOBY_SENSOR_LOC_VAL:心率人体传感器位置特征值索引
- HRS_IDX_HR_CTNL_PT_CHAR:心率控制点特征指数
- HRS_IDX_HR_CTNL_PT_VAL:心率控制点特征值指数
- HRS_IDX_NB:表中元素的数量。
对应关系
void app_main(void)
{
esp_err_t ret;
/* Initialize NVS. 这部分代码最好是只初始化一次 */
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 );
/* Initialize NVS. 这部分代码最好是只初始化一次 */
//释放经典蓝牙的内存
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));
//默认的蓝牙控制器
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;
}
//使能蓝牙控制器
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协议栈
ret = esp_bluedroid_init();
if (ret) {
ESP_LOGE(GATTS_TABLE_TAG, "%s init bluetooth failed: %s", __func__, esp_err_to_name(ret));
return;
}
//使能蓝牙bluedroid协议栈
ret = esp_bluedroid_enable();
if (ret) {
ESP_LOGE(GATTS_TABLE_TAG, "%s enable bluetooth failed: %s", __func__, esp_err_to_name(ret));
return;
}
//注册gatt事件回调函数
ret = esp_ble_gatts_register_callback(gatts_event_handler);
if (ret){
ESP_LOGE(GATTS_TABLE_TAG, "gatts register error, error code = %x", ret);
return;
}
//注册gap事件回调函数
ret = esp_ble_gap_register_callback(gap_event_handler);
if (ret){
ESP_LOGE(GATTS_TABLE_TAG, "gap register error, error code = %x", ret);
return;
}
//注册gap事件回调函数 触发 ESP_GATTS_REG_EVT事件. ESP_APP_ID是一个UUID
ret = esp_ble_gatts_app_register(ESP_APP_ID);
if (ret){
ESP_LOGE(GATTS_TABLE_TAG, "gatts app register error, error code = %x", ret);
return;
}
//此功能会触发 ESP_GATTC_CFG_MTU_EVT 事件。
//您可以在调用此 API 之前先调用 esp_ble_gatt_set_local_mtu 来本地设置所需的 MTU 大小。如果不进//行设置,GATT 通道将使用默认的 MTU 大小(23 字节)。
esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(500);
if (local_mtu_ret){
ESP_LOGE(GATTS_TABLE_TAG, "set local MTU failed, error code = %x", local_mtu_ret);
}
}
可以把这个app_maing改为你的初始化函数,直接调用就行了,可以完成一个初始化的功能。