ESP IoT Solution 蓝牙健康温度计 Profile(HTP)组件与示例深度解析
【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution
本指南围绕 ESP IoT Solution 仓库中的
ble_htp组件展开,讲解如何在 GATT Client 端通过该组件从符合蓝牙健康温度计服务(Health Thermometer Service,UUID 0x1809)的体温计设备获取温度数据。读完本文,你将掌握 HTP 服务的特征值构成、esp_htp组件的 API 与事件机制、底层 BLE 连接管理调用链,以及基于控制台命令的完整示例实操流程。
一、HTP 是什么:健康温度计服务的标准特征
Health Thermometer Profile(HTP,健康温度计配置文件)是蓝牙 SIG 定义的标准 BLE Profile,用于让数据收集设备(GATT Client)从体温计传感器(GATT Server)读取体温测量值。ESP IoT Solution 的ble_htp组件提供了一套简化 API,封装了 HTP 在 GATT Client 端的常用操作(见 组件 README)。
在 esp_htp.h 中定义了该服务及其特征值的 16 位 UUID:
| 名称 | UUID 宏 | 数值 | 用途 |
|---|---|---|---|
| Health Thermometer Service | BLE_HTP_UUID16 | 0x1809 | 健康温度计服务 |
| Temperature Measurement | BLE_HTP_CHR_UUID16_TEMPERATURE_MEASUREMENT | 0x2A1C | 最终体温测量值(Indication) |
| Temperature Type | BLE_HTP_CHR_UUID16_TEMPERATURE_TYPE | 0x2A1D | 测量部位类型(可读) |
| Intermediate Temperature | BLE_HTP_CHR_UUID16_INTERMEDIATE_TEMPERATURE | 0x2A1E | 测量过程中的中间温度(Notification) |
| Measurement Interval | BLE_HTP_CHR_UUID16_MEASUREMENT_INTERVAL | 0x2A21 | 周期测量间隔(可读/可写) |
其中0x2A1C与0x2A1E共享同一套数据格式(温度测量值),均由esp_ble_htp_data_t结构体描述。
二、组件结构与依赖关系
2.1 仓库中的位置
ble_htp位于 BLE 标准 Profile 组件目录下:
- 组件入口:components/bluetooth/ble_profiles/std/ble_htp(含
include/esp_htp.h、src/esp_htp.c、CMakeLists.txt、Kconfig、idf_component.yml) - 配套示例:examples/bluetooth/ble_profiles/ble_htp
按 idf_component.yml 的描述,该组件即 "BLE standard profile support Health Thermometer"。
2.2 添加组件依赖
与仓库内其他 Profile 组件一致,可以通过 ESP-IDF 组件管理器添加依赖(编译时自动下载):
idf.py add-dependency "espressif/ble_htp=*"也可直接从示例模板创建工程:
idf.py create-project-from-example "espressif/ble_htp=*:ble_htp"组件 README 特别提示:如果执行create-project-from-example时提示CMakeLists.txt not found in project directory,说明使用的是旧版组件管理器,需在 ESP-IDF 环境中执行pip install -U idf-component-manager升级。
2.3 对底层连接管理组件的依赖
从源码看,esp_htp.c直接依赖esp_ble_conn_mgr.h(#include "esp_ble_conn_mgr.h",见 esp_htp.c),所有 GATT 读写操作都通过esp_ble_conn_mgr的esp_ble_conn_read/esp_ble_conn_write完成。因此使用该组件时,工程需同时具备ble_conn_mgr的初始化与连接管理逻辑(示例中由 app_main.c 完成)。
三、核心数据结构:温度测量值格式
3.1 esp_ble_htp_data_t 结构体
esp_ble_htp_data_t(定义于 esp_htp.h)以紧凑位域方式还原了蓝牙规范中 Temperature Measurement 特征的数据布局:
typedef struct { struct { uint8_t temperature_unit: 1; /*!< 温度单位标志位 */ uint8_t time_stamp: 1; /*!< 时间戳标志位 */ uint8_t temperature_type: 1; /*!< 温度类型标志位 */ uint8_t reserved: 5; /*!< 保留位 */ } flags; union { uint32_t celsius; /*!< 摄氏温度值 */ uint32_t fahrenheit; /*!< 华氏温度值 */ } temperature; struct { uint16_t year; /*!< 公历年,有效范围 1582~9999 */ uint8_t month; /*!< 月,1(一月)~ 12(十二月) */ uint8_t day; /*!< 日,1~31 */ uint8_t hours; /*!< 小时,0~23 */ uint8_t minutes; /*!< 分钟,0~59 */ uint8_t seconds; /*!< 秒,0~59 */ } __attribute__((packed)) timestamp; /*!< 测量时间 */ uint8_t location; /*!< 温度测量部位 */ } __attribute__((packed)) esp_ble_htp_data_t;各标志位掩码(esp_htp.h):
| 掩码宏 | 值 | 含义 |
|---|---|---|
BLE_HTP_FLAGS_BM_TEMPERATURE_UNITS | 0x01 | Bit0:0 为摄氏度,1 为华氏度 |
BLE_HTP_FLAGS_BM_TIME_STAMP | 0x02 | Bit1:数据中是否携带时间戳 |
BLE_HTP_FLAGS_BM_TEMPERATURE_TYPE | 0x04 | Bit2:数据中是否携带温度类型 |
需要说明的是,规范中温度值字段本身是 IEEE 11073 标准的 32 位浮点编码,组件以uint32_t原始值原样保存,示例中直接打印该原始值(如日志中的62212°C),实际业务如需按摄氏度/华氏度显示,需要按 IEEE 11073 格式解析该 32 位字段。
3.2 温度类型枚举
location字段(Temperature Type)的取值定义在 esp_htp.h:
| 宏 | 值 | 测量部位 |
|---|---|---|
BLE_HTP_CHR_TEMPERATURE_TYPE_RFU | 0 | 保留 |
BLE_HTP_CHR_TEMPERATURE_TYPE_ARMPIT | 1 | 腋下 |
BLE_HTP_CHR_TEMPERATURE_TYPE_BODY | 2 | 身体(体表) |
BLE_HTP_CHR_TEMPERATURE_TYPE_EAR | 3 | 耳朵 |
BLE_HTP_CHR_TEMPERATURE_TYPE_FINGER | 4 | 手指 |
BLE_HTP_CHR_TEMPERATURE_TYPE_GAST_TRACT | 5 | 胃肠道 |
BLE_HTP_CHR_TEMPERATURE_TYPE_MOUTH | 6 | 口腔 |
BLE_HTP_CHR_TEMPERATURE_TYPE_RECTUM | 7 | 直肠 |
BLE_HTP_CHR_TEMPERATURE_TYPE_TOE | 8 | 脚趾 |
BLE_HTP_CHR_TEMPERATURE_TYPE_TYMPANUM | 9 | 鼓膜 |
BLE_HTP_CHR_TEMPERATURE_TYPE_MAX | 10 | 枚举上界 |
3.3 数据解析流程
esp_htp.c 中的静态函数esp_ble_htp_get_temp_data()按规范顺序逐字段解析原始字节流:
- 先复制 1 字节 Flags 字段;
- 依据
temperature_unit标志选择拷贝温度字段到temperature.celsius或temperature.fahrenheit; - 若
time_stamp标志置位,拷贝 7 字节时间戳; - 若
temperature_type标志置位,读取温度类型到location。
函数首先检查inbuf有效性且inlen >= 5(Flags + 温度值的理论最小长度),否则返回ESP_ERR_INVALID_ARG。
四、API 一览:初始化、读写与事件
4.1 组件提供的五个接口
全部声明于 esp_htp.h:
| 函数 | 说明 | 返回值 |
|---|---|---|
esp_ble_htp_init(void) | 初始化 HTP,注册底层连接管理事件回调 | ESP_OK/ESP_ERR_INVALID_ARG/ESP_FAIL |
esp_ble_htp_deinit(void) | 反初始化,注销事件回调 | 同上 |
esp_ble_htp_get_temp_type(uint8_t *temp_type) | 读取设备的温度类型特征值(0x2A1D) | ESP_OK/ESP_ERR_INVALID_ARG(入参为空指针) |
esp_ble_htp_get_measurement_interval(uint16_t *interval_val) | 读取测量间隔特征值(0x2A21) | 同上 |
esp_ble_htp_set_measurement_interval(uint16_t interval_val) | 写入测量间隔特征值(0x2A21) | ESP_OK/ 底层写失败错误码 |
初始化与反初始化的实现非常轻量(esp_htp.c):
esp_err_t esp_ble_htp_init(void) { return esp_event_handler_register(BLE_CONN_MGR_EVENTS, ESP_EVENT_ANY_ID, esp_ble_htp_event, NULL); } esp_err_t esp_ble_htp_deinit(void) { return esp_event_handler_unregister(BLE_CONN_MGR_EVENTS, ESP_EVENT_ANY_ID, esp_ble_htp_event); }即:注册/注销一个针对BLE_CONN_MGR_EVENTS全部事件的回调,将连接管理层的接收数据转发为 HTP 上层事件。
4.2 事件机制:按特征 UUID 分发
组件声明了独立的事件基BLE_HTP_EVENTS(ESP_EVENT_DECLARE_BASE/ESP_EVENT_DEFINE_BASE)。在 esp_htp.c 的回调中:
case ESP_BLE_CONN_EVENT_DATA_RECEIVE: esp_ble_conn_data_t *conn_data = (esp_ble_conn_data_t *)event_data; esp_ble_htp_data_t ble_htp_data; memset(&ble_htp_data, 0, sizeof(esp_ble_htp_data_t)); esp_ble_htp_get_temp_data(&ble_htp_data, conn_data->data, conn_data->data_len); esp_event_post(BLE_HTP_EVENTS, conn_data->uuid.uuid16, &ble_htp_data, sizeof(esp_ble_htp_data_t), portMAX_DELAY);关键设计是:事件 ID 直接使用来源特征值的 16 位 UUID。上层应用监听BLE_HTP_EVENTS时,只需把事件 ID 与BLE_HTP_CHR_UUID16_TEMPERATURE_MEASUREMENT(0x2A1C,最终温度,Indication)或BLE_HTP_CHR_UUID16_INTERMEDIATE_TEMPERATURE(0x2A1E,中间温度,Notification)比较,即可区分两类温度数据,收到的event_data即解析好的esp_ble_htp_data_t。
从 esp_ble_conn_mgr.h 的注释可知,ESP_BLE_CONN_EVENT_DATA_RECEIVE事件中的data字段由 ble_conn_mgr 堆上分配,组件在解析后随即 post 到 HTP 事件,上层无需关心底层缓冲生命周期。
4.3 底层读写调用链
三个读写接口均通过ble_conn_mgr执行 GATT 操作,以 16 位 UUID 定位特征值(BLE_CONN_UUID_TYPE_16):
- 读操作构造
esp_ble_conn_data_t后调用esp_ble_conn_read(),成功后将返回缓冲区数据拷贝给调用方(温度类型取首字节,测量间隔按MIN(data_len, sizeof(uint16_t))拷贝); - 写操作将 2 字节
uint16_t小端值填入esp_ble_conn_write()的入参,成功时打印十六进制日志ESP_LOG_BUFFER_HEX_LEVEL。
esp_ble_conn_read/esp_ble_conn_write在 esp_nimble.c(NimBLE 后端)与 esp_bluedroid.c(Bluedroid 后端)中均有实现,因此ble_htp组件对两种经典蓝牙主机协议栈均透明兼容。
五、示例实操:连接体温计并收发温度数据
示例位于 examples/bluetooth/ble_profiles/ble_htp,支持 ESP32 / ESP32-C3 / ESP32-C2 / ESP32-S3 目标芯片。其行为是:创建 GATT Client 并被动扫描,若对端广播且声明主服务 UUID 为 0x1809,则发起连接;连接后自动发现服务、特征与描述符;同时启动交互式串口控制台,用于模拟 HTP 的读写操作。
5.1 构建与运行
idf.py set-target <chip_name> # 例如 esp32c3 idf.py menuconfig # Example Configuration 菜单下可配置广播名 idf.py -p PORT flash monitor # 编译、烧录并打开监视器Example Configuration菜单(见 Kconfig.projbuild)默认配置为:
EXAMPLE_BLE_ADV_NAME:广播名,默认BLE_HTSEXAMPLE_BLE_SUB_ADV:后续广播数据,默认SUB_ADV
测试对端可使用任意宣称支持健康温度计服务(0x1809)并在 GATT 数据库中包含该服务的 BLE GATT Server App。若对端声称支持 HTP 服务却不提供所需特征/描述符,或 GATT 流程失败,示例会立即断开连接。
5.2 控制台命令
控制台注册了htp命令(参数定义见 app_htp.c):
htp [-t <01~03>] [-i <0~65535>] -t, --type=<01~03> 01 读取温度类型特征值 02 读取测量间隔特征值 03 写入测量间隔特征值 -i, --interval=<0~65535> 0: 不进行周期测量;其他值: 测量间隔时长典型操作:
# 读取温度类型 htp -t 1 # 读取测量间隔 htp -t 2 # 设置测量间隔为 0(关闭周期 Indication) htp -t 3 -i 0 # 设置测量间隔为 10 秒 htp -t 3 -i 10app_htp_run()(app_htp.c)将命令类型 1/2/3 分别映射到esp_ble_htp_get_temp_type/esp_ble_htp_get_measurement_interval/esp_ble_htp_set_measurement_interval。
5.3 应用层事件处理
示例通过esp_event_handler_register(BLE_HTP_EVENTS, ESP_EVENT_ANY_ID, app_htp_event_handler, NULL)订阅温度数据(app_htp.c)。回调按事件 ID(即特征 UUID)分流:
BLE_HTP_CHR_UUID16_TEMPERATURE_MEASUREMENT:打印时间戳(若带)、温度类型(若带)与Measurement Temperature(按标志位输出°C或°F);BLE_HTP_CHR_UUID16_INTERMEDIATE_TEMPERATURE:同样解析并打印Intermediate Temperature。
5.4 运行日志解读
以 ESP32-C3 上成功连接后的日志为例(节选自 示例 README):
esp32c3> htp -t 1 I (12397) esp_nimble: characteristic read; conn_handle=1 attr_handle=16 len=1 I (12397) app_htp: Gets the current temperature type value 7 esp32c3> htp -t 2 I (14477) esp_nimble: characteristic read; conn_handle=1 attr_handle=23 len=2 I (14477) app_htp: Gets the measurement interval value 2068 esp32c3> htp -t 3 -i 0 I (23707) esp_htp: Sets the measurement interval value Success! I (23717) esp_htp: ESP_BLE_CONN_EVENT_DATA_RECEIVE I (23727) app_htp: Measurement Temperature 62212°C I (23727) app_htp: Sets the measurement interval value 0 I (23797) esp_htp: ESP_BLE_CONN_EVENT_DATA_RECEIVE I (23817) app_htp: Intermediate Temperature 50762℉ esp32c3> htp -t 3 -i 10 I (27307) esp_htp: Sets the measurement interval value Success! I (27337) app_htp: Temperature Type 7 I (27337) app_htp: Measurement Temperature 7873°C I (27337) app_htp: Sets the measurement interval value 10 I (27417) app_htp: Intermediate Temperature 13545°C日志清晰展示了完整数据流:写测量间隔成功后,对端随即通过 Indication(0x2A1C)上报最终温度、通过 Notification(0x2A1E)上报中间温度;ESP_BLE_CONN_EVENT_DATA_RECEIVE由底层连接管理触发,esp_htp完成解析,app_htp负责打印展示。
5.5 主程序初始化顺序
app_main.c 给出了标准的接入顺序,可作为二次开发模板:
nvs_flash_init()初始化 NVS(失败且为ESP_ERR_NVS_NO_FREE_PAGES/ESP_ERR_NVS_NEW_VERSION_FOUND时先擦除再初始化);esp_event_loop_create_default()创建默认事件循环;- 注册
BLE_CONN_MGR_EVENTS的全局连接状态回调(CONNECTED / DISCONNECTED / DISC_COMPLETE / DATA_RECEIVE); app_console_init()初始化控制台,register_htp()注册htp命令并调用esp_ble_htp_init();esp_ble_conn_init(&config)传入esp_ble_conn_config_t(含device_name与broadcast_data),esp_ble_conn_start()启动扫描连接流程;失败时依次esp_ble_conn_stop()、esp_ble_conn_deinit()并注销事件。
六、小结
ble_htp组件在 ESP IoT Solution 中属于“BLE 标准 Profile”组件族(与ble_hrp、ble_anp、ble_otp并列于 components/bluetooth/ble_profiles/std)。它并不直接管理扫描、连接与 GATT 发现,而是依赖ble_conn_mgr完成这些底层工作,自身专注于两件事:把 HTP 特征的原始字节解析为结构化的esp_ble_htp_data_t,以及以“特征 UUID 即事件 ID”的方式向应用层分发温度数据。这种分层设计使开发者只需调用 5 个 API 并订阅BLE_HTP_EVENTS,即可快速接入符合蓝牙规范的体温计设备,可用于健康监测、医疗数据采集等物联网应用场景。
【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考