深入NimBLE GATT手把手构建一个BLE温湿度服务器与客户端附完整项目源码在物联网设备爆发式增长的今天低功耗蓝牙BLE技术因其低功耗、低成本的特点成为智能家居、可穿戴设备和传感器网络的理想选择。而GATTGeneric Attribute Profile作为BLE的核心数据交换协议决定了设备如何组织和服务数据。本文将带你从零开始用NimBLE协议栈构建一个完整的温湿度监测系统包含服务端STM32和客户端ESP32的实现让你彻底掌握自定义BLE服务的开发精髓。1. 项目架构与环境搭建1.1 硬件选型与准备本项目的硬件配置采用典型的BLE开发组合服务端STM32F4 Discovery开发板内置BLE射频客户端ESP32 DevKitC开发板双模蓝牙传感器DHT22温湿度传感器精度±0.5℃提示若使用其他硬件平台需确保其支持NimBLE协议栈或兼容的BLE 4.0标准1.2 开发环境配置服务端开发需要以下工具链# STM32开发环境 sudo apt install arm-none-eabi-gcc git clone https://github.com/apache/mynewt-nimble客户端开发环境配置# ESP-IDF环境以v4.4为例 git clone --recursive https://github.com/espressif/esp-idf.git cd esp-idf ./install.sh2. 服务端构建自定义GATT服务2.1 定义温湿度服务UUID自定义服务需要遵循UUID规范我们采用128位随机UUID避免冲突// 自定义服务UUID #define TH_SERVICE_UUID 0x12345678,0x1234,0x5678,0x1234,0x56789abcdef0 // 温度特征UUID #define TEMP_CHAR_UUID 0x12345678,0x1234,0x5678,0x1234,0x56789abcdef1 // 湿度特征UUID #define HUMI_CHAR_UUID 0x12345678,0x1234,0x5678,0x1234,0x56789abcdef22.2 实现特征与描述符关键代码实现温度特征的属性配置static const struct ble_gatt_chr_def temp_char { .uuid BLE_UUID128_DECLARE(TEMP_CHAR_UUID), .access_cb th_char_access, .flags BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY, .val_handle temp_val_handle, .descriptors (struct ble_gatt_dsc_def[]) { { .uuid BLE_UUID16_DECLARE(BLE_GATT_DSC_CLT_CFG_UUID16), .access_cb th_desc_access, .att_flags BLE_ATT_F_READ | BLE_ATT_F_WRITE }, { 0 } } };2.3 数据更新与通知机制传感器数据采集周期设置为2秒通过通知机制主动推送void update_sensor_data() { float temp dht22_read_temp(); float humi dht22_read_humi(); // 更新特征值 ble_gattc_notify_custom(conn_handle, temp_val_handle, (uint8_t*)temp, sizeof(temp)); ble_gattc_notify_custom(conn_handle, humi_val_handle, (uint8_t*)humi, sizeof(humi)); }3. 客户端服务发现与数据交互3.1 服务发现流程ESP32客户端通过以下步骤发现服务启动扫描并过滤目标设备连接后发现所有主服务遍历服务查找自定义UUID解析特征和描述符关键发现函数实现void discover_services(ble_gatt_conn_desc *desc) { ble_gattc_service services[10]; int count ble_gattc_disc_all_svcs(conn_handle, services, 10); for (int i0; icount; i) { if (is_th_service(services[i])) { register_service(services[i]); } } }3.2 数据订阅与处理配置客户端特征描述符以启用通知void enable_notification(uint16_t chr_val_handle) { uint8_t cccd_value[2] {0x01, 0x00}; // 启用通知 ble_gattc_write_flat(conn_handle, chr_val_handle1, // CCCD句柄 cccd_value, 2); }数据到达时的回调处理static int on_data_received(uint16_t conn_handle, struct ble_gatt_access_ctxt *ctxt) { float value *(float*)ctxt-om-om_data; if (ctxt-chr-uuid TEMP_UUID) { current_temp value; } else { current_humi value; } return 0; }4. 项目优化与调试技巧4.1 功耗优化策略优化措施电流消耗(mA)说明默认配置12.5无任何优化调整广播间隔8.2广播间隔从100ms增至500ms启用睡眠模式3.7在空闲时进入低功耗模式优化通知频率2.1数据更新从1s改为5s4.2 常见问题排查连接不稳定检查射频匹配电路验证天线阻抗通常50Ω调整发射功率0dBm至10dBm数据丢失确认MTU大小默认23字节检查通知使标志位验证特征属性是否支持Notify高延迟优化连接参数struct ble_gap_conn_params params { .scan_itvl 0x60, .scan_window 0x30, .itvl_min BLE_GAP_INITIAL_CONN_ITVL_MIN, .itvl_max BLE_GAP_INITIAL_CONN_ITVL_MAX };5. 进阶功能扩展5.1 安全加密通信添加LE Secure Connection配对static const struct ble_gap_pairing_params pairing_params { .bond 1, .mitm 1, .io_cap BLE_HS_IO_DISPLAY_ONLY, .oob 0, .key_size 16, .authreq BLE_SM_PAIR_AUTHREQ_SC };5.2 多客户端支持实现服务端同时连接多个客户端的策略使用连接参数协商为每个连接维护独立的状态机采用动态内存分配管理连接资源关键实现代码struct connection_state { uint16_t conn_handle; uint16_t temp_ccc; uint16_t humi_ccc; bool notify_enabled; }; struct connection_state *clients[CONFIG_MAX_CONNECTIONS];在实际部署中发现当环境存在多个ESP32客户端时合理设置连接间隔和延迟参数可以显著提高系统稳定性。特别是在智能家居场景中建议将连接间隔设置为15-30ms范围既能保证实时性又不会过度消耗带宽。