1. ESP32蓝牙模块开发基础
ESP32作为一款功能强大的物联网芯片,其蓝牙功能尤为突出。ESP32支持双模蓝牙(经典蓝牙和低功耗蓝牙BLE),这使得它能够适应各种不同的应用场景。在开始开发前,我们需要先了解一些基础概念。
1.1 蓝牙技术概述
蓝牙技术是一种短距离无线通信技术,工作频段为2.4GHz ISM频段。ESP32支持蓝牙4.2及以上版本,这意味着我们可以利用BLE的低功耗特性来开发各种物联网设备。
蓝牙协议栈分为控制器(Controller)和主机(Host)两部分:
- 控制器:处理底层射频通信
- 主机:实现高层协议和应用接口
ESP32的蓝牙协议栈采用Bluedroid架构,这是Android系统广泛使用的蓝牙协议栈实现。
1.2 ESP32蓝牙开发环境搭建
要开发ESP32蓝牙应用,需要准备以下环境:
- ESP-IDF开发框架(建议使用v4.4及以上版本)
- 合适的开发工具链
- 串口调试工具
安装步骤:
bash复制# 克隆ESP-IDF
git clone --recursive https://github.com/espressif/esp-idf.git
# 设置工具链
cd esp-idf
./install.sh
# 设置环境变量
. ./export.sh
1.3 蓝牙开发基本概念
在ESP32蓝牙开发中,有几个关键概念需要理解:
- GAP(Generic Access Profile):负责设备发现、连接建立和安全设置
- GATT(Generic Attribute Profile):定义服务发现和数据交换的方式
- Service:设备提供的功能集合
- Characteristic:服务中的具体数据点
- Descriptor:特性的附加信息
2. ESP32蓝牙GATT服务器实现
2.1 GATT服务器架构设计
GATT服务器是BLE通信中的核心组件,它负责提供数据和服务。在ESP32上实现GATT服务器需要遵循以下步骤:
- 初始化蓝牙控制器
- 初始化Bluedroid协议栈
- 注册应用回调函数
- 创建服务和特性
- 启动服务
2.2 蓝牙控制器初始化
蓝牙控制器初始化是第一步,示例代码如下:
c复制void ble_init() {
esp_err_t ret;
// 初始化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);
// 释放经典蓝牙内存(仅使用BLE)
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();
ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg));
// 启用BLE模式
ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_BLE));
// 初始化Bluedroid协议栈
ESP_ERROR_CHECK(esp_bluedroid_init());
ESP_ERROR_CHECK(esp_bluedroid_enable());
}
2.3 服务与特性创建
创建服务和特性是GATT服务器的核心部分。下面是一个创建简单服务的示例:
c复制#define GATTS_SERVICE_UUID_TEST_A 0x00FF
#define GATTS_CHAR_UUID_TEST_A 0xFF01
#define GATTS_DESCR_UUID_TEST_A 0x3333
#define GATTS_NUM_HANDLE_TEST_A 4
static void create_service_a() {
esp_bt_uuid_t service_uuid;
service_uuid.len = ESP_UUID_LEN_16;
service_uuid.uuid.uuid16 = GATTS_SERVICE_UUID_TEST_A;
esp_bt_uuid_t char_uuid;
char_uuid.len = ESP_UUID_LEN_16;
char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_TEST_A;
esp_bt_uuid_t descr_uuid;
descr_uuid.len = ESP_UUID_LEN_16;
descr_uuid.uuid.uuid16 = GATTS_DESCR_UUID_TEST_A;
// 创建服务
esp_gatt_srvc_id_t service_id = {
.is_primary = true,
.id = {
.inst_id = 0x00,
.uuid = service_uuid
}
};
esp_err_t ret = esp_ble_gatts_create_service(gatts_if, &service_id, GATTS_NUM_HANDLE_TEST_A);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to create service: %s", esp_err_to_name(ret));
return;
}
// 添加特性
esp_attr_value_t char_value = {
.attr_max_len = 20,
.attr_len = 4,
.attr_value = {0x11, 0x22, 0x33, 0x44}
};
esp_gatt_perm_t perm = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;
esp_gatt_char_prop_t property = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE;
ret = esp_ble_gatts_add_char(service_handle, &char_uuid, perm, property, &char_value, NULL);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to add characteristic: %s", esp_err_to_name(ret));
return;
}
// 添加描述符
ret = esp_ble_gatts_add_char_descr(service_handle, &descr_uuid, perm, NULL, NULL);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to add descriptor: %s", esp_err_to_name(ret));
return;
}
// 启动服务
ret = esp_ble_gatts_start_service(service_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to start service: %s", esp_err_to_name(ret));
return;
}
}
3. 蓝牙通信事件处理
3.1 GAP事件处理
GAP事件处理主要包括设备发现、连接建立和安全设置等。下面是一个典型的GAP事件处理函数:
c复制static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
switch (event) {
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
ESP_LOGI(TAG, "Advertisement data set complete");
// 开始广播
esp_ble_gap_start_advertising(&adv_params);
break;
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) {
ESP_LOGE(TAG, "Advertisement start failed");
} else {
ESP_LOGI(TAG, "Advertisement started successfully");
}
break;
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
ESP_LOGI(TAG, "Advertisement stopped");
break;
case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
ESP_LOGI(TAG, "Connection parameters updated: min_int=%d, max_int=%d, latency=%d, timeout=%d",
param->update_conn_params.min_int,
param->update_conn_params.max_int,
param->update_conn_params.latency,
param->update_conn_params.timeout);
break;
default:
break;
}
}
3.2 GATT事件处理
GATT事件处理主要包括服务发现、特性读写等操作。下面是一个典型的GATT事件处理函数:
c复制static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t *param) {
switch (event) {
case ESP_GATTS_REG_EVT:
ESP_LOGI(TAG, "Service registered, app_id=%d", param->reg.app_id);
// 保存gatts_if接口
gl_profile_tab[param->reg.app_id].gatts_if = gatts_if;
break;
case ESP_GATTS_CREATE_EVT:
ESP_LOGI(TAG, "Service created, handle=%d", param->create.service_handle);
// 保存服务句柄
gl_profile_tab[param->create.service_handle].service_handle = param->create.service_handle;
break;
case ESP_GATTS_ADD_CHAR_EVT:
ESP_LOGI(TAG, "Characteristic added, handle=%d", param->add_char.attr_handle);
// 保存特性句柄
gl_profile_tab[param->add_char.service_handle].char_handle = param->add_char.attr_handle;
break;
case ESP_GATTS_READ_EVT:
handle_read_event(gatts_if, param);
break;
case ESP_GATTS_WRITE_EVT:
handle_write_event(gatts_if, param);
break;
case ESP_GATTS_CONNECT_EVT:
ESP_LOGI(TAG, "Device connected, conn_id=%d", param->connect.conn_id);
// 保存连接ID
gl_profile_tab[param->connect.conn_id].conn_id = param->connect.conn_id;
break;
case ESP_GATTS_DISCONNECT_EVT:
ESP_LOGI(TAG, "Device disconnected");
// 重新开始广播
esp_ble_gap_start_advertising(&adv_params);
break;
default:
break;
}
}
4. 蓝牙数据通信实现
4.1 数据读取处理
当客户端读取特性值时,服务器需要响应读取请求。下面是一个处理读取事件的示例:
c复制static void handle_read_event(esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
ESP_LOGI(TAG, "Read event, handle=%d", param->read.handle);
// 准备响应数据
esp_gatt_rsp_t rsp;
memset(&rsp, 0, sizeof(esp_gatt_rsp_t));
rsp.attr_value.handle = param->read.handle;
// 根据不同的句柄返回不同的数据
if (param->read.handle == char_handle) {
rsp.attr_value.len = 4;
rsp.attr_value.value[0] = 0x01;
rsp.attr_value.value[1] = 0x02;
rsp.attr_value.value[2] = 0x03;
rsp.attr_value.value[3] = 0x04;
} else if (param->read.handle == descr_handle) {
rsp.attr_value.len = 2;
rsp.attr_value.value[0] = 0x00;
rsp.attr_value.value[1] = 0x00;
} else {
ESP_LOGE(TAG, "Unknown handle: %d", param->read.handle);
esp_ble_gatts_send_response(gatts_if, param->read.conn_id,
param->read.trans_id, ESP_GATT_INVALID_HANDLE, NULL);
return;
}
// 发送响应
esp_ble_gatts_send_response(gatts_if, param->read.conn_id,
param->read.trans_id, ESP_GATT_OK, &rsp);
}
4.2 数据写入处理
当客户端写入特性值时,服务器需要处理写入请求。下面是一个处理写入事件的示例:
c复制static void handle_write_event(esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
ESP_LOGI(TAG, "Write event, handle=%d, len=%d", param->write.handle, param->write.len);
// 检查是否是长写入(分片写入)
if (param->write.is_prep) {
// 处理长写入
handle_long_write(gatts_if, param);
return;
}
// 处理普通写入
if (param->write.handle == char_handle) {
// 更新特性值
ESP_LOGI(TAG, "Received new value:");
esp_log_buffer_hex(TAG, param->write.value, param->write.len);
// 如果需要响应
if (param->write.need_rsp) {
esp_ble_gatts_send_response(gatts_if, param->write.conn_id,
param->write.trans_id, ESP_GATT_OK, NULL);
}
} else if (param->write.handle == descr_handle) {
// 处理描述符写入(如CCCD)
if (param->write.len == 2) {
uint16_t value = param->write.value[1] << 8 | param->write.value[0];
ESP_LOGI(TAG, "Descriptor write value: 0x%04x", value);
if (value == 0x0001) {
// 通知使能
ESP_LOGI(TAG, "Notifications enabled");
} else if (value == 0x0002) {
// 指示使能
ESP_LOGI(TAG, "Indications enabled");
} else if (value == 0x0000) {
// 通知/指示禁用
ESP_LOGI(TAG, "Notifications/indications disabled");
}
if (param->write.need_rsp) {
esp_ble_gatts_send_response(gatts_if, param->write.conn_id,
param->write.trans_id, ESP_GATT_OK, NULL);
}
}
} else {
ESP_LOGE(TAG, "Unknown handle: %d", param->write.handle);
if (param->write.need_rsp) {
esp_ble_gatts_send_response(gatts_if, param->write.conn_id,
param->write.trans_id, ESP_GATT_INVALID_HANDLE, NULL);
}
}
}
4.3 通知和指示发送
通知和指示是BLE中服务器主动向客户端发送数据的两种方式:
c复制void send_notification(esp_gatt_if_t gatts_if, uint16_t conn_id, uint16_t char_handle) {
uint8_t value[20];
for (int i = 0; i < sizeof(value); i++) {
value[i] = i % 256;
}
esp_err_t ret = esp_ble_gatts_send_indicate(gatts_if, conn_id, char_handle,
sizeof(value), value, false);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to send notification: %s", esp_err_to_name(ret));
}
}
void send_indication(esp_gatt_if_t gatts_if, uint16_t conn_id, uint16_t char_handle) {
uint8_t value[20];
for (int i = 0; i < sizeof(value); i++) {
value[i] = 0xFF - (i % 256);
}
esp_err_t ret = esp_ble_gatts_send_indicate(gatts_if, conn_id, char_handle,
sizeof(value), value, true);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to send indication: %s", esp_err_to_name(ret));
}
}
5. 蓝牙连接参数优化
5.1 连接参数设置
BLE连接参数对功耗和性能有重要影响。以下是设置连接参数的示例:
c复制void update_connection_params(esp_bd_addr_t remote_bda) {
esp_ble_conn_update_params_t conn_params = {
.bda = {0}, // 将被覆盖
.min_int = 0x10, // 20ms (0x10 * 1.25ms)
.max_int = 0x20, // 40ms (0x20 * 1.25ms)
.latency = 0, // 无延迟
.timeout = 400 // 4s (400 * 10ms)
};
memcpy(conn_params.bda, remote_bda, sizeof(esp_bd_addr_t));
esp_err_t ret = esp_ble_gap_update_conn_params(&conn_params);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to update connection parameters: %s", esp_err_to_name(ret));
}
}
5.2 广播参数设置
广播参数决定了设备被发现的方式和频率:
c复制void set_advertisement_params() {
esp_ble_adv_params_t adv_params = {
.adv_int_min = 0x20, // 32 * 0.625ms = 20ms
.adv_int_max = 0x40, // 64 * 0.625ms = 40ms
.adv_type = ADV_TYPE_IND,
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
.channel_map = ADV_CHNL_ALL,
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};
esp_err_t ret = esp_ble_gap_config_adv_data(&adv_data);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to config adv data: %s", esp_err_to_name(ret));
return;
}
ret = esp_ble_gap_start_advertising(&adv_params);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to start advertising: %s", esp_err_to_name(ret));
}
}
6. 蓝牙安全设置
6.1 配对和绑定
为了提高通信安全性,可以实现配对和绑定功能:
c复制void set_security() {
// 设置IO能力
esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;
esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
// 设置认证需求
uint8_t auth_req = ESP_LE_AUTH_REQ_SC_MITM_BOND;
esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &auth_req, sizeof(uint8_t));
// 设置密钥大小
uint8_t key_size = 16;
esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, sizeof(uint8_t));
// 设置初始化密钥分发
uint8_t init_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &init_key, sizeof(uint8_t));
// 设置响应密钥分发
uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &rsp_key, sizeof(uint8_t));
}
6.2 安全事件处理
安全相关事件需要特别处理:
c复制static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
switch (event) {
// ... 其他事件处理
case ESP_GAP_BLE_SEC_REQ_EVT:
ESP_LOGI(TAG, "Security request received");
esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
break;
case ESP_GAP_BLE_AUTH_CMPL_EVT:
if (param->ble_security.auth_cmpl.success) {
ESP_LOGI(TAG, "Authentication success");
} else {
ESP_LOGE(TAG, "Authentication failed");
}
break;
case ESP_GAP_BLE_KEY_EVT:
ESP_LOGI(TAG, "Key event, key_type=%d", param->ble_security.ble_key.key_type);
break;
default:
break;
}
}
7. 蓝牙开发常见问题与解决方案
7.1 常见编译错误
-
未定义引用错误:
- 原因:缺少必要的组件依赖
- 解决:在menuconfig中启用蓝牙组件
bash复制idf.py menuconfig # Component config -> Bluetooth -> Enable Bluetooth -
内存不足错误:
- 原因:蓝牙协议栈占用内存较大
- 解决:优化内存使用,或增加堆大小
c复制// 在sdkconfig中增加堆大小 CONFIG_ESP32_PHY_MAC_BB_PD=y CONFIG_BTDM_CTRL_BLE_MAX_CONN=3
7.2 运行时常见问题
-
广播无法启动:
- 检查蓝牙控制器是否初始化成功
- 确认广播参数设置正确
- 检查是否有其他蓝牙设备干扰
-
连接不稳定:
- 优化连接参数(间隔、延迟、超时)
- 检查天线设计和RF环境
- 确保电源稳定
-
数据吞吐量低:
- 减小连接间隔
- 使用更高效的ATT MTU大小
- 考虑使用数据聚合
7.3 调试技巧
-
启用详细日志:
c复制// 在app_main()开始处添加 esp_log_level_set("*", ESP_LOG_DEBUG); -
使用蓝牙嗅探器:
- Ellisys、Frontline等专业设备
- nRF Sniffer等低成本方案
-
功耗优化技巧:
- 合理设置连接参数
- 在空闲时进入低功耗模式
- 批量发送数据而非频繁小包
8. 蓝牙应用实例
8.1 蓝牙串口透传
实现一个简单的蓝牙串口透传功能:
c复制#define BLE_UART_SERVICE_UUID 0xFFE0
#define BLE_UART_CHAR_UUID 0xFFE1
static uint16_t uart_service_handle;
static uint16_t uart_char_handle;
static void create_uart_service() {
esp_bt_uuid_t service_uuid = {
.len = ESP_UUID_LEN_16,
.uuid.uuid16 = BLE_UART_SERVICE_UUID
};
esp_bt_uuid_t char_uuid = {
.len = ESP_UUID_LEN_16,
.uuid.uuid16 = BLE_UART_CHAR_UUID
};
// 创建服务
esp_gatt_srvc_id_t service_id = {
.is_primary = true,
.id = {
.inst_id = 0x00,
.uuid = service_uuid
}
};
esp_err_t ret = esp_ble_gatts_create_service(gatts_if, &service_id, 4);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to create UART service");
return;
}
// 添加特性
esp_attr_value_t char_value = {
.attr_max_len = 20,
.attr_len = 0,
.attr_value = {0}
};
esp_gatt_perm_t perm = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE;
esp_gatt_char_prop_t property = ESP_GATT_CHAR_PROP_BIT_READ |
ESP_GATT_CHAR_PROP_BIT_WRITE |
ESP_GATT_CHAR_PROP_BIT_NOTIFY;
ret = esp_ble_gatts_add_char(uart_service_handle, &char_uuid, perm, property, &char_value, NULL);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to add UART characteristic");
return;
}
// 启动服务
ret = esp_ble_gatts_start_service(uart_service_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to start UART service");
}
}
static void handle_uart_write(esp_ble_gatts_cb_param_t *param) {
if (param->write.handle == uart_char_handle) {
// 将接收到的数据通过UART发送出去
uart_write_bytes(UART_NUM_0, (const char *)param->write.value, param->write.len);
if (param->write.need_rsp) {
esp_ble_gatts_send_response(gatts_if, param->write.conn_id,
param->write.trans_id, ESP_GATT_OK, NULL);
}
}
}
void send_uart_data_over_ble(const uint8_t *data, size_t length) {
if (length > 20) {
length = 20; // 限制为ATT MTU大小
}
esp_ble_gatts_send_indicate(gatts_if, conn_id, uart_char_handle, length, data, false);
}
8.2 蓝牙传感器数据采集
实现一个通过蓝牙传输传感器数据的应用:
c复制#define SENSOR_SERVICE_UUID 0x181A // Environmental Sensing Service
#define TEMP_CHAR_UUID 0x2A6E // Temperature
#define HUMID_CHAR_UUID 0x2A6F // Humidity
static uint16_t sensor_service_handle;
static uint16_t temp_char_handle;
static uint16_t humid_char_handle;
static void create_sensor_service() {
// 创建环境感知服务
esp_bt_uuid_t service_uuid = {
.len = ESP_UUID_LEN_16,
.uuid.uuid16 = SENSOR_SERVICE_UUID
};
esp_gatt_srvc_id_t service_id = {
.is_primary = true,
.id = {
.inst_id = 0x00,
.uuid = service_uuid
}
};
esp_err_t ret = esp_ble_gatts_create_service(gatts_if, &service_id, 6);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to create sensor service");
return;
}
// 添加温度特性
esp_bt_uuid_t temp_uuid = {
.len = ESP_UUID_LEN_16,
.uuid.uuid16 = TEMP_CHAR_UUID
};
esp_attr_value_t temp_value = {
.attr_max_len = 4,
.attr_len = 2,
.attr_value = {0} // 初始值为0
};
ret = esp_ble_gatts_add_char(sensor_service_handle, &temp_uuid,
ESP_GATT_PERM_READ,
ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_NOTIFY,
&temp_value, NULL);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to add temperature characteristic");
return;
}
// 添加湿度特性
esp_bt_uuid_t humid_uuid = {
.len = ESP_UUID_LEN_16,
.uuid.uuid16 = HUMID_CHAR_UUID
};
esp_attr_value_t humid_value = {
.attr_max_len = 4,
.attr_len = 2,
.attr_value = {0} // 初始值为0
};
ret = esp_ble_gatts_add_char(sensor_service_handle, &humid_uuid,
ESP_GATT_PERM_READ,
ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_NOTIFY,
&humid_value, NULL);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to add humidity characteristic");
return;
}
// 启动服务
ret = esp_ble_gatts_start_service(sensor_service_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to start sensor service");
}
}
void update_sensor_data(float temperature, float humidity) {
// 温度转换为IEEE-11073格式
int16_t temp_value = (int16_t)(temperature * 100);
uint8_t temp_data[2] = {temp_value & 0xFF, (temp_value >> 8) & 0xFF};
// 湿度转换为百分比(0.01%单位)
uint16_t humid_value = (uint16_t)(humidity * 100);
uint8_t humid_data[2] = {humid_value & 0xFF, (humid_value >> 8) & 0xFF};
// 更新特性值
esp_ble_gatts_set_attr_value(temp_char_handle, 2, temp_data);
esp_ble_gatts_set_attr_value(humid_char_handle, 2, humid_data);
// 发���通知
esp_ble_gatts_send_indicate(gatts_if, conn_id, temp_char_handle, 2, temp_data, false);
esp_ble_gatts_send_indicate(gatts_if, conn_id, humid_char_handle, 2, humid_data, false);
}
9. 蓝牙性能优化
9.1 提高数据吞吐量
-
增大ATT MTU:
c复制// 在连接建立后协商更大的MTU esp_ble_gatt_set_local_mtu(512); // 设置为最大支持的值 -
使用数据聚合:
- 将多个小数据包合并为一个大包发送
- 减少协议开销
-
优化连接参数:
- 减小连接间隔
- 减少延迟
9.2 降低功耗
-
选择合适的连接参数:
- 根据应用需求平衡响应时间和功耗
- 典型低功耗设置:
c复制.min_int = 0x20, // 40ms .max_int = 0x40, // 80ms .latency = 4, // 跳过4个连接事件 .timeout = 800 // 8s
-
合理使用广播:
- 在不需要连接时使用非连接广播
- 设置适当的广播间隔
-
电源管理:
- 在空闲时进入低功耗模式
- 动态调整CPU频率
10. 蓝牙与其他技术的结合
10.1 蓝牙与Wi-Fi共存
ESP32同时支持蓝牙和Wi-Fi,但需要合理配置以避免干扰:
c复制void init_wifi_bt_coex() {
// 设置Wi-Fi和蓝牙共存模式
esp_coex_preference_t coex_pref = ESP_COEX_PREFER_BALANCED;
esp_err_t ret = esp_wifi_set_coex_preference(&coex_pref);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set coexistence preference");
}
// 初始化Wi-Fi
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&cfg);
esp_wifi_set_storage(WIFI_STORAGE_RAM);
esp_wifi_set_mode(WIFI_MODE_STA);
esp_wifi_start();
// 初始化蓝牙
ble_init();
}
10.2 蓝牙与Mesh网络
ESP32支持蓝牙Mesh网络,可以实现多设备组网:
c复制#include "esp_ble_mesh_defs.h"
void init_ble_mesh() {
esp_ble_mesh_prov_t prov = {
.uuid = {0}, // 将被设置
.output_size = 0,
.output_action = ESP_BLE_MESH_NO_OUTPUT,
.prov_attention = 0
};
// 生成随机UUID
esp_fill_random(prov.uuid, 16);
esp_ble_mesh_init(&prov, &comp);
// 启动Mesh网络
esp_ble_mesh_node_prov_enable(ESP_BLE_MESH_PROV_ADV | ESP_BLE_MESH_PROV_GATT);
}
在实际项目中,蓝牙Mesh网络的配置更为复杂,需要定义元素、模型和发布/订阅关系等。
