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 esp322. 自定义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确保手机端已正确配置CCCD5.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可以明显提升数据传输的实时性。