Jan 31, 2026
4 min read
ESP32,
Rust,
C,
Embedded Systems,

使用 Rust 做嵌入式开发:ESP32S3开发板上DHT11温湿度传感器的C语言和Rust实现对比分析

深入对比ESP32S3开发板上DHT11温湿度传感器的C语言和Rust实现,详细分析C语言版本遇到的响应缓慢问题及CPU任务调度影响,提供完整的代码示例和调试经验分享。

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

dht11

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

电路原理图如下:

dht11-schematic

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打印日志。。。