DHT12支持温度湿度读取,精度高于DHT11
DHT12支持单总线和I2C两种方式读取,在使用过程中, I2C通信时需要加上拉电阻, 这一点尤为重要
- 以下代码在ESP8266_RTOS_SDK及ESP-IDF 3.x测试通过
- 单总线通信方式也支持DHT11,只是精度下降
- 设置管脚电平的函数可自行实现, 我这里是封装过的, 非常简单
- 代码有点冗余, 还可以优化一下
/* dht12.h */
#ifndef DHT12_H
#define DHT12_H
#include "common.h"
#include "core.h"
int Dht12Init(sNormalMod* pMod);
void Dht12Thread(sNormalMod* pMod);
float TempGet(void);
float HumGet(void);
typedef struct {
u8 State;
float Hum;
float Temp;
} sDht12Data;
sDht12Data Dht12DataGet(void);
#endif
/* dht12.c */
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "common.h"
#include "dht12.h"
#include "driver/i2c.h"
#include "driver/gpio.h"
#include "config.h"
#include "gpio.h"
#if CONFIG_BOARD_MODAL == BOARD_NODEMCU || CONFIG_BOARD_MODAL == BOARD_GOKIT3
#define DHT12_DATA_IO_NUM 5
#define READ_SDA() IoGet(DHT12_DATA_IO_NUM)
#define SEND_SDA(value) IoSet(DHT12_DATA_IO_NUM, value)
#elif CONFIG_BOARD_MODAL == BOARD_ESP32
#define DHT12_SDA_IO_NUM 18
#define DHT12_SCL_IO_NUM 19
#define CONFIG_DHT12_I2C_FREQ 100000
#endif
#define DHT12_OK 0
#define DHT12_ERROR_CHECKSUM -10
#define DHT12_ERROR_CONNECT -11
#define DHT12_MISSING_BYTES -12
#define DHT12_ADDRESS ((u8)0xB8)
#define MOD_TAG "DHT1X"
#if CONFIG_BOARD_MODAL == BOARD_NODEMCU || CONFIG_BOARD_MODAL == BOARD_GOKIT3
#define ENTER_CRITICAL() portENTER_CRITICAL()
#define EXIT_CRITICAL() portEXIT_CRITICAL()
#define delay_us ets_delay_us
#endif
static struct {
float Temp;
float Hum;
u8 SensorAnswerFlag;
u8 SensorErrorFlag;
} Dht12State;
static sDht12Data Dht12Data;
SemaphoreHandle_t Lock;
sDht12Data Dht12DataGet(void)
{
sDht12Data Temp;
xSemaphoreTake(Lock, portMAX_DELAY);
memcpy(&Temp, &Dht12Data, sizeof(Dht12Data));
xSemaphoreGive(Lock);
return Temp;
}
int Dht12Init(sNormalMod* pMod)
{
int Ret = -1;
#if CONFIG_BOARD_MODAL == BOARD_NODEMCU || CONFIG_BOARD_MODAL == BOARD_GOKIT3
Ret = CfgIo(DHT12_DATA_IO_NUM, GPIO_MODE_OUTPUT_OD, GPIO_PULLUP_ONLY, GPIO_INTR_DISABLE);
if(Ret) {
ESP_LOGE(MOD_TAG, "Set GPIO mode failed");
return -1;
}
IoSet(DHT12_DATA_IO_NUM, 0);
#ifdef DHT12_SCL_IO_NUM
Ret = CfgIo(DHT12_SCL_IO_NUM, GPIO_MODE_OUTPUT_OD, GPIO_PULLUP_ONLY, GPIO_INTR_DISABLE);
if(Ret) {
ESP_LOGE(MOD_TAG, "Set GPIO mode failed");
return -1;
}
IoSet(DHT12_SCL_IO_NUM, 0);
#endif
#elif CONFIG_BOARD_MODAL == BOARD_ESP32
i2c_config_t I2cConfig = {
.mode = I2C_MODE_MASTER,
.sda_io_num = DHT12_SDA_IO_NUM,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = DHT12_SCL_IO_NUM,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = CONFIG_DHT12_I2C_FREQ
};
Ret = i2c_param_config(I2C_NUM_1, &I2cConfig);
if(Ret != ESP_OK) {
ESP_LOGE(MOD_TAG, "I2C config failed");
return -1;
}
Ret = i2c_driver_install(I2C_NUM_1, I2cConfig.mode, 0, 0, 0);
if(Ret != ESP_OK) {
ESP_LOGE(MOD_TAG, "I2C driver install failed");
return -1;
}
#endif
Lock = xSemaphoreCreateMutex();
if (!Lock) {
return -1;
}
ESP_LOGW(MOD_TAG, "Inited.");
return 0;
}
#if CONFIG_BOARD_MODAL == BOARD_NODEMCU || CONFIG_BOARD_MODAL == BOARD_GOKIT3
/* 单总线 */
u8 Dht12ReadByte(void)
{
u16 j = 0;
u8 data = 0, bit = 0;
for(u8 i = 0; i < 8; i++) {
// 检测上次低电平是否结束
while(!READ_SDA()) {
// 防止进入死循环
if(++j>=50000) {
break;
}
}
// 延时Min=26us Max70us 跳过数据"0" 的高电平
delay_us(30);
// 判断传感器发送数据位
bit = READ_SDA();
j = 0;
// 等待高电平结束
while(READ_SDA()) {
// 防止进入死循环
if(++j >= 50000) {
break;
}
}
data <<= 1;
data |= bit;
}
return data;
}
static esp_err_t Dht11Read()
{
u32 j;
u8 HumHigh, HumLow, TempHigh, TempLow, TempChecksum, Temp;
// 进入临界区, 防止调度干扰数据读取
ENTER_CRITICAL();
SEND_SDA(0); // 主机把数据总线(SDA)拉低
delay_us(20000); // 拉低一段时间(至少18ms), 通知传感器准备数据
SEND_SDA(1); // 释放总线
delay_us(30); // 延时30us
Dht12State.SensorAnswerFlag = 0;
// 判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行
if(READ_SDA() == 0) {
Dht12State.SensorAnswerFlag = 1; //收到起始信号
j = 0;
// 判断从机发出 80us 的低电平响应信号是否结束
while((!READ_SDA())) {
// 防止进入死循环
if(++j >= 500) {
Dht12State.SensorErrorFlag = 1;
break;
}
}
j = 0;
// 判断从机是否发出 80us 的高电平,如发出则进入数据接收状态
while(READ_SDA()) {
// 防止进入死循环
if(++j >= 800) {
Dht12State.SensorErrorFlag = 1;
break;
}
}
// 接收数据
HumHigh = Dht12ReadByte();
HumLow = Dht12ReadByte();
TempHigh = Dht12ReadByte();
TempLow = Dht12ReadByte();
TempChecksum = Dht12ReadByte();
EXIT_CRITICAL();
// ets_printf("%02x", HumHigh);
// ets_printf("%02x", HumLow);
// ets_printf("%02x", TempHigh);
// ets_printf("%02x", TempLow);
// ets_printf("%02x", TempChecksum);
Temp = (u8)(HumHigh + HumLow + TempHigh + TempLow);
//如果校验成功,往下运行
if(TempChecksum == Temp) {
Dht12State.Hum = HumHigh * 10 + HumLow; //湿度
// 为负温度
if(TempLow & 0x80) {
Dht12State.Temp = 0 - (TempHigh * 10 + ((TempLow & 0x7F)));
}
else {
Dht12State.Temp = TempHigh * 10 + TempLow; //为正温度
}
// 判断数据是否超过量程(温度:-20℃~60℃,湿度20%RH~95%RH)
if(Dht12State.Hum > 950) {
Dht12State.Hum = 950;
}
if(Dht12State.Hum < 200) {
Dht12State.Hum = 200;
}
if(Dht12State.Temp > 600) {
Dht12State.Temp = 600;
}
if(Dht12State.Temp < -200) {
Dht12State.Temp = -200;
}
Dht12State.Temp /= 10; // 计算为温度值
Dht12State.Hum /= 10; // 计算为湿度值
// ESP_LOGW(MOD_TAG, "TEMP: %.2f", Dht12State.Temp);
// ESP_LOGW(MOD_TAG, "HUM: %.2f", Dht12State.Hum);
Dht12Data.Temp = Dht12State.Temp;
Dht12Data.Hum = Dht12State.Hum;
Dht12Data.State = 0;
}
else {
Dht12Data.State = 1;
ESP_LOGE(MOD_TAG, "Checksum Error!");
}
}
else {
Dht12State.SensorErrorFlag = 0; //未收到传感器响应
Dht12Data.State = 2;
ESP_LOGE(MOD_TAG, "Sensor Error!");
return ESP_FAIL;
}
return ESP_OK;
}
#elif CONFIG_BOARD_MODAL == BOARD_ESP32
/* I2C */
static esp_err_t Dht11Read()
{
int Ret = -1;
u8 Buffer[10];
memset(Buffer, 0, 10);
i2c_cmd_handle_t I2cHandle = i2c_cmd_link_create();
i2c_master_start(I2cHandle);
i2c_master_write_byte(I2cHandle, (u8)0xB8, I2C_MASTER_ACK);
i2c_master_write_byte(I2cHandle, (u8)0x0, I2C_MASTER_ACK);
i2c_master_start(I2cHandle);
i2c_master_write_byte(I2cHandle, (u8)0xB9, I2C_MASTER_ACK);
i2c_master_read_byte(I2cHandle, &Buffer[0], I2C_MASTER_ACK);
i2c_master_read_byte(I2cHandle, &Buffer[1], I2C_MASTER_ACK);
i2c_master_read_byte(I2cHandle, &Buffer[2], I2C_MASTER_ACK);
i2c_master_read_byte(I2cHandle, &Buffer[3], I2C_MASTER_ACK);
i2c_master_read_byte(I2cHandle, &Buffer[4], I2C_MASTER_NACK);
i2c_master_stop(I2cHandle);
Ret = i2c_master_cmd_begin(I2C_NUM_1, I2cHandle, 100 / portTICK_RATE_MS);
i2c_cmd_link_delete(I2cHandle);
if(Ret != ESP_OK) {
Dht12Data.State = 1;
ESP_LOGE(MOD_TAG, "Data was not vaild");
return -1;
}
u8 Checksum = Buffer[0] + Buffer[1] + Buffer[2] + Buffer[3];
if (Buffer[4] != Checksum) {
Dht12Data.State = 1;
ESP_LOGE(MOD_TAG, "Data was not vaild");
return -1;
} else {
Dht12State.Hum = Buffer[0] + Buffer[1] * 0.1;
Dht12State.Temp = Buffer[2] + (Buffer[3] & 0x7F) * 0.1;
if (Buffer[4] & 0x80) {
Dht12State.Temp = -Dht12State.Temp;
}
Dht12Data.State = 0;
Dht12Data.Temp = Dht12State.Temp;
Dht12Data.Hum = Dht12State.Hum;
return 0;
}
}
#endif
void Dht12Thread(sNormalMod* pMod)
{
int Ret = 0;
while(1) {
vTaskDelay(5000 / portTICK_RATE_MS);
xSemaphoreTake(Lock, portMAX_DELAY);
Ret = Dht11Read();
xSemaphoreGive(Lock);
if (Ret != ESP_OK) {
ESP_LOGE(MOD_TAG, "Dht11 data was not vaild");
} else {
ESP_LOGI(MOD_TAG, "Hum: %.2f, Temp: %.2f", Dht12State.Hum, Dht12State.Temp);
}
}
}