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);
}
}
}