ESP32 BLE实战:5分钟搞定自定义GATT服务(附完整代码)
ESP32 BLE实战:5分钟搞定自定义GATT服务(附完整代码)
在物联网开发中,BLE(低功耗蓝牙)技术因其低功耗、低成本的特点成为设备间短距离通信的首选方案。ESP32作为一款集成了Wi-Fi和蓝牙双模的芯片,凭借其出色的性价比和丰富的开发资源,成为物联网开发者的热门选择。本文将带你快速实现ESP32上的自定义GATT服务开发,涵盖从基础概念到实战代码的全过程。
1. BLE GATT基础概念速成
1.1 GATT架构核心要素
BLE通信建立在GATT(通用属性协议)之上,其核心架构包含以下关键要素:
Service(服务):功能逻辑单元,例如电池服务、设备信息服务等。每个服务包含:
- 16位或128位UUID(唯一标识符)
- 一个或多个Characteristic
- 可选Include引用其他服务
Characteristic(特征):服务中的数据点,包含:
- 属性值(Value)
- 属性配置(Properties):读/写/通知等权限
- 描述符(Descriptor):如CCCD(客户端特征配置描述符)
属性权限类型对照表:
| 权限类型 | 说明 | 典型应用场景 |
|---|---|---|
| READ | 允许读取 | 传感器数据读取 |
| WRITE | 允许写入 | 设备控制命令 |
| NOTIFY | 服务器主动通知(无需确认) | 实时数据推送 |
| INDICATE | 服务器主动指示(需客户端确认) | 重要状态变更 |
1.2 ESP32 BLE开发环境准备
开发ESP32 BLE应用需要:
硬件准备:
- ESP32开发板(如ESP32-WROOM-32)
- 手机(Android/iOS)或BLE调试工具
软件环境:
# 安装ESP-IDF(以v4.4为例) git clone -b v4.4 --recursive https://github.com/espressif/esp-idf.git cd esp-idf ./install.sh source export.sh项目创建:
cp -r $IDF_PATH/examples/bluetooth/bluedroid/ble/gatt_server . cd gatt_server idf.py set-target esp32
2. 自定义GATT服务实现步骤
2.1 服务定义与初始化
在gatt_server示例基础上,我们添加自定义UUID的服务:
// 自定义服务UUID(可替换为你的UUID) #define CUSTOM_SERVICE_UUID 0xABCD #define CUSTOM_CHAR_UUID 0x1234 // 特征值属性配置 static esp_attr_value_t gatts_char_val = { .attr_max_len = 20, .attr_len = 4, .attr_value = {0x01, 0x02, 0x03, 0x04}, }; // 服务结构体定义 static struct gatts_profile_inst { esp_gatts_cb_t gatts_cb; uint16_t gatts_if; uint16_t app_id; uint16_t conn_id; uint16_t service_handle; esp_gatt_srvc_id_t service_id; uint16_t char_handle; esp_bt_uuid_t char_uuid; esp_gatt_perm_t perm; esp_gatt_char_prop_t property; } custom_profile;2.2 服务注册与创建
在gatts_profile_event_handler中添加服务创建逻辑:
case ESP_GATTS_REG_EVT: // 设置服务UUID custom_profile.service_id.is_primary = true; custom_profile.service_id.id.inst_id = 0x00; custom_profile.service_id.id.uuid.len = ESP_UUID_LEN_16; custom_profile.service_id.id.uuid.uuid.uuid16 = CUSTOM_SERVICE_UUID; // 创建服务 esp_ble_gatts_create_service(gatts_if, &custom_profile.service_id, 4); break; case ESP_GATTS_CREATE_EVT: // 启动服务 esp_ble_gatts_start_service(param->create.service_handle); // 添加特征 custom_profile.char_uuid.len = ESP_UUID_LEN_16; custom_profile.char_uuid.uuid.uuid16 = CUSTOM_CHAR_UUID; custom_profile.property = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY; esp_ble_gatts_add_char(param->create.service_handle, &custom_profile.char_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, custom_profile.property, &gatts_char_val, NULL); break;2.3 特征值操作处理
实现读写和通知功能的关键事件处理:
case ESP_GATTS_READ_EVT: { // 读取特征值处理 esp_gatt_rsp_t rsp = {0}; rsp.attr_value.handle = param->read.handle; rsp.attr_value.len = 4; memcpy(rsp.attr_value.value, "DATA", 4); esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, ESP_GATT_OK, &rsp); break; } case ESP_GATTS_WRITE_EVT: { // 写入特征值处理 if (!param->write.is_prep) { ESP_LOGI(TAG, "GATT_WRITE_EVT, value len %d", param->write.len); // 处理CCCD配置(通知使能) if (param->write.handle == custom_profile.descr_handle) { uint16_t descr_value = param->write.value[1]<<8 | param->write.value[0]; if (descr_value == 0x0001) { // 启用通知 uint8_t notify_data[10] = {0}; esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, custom_profile.char_handle, sizeof(notify_data), notify_data, false); } } } break; }3. 性能优化实战技巧
3.1 MTU大小优化
默认ATT_MTU为23字节,通过协商可提升传输效率:
// 设置本地MTU(最大512字节) esp_ble_gatt_set_local_mtu(247); // 推荐值:247 // MTU更新事件处理 case ESP_GATTS_MTU_EVT: ESP_LOGI(TAG, "MTU updated to %d", param->mtu.mtu); break;3.2 连接参数优化
合理的连接参数可平衡功耗和响应速度:
case ESP_GATTS_CONNECT_EVT: { esp_ble_conn_update_params_t conn_params = { .min_int = 16, // 20ms (1.25ms单位) .max_int = 32, // 40ms .latency = 0, .timeout = 400 // 4s }; memcpy(conn_params.bda, param->connect.remote_bda, 6); esp_ble_gap_update_conn_params(&conn_params); break; }3.3 数据分包处理
当数据超过MTU时,需要实现长特征写入:
case ESP_GATTS_WRITE_EVT: if (param->write.is_prep) { // 处理分块写入 store_to_buffer(param->write.value, param->write.len, param->write.offset); } break; case ESP_GATTS_EXEC_WRITE_EVT: if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) { // 执行最终写入 process_complete_write(); } break;4. 手机端交互实现
4.1 Android端关键代码
使用Android BLE API进行交互:
// 发现服务 bluetoothGatt.discoverServices(); // 写入特征 BluetoothGattCharacteristic characteristic = service.getCharacteristic(CUSTOM_CHAR_UUID); characteristic.setValue("CMD"); bluetoothGatt.writeCharacteristic(characteristic); // 启用通知 bluetoothGatt.setCharacteristicNotification(characteristic, true); BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CCCD_UUID); descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); bluetoothGatt.writeDescriptor(descriptor);4.2 iOS端关键代码
使用CoreBluetooth框架实现:
// 发现特征 func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { guard let characteristics = service.characteristics else { return } for characteristic in characteristics { if characteristic.uuid == CBUUID(string: "1234") { // 订阅通知 peripheral.setNotifyValue(true, for: characteristic) } } } // 处理通知数据 func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { if let data = characteristic.value { print("Received: \(data)") } }5. 常见问题排查指南
5.1 服务注册失败
- 可能原因:UUID冲突或内存不足
- 解决方案:
// 检查返回状态 if (ret != ESP_OK) { ESP_LOGE(TAG, "Service register failed: %s", esp_err_to_name(ret)); }
5.2 通知延迟
- 优化措施:
- 减小连接间隔(connection interval)
- 使用
esp_ble_gatts_send_indicate替代esp_ble_gatts_send_response - 确保手机端已正确配置CCCD
5.3 跨平台兼容性问题
- UUID格式:确保各平台使用相同的UUID格式
// 16位UUID转换为128位格式 0x1234 -> 00001234-0000-1000-8000-00805F9B34FB
完整代码示例
以下为精简后的核心代码框架:
#include "esp_gatts_api.h" #define PROFILE_NUM 1 #define PROFILE_APP_ID 0 static struct gatts_profile_inst profile_tab[PROFILE_NUM]; void app_main() { // 1. 初始化NVS esp_err_t ret = nvs_flash_init(); // 2. 初始化蓝牙控制器 esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); esp_bt_controller_init(&bt_cfg); esp_bt_controller_enable(ESP_BT_MODE_BLE); // 3. 初始化Bluedroid esp_bluedroid_init(); esp_bluedroid_enable(); // 4. 注册GATT回调 esp_ble_gatts_register_callback(gatts_event_handler); // 5. 注册应用Profile esp_ble_gatts_app_register(PROFILE_APP_ID); } static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { // 事件分发逻辑 if (event == ESP_GATTS_REG_EVT) { if (param->reg.app_id == PROFILE_APP_ID) { profile_tab[PROFILE_APP_ID].gatts_if = gatts_if; } } // 其他事件处理... }实际开发中遇到连接不稳定问题时,我发现调整连接参数和增加重试机制能显著改善体验。例如将最小连接间隔从默认的45ms降低到20ms,可以明显提升数据传输的实时性。
