基于esp-idf框架的DHT22驱动
目录

一、DHT22概述
DHT22 是一款数字温湿度复合传感器,使用AM2302温湿度传感器,并连接了一个 8 位单片机,输出已校准的数字信号,采用单总线协议。
1.关键特性
-
湿度测量范围:0 ~ 100% RH,精度 ±2% RH
-
温度测量范围:-40 ~ 80℃,精度 ±0.5℃
-
分辨率:16 位(湿度)/ 16 位(温度)
-
采样周期:≥ 2 秒
-
供电电压:3.3 ~ 6V DC
-
接口:单总线(DATA)
2.引脚定义
| 引脚号 | 名称 | 功能说明 |
|---|---|---|
| 1 | VDD | 供电电源,3.3 ~ 6V DC |
| 2 | DATA | 串行数据总线(单总线) |
| 3 | NC | 空脚,请悬空(不连接) |
| 4 | GND | 电源地 |
3.原理图

注:模块已内置4.7k上拉电阻和100nF电容
4.单总线协议描述
DHT22 使用单总线协议,一次完整通信约 5ms,传输 40 bit 数据(高位先出)
4.1数据格式
| 字节 | 内容 | 说明 |
|---|---|---|
| 0 | 湿度高 8 位(整数部分) | 16 bit 湿度数据 |
| 1 | 湿度低 8 位(小数部分) | |
| 2 | 温度高 8 位(整数部分) | 16 bit 温度数据 |
| 3 | 温度低 8 位(小数部分) | |
| 4 | 校验和(CRC) | 用于验证数据完整性 |

4.2数据计算
-
湿度值(%RH) =
(湿度高8位 << 8 | 湿度低8位) / 10.0 -
温度值(℃) =
(温度高8位 << 8 | 温度低8位) / 10.0 -
校验规则:
(Byte0 + Byte1 + Byte2 + Byte3)的低 8 位应等于Byte4
4.3负值的处理
当温度 低于 0℃ 时,温度数据(16 bit)的最高位(第 15 位)置 1。
eg:-10.1℃ 的原始数据为 1000 0000 1010 1010(即 0x80AA)
5.通信时序

5.1 起始信号(主机发起)
-
主机(MCU)将 DATA 线拉低 ≥ 500 μs(建议 1~2 ms)。
-
释放总线,等待 20 ~ 40 μs。

5.2 从机响应
-
DHT22 拉低 DATA 线约 80 μs(响应信号)。
-
随后拉高 DATA 线约 80 μs(准备发送数据)。


5.3 数据位传输
-
每个 bit 以 约 50 μs 的低电平 开始(位同步信号)。
-
随后是高电平,其持续时间决定 bit 值:
-
高电平持续 26~28 μs → 逻辑 '0'
-
高电平持续 70 μs → 逻辑 '1'
-



5.4 结束信号
-
40 bit 数据传输完成后,从机拉低 DATA 线约 50 μs,然后释放总线,由外部上拉电阻拉高。

二、硬件搭建
1.总览
2.DHT22 → ESP32
|
DHT22 引脚 |
ESP32 引脚 |
说明 |
|---|---|---|
|
VCC |
3.3V |
供电(3.3V ~ 5V) |
|
GND |
GND |
共地 |
|
DATA |
GPIO 4 |
数据线(内部上拉已启用) |
3.ESP32 → USB to TLL
|
ESP32 引脚 |
功能 |
USB to TLL |
说明 |
|---|---|---|---|
|
GPIO 17 |
TX |
RX |
ESP32 发送 → 对端设备接收 |
|
GPIO 16 |
RX |
TX |
ESP32 接收 ← 对端设备发送 |
|
GND |
— |
GND |
共地(确保参考电平一致) |
三、软件环境
- VS Code + esp-idf插件
- 串口助手
四、驱动代码
1.DHT22
dht22.c
#include "dht22.h"
#include "driver/gpio.h"
#include "esp_rom_sys.h" // 提供 esp_rom_delay_us
#include "freertos/FreeRTOS.h"
#include "freertos/task.h" // 提供 vTaskDelay
#include "esp_log.h" // 提供 ESP_LOGI 宏定义
// 宏定义微秒延迟
#define DELAY_US(us) esp_rom_delay_us(us)
// 引脚操作宏
#define DHT22_PIN DHT22_GPIO_PIN
#define DHT22_HIGH() gpio_set_level(DHT22_PIN, 1) // 拉高总线
#define DHT22_LOW() gpio_set_level(DHT22_PIN, 0) // 拉低总线
#define DHT22_READ() gpio_get_level(DHT22_PIN) // 读取总线电平
// 设置引脚方向
#define DHT22_OUTPUT() gpio_set_direction(DHT22_PIN, GPIO_MODE_OUTPUT)
#define DHT22_INPUT() gpio_set_direction(DHT22_PIN, GPIO_MODE_INPUT)
// 上电初始化
void dht22_init(void){
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << DHT22_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&io_conf);
// 延时 1 秒,保证传感器稳定
vTaskDelay(pdMS_TO_TICKS(1000));
ESP_LOGI("DHT22", "DHT22 初始化完成, 引脚: %d", DHT22_PIN);
}
// 读取原始 40bit 数据(5 字节),成功返回 true
static bool dht22_read_raw(uint8_t data[5]){
uint8_t i, j;
uint8_t counter = 0;
// 1. 主机发起起始信号:拉低总线至少 500us(此处用 2ms)后释放总线,等待 20~40us 后检测从机响应
DHT22_OUTPUT();
DHT22_LOW();
DELAY_US(2000);
DHT22_HIGH();
DELAY_US(40); // 释放后等待 20~40us
// 2. 切换为输入,检测从机响应
DHT22_INPUT();
// 等待从机拉低总线(从机响应低电平约 80us)
uint32_t timeout = 0;
while (DHT22_READ() == 1){
DELAY_US(1);
if (++timeout > 100) return false; // 超时
}
timeout = 0;
while (DHT22_READ() == 0){
DELAY_US(1);
if (++timeout > 100) return false; // 超时
}
// 响应后的高电平(约 80us),随后进入数据传输阶段
timeout = 0;
while (DHT22_READ() == 1){
DELAY_US(1);
if (++timeout > 100) return false; // 超时
}
// 3. 读取 40 位数据(5 字节)
for (j = 0; j < 5; j++){
data[j] = 0;
for (i = 0; i < 8; i++){
// 等待每个 bit 开始的低电平(约 50us),即数据位起始位
timeout = 0;
while (DHT22_READ() == 0){
DELAY_US(1);
if (++timeout > 100) return false; // 超时
}
// 测量高电平持续时间
timeout = 0;
while (DHT22_READ() == 1){
DELAY_US(1);
if (++timeout > 100) return false;
}
// 高电平持续时间 > 50us 视为 '1',否则为 '0'(高电平持续时间70us表示1,26-28us表示0)
if (timeout > 40) {
data[j] |= (1 << (7 - i)); // 高位先出
}
}
}
// 4. 结束信号:从机拉低 50us 后释放总线(无需特殊处理)
return true;
}
/*
数据格式:
40bit数据=16bit湿度数据+16bit温度数据+8bit校验和, 高位先出
校验:
(湿度高8位+湿度低8位+温度高8位+温度低8位)的末8位=校验和
*/
// CRC 校验:前 4 字节之和的低 8 位等于校验字节
static bool dht22_check_crc(const uint8_t data[5]){
uint8_t sum = data[0] + data[1] + data[2] + data[3]; // 截取后8位
return (sum == data[4]);
}
// 解析数据,处理负温度
static void dht22_parse_data(const uint8_t data[5], float *humidity, float *temperature){
uint16_t rawHum = (data[0] << 8) | data[1]; // 高位左移 8 位,低位直接拼接,形成16位湿度数据
uint16_t rawTemp = (data[2] << 8) | data[3]; // 高位左移 8 位,低位直接拼接,形成16位温度数据
*humidity = rawHum / 10.0f;
if (rawTemp & 0x8000){ // 温度最高位为 1 表示负值
rawTemp = rawTemp & 0x7FFF;
*temperature = - (rawTemp / 10.0f);
}
else{
*temperature = rawTemp / 10.0f;
}
}
// 公开的读取函数
bool dht22_read(float *humidity, float *temperature){
uint8_t data[5] = {0};
if (!dht22_read_raw(data)) {
ESP_LOGE("DHT22", "读取原始数据失败");
return false;
}
ESP_LOGI("DHT22", "Raw: %02X %02X %02X %02X %02X", data[0], data[1], data[2], data[3], data[4]);
if (!dht22_check_crc(data)) {
ESP_LOGE("DHT22", "校验和错误,计算和=0x%02X, 收到=0x%02X", (data[0]+data[1]+data[2]+data[3]) & 0xFF, data[4]);
return false;
}
dht22_parse_data(data, humidity, temperature);
return true;
}
dht22.h
#ifndef DHT22_H
#define DHT22_H
#include <stdint.h>
#include <stdbool.h>
#define DHT22_GPIO_PIN GPIO_NUM_4
void dht22_init(void);
bool dht22_read(float *humidity, float *temperature);
#endif
CMakeLists.txt
idf_component_register(
SRCS "dht22.c"
INCLUDE_DIRS "."
REQUIRES driver
)
2.串口发送
uart_sender.c
#include "uart_sender.h"
#include "driver/uart.h"
#include "esp_log.h"
#include <stdio.h>
#include <string.h>
static const char *TAG = "UART_SENDER";
void uart_sender_init(void)
{
// 配置 UART 参数
const uart_config_t uart_config = {
.baud_rate = UART_SEND_BAUDRATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
// 安装 UART 驱动
ESP_ERROR_CHECK(uart_driver_install(UART_SEND_PORT, 2048, 0, 0, NULL, 0));
// 配置 UART 参数
ESP_ERROR_CHECK(uart_param_config(UART_SEND_PORT, &uart_config));
// 设置 TX/RX 引脚
ESP_ERROR_CHECK(uart_set_pin(UART_SEND_PORT,
UART_SEND_TX_PIN,
UART_SEND_RX_PIN,
UART_PIN_NO_CHANGE, // RTS
UART_PIN_NO_CHANGE)); // CTS
ESP_LOGI(TAG, "UART 发送器初始化完成, TX:%d, RX:%d, 波特率:%d",
UART_SEND_TX_PIN, UART_SEND_RX_PIN, UART_SEND_BAUDRATE);
}
bool uart_send_th(float temperature, float humidity)
{
// 格式化数据: T:xx.x,H:xx.x\n
char buffer[64]; //发送缓冲区
int len = snprintf(buffer, sizeof(buffer), "T:%.1f,H:%.1f\r\n", temperature, humidity);
if (len < 0 || len >= sizeof(buffer)) {
ESP_LOGE(TAG, "数据格式化失败");
return false;
}
// 通过 UART 发送
int sent = uart_write_bytes(UART_SEND_PORT, buffer, len);
if (sent != len) {
ESP_LOGE(TAG, "UART 发送失败, 预期:%d, 实际:%d", len, sent);
return false;
}
ESP_LOGI(TAG, "已发送: %s", buffer);
return true;
}
uart_sender.h
#ifndef UART_SENDER_H
#define UART_SENDER_H
#include <stdbool.h>
#include "driver/gpio.h"
#include "driver/uart.h"
// UART 配置参数
#define UART_SEND_PORT UART_NUM_1 // 使用 UART1,UART0 一般用于调试输出
#define UART_SEND_TX_PIN GPIO_NUM_17 // TX 引脚
#define UART_SEND_RX_PIN GPIO_NUM_16 // RX 引脚
#define UART_SEND_BAUDRATE 115200 // 波特率
/**
* @brief 初始化 UART 发送通道
*/
void uart_sender_init(void);
/**
* @brief 通过串口发送温湿度数据
* @param temperature 温度值 (℃)
* @param humidity 湿度值 (%RH)
* @return true 发送成功, false 发送失败
*/
bool uart_send_th(float temperature, float humidity);
#endif
CMakeLists.txt
idf_component_register(
SRCS "uart_sender.c"
INCLUDE_DIRS "."
REQUIRES driver
)
3.主函数
main.c
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "dht22.h"
#include "uart_sender.h"
#include "wifi_sender.h"
void app_main(void)
{
dht22_init(); // 初始化 DHT22 传感器
uart_sender_init(); // 初始化 UART 有线通道(始终可用)
wifi_sender_init(); // 初始化 WiFi 无线通道(尽力而为)
float h, t;
while (1) {
// 两次读取之间间隔必须 ≥ 2 秒
vTaskDelay(pdMS_TO_TICKS(3000));
if (dht22_read(&h, &t)) {
// 控制台打印
printf("湿度: %.1f %%RH, 温度: %.1f ℃\n", h, t);
// 有线通道: 通过串口发送给 ARM 开发板(始终发送)
uart_send_th(t, h);
// 无线通道: 通过 WiFi 发送给 ESP8266 → ARM(尽力而为)
wifi_sender_send_th(t, h);
} else {
printf("读取失败,请检查连接\n");
}
}
}
CMakeLists.txt
idf_component_register(
SRCS "main.c"
INCLUDE_DIRS "."
REQUIRES dht-22 uart-sender wifi-sender
)
五、运行示例
串口助手:

esp-idf监视设备:


更多推荐

所有评论(0)