ESP8266或ESP32使用ESP-IDF开发读取DHT12温度湿度

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);
		}
		
	}
}
posted @ 2019-05-12 23:13  rootming  阅读(3629)  评论(0编辑  收藏  举报