DHT11 是一个可以同时检测温度和湿度的传感器。它长这样:

四个针脚分别为 DD, DATA, NC,GND。NC 是空脚,不用管它。 也就是说, DHT11 传数据的引脚是 DATA,我这里把它接到空闲的 GPIO7 上。
电路原理图如下:

C 语言使用这个库:
idf.py add-dependency "esp-idf-lib/dht^1.2.0"
C 实现如下:
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "esp_log.h"
#include "dht.h"
#define DHT_GPIO_NUM 7 // GPIO引脚,根据实际情况修改
#define DHT_TYPE DHT_TYPE_DHT11 // 根据实际传感器型号选择
static const char* TAG = "DHT";
void app_main(void)
{
int16_t temperature;
int16_t humidity;
float f_temperature;
float f_humidity;
// 配置GPIO - 必须启用上拉电阻
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << DHT_GPIO_NUM),
.mode = GPIO_MODE_OUTPUT_OD, // 开漏输出
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
ESP_ERROR_CHECK(gpio_config(&io_conf));
gpio_set_level(DHT_GPIO_NUM, 1); // 设置为高电平闲置状态
ESP_LOGI(TAG, "Starting DHT sensor test...");
ESP_LOGI(TAG, "Sensor: DHT11, GPIO: %d", DHT_GPIO_NUM);
while(1) {
vTaskDelay(pdMS_TO_TICKS(2000)); // DHT传感器需要至少2秒的间隔
// 使用浮点数版本读取数据
esp_err_t result = dht_read_float_data(DHT_TYPE, DHT_GPIO_NUM, &f_humidity, &f_temperature);
if (result == ESP_OK) {
ESP_LOGI(TAG, "Float Readings: Humidity: %.1f%% Temp: %.1f°C",
f_humidity, f_temperature);
} else {
ESP_LOGE(TAG, "Could not read float data from sensor, error: %d", result);
}
}
}
输出如下:
I (240) sleep_gpio: Configure to isolate all GPIO pins in sleep state
I (247) sleep_gpio: Enable automatic switching of GPIO sleep configuration
I (253) main_task: Started on CPU0
I (263) main_task: Calling app_main()
I (263) DHT: Starting DHT sensor test...
I (263) DHT: Sensor: DHT11, GPIO: 7
I (301243) DHT: Float Readings: Humidity: 52.0% Temp: 20.0°C
I (396183) DHT: Float Readings: Humidity: 49.0% Temp: 20.0°C
I (725443) DHT: Float Readings: Humidity: 49.0% Temp: 20.0°C
I (1345583) DHT: Float Readings: Humidity: 50.0% Temp: 20.0°C
I (2525263) DHT: Float Readings: Humidity: 48.0% Temp: 20.0°C
问题点
目前这个电路接法,上面 C 代码实现的传感器返回的数据非常的慢(不应该叫慢,而是异常,大部分时候不返回数据)。大概要好几分钟才会返回一次数据。和实际传感器的描述严重不符。
最初的怀疑点是电压问题,由于我的 esp32s3 的供电针脚的电压是 3.3V,而传感器的工作电压是区间是3.3V - 5V。这是有可能达不到传感器的工作电压而导致传感器工作异常。
不过有趣的是,网络上有人和我一样碰到同样的问题,也有人实现上是可以工作正常的。
更有趣的是, 下面 Rust 代码就很正常,完全没有问题!
Rust 的实现
Rust 也不需要自己实现 DHT11 协议,因为有一个库可以直接使用。
embedded-dht-rs = { version = "0.5.0", features = ["dht11"] }
和 C一样,Rust 也需要把 GPIO 初始化一下,设置为开漏输出,并且启用上拉电阻。
let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
let peripherals = esp_hal::init(config);
let mut dht11_pin = Flex::new(peripherals.GPIO15);
dht11_pin.apply_output_config(
&OutputConfig::default()
.with_drive_mode(DriveMode::OpenDrain)
.with_pull(Pull::None),
);
dht11_pin.set_output_enable(true);
dht11_pin.set_input_enable(true);
dht11_pin.set_high();
Rust 的实现输出如下:
I (107) esp_image: segment 3: paddr=000129d8 vaddr=00000000 size=0d640h ( 54848)
I (126) esp_image: segment 4: paddr=00020020 vaddr=42010020 size=02c68h ( 11368) map
I (130) boot: Loaded app from partition at offset 0x10000
I (130) boot: Disabling RNG early entropy source...
DHT11 - Temperature: 21 °C, humidity: 47 %
DHT11 - Temperature: 21 °C, humidity: 47 %
DHT11 - Temperature: 21 °C, humidity: 47 %
DHT11 - Temperature: 21 °C, humidity: 47 %
DHT11 - Temperature: 21 °C, humidity: 47 %
DHT11 - Temperature: 21 °C, humidity: 47 %
DHT11 - Temperature: 21 °C, humidity: 47 %
DHT11 - Temperature: 21 °C, humidity: 47 %
DHT11 - Temperature: 21 °C, humidity: 47 %
DHT11 - Temperature: 21 °C, humidity: 47 %
DHT11 - Temperature: 21 °C, humidity: 47 %
DHT11 - Temperature: 21 °C, humidity: 47 %
DHT11 - Temperature: 21 °C, humidity: 47 %
DHT11 - Temperature: 21 °C, humidity: 47 %
DHT11 - Temperature: 21 °C, humidity: 47 %
DHT11 - Temperature: 21 °C, humidity: 47 %
完整实现:
#![no_std]
#![no_main]
#![deny(
clippy::mem_forget,
reason = "mem::forget is generally not safe to do with esp_hal types, especially those \
holding buffers for the duration of a data transfer."
)]
#![deny(clippy::large_stack_frames)]
use embedded_dht_rs::dht11::Dht11;
use esp_hal::main;
use esp_hal::{
clock::CpuClock,
delay::Delay,
gpio::{DriveMode, Flex, OutputConfig, Pull},
};
#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
loop {}
}
extern crate alloc;
esp_bootloader_esp_idf::esp_app_desc!();
#[allow(
clippy::large_stack_frames,
reason = "it's not unusual to allocate larger buffers etc. in main"
)]
#[main]
fn main() -> ! {
// generator version: 1.2.0
let delay = Delay::new();
rtt_target::rtt_init_print!();
let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
let peripherals = esp_hal::init(config);
let mut dht11_pin = Flex::new(peripherals.GPIO15);
dht11_pin.apply_output_config(
&OutputConfig::default()
.with_drive_mode(DriveMode::OpenDrain)
.with_pull(Pull::None),
);
dht11_pin.set_output_enable(true);
dht11_pin.set_input_enable(true);
dht11_pin.set_high();
let mut dht11 = Dht11::new(dht11_pin, Delay::new());
esp_alloc::heap_allocator!(#[esp_hal::ram(reclaimed)] size: 73744);
loop {
delay.delay_millis(2000);
match dht11.read() {
Ok(sensor_reading) => {
esp_println::println!(
"DHT11 - Temperature: {} °C, humidity: {} %",
sensor_reading.temperature,
sensor_reading.humidity
);
}
Err(error) => {
esp_println::dbg!("Failed to read DHT11 sensor: {:?}", error);
}
}
}
}
理论再接个小屏幕,就实现了一个温湿度计。
Why?
对于 C 代码为什么不正常,而 Rust 代码正常的问题,最初并没有找到原因。我甚至重新使用C代码实现了一次 DHT11 协议,但是结果还是一样。
由于 DHT11 协议是通过一条数据线发送高低电平信号来传输数据的,所以我怀疑是 C 代码在实现的时候,没有正确处理时间延时,导致传感器无法正常工作。
直到我有一次把 这个函数绑定到了 esp32s3 的 CPU1 上,它终于正常工作了!
xTaskCreatePinnedToCore(dht_task, "dht_task",4096, NULL, 3, NULL, 1); //放到 CPU1 上
这说明, 在 CPU0 上,有其他优化级更高的任务在和 DHT 抢占了时间片,导致 DHT 传感器无法正常工作。
查看了一下 sdkconfig ,发现配置里开启了 wifi 功能,但不确定是否会导致和 DHT 冲突。
还有一个点就是,DHT11 需要调用微秒级别的计时器,所以不能在通信的过程中做过于耗时的操作,否则会导致通信失败。比如,使用ESP_LOGI打印日志。。。