news 2026/5/8 21:07:53

用ESP32做个蓝牙温湿度计:手把手教你从传感器读取到手机App显示的全栈配置

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用ESP32做个蓝牙温湿度计:手把手教你从传感器读取到手机App显示的全栈配置

从零构建ESP32蓝牙温湿度监测系统:硬件连接、GATT服务定制与手机端交互全解析

项目背景与核心价值

在智能家居和工业物联网快速发展的今天,低功耗蓝牙(BLE)技术因其能耗低、兼容性广的特点,成为传感器数据无线传输的理想选择。ESP32系列芯片凭借双核处理器、丰富的外设接口和完整的BLE协议栈支持,让开发者能够快速实现从传感器数据采集到无线传输的完整解决方案。

这个项目将带领你完成一个具有实用价值的蓝牙温湿度监测系统:通过ESP32读取DHT22传感器的数据,构建自定义GATT服务,最终在手机端实现实时数据显示。不同于简单的代码示例,我们将深入探讨:

  • 如何设计合理的蓝牙属性表结构
  • 传感器数据采集与蓝牙事件处理的协同机制
  • 手机端调试技巧与数据可视化方案

1. 硬件准备与环境搭建

1.1 所需组件清单

组件型号备注
开发板ESP32-C3-DevKitM-1内置BLE 5.0
温湿度传感器DHT22精度±0.5℃
电阻4.7kΩ上拉电阻
连接线杜邦线若干
手机Android/iOS支持BLE

1.2 电路连接示意图

ESP32-C3 DHT22 3.3V ------ VCC GND ------ GND GPIO5 ------ DATA └── 4.7kΩ上拉至3.3V

提示:DHT22为单总线设备,DATA引脚必须接上拉电阻,否则可能无法稳定通信

1.3 开发环境配置

  1. 安装最新版ESP-IDF(v5.0+):
git clone --recursive https://github.com/espressif/esp-idf.git cd esp-idf ./install.sh source export.sh
  1. 创建项目模板:
cp -r examples/bluetooth/bluedroid/ble/gatt_server_template my_ble_sensor cd my_ble_sensor
  1. 添加DHT库支持:
idf.py add-dependency "espressif/dht^1.4.0"

2. 蓝牙GATT服务设计与实现

2.1 自定义服务UUID规划

一个完整的温湿度监测服务需要定义:

  • 主服务(Service):作为数据容器
  • 特征值(Characteristic):实际存储数据
  • 描述符(Descriptor):配置通知属性
// 自定义UUID定义 #define ENV_SERVICE_UUID 0xA001 #define TEMP_CHAR_UUID 0xA002 #define HUMIDITY_CHAR_UUID 0xA003 #define DESC_UUID 0x2902 // 16位UUID转128位标准格式 static esp_bt_uuid_t env_service_uuid = { .len = ESP_UUID_LEN_16, .uuid = {.uuid16 = ENV_SERVICE_UUID} };

2.2 属性表(Attribute Table)构建

完整的属性表示例结构:

  1. 服务声明
  2. 温度特征声明
  3. 温度特征值
  4. 温度描述符
  5. 湿度特征声明
  6. 湿度特征值
  7. 湿度描述符
static const esp_gatts_attr_db_t gatt_db[] = { // 服务声明 [IDX_SVC] = { {ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ, sizeof(env_service_uuid), sizeof(env_service_uuid), (uint8_t *)&env_service_uuid}}, // 温度特征声明 [IDX_TEMP_CHAR] = { {ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_notify}}, // 温度特征值 [IDX_TEMP_VAL] = { {ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&temp_char_uuid, ESP_GATT_PERM_READ, TEMP_VAL_LEN, sizeof(temp_value), (uint8_t *)temp_value}}, // 温度描述符 [IDX_TEMP_CFG] = { {ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&desc_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, sizeof(uint16_t), sizeof(notify_enable), (uint8_t *)¬ify_enable}}, // 湿度特征...(结构类似) };

2.3 关键事件处理逻辑

蓝牙协议栈通过事件回调与应用交互,主要处理以下事件:

  • ESP_GATTS_REG_EVT:服务注册完成
  • ESP_GATTS_READ_EVT:客户端读取特征值
  • ESP_GATTS_WRITE_EVT:客户端写入描述符(启用通知)
  • ESP_GATTS_CREATE_EVT:服务实例创建成功

示例回调处理片段:

case ESP_GATTS_WRITE_EVT: if (!param->write.is_prep) { if (param->write.handle == temp_desc_handle) { // 客户端配置了温度通知 notify_enable = param->write.value[0]; start_temp_notify(); } } break;

3. 传感器数据采集与蓝牙同步

3.1 DHT22数据读取实现

创建独立任务周期性读取传感器:

void dht22_task(void *pvParameters) { gpio_set_direction(DHT_GPIO, GPIO_MODE_OUTPUT); dht22_init(); while(1) { float temp, humidity; if(dht22_read(&temp, &humidity) == ESP_OK) { // 更新蓝牙特征值 update_ble_values(temp, humidity); } vTaskDelay(pdMS_TO_TICKS(2000)); // 2秒间隔 } }

3.2 数据更新与通知机制

当传感器数据变化时,需要:

  1. 更新特征值
  2. 检查通知是否启用
  3. 发送通知给已连接的客户端
void update_ble_values(float temp, float humidity) { uint16_t temp_raw = (uint16_t)(temp * 100); // 转换为整数保存 esp_ble_gatts_set_attr_value(temp_val_handle, sizeof(temp_raw), (uint8_t *)&temp_raw); if(notify_enable) { esp_ble_gatts_send_indicate(gatts_if, conn_id, temp_val_handle, sizeof(temp_raw), (uint8_t *)&temp_raw, false); } }

注意:esp_ble_gatts_set_attr_value必须在连接建立后调用,否则会导致内存错误

4. 手机端调试与数据可视化

4.1 使用nRF Connect进行测试

  1. 下载安装nRF Connect(iOS/Android通用)
  2. 扫描并找到ESP32设备(默认名称"ESP_ENV_SENSOR")
  3. 连接后浏览服务列表,应能看到自定义的温湿度服务
  4. 点击温度特征值的"通知"图标启用数据推送
  5. 观察实时数据变化

4.2 数据格式解析技巧

由于BLE协议限制,特征值通常以原始字节传输。在手机端需要反向转换:

  • 温度值:接收的2字节无符号整数 ÷ 100 = 实际温度(℃)
  • 湿度值:接收的2字节无符号整数 ÷ 100 = 实际湿度(%RH)

示例JavaScript解析代码(适用于App开发):

function parseSensorData(rawValue) { const view = new DataView(rawValue.buffer); const temp = view.getUint16(0, true) / 100; const humidity = view.getUint16(2, true) / 100; return { temp, humidity }; }

4.3 常见连接问题排查

现象可能原因解决方案
扫描不到设备ESP32未启动广播检查esp_ble_gap_start_advertising调用
连接后立即断开MTU设置过小在连接后协商更大的MTU
收不到通知描述符未配置确认客户端已写入0x0001到CCC描述符
数据异常字节序问题统一使用小端格式传输

5. 进阶优化与扩展思路

5.1 低功耗设计技巧

  • 调整广播间隔:adv_params.adv_int_min = 512;(单位0.625ms)
  • 使用ESP32的深度睡眠模式,配合GPIO唤醒
  • 动态传感器采样率:根据环境变化率自动调整

5.2 多客户端连接管理

虽然BLE协议限制单个外设只能被一个中心设备连接,但可以通过以下方式增强可用性:

  1. 实现快速重连机制
  2. 添加连接状态特征,供客户端查询
  3. 使用绑定/配对实现自动重连
// 在连接事件中保存连接信息 case ESP_GATTS_CONNECT_EVT: conn_id = param->connect.conn_id; memcpy(remote_bda, param->connect.remote_bda, sizeof(esp_bd_addr_t)); break;

5.3 数据安全增强

  • 启用LE Secure Connections配对
  • 为敏感特征添加加密权限
  • 实现数据签名验证
// 特征值权限设置示例 [TEMP_VAL] = { {ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&temp_char_uuid, ESP_GATT_PERM_READ_ENCRYPTED, // 需要加密连接 TEMP_VAL_LEN, sizeof(temp_value), (uint8_t *)temp_value}},

在实际部署中发现,当环境温度变化剧烈时,DHT22的响应速度可能跟不上需求。这时可以考虑切换为BME280等更高性能的传感器,或者增加软件滤波算法。蓝牙通信方面,合理设置MTU大小(建议247字节)可以显著提升数据传输效率,特别是在需要传输历史数据时。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/8 21:07:44

别再只会用默认样式了!MATLAB R2023b绘图配色与线型搭配实战指南

MATLAB R2023b科研绘图美学:从配色理论到学术图表实战 科研图表的第一印象往往决定了读者对研究成果的信任度。当审稿人打开论文,或是听众看到演示文稿时,一张配色混乱、线型随意的图表可能瞬间降低内容的专业可信度。MATLAB作为工程与科研领…

作者头像 李华
网站建设 2026/5/8 21:04:18

别再傻傻分不清!医疗器械UDI码里的DI和PI,到底怎么用?

医疗器械UDI码实战指南:DI与PI的精准解析与应用 在医疗器械行业,UDI码就像产品的"身份证",而其中的DI和PI则是这张身份证上最关键的信息区块。许多从业者虽然每天都在扫描这些条形码,却未必真正理解如何高效利用这两组数…

作者头像 李华
网站建设 2026/5/8 21:00:31

SOAFEE:云原生技术如何重塑汽车嵌入式软件开发

1. 项目概述:当汽车软件遇上云原生如果你在汽车电子或嵌入式软件领域摸爬滚打过几年,一定对“开发-测试-集成-标定”这个漫长且昂贵的循环深有体会。一套新的ADAS算法,从云端写好代码,到最终能在实车的域控制器上稳定、安全地跑起…

作者头像 李华