4、温湿度数据采集实战:使用ESP32读取DHT11/SHT30温湿度数据
温湿度数据是环境感知项目中最基础、最常用的物理量。无论是智能家居的舒适度调节、农业大棚的种植监控,还是实验室的恒温恒湿管理,都离不开对温度和湿度的精确采集。本章将聚焦于ESP32微控制器,深入讲解如何通过GPIO(通用输入输出)接口和I2C(集成电路总线)协议,分别驱动DHT11单总线数字传感器与SHT30高精度I2C传感器,并实现定时采集与串口打印的完整工程链路。我们将从传感器选型对比、硬件接线原理、代码架构设计到实际调试排障,构建一个可复用的温湿度采集模块,为后续的TinyML边缘推理提供可靠的数据源。
上图展示了本章实战项目的整体硬件链路与数据流架构。ESP32作为主控核心,左侧连接DHT11(单总线)与SHT30(I2C)两种传感器,右侧通过UART串口将采集到的数据输出至PC监视器,并为后续TinyML推理预留数据接口。下面我们将从传感器原理、硬件接线、代码实现到调试方法,逐步拆解整个采集流程。
4.1 核心原理:DHT11与SHT30的工作机制
在编写代码之前,理解传感器的物理层协议与数据格式至关重要。DHT11和SHT30虽然都输出数字信号,但它们的通信方式、精度和内部结构截然不同。
4.1.1 DHT11单总线协议
DHT11是一款经典的温湿度复合传感器,内部包含一个电阻式感湿元件和一个NTC(负温度系数)测温元件,并连接一个8位微处理器。它采用简化的单总线(1-Wire)协议,仅需一根数据线即可完成与主控的双向通信。其数据帧格式为40位:8位湿度整数 + 8位湿度小数 + 8位温度整数 + 8位温度小数 + 8位校验和。校验和等于前四个字节之和的低8位,用于检测传输错误。
通信时序要求严格:主机发起起始信号(拉低数据线至少18ms),传感器响应后拉低80us再拉高80us,随后逐位输出数据。每一位的“0”或“1”由高电平持续时间区分——26-28us表示“0”,70us表示“1”。由于时序敏感,建议在代码中使用微秒级延时函数,并关闭中断以避免干扰。
4.1.2 SHT30 I2C协议
SHT30是Sensirion公司推出的高精度数字传感器,内部集成了CMOSens®芯片,将传感器元件、信号处理电路和I2C接口集成于一体。其默认I2C地址为0x44(ADDR引脚接低电平时),支持标准模式(100kHz)和快速模式(400kHz)。SHT30的测量命令分为单次测量和周期测量两种模式,单次测量命令如0x2C 0x06(高重复性,时钟拉伸使能),周期测量则需先发送0x20 0x00启动2Hz的连续采样。
SHT30返回的数据为6字节:2字节温度 + 1字节CRC校验 + 2字节湿度 + 1字节CRC校验。温度计算公式为:T = -45 + 175 * (rawTemp / 65535.0),湿度计算公式为:RH = 100 * (rawHumidity / 65535.0)。相比DHT11,SHT30提供了更宽的测量范围(-40~125°C,0~100%RH)和更高的精度(±0.2°C,±2%RH)。
| 对比维度 | DHT11 | SHT30 |
|---|---|---|
| 通信接口 | 单总线(1-Wire) | I2C(地址0x44) |
| 温度精度 | ±2°C | ±0.2°C |
| 湿度精度 | ±5%RH | ±2%RH |
| 采样周期 | ≥1秒 | ≥2ms(快速模式) |
| 数据帧格式 | 40位原始数据 | 6字节含CRC校验 |
| 典型应用 | 低成本、低精度场景 | 高精度、工业级场景 |
4.2 硬件接线与工程准备
正确的硬件连接是数据采集成功的前提。ESP32开发板通常提供多个GPIO引脚,但并非所有引脚都适合连接传感器。对于DHT11,我们选择GPIO4作为数据引脚;对于SHT30,使用默认的I2C引脚GPIO21(SDA)和GPIO22(SCL)。
接线清单如下:
- DHT11:VCC接3.3V,GND接GND,DATA接GPIO4(需外接10kΩ上拉电阻至3.3V,部分模块已内置)。
- SHT30:VCC接3.3V,GND接GND,SCL接GPIO22,SDA接GPIO21。若使用多个I2C设备,注意地址冲突。
- 串口:ESP32的UART0(TX=GPIO1,RX=GPIO3)通过USB转串口芯片连接PC,无需额外接线。
在Arduino IDE中,需要安装ESP32开发板支持包。打开“文件”->“首选项”,在“附加开发板管理器网址”中添加:https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json,然后在“工具”->“开发板”->“开发板管理器”中搜索“esp32”并安装。对于SHT30,还需要安装Adafruit_SHT31库(通过库管理器搜索“Adafruit SHT31”)。
4.3 代码实现:定时采集与串口打印
我们将编写一个完整的Arduino程序,实现以下功能:每2秒采集一次DHT11和SHT30的数据,通过串口以格式化文本输出,并包含错误处理机制。代码采用模块化设计,便于后续扩展。
#include <Wire.h>
#include <Adafruit_SHT31.h>
#include <DHT.h>
// DHT11配置
#define DHT_PIN 4
#define DHT_TYPE DHT11
DHT dht(DHT_PIN, DHT_TYPE);
// SHT30配置
Adafruit_SHT31 sht30 = Adafruit_SHT31();
// 定时器变量
unsigned long previousMillis = 0;
const long interval = 2000; // 2秒采集间隔
void setup() {
Serial.begin(115200);
delay(100);
Serial.println(F("ESP32 温湿度采集系统启动"));
// 初始化DHT11
dht.begin();
Serial.println(F("DHT11 初始化完成"));
// 初始化SHT30
if (!sht30.begin(0x44)) {
Serial.println(F("SHT30 初始化失败,请检查接线!"));
while (1) delay(10);
}
Serial.println(F("SHT30 初始化成功"));
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
readAndPrintSensors();
}
}
void readAndPrintSensors() {
// 读取DHT11
float dhtTemp = dht.readTemperature();
float dhtHum = dht.readHumidity();
// 读取SHT30
float shtTemp = sht30.readTemperature();
float shtHum = sht30.readHumidity();
// 打印分隔线
Serial.println(F("-----------------------------------"));
// 打印DHT11数据
if (isnan(dhtTemp) || isnan(dhtHum)) {
Serial.println(F("[DHT11] 读取失败,请检查连接"));
} else {
Serial.print(F("[DHT11] 温度: "));
Serial.print(dhtTemp);
Serial.print(F(" °C, 湿度: "));
Serial.print(dhtHum);
Serial.println(F(" %RH"));
}
// 打印SHT30数据
if (isnan(shtTemp) || isnan(shtHum)) {
Serial.println(F("[SHT30] 读取失败,请检查连接"));
} else {
Serial.print(F("[SHT30] 温度: "));
Serial.print(shtTemp);
Serial.print(F(" °C, 湿度: "));
Serial.print(shtHum);
Serial.println(F(" %RH"));
}
// 数据对比提示
if (!isnan(dhtTemp) && !isnan(shtTemp)) {
float diff = abs(dhtTemp - shtTemp);
if (diff > 2.0) {
Serial.print(F("[警告] 两传感器温差超过2°C,差值: "));
Serial.println(diff);
}
}
}
代码逻辑说明:setup()中初始化串口、DHT11和SHT30,若SHT30初始化失败则进入死循环提示。主循环使用millis()实现非阻塞定时,避免delay()阻塞其他任务。读取函数中,isnan()用于检测传感器返回的非法浮点值(NaN),这是传感器通信异常时的典型表现。打印格式采用[传感器名] 数据的格式,便于串口监视器过滤查看。
4.4 案例拆解:从原始数据到可靠工程实践
为了更深入地理解实际工程中的挑战,我们模拟一个智能花盆项目场景。项目要求:实时监测土壤环境温度与空气湿度,当温度超过35°C或湿度低于20%时,触发浇水继电器。
初始现象与目标:使用DHT11采集数据,发现温度读数在30°C左右波动,但湿度读数经常跳变到99.9%或0%,导致误触发浇水。目标是将湿度读数稳定在±5%误差范围内。
关键数据与日志分析:通过串口打印原始数据,发现DHT11的湿度值在短时间内从45%跳变到99.9%,且伴随校验错误。进一步检查发现,传感器数据线过长(超过1米),且未使用屏蔽线,导致信号完整性受损。
分析判断过程:DHT11的单总线协议对时序要求极高,长线缆会增加信号上升沿时间,导致主控采样点偏移。此外,电源纹波也可能干扰传感器内部ADC转换。
实际操作步骤:
- 将DHT11数据线缩短至20cm以内,并采用双绞线结构。
- 在DHT11的VCC和GND之间并联一个100nF去耦电容,滤除高频噪声。
- 将采集间隔从1秒调整为2秒,给传感器足够的稳定时间。
- 在代码中增加软件滤波:连续读取3次,取中位数作为有效值。
结果对比:优化后,湿度读数稳定在42%~48%之间,不再出现跳变。温度读数波动从±1.5°C降低到±0.5°C。
风险与代价:缩短线缆限制了传感器安装位置,软件滤波增加了约10ms的处理时间,但换来了数据可靠性的大幅提升。
复盘经验:对于单总线传感器,硬件布局和电源完整性比代码逻辑更重要。在项目初期就应规划好传感器位置,避免后期改动。
4.5 常见误区与调试技巧
在实际开发中,以下问题出现频率最高,需要特别注意:
DHT11的数据手册明确标注“采样周期不得低于1秒”。如果主循环中无延时地连续读取,传感器将无法正确响应,导致返回0或NaN。解决方案是使用定时器或
millis()控制读取间隔。
ESP32的GPIO21和GPIO22内部有弱上拉,但驱动能力有限。当I2C总线长度超过10cm或挂载多个设备时,应外接2.2kΩ~4.7kΩ的上拉电阻至3.3V,否则通信可能间歇性失败。
代码中设置
Serial.begin(115200),但串口监视器窗口未选择相同波特率,导致输出乱码。务必在监视器右下角选择“115200 baud”。
调试技巧:使用逻辑分析仪或示波器观察DHT11数据线的波形,确认时序是否符合规格书。对于I2C设备,可以使用I2C扫描程序(如Wire.begin(); for(address=1; address<127; address++))快速检测设备地址是否正确。
总结复盘:本章通过DHT11和SHT30的对比实战,完成了从传感器原理、硬件接线、代码编写到调试优化的完整闭环。DHT11适合成本敏感、精度要求不高的场景,而SHT30则适用于需要高可靠性和精度的工业级应用。在实际项目中,应根据环境约束(如线缆长度、电源质量、采样频率)选择合适的传感器,并做好硬件层面的抗干扰设计。下一阶段,我们将把采集到的温湿度数据作为特征输入,结合人体存在传感器,构建TinyML本地推理模型,实现环境状态的智能判断。