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#endifstatic 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; }}#endifvoid 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); } }}