基于 Tasmota 仓库的 Adafruit DHT 传感器库深度解析:从单总线时序到 ESP32 温湿度采集实战
2026/9/13 1:16:11 网站建设 项目流程

基于 Tasmota 仓库的 Adafruit DHT 传感器库深度解析:从单总线时序到 ESP32 温湿度采集实战

【免费下载链接】TasmotaAlternative firmware for ESP8266 and ESP32 based devices with easy configuration using webUI, OTA updates, automation using timers or rules, expandability and entirely local control over MQTT, HTTP, Serial or KNX. Full documentation at项目地址: https://gitcode.com/GitHub_Trending/ta/Tasmota

导读

DHT11/DHT22 系列是 ESP32 与 ESP8266 项目中最常用的低成本温湿度传感器,而 Adafruit 出品的 DHT sensor library 则是 Arduino 生态中最经典的驱动实现。本文以 Tasmota 仓库内随固件一并分发的 DHT-sensor-library(版本 1.4.3,MIT 协议)为主体,结合其 DHT.cpp 源码与 Tasmota 传感器驱动 xsns_06_dht_v7.ino 的实际集成方式,系统讲解该库的 API 用法、单总线通信时序原理、常见坑点排查,以及如何将温湿度数据接入 Tasmota 的 WebUI 与 MQTT 上报链路。读完本文,你将能够独立完成 DHT 传感器的接线、驱动移植与生产级轮询配置。

一、库的定位与依赖

该库官方描述为"Arduino library for DHT series of low-cost temperature/humidity sensors",即针对 DHT 系列低成本温湿度传感器的 Arduino 驱动库。它在当前仓库中以第三方库形式被纳入lib/libesp32目录,专供 ESP32 目标编译使用,其 library.properties 声明的适用架构为architectures=esp32

使用该库有两个前置依赖:

  • Adafruit Unified Sensor Driver(Adafruit_Sensor):这是库的硬性依赖。从 DHT_Unified_Sensor.ino 可看到,Unified 模式需要同时#include <Adafruit_Sensor.h>#include <DHT.h>#include <DHT_U.h>;即便只使用原生 API,DHT.h 也要求系统中安装 Adafruit Unified Sensor Library 才能编译通过。
  • Arduino 核心库DHT.h顶部#include "Arduino.h",依赖 Arduino 的pinModedigitalReaddelayMicroseconds等标准 API。

安装方式:通过 Arduino IDE 的 Library Manager 搜索 "DHT sensor library" 一键安装;或如本仓库做法,将库目录整体放入工程的lib/目录由 PlatformIO 自动解析。

注意:在当前 Tasmota 仓库中,该库位于lib/libesp32(仅 ESP32 构建时启用),而 ESP8266 构建并不使用此目录下的版本——从 Tasmota 的dht_delays_const表中#ifdef ESP8266#else分支的差异即可看出双平台有不同的时序处理策略。

二、核心 API 与传感器类型常量

库的核心是DHT类,定义于 DHT.h,公开接口如下:

方法签名说明
构造函数DHT(uint8_t pin, uint8_t type, uint8_t count = 6)指定数据引脚、传感器类型;count参数在 v1.4.x 已弃用(源码中以(void)count规避告警,读取算法会依据处理器速度自适应)
初始化void begin(uint8_t usec = 55)将数据引脚配置为INPUT_PULLUP,并设置读取前数据线上拉等待时间(默认 55 微秒)
读温度float readTemperature(bool S = false, bool force = false)S=true返回华氏度,默认摄氏度;force=true强制发起新读取
读湿度float readHumidity(bool force = false)返回相对湿度百分比
读原始值bool read(bool force = false)执行完整单总线读取并做校验和验证
温度换算convertCtoF(float)/convertFtoC(float)摄氏度与华氏度互转
体感温度computeHeatIndex(bool isFahrenheit = true)/computeHeatIndex(float t, float h, bool isFahrenheit)基于 Rothfusz 与 Steadman 公式计算热指数

传感器类型由 DHT.h 中的常量标识:

static const uint8_t DHT11{11}; /**< DHT TYPE 11 */ static const uint8_t DHT12{12}; /**< DHY TYPE 12 */ static const uint8_t DHT21{21}; /**< DHT TYPE 21 */ static const uint8_t DHT22{22}; /**< DHT TYPE 22 */ static const uint8_t AM2301{21}; /**< AM2301 */

从源码可见 DHT21 与 AM2301 共用类型 21,DHT22、AM2302、AM2321 均以类型 22 处理——这与 Tasmota 驱动中GPIO_DHT22同时覆盖 "DHT21, DHT22, AM2301, AM2302, AM2321" 的注释完全一致。

2.1 不同型号的数据解码差异

DHT.cpp 展示了同一条 40 bit 数据流在不同型号下的解码规则,这是选型时必须理解的核心差异:

  • DHT11:温度整数位存于data[2]data[3]高 4 位为符号位(0x80置位表示负温)、低 4 位为小数位(×0.1);湿度为data[0] + data[1]*0.1。分辨率低,典型精度 ±2°C / ±5%RH。
  • DHT12:与 DHT11 类似,但符号位在data[2]的最高位(0x80)。
  • DHT22/DHT21:温度与湿度均为 16 bit 大端数据((word)data[0]<<8 | data[1]),整体乘以 0.1,符号位在温度高字节0x80。分辨率 0.1 单位,精度 ±0.5°C / ±2%RH,是精度敏感场景的推荐型号。

读取失败时返回值是NAN,因此调用方必须用isnan()判断(见下文示例)。

三、单总线时序读取原理(源码级)

DHT 系列采用自定义单总线协议,无标准时钟线,时序极敏感。核心实现在 DHT.cpp 的read()方法中,完整流程如下:

  1. 节流保护MIN_INTERVAL定义为 2000 毫秒(DHT.cpp),非force模式下若距上次读取不足 2 秒,直接返回上次结果,避免高频轮询打乱传感器时序。begin()中将_lastreadtime初始化为millis() - MIN_INTERVAL,保证首次调用必然发起真实读取。
  2. 发送起始信号pinMode(_pin, INPUT_PULLUP)让上拉电阻抬升电平,随后切换为OUTPUT并拉低数据线,持续时间按型号区分:DHT22/DHT21 为delayMicroseconds(1100)(数据手册要求"至少 1ms"),DHT11 为delay(20)(手册要求至少 18ms)。
  3. 等待应答:数据线恢复高电平后,delayMicroseconds(pullTime)(默认 55µs)等待传感器拉低应答。此处的pullTimebegin(usec)参数,Tasmota 中对应DhtDelay命令的第二个延时参数。
  4. 中断锁定:读取 80 个脉冲期间通过InterruptLock类(DHT.h)调用noInterrupts()关闭全局中断——这是时序正确的关键,ESP32 上若不禁中断极易读到错乱数据(Tasmota v6 版本更新日志中专门提到"Fix ESP32 interrupt control to solve intermittent results")。
  5. 脉冲测量expectPulse()(DHT.cpp)以忙等循环统计引脚停留在目标电平的周期数,_maxcycles = microsecondsToClockCycles(1000)即 1ms 超时阈值。位判定逻辑:50µs 低脉冲后,高脉冲周期短于低脉冲判为 0,长于低脉冲判为 1。
  6. 校验和data[4] == ((data[0]+data[1]+data[2]+data[3]) & 0xFF)通过才返回true,否则打印DHT checksum failure!

平台差异:AVR 平台使用直接端口访问(*portInputRegister(_port))以提升时序捕获能力;ESP8266/ESP32 则退化为digitalRead轮询。另外 ESP8266 分支在读取前会调用yield()以喂狗并让出 WiFi 协程(DHT.cpp)。

调试提示:取消 DHT.h 中//#define DHT_DEBUG的注释,可让库通过Serial打印每次读取的 5 字节原始数据与校验和,是排查时序与接线问题的利器。

四、两种使用模式与示例代码

4.1 原生 API 模式(DHTtester)

dhttester 示例 展示最简用法:

#include "DHT.h" #define DHTPIN 2 // 数据引脚 #define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321 DHT dht(DHTPIN, DHTTYPE); void setup() { Serial.begin(9600); dht.begin(); } void loop() { delay(2000); // 每次读取间隔建议不小于 2 秒 float h = dht.readHumidity(); float t = dht.readTemperature(); float f = dht.readTemperature(true); // 华氏度 if (isnan(h) || isnan(t) || isnan(f)) { Serial.println(F("Failed to read from DHT sensor!")); return; } float hif = dht.computeHeatIndex(f, h); // 华氏热指数 float hic = dht.computeHeatIndex(t, h, false); // 摄氏热指数 Serial.print(F("Humidity: ")); Serial.print(h); Serial.print(F("% Temperature: ")); Serial.print(t); Serial.print(F("°C ")); Serial.print(f); Serial.print(F("°F Heat index: ")); Serial.print(hic); Serial.print(F("°C ")); Serial.print(hif); Serial.println(F("°F")); }

要点:

  • 接线:引脚 1 接电源(3.3V/5V 视板子而定),引脚 2 接数据脚并外接 10KΩ 上拉电阻到电源,引脚 3/4 接地。
  • 注释中特别提醒 ESP8266 的 Feather HUZZAH 板建议使用 3、4、5、12、13、14 号引脚,引脚 15 虽可用但烧录固件时必须断开 DHT。
  • 一次完整读取约耗时 250ms,且数据可能滞后最多 2 秒,适合秒级采样。

4.2 Unified Sensor 模式(DHT_Unified_Sensor)

dht_unified 示例 通过DHT_Unified包装类接入 Adafruit 的统一传感器框架,可获得跨传感器的标准元数据(型号、版本、唯一 ID、量程、分辨率)与事件驱动接口:

#include <Adafruit_Sensor.h> #include <DHT.h> #include <DHT_U.h> #define DHTPIN 2 #define DHTTYPE DHT22 DHT_Unified dht(DHTPIN, DHTTYPE); void setup() { Serial.begin(9600); dht.begin(); sensor_t sensor; dht.temperature().getSensor(&sensor); // 打印 sensor.name / sensor.version / sensor.max_value / sensor.min_value / sensor.resolution } void loop() { delay(2000); sensors_event_t event; dht.temperature().getEvent(&event); if (isnan(event.temperature)) { Serial.println(F("Error reading temperature!")); } else { Serial.print(F("Temperature: ")); Serial.print(event.temperature); Serial.println(F("°C")); } dht.humidity().getEvent(&event); // 同样以 isnan(event.relative_humidity) 判断湿度 }

Unified 模式的收益是代码可无缝切换到其他 Adafruit 传感器,代价是额外引入Adafruit_Sensor依赖;若只服务 DHT 单一传感器,原生模式更轻量。

五、Tasmota 中的实际集成:从库到 WebUI/MQTT 的完整链路

在 Tasmota 固件中,DHT 支持由 xsns_06_dht_v7.ino 实现,编译开关为USE_DHT,可同时挂载最多 4 路传感器(DHT_MAX_SENSORS默认 4)。虽然 Tasmota 出于固件体积与灵活性的考虑,在 ESP32 构建中倾向于内置独立的位级读取实现而非直接调用 Adafruit 库类,但两者的协议时序完全同源——Adafruit 库的begin()read()expectPulse()在 Tasmota 驱动中分别对应DhtRead()DhtExpectPulse()等函数,且 Tasmota 的延时参数表直接来源于 DHT 数据手册的同一组时序要求:

传感器Adafruit 库起始低电平延时Tasmotadelay_lo(ESP32)
DHT11delay(20)(≥18ms)19000µs
DHT22/DHT21/AM2301/AM2302/AM2321delayMicroseconds(1100)2000µs
SI7021 / THS-01400µs
MS01400µs

5.1 配置命令:DhtDelay

Tasmota 将 Adafruit 库构造函数中固定的时序参数开放为运行时命令,对应 v7 驱动新增的DhtDelay

DhtDelay1 // 查看第一路传感器当前延时 DhtDelay1 1 // 重置为默认值 DhtDelay1 500,40 // 设置低电平延时 500µs、上拉等待 40µs DhtDelay4 500,40 // 设置第四路传感器

delay_lo(对应 Adafruit 的起始信号低电平时间)与delay_hi(对应begin(usec)pullTime)均可单独微调,用于适配走线过长、上拉电阻偏差等导致的时序漂移——这正是 Adafruit 库注释中"algorithm adjusts itself based on the speed of the processor"之外,留给用户的最终兜底手段。

5.2 故障排查的源码依据

  • 读取失败与NAN:Tasmota 驱动对每次读取保留lastresult与重试上限DHT_MAX_RETRY(8 次),对应 Adafruit 库中read()在起始脉冲超时或校验和不匹配时置_lastresult=false并返回NAN的行为。
  • ESP32 上的时序敏感点:Tasmota 源码注释明确写道,SI7021/MS01 在 ESP32 上delayMicroseconds(400)"Higher (or lower) results in Timeout waiting for high pulse on ESP32",说明 ESP32 的中断延迟与 GPIO 读写开销显著影响单总线时序,这也是 Adafruit 库用InterruptLock关闭中断读取脉冲的根本原因。
  • 平台差异:ESP8266 与 ESP32 在 SI7021/MS01 的延时参数上不同(500/450µs vs 400µs),移植代码到另一平台时必须同步调整。

5.3 数据上报链路

DHT 读数在 Tasmota 中注册为XSNS_06传感器,WebUI 与 MQTT 上报的 JSON 结构包含"Temperature""Humidity"字段,配合TelePeriod即可周期推送至 Home Assistant 等平台。若希望获得更高的采样频率或自定义上报逻辑,可直接在 Berry 脚本中调用tasmota.read_sensors()读取本驱动缓存的温湿度值,无需直接操作 GPIO。

六、移植与使用注意事项(经验汇总)

综合源码与 Tasmota 集成实践,以下几点对稳定性至关重要:

  1. 上拉电阻不可省略:DHT 是开漏输出,数据线必须通过 4.7K~10KΩ 电阻上拉,库内INPUT_PULLUP仅作为内部弱上拉辅助,长线场景应使用外部电阻。
  2. 采样频率限制:库内部强制 2 秒最小间隔(MIN_INTERVAL),force=true虽可绕过,但频繁强读会降低传感器可靠性并增加校验失败概率,业务层建议保持 ≥2s 轮询。
  3. 中断敏感:读取期间全局关中断约 3~4ms(80 个脉冲 × 数十µs 量级),在 ESP32 上若同时运行 WiFi/蓝牙等实时任务,建议评估是否可接受;Tasmota 选择在应用层错峰调度。
  4. 接线长度与电源:数据线建议 <20m,供电需稳定 3.3V/5V 且共地;长线或弱上拉时优先用DhtDelay/begin(usec)微调时序。
  5. 读取结果必须判空readTemperature/readHumidity失败返回NAN,示例代码统一用isnan()防护,避免脏数据进入控制逻辑。
  6. 型号常量勿混用:DHT11 与 DHT22 的起始信号时长、数据位宽、校验规则均不同,DHTTYPE选错将导致持续超时或数值错乱。

七、总结

Adafruit DHT sensor library(v1.4.3)以约 400 行代码完整实现了 DHT 单总线协议的起始信号、80 脉冲采样、位判定与校验和验证,并通过InterruptLock、按平台分支的expectPulse与 2 秒节流机制兼顾了时序精度与系统稳定性。在当前 Tasmota 仓库中,它既是 ESP32 工程的第三方依赖(lib/libesp32/DHT-sensor-library),也是理解 xsns_06_dht_v7.ino 内部位级读取实现的最佳参照——两者共享同一份协议时序,而 Tasmota 额外提供了DhtDelay命令让延时参数可在线调节。对开发者而言,理解本库的 API 与底层时序,等于同时掌握了在 Arduino 原生环境与 Tasmota 生态中稳定采集 DHT11/DHT22 温湿度数据的能力。

该库基于 MIT 许可分发(详见 license.txt),由 Adafruit Industries 维护,源码可自由用于商业与非商业项目,其代码规范与 doxygen 注释风格亦值得作为自研传感器驱动的模板参考。

【免费下载链接】TasmotaAlternative firmware for ESP8266 and ESP32 based devices with easy configuration using webUI, OTA updates, automation using timers or rules, expandability and entirely local control over MQTT, HTTP, Serial or KNX. Full documentation at项目地址: https://gitcode.com/GitHub_Trending/ta/Tasmota

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询