ESP32 使用ESP-IDF驱动DHT11温湿度计源码分享

ESP32 使用ESP-IDF驱动DHT11温湿度计源码分享

一、源码分享

1、效果展示

2、开发环境搭建

参考我这篇博文:VS Code 在线安装ESP-IDF,ESP32开发环境搭建详细教程

3、源码分享

3.1、dht11.h

c 复制代码
#ifndef __DHT11_H
#define __DHT11_H

#include "driver/gpio.h" 
#include "esp_log.h"

/* 引脚定义 */
#define DHT11_DQ_GPIO_PIN       GPIO_NUM_0

/* IO操作 */
#define DHT11_DQ_IN             gpio_get_level(DHT11_DQ_GPIO_PIN)

/* DHT11端口定义 */
#define DHT11_DQ_OUT(x)         do { x ?                                   \
                                     gpio_set_level(DHT11_DQ_GPIO_PIN, 1): \
                                     gpio_set_level(DHT11_DQ_GPIO_PIN, 0); \
                                   } while(0)

/* 函数声明 */
void dht11_reset(void);                                 /* 复位DHT11 */
uint8_t dht11_init(void);                               /* 初始化DHT11 */
uint8_t dht11_check(void);                              /* 等待DHT11的回应 */
uint8_t dht11_read_data(short *temp, short *humi);      /* 读取温湿度 */

#endif

3.2、 dht11.c

c 复制代码
#include "dht11.h"


/**
 * @brief       复位DHT11
 * @param       无
 * @retval      无
 */
void dht11_reset(void)
{
    DHT11_DQ_OUT(0);            /* 拉低DQ */
    esp_rom_delay_us(25000);    /* 拉低至少18ms */
    DHT11_DQ_OUT(1);            /* DQ=1 */
    esp_rom_delay_us(30);       /* 主机拉高10~35us */
}

/**
 * @brief       等待DHT11的回应
 * @param       无
 * @retval      0,DHT11正常; 1,DHT11异常/不存在
 */
uint8_t dht11_check(void)
{
    uint8_t retry = 0;
    uint8_t rval = 0;

    while (DHT11_DQ_IN && retry < 100)      /* DHT11会拉低40~80us */
    {
        retry++;
        esp_rom_delay_us(1);
    }

    if (retry >= 100)
    {
        rval = 1;
    }
    else
    {
        retry = 0;

        while (!DHT11_DQ_IN && retry < 100) /* DHT11拉低后会再次拉高87us */
        {
            retry++;
            esp_rom_delay_us(1);
        }
        
        if (retry >= 100)
        {
            rval = 1;
        }
    }
    
    return rval;
}

/**
 * @brief       从DHT11读取一个位
 * @param       无
 * @retval      读取到的位值: 0 / 1
 */
uint8_t dht11_read_bit(void)
{
    uint8_t retry = 0;

    while (DHT11_DQ_IN && retry < 100)  /* 等待变为低电平 */
    {
        retry++;
        esp_rom_delay_us(1);
    }

    retry = 0;

    while (!DHT11_DQ_IN && retry < 100) /* 等待变高电平 */
    {
        retry++;
        esp_rom_delay_us(1);
    }

    esp_rom_delay_us(40);               /* 等待40us */

    if (DHT11_DQ_IN)                    /* 根据引脚状态返回 bit */
    {
        return 1;
    }
    else 
    {
        return 0;
    }
}

/**
 * @brief       从DHT11读取一个字节
 * @param       无
 * @retval      data:读到的数据
 */
static uint8_t dht11_read_byte(void)
{
    uint8_t i, data = 0;

    for (i = 0; i < 8; i++)         /* 循环读取8位数据 */
    {
        data <<= 1;                 /* 高位数据先输出, 先左移一位 */
        data |= dht11_read_bit();   /* 读取1bit数据 */
    }

    return data;
}

/**
 * @brief       从DHT11读取一次数据(温度和湿度的整数部分)
 * @param       temp: 温度值(范围:-20~60°)(放大10倍)
 * @param       humi: 湿度值(范围:5%~95%)(放大10倍)
 * @retval      0, 正常; 1, 失败
 */
uint8_t dht11_read_data(short *temp, short *humi)
{
    uint8_t buf[5];
    uint8_t i;
    short raw_temp = 0;
    short raw_humi = 0;

    dht11_reset();

    if (dht11_check() == 0)
    {
        for (i = 0; i < 5; i++) /* 读取40位数据 buf[0]湿度高8位 buf[1]湿度低8位 buf[2]温度高8位 buf[3]温度低8位 */
        {
            buf[i] = dht11_read_byte();
        }

        if ((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4])  /* 数据校验正确 */
        {
            raw_humi = buf[0] * 10 + buf[1];    			/* 获取湿度数据 */

            if (buf[3] & 0x80)                  			/* 温度为负值 */
            {
                raw_temp = buf[2] * 10 + (buf[3] & 0x7F);
                raw_temp = -raw_temp;
            }
            else
            {
                raw_temp = buf[2] * 10 + buf[3];  			/* 温度数据 */
            }

            *humi = raw_humi;
            *temp = raw_temp;
        }
    }
    else
    {
        return 1;
    }
    
    return 0;
}

/**
 * @brief       初始化DHT11
 * @param       无
 * @retval      0, 正常; 1, 不存在/不正常
 */
uint8_t dht11_init(void)
{
    gpio_config_t gpio_init_struct;

    gpio_init_struct.intr_type = GPIO_INTR_DISABLE;             /* 失能引脚中断 */
    gpio_init_struct.mode = GPIO_MODE_INPUT_OUTPUT_OD;          /* 开漏模式的输入和输出 */
    gpio_init_struct.pull_up_en = GPIO_PULLUP_ENABLE;           /* 使能上拉 */
    gpio_init_struct.pull_down_en = GPIO_PULLDOWN_DISABLE;      /* 失能下拉 */
    gpio_init_struct.pin_bit_mask = 1ull << DHT11_DQ_GPIO_PIN;  /* 设置的引脚的位掩码 */
    gpio_config(&gpio_init_struct);                             /* 配置DHT11引脚 */

    dht11_reset();
    return dht11_check();
}

3.3、 main.c

cpp 复制代码
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "nvs_flash.h"
#include "led.h"
#include "dht11.h"
#include <stdio.h>


void app_main(void)
{
    esp_err_t ret;
    uint8_t t = 0;
    short temperature = 0;
    short humidity = 0;

    ret = nvs_flash_init();     /* 初始化NVS */
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ESP_ERROR_CHECK(nvs_flash_init());
    }

    led_init();                 /* LED初始化 */
    while (dht11_init())        /* 初始化DHT11数字温湿度传感器 */
    {
        printf("DHT11 Error\r\n");
        vTaskDelay(pdMS_TO_TICKS(200));
    }

    
    while (1)
    {
        if (t % 10 == 0)    /* 每100ms读取一次 */
        {
            dht11_read_data(&temperature, &humidity);               /* 读取温湿度值 */
            printf("temp = %d,humidity = %d\r\n",temperature /10,humidity/10);
        }

        vTaskDelay(pdMS_TO_TICKS(10));
        t++;

        if (t == 20)
        {
            t = 0;
            LED0_TOGGLE();
        }
    }
}

二、dht11温湿度计介绍

1、 概述

DHT11 是一款低成本、低功耗的数字式温湿度复合传感器。它通过单总线协议与微控制器(如 Arduino、Raspberry Pi、STM32 等)进行通信,输出经过校准的数字信号。其核心部分是一个电阻式湿度感测元件和一个热敏电阻(NTC)温度感测元件,内部集成了一个 8 位微控制器进行信号处理。

2、主要特性

  • 测量范围:
    • 湿度: 5 % ∼ 90 % R H 5\% \sim 90\% RH 5%∼90%RH(相对湿度)
    • 温度: − 20 ∘ C ∼ 95 ∘ C -20^{\circ}C \sim 95^{\circ}C −20∘C∼95∘C
  • 精度:
    • 湿度: ± 5 % R H \pm 5\% RH ±5%RH(典型值)
    • 温度: ± 2 ∘ C \pm 2^{\circ}C ±2∘C(典型值)
    • 注意:精度相对较低,适用于一般性监测,不适用于高精度应用。
  • 分辨率:
    • 湿度: 1 % R H 1\% RH 1%RH
    • 温度: 1 ∘ C 1^{\circ}C 1∘C
  • 响应时间: 约 1 ∼ 2 1 \sim 2 1∼2 秒
  • 工作电压: 3.3 V ∼ 5.5 V 3.3V \sim 5.5V 3.3V∼5.5V DC
  • 接口: 单总线数字信号输出
  • 尺寸: 小型化封装
  • 功耗: 测量时约 2.5 m A 2.5mA 2.5mA,待机时很低

3、引脚定义

DHT11 模块通常有 3 个或 4 个引脚:

  1. VCC (或 +): 电源正极( 3.3 V ∼ 5 V 3.3V \sim 5V 3.3V∼5V)
  2. GND (或 -): 电源负极/地
  3. DATA (或 OUT): 数据输入/输出引脚(单总线)
  4. NC (可选): 空引脚(不连接)

4、工作原理与通信协议

DHT11 使用单总线协议进行通信,这意味着数据线同时用于发送和接收数据。通信过程由微控制器(主机)发起和协调:

  1. 主机发起通信:
    • 主机将 DATA 线拉低至少 18 m s 18ms 18ms(起始信号)。
    • 然后主机释放总线(将 DATA 线置高),等待 DHT11 响应。
  2. DHT11 响应:
    • DHT11 检测到起始信号后,会先将 DATA 线拉低约 80 μ s 80\mu s 80μs(应答信号)。
    • 接着 DHT11 再将 DATA 线拉高约 80 μ s 80\mu s 80μs,表示准备发送数据。
  3. 数据传输:
    • DHT11 开始发送 40 位数据。数据由 5 个字节组成:
      • 湿度整数部分(8 位)
      • 湿度小数部分(8 位)(DHT11 此部分恒为 0)
      • 温度整数部分(8 位)
      • 温度小数部分(8 位)(DHT11 此部分恒为 0)
      • 校验和(8 位,等于前四个字节之和的低 8 位)
    • 每一位数据由一个低电平起始位(约 50 μ s 50\mu s 50μs)开始。
    • 紧接着是一个高电平脉冲:
      • 高电平持续约 26 μ s ∼ 28 μ s 26\mu s \sim 28\mu s 26μs∼28μs 表示逻辑 '0'
      • 高电平持续约 70 μ s 70\mu s 70μs 表示逻辑 '1'
    • 传输完 40 位后,DHT11 拉低 DATA 线约 50 μ s 50\mu s 50μs 后释放总线,进入空闲状态。

5、数据格式

如前所述,传输的 40 位数据(5 字节)含义如下:

  • Byte 0: 湿度整数 (Humidity High Byte)
  • Byte 1: 湿度小数 (Humidity Low Byte) (DHT11 固定为 0)
  • Byte 2: 温度整数 (Temperature High Byte)
  • Byte 3: 温度小数 (Temperature Low Byte) (DHT11 固定为 0)
  • Byte 4: 校验和 (Checksum) = (Byte0 + Byte1 + Byte2 + Byte3) & 0xFF

计算示例:

假设接收到的 5 个字节是:45, 00, 28, 00, 6D

  • 湿度 = 45 = 69 % R H 69\% RH 69%RH
  • 温度 = 28 = 40 ∘ C 40^{\circ}C 40∘C
  • 校验和计算: 45 + 00 + 28 + 00 = 109 ( 6 D h e x 6D_{hex} 6Dhex) 匹配,数据有效。

6、使用注意事项

  1. 电压匹配: 确保供电电压在 3.3 V ∼ 5 V 3.3V \sim 5V 3.3V∼5V 范围内。如果主机是 3.3 V 3.3V 3.3V 逻辑电平(如 Raspberry Pi),建议给 DHT11 也供 3.3 V 3.3V 3.3V,或在 DATA 线上加电平转换器,避免损坏主机 GPIO。
  2. 上拉电阻: DATA 线在空闲状态时由 DHT11 内部上拉到 VCC。对于长导线或强干扰环境,可在 DATA 线与 VCC 之间外接一个 4.7 K Ω ∼ 10 K Ω 4.7K\Omega \sim 10K\Omega 4.7KΩ∼10KΩ 的上拉电阻。
  3. 读取间隔: DHT11 功耗低,但其感测元件需要时间稳定。两次读取操作之间应间隔至少 1 1 1 秒。
  4. 时序要求: 通信协议对时序要求严格。在资源受限的单片机(如 Arduino)上,读取时可能需要暂时关闭中断以保证时序准确。
  5. 校验和: 务必检查校验和,确保数据传输无误。
  6. 环境: 避免将传感器置于结露、腐蚀性气体或过高温度的环境中。避免阳光直射。

三、ESP-IDF详解

1、ESP32 概述

ESP32 是由乐鑫科技(Espressif Systems)推出的一款高性能、低功耗、高集成度的 Wi-Fi & 蓝牙双模系统级芯片(SoC)。它广泛应用于物联网(IoT)、智能家居、工业控制等领域。其关键特性包括:

  • 双核处理器(通常为 Xtensa LX6,某些型号为 RISC-V)
  • 集成 Wi-Fi (802.11 b/g/n) 和蓝牙 (包括经典蓝牙和低功耗蓝牙 BLE)
  • 丰富的外设接口: S P I SPI SPI, I 2 C I2C I2C, I 2 S I2S I2S, U A R T UART UART, A D C ADC ADC, D A C DAC DAC, P W M PWM PWM, 触摸传感器等
  • 充足的内存(RAM 和 Flash 选项多样)
  • 强大的安全特性(如加密加速器)
  • 超低功耗设计,支持多种休眠模式

2、 ESP-IDF 详解

ESP-IDF (Espressif IoT Development Framework) 是乐鑫官方为 ESP32、ESP32-S 系列、ESP32-C 系列等芯片提供的开发框架和 SDK(软件开发工具包)。它是开发基于 ESP32 芯片应用程序的首选和官方推荐工具。

2.1 、ESP-IDF 的核心组件与架构

ESP-IDF 是一个分层、模块化的框架:

  • 硬件抽象层 (HAL): 提供对芯片硬件资源(如 GPIO、UART、SPI、I2C、定时器、ADC、DAC、RTC、Wi-Fi、蓝牙等)进行操作的统一接口,屏蔽底层硬件细节。
  • 驱动 (Drivers): 在 HAL 之上,提供更易用、功能更丰富的设备驱动(如 SPI Flash 驱动、SD 卡驱动、以太网驱动等)。
  • FreeRTOS: ESP-IDF 深度集成了开源的 FreeRTOS 实时操作系统内核。它提供了任务管理、队列、信号量、软件定时器、中断管理等机制,充分利用 ESP32 的多核特性(任务可以在不同核心上运行)。
  • 中间件:
    • Wi-Fi 协议栈: 实现 Wi-Fi 的 Station(客户端)、SoftAP(接入点)、Promiscuous(监听)模式。
    • 蓝牙协议栈: 实现经典蓝牙和低功耗蓝牙 (BLE) 的各种角色(如 GATT Client/Server, GAP Central/Peripheral)。
    • TCP/IP 协议栈 (lwIP): 轻量级的 TCP/IP 协议栈,支持 IPv4/IPv6 (部分型号)、DHCP、DNS、Socket API 等。
    • 文件系统 (FATFS/VFS): 支持在 SPI Flash 或外部存储设备上使用 FAT 文件系统。
    • 加密库: 提供 AES、SHA、RSA 等加密算法的软件和硬件加速实现。
    • HTTP/WebSocket/MQTT 等协议库: 方便构建网络应用。
  • 应用程序: 用户编写的业务逻辑代码,运行在 FreeRTOS 的任务中,通过调用下层 API 实现功能。

2.2 、ESP-IDF 开发环境与工具链

  • 工具链: 基于 GNU GCC 编译器(针对 Xtensa 或 RISC-V 架构)。
  • 构建系统: 使用 CMake (早期版本使用 GNU Make)。开发者编写 CMakeLists.txt 文件来定义项目结构、源文件、依赖组件等。
  • 配置工具: menuconfig。这是一个基于文本的图形化配置工具(类似于 Linux Kernel 的 make menuconfig),用于配置 ESP-IDF 的众多选项,如:
    • 选择目标芯片型号
    • 配置 Wi-Fi/BT 参数
    • 调整 FreeRTOS 设置(任务栈大小、优先级等)
    • 配置日志输出级别和方式
    • 配置内存布局
    • 启用/禁用特定功能和外设驱动
  • 核心工具 - idf.py 这是 ESP-IDF 提供的命令行工具,用于执行构建、烧录、调试、监视串口输出等几乎所有开发任务。常用命令如:
    • idf.py set-target esp32 (设置目标芯片)
    • idf.py menuconfig (启动配置工具)
    • idf.py build (编译项目)
    • idf.py -p PORT flash (烧录固件到设备,PORT 为串口,如 COM3 或 /dev/ttyUSB0)
    • idf.py -p PORT monitor (启动串口监视器,查看设备日志输出)
    • idf.py fullclean (彻底清理构建目录)

2.3 、ESP-IDF 开发流程简述

  1. 环境搭建: 在 Windows、Linux 或 macOS 上安装 ESP-IDF 开发环境(包括工具链、Python、Git 等)。
  2. 创建项目: 使用 idf.py create-project 或复制示例项目模板。
  3. 编写代码:main 目录下编写应用程序代码 (通常是 main.capp_main() 函数)。
  4. 配置项目: 运行 idf.py menuconfig 根据需求进行配置。
  5. 编译项目: 运行 idf.py build 生成可执行固件 (.bin 文件)。
  6. 烧录固件: 将 ESP32 开发板连接到电脑,运行 idf.py -p PORT flash 将固件烧录到设备 Flash 中。
  7. 监视输出: 运行 idf.py -p PORT monitor 查看设备运行日志,进行调试。可以使用 ESP_LOGx (如 ESP_LOGI, ESP_LOGE) 函数打印不同级别的日志。

2.4、 示例代码结构 (最简单的 Hello World)

c 复制代码
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

static const char *TAG = "MAIN";

void app_main(void)
{
    while (1) {
        ESP_LOGI(TAG, "Hello World!"); // 打印 Info 级别日志
        vTaskDelay(pdMS_TO_TICKS(1000)); // 延时 1000 毫秒 (FreeRTOS 延时)
    }
}

2.5、 优势与特点

  • 官方支持: 由芯片原厂维护,更新及时,与新芯片特性同步快。
  • 功能全面: 提供对 ESP32 所有硬件特性和外设的底层访问。
  • 性能优化: 针对 ESP32 硬件进行了深度优化(如 Wi-Fi/BT 共存)。
  • 稳定性高: 经过大量商业产品验证。
  • 社区活跃: 用户多,社区支持好,问题容易找到解决方案。
  • 文档完善: 官方提供详尽的 API 参考指南、编程指南和示例代码。
  • 模块化: 易于扩展和复用代码,方便管理大型项目。
  • 开源免费: 基于 Apache 2.0 许可证。

2.6 、适用场景

ESP-IDF 适用于需要深度控制硬件、追求高性能、高稳定性或需要使用 ESP32 特有功能(如超低功耗协处理器 ULP)的应用开发。对于快速原型开发,也可以考虑基于 ESP-IDF 构建的更高级框架(如 Arduino for ESP32、MicroPython),但这些框架最终都依赖于 ESP-IDF 的底层功能。

3、总结

ESP-IDF 是开发 ESP32 系列芯片的强大、灵活且功能完备的官方框架。它提供了从硬件操作到网络协议栈的全套解决方案,结合 FreeRTOS 实现了高效的多任务处理。虽然其学习曲线相对陡峭,尤其是对于不熟悉嵌入式开发和 RTOS 的开发者,但它提供了最直接、最强大的方式来充分利用 ESP32 的潜力,是开发复杂或高性能 ESP32 应用的基石。掌握 idf.py 工具链和 menuconfig 配置是使用 ESP-IDF 的关键。

相关推荐
小灰灰搞电子2 天前
ESP32 使用ESP-IDF WiFi一键配网 源码分享
esp32·一键配网
风痕天际2 天前
ESP32-S3开发教程6:硬件定时器
单片机·嵌入式硬件·嵌入式·esp32·freertos·esp32s3
小灰灰搞电子2 天前
ESP32+ESP-IDF 使用MQTT协议连接阿里云物联网平台源码分享
物联网·阿里云·esp32
小灰灰搞电子3 天前
ESP32 使用ESP-IDF 驱动红外遥控器源码分享
esp32·红外遥控
勇敢牛牛_3 天前
ESP32 + Rust 开发的简易语音助手
rust·嵌入式·esp32·语音助手
liwulin05065 天前
【ESP32-S3】ESP32-S3 + YDLIDAR X2 + ROS2 远程导航完整落地方案
esp32·ros2·ydlidar
风痕天际5 天前
ESP32-S3开发教程五-按键中断2(使用FreeRTOS)
单片机·嵌入式硬件·esp32·vs code·esp32s3·esp-idf
戏舟的嵌入式开源笔记6 天前
基于ESP32(PIO+Arduino)简单上手LVGL9
esp32·嵌入式软件
小灰灰搞电子6 天前
ESP32 使用ESP-IDF 建立WiFi热点(AP模式)并使用TCP客户端通信源码分享
tcp/ip·esp32·esp-idf