ESP32开发之路(11)— ESP32读取SHT3X温湿度传感器的值
一、前言
我们在之前完成了ESP32的IIC读取AT24C02:ESP32开发之路(10)— ESP32的I2C通信读写AT24C02
接下来我们使用一个IIC通信更复杂一点的器件,SHT3X温湿度传感器。
二、SHT3X驱动程序
新建sht3x.c
文件,首先设置IIC相关的参数
#define WRITE_BIT 0x00 /*!< I2C master write */
#define READ_BIT 0x01 /*!< I2C master read */
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave */
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
#define ACK_VAL 0x0 /*!< I2C ack value */
#define NACK_VAL 0x1 /*!< I2C nack value */
#if 0
#define IIC_CTRL_NUM I2C_NUM_1 /*!< I2C port number */
#define SDA_PIN_NUM 25 /*!< gpio number for I2C data */
#define SCL_PIN_NUM 26 /*!< gpio number for I2C clock */
#else
#define IIC_CTRL_NUM I2C_NUM_0 /*!< I2C port number */
#define SDA_PIN_NUM 18 /*!< gpio number for I2C data */
#define SCL_PIN_NUM 19 /*!< gpio number for I2C clock */
#endif
#define SHT3X_DeviceAddr (0x44<<1) /* SHT3X的器件地址 */
然后需要对SHT3X的一些命令做定义,将其放入一个枚举类型:
/* 枚举SHT3x命令列表 */
typedef enum
{
/* 软件复位命令 */
SOFT_RESET_CMD = 0x30A2,
/* 单次测量模式
命名格式:Repeatability_CS_CMD
CS: Clock stretching */
HIGH_ENABLED_CMD = 0x2C06,
MEDIUM_ENABLED_CMD = 0x2C0D,
LOW_ENABLED_CMD = 0x2C10,
HIGH_DISABLED_CMD = 0x2400,
MEDIUM_DISABLED_CMD = 0x240B,
LOW_DISABLED_CMD = 0x2416,
/* 周期测量模式
命名格式:Repeatability_MPS_CMD
MPS:measurement per second */
HIGH_0_5_CMD = 0x2032,
MEDIUM_0_5_CMD = 0x2024,
LOW_0_5_CMD = 0x202F,
HIGH_1_CMD = 0x2130,
MEDIUM_1_CMD = 0x2126,
LOW_1_CMD = 0x212D,
HIGH_2_CMD = 0x2236,
MEDIUM_2_CMD = 0x2220,
LOW_2_CMD = 0x222B,
HIGH_4_CMD = 0x2334,
MEDIUM_4_CMD = 0x2322,
LOW_4_CMD = 0x2329,
HIGH_10_CMD = 0x2737,
MEDIUM_10_CMD = 0x2721,
LOW_10_CMD = 0x272A,
/* 周期测量模式读取数据命令 */
READOUT_FOR_PERIODIC_MODE = 0xE000,
/* 读取传感器编号命令 */
READ_SERIAL_NUMBER = 0x3780,
} sht3x_cmd_t;
然后编写发送命令和接收数据函数
/* 描述:向SHT30发送一条16bit指令
* 参数cmd:SHT30指令(在SHT30_MODE中枚举定义)
* 返回值:成功返回ESP_OK */
static esp_err_t SHT3x_Send_Cmd(sht3x_cmd_t sht3x_cmd)
{
uint8_t cmd_buffer[2];
cmd_buffer[0] = sht3x_cmd >> 8;
cmd_buffer[1] = sht3x_cmd;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, SHT3X_DeviceAddr | WRITE_BIT, ACK_CHECK_EN);
i2c_master_write_byte(cmd, cmd_buffer[0], ACK_CHECK_EN);
i2c_master_write_byte(cmd, cmd_buffer[1], ACK_CHECK_EN);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(IIC_CTRL_NUM, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
return ret;
}
/* 描述:从SHT3x读取数据
* 参数data_len:读取多少个字节数据
* 参数data_arr:读取的数据存放在一个数组里
* 返回值:读取成功返回ESP_OK
*/
static esp_err_t SHT3x_Recv_Data(uint8_t data_len, uint8_t* data_arr)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, SHT3X_DeviceAddr | READ_BIT, ACK_CHECK_EN);
if (data_len > 1) {
i2c_master_read(cmd, data_arr, data_len - 1, ACK_VAL);
}
i2c_master_read_byte(cmd, data_arr + data_len - 1, NACK_VAL);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(IIC_CTRL_NUM, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
return ret;
}
接下来我们尝试读取一下传感器编号
/* 描述:读取传感器编号
* 参数:存储编号数据的指针
* 返回值:0-读取成功,1-读取失败 */
uint8_t SHT3x_ReadSerialNumber(uint32_t* serialNumber)
{
uint8_t Num_buf[4] = {0xFF,0xFF,0xFF,0xFF};
SHT3x_Send_Cmd(READ_SERIAL_NUMBER);
vTaskDelay(500 / portTICK_PERIOD_MS); /* 延时50ms,有问题时需要适当延长!!!!!!*/
esp_err_t ret = SHT3x_Recv_Data(4,Num_buf);
*serialNumber = ((Num_buf[0] << 24) | (Num_buf[1] << 16) |(Num_buf[2] << 8) |(Num_buf[3]));
if(0xFFFFFFFF == *serialNumber) return 1;
else if(ret == ESP_OK) return 0;
printf("SHT3x_ReadSerialNumber ERR :%s\n",esp_err_to_name(ret));
return ret;
}
在app_main()中调用,编译下载运行,可以看到,读取成功
/* 初始化IIC控制器 */
ESP_ERROR_CHECK(i2c_master_init());
/* 读取SHT3x的SerialNumber值 */
if(SHT3x_ReadSerialNumber(&data_buf) == 0)
printf("Read SerialNumber 0k: 0x%x \n",data_buf);
else
printf("Read SerialNumber err \n");
接下来我们编写一个SHT3X初始化函数,将其设置为连续周期读取模式
/* 描述:SHT3x初始化函数,并将其设置为周期测量模式
* 参数:无
* 返回值:初始化成功返回ESP_OK */
esp_err_t sht3x_mode_init(void)
{
/* 初始化IIC控制器 */
ESP_ERROR_CHECK(i2c_master_init());
/* 设置为周期测量模式 */
esp_err_t ret = SHT3x_Send_Cmd(MEDIUM_2_CMD);
return ret;
}
在读取温湿度数据之前我们先编写一个CRC校验函数:
/* 描述:数据CRC校验
* 参数message:需要校验的数据
* 参数initial_value:crc初始值
* 返回值:计算得到的CRC码 */
#define CRC8_POLYNOMIAL 0x131
uint8_t CheckCrc8(uint8_t* const message, uint8_t initial_value)
{
uint8_t remainder; //余数
uint8_t i = 0, j = 0; //循环变量
/* 初始化 */
remainder = initial_value;
for(j = 0; j < 2;j++)
{
remainder ^= message[j];
/* 从最高位开始依次计算 */
for (i = 0; i < 8; i++)
{
if (remainder & 0x80)
remainder = (remainder << 1)^CRC8_POLYNOMIAL;
else
remainder = (remainder << 1);
}
}
/* 返回计算的CRC码 */
return remainder;
}
然后以循环模式读取温湿度的值,
/* 描述:温湿度数据获取函数,周期读取,注意,需要提前设置周期模式
* 参数Tem_val:存储温度数据的指针, 温度单位为°C
* 参数Hum_val:存储湿度数据的指针, 温度单位为%
* 返回值:0-读取成功,1-读取失败 **********************************/
uint8_t sht3x_get_humiture_periodic(double *Tem_val,double *Hum_val)
{
uint8_t ret=0;
uint8_t buff[6];
uint16_t tem,hum;
double Temperature=0;
double Humidity=0;
ret = SHT3x_Send_Cmd(READOUT_FOR_PERIODIC_MODE);
ret = SHT3x_Recv_Data(6,buff);
/* 校验温度数据和湿度数据是否接收正确 */
if(CheckCrc8(buff, 0xFF) != buff[2] || CheckCrc8(&buff[3], 0xFF) != buff[5])
{
printf("CRC_ERROR,ret = 0x%x\r\n",ret);
return 1;
}
/* 转换温度数据 */
tem = (((uint16_t)buff[0]<<8) | buff[1]);//温度数据拼接
Temperature= (175.0*(double)tem/65535.0-45.0) ; // T = -45 + 175 * tem / (2^16-1)
/* 转换湿度数据 */
hum = (((uint16_t)buff[3]<<8) | buff[4]);//湿度数据拼接
Humidity= (100.0*(double)hum/65535.0); // RH = hum*100 / (2^16-1)
/* 过滤错误数据 */
if((Temperature>=-20)&&(Temperature<=125)&&(Humidity>=0)&&(Humidity<=100))
{
*Tem_val = Temperature;
*Hum_val = Humidity;
return 0;
}
else
return 1;
}
三、采集数据
在app_main()函数中每隔两秒调用一次温湿度采集函数,然后将温湿度数据打印出来,app_main.c如下:
#include <string.h>
#include <sys/param.h>
#include "driver/i2c.h"
#define GPIO_LED_NUM 2 /* LED引脚编号 */
esp_err_t sht3x_mode_init(void);
uint8_t sht3x_get_humiture_periodic(double *Tem_val,double *Hum_val);
void app_main(void)
{
double Tem_val,Hum_val;
/* 打印Hello world! */
printf("Hello world!\n");
ESP_ERROR_CHECK(sht3x_mode_init());
/* 定义一个gpio配置结构体 初始化LED */
gpio_config_t gpio_config_structure;
/* 初始化gpio配置结构体*/
gpio_config_structure.pin_bit_mask = (1ULL << GPIO_LED_NUM);/* 选择gpio2 */
gpio_config_structure.mode = GPIO_MODE_OUTPUT; /* 输出模式 */
gpio_config_structure.pull_up_en = 0; /* 不上拉 */
gpio_config_structure.pull_down_en = 0; /* 不下拉 */
gpio_config_structure.intr_type = GPIO_PIN_INTR_DISABLE; /* 禁止中断 */
/* 根据设定参数初始化并使能 */
gpio_config(&gpio_config_structure);
/* 默认熄灭LED */
gpio_set_level(GPIO_LED_NUM, 0); /* 熄灭 */
while(1)
{
/* 采集温湿度数据 */
if(sht3x_get_humiture_periodic(&Tem_val,&Hum_val) == 0)
{
printf("temperature:%6.2lf °C, humidity:%6.2lf %% \n",Tem_val,Hum_val);
}
else
printf("Get_Humiture ERR\r\n");
gpio_set_level(GPIO_LED_NUM, 0); /* 熄灭 */
vTaskDelay(500 / portTICK_PERIOD_MS); /* 延时500ms*/
gpio_set_level(GPIO_LED_NUM, 1); /* 点亮 */
vTaskDelay(500 / portTICK_PERIOD_MS); /* 延时500ms*/
}
}
编译烧录运行,可以看到,读取成功:
四、代码
贴上sht3x.c
的代码:
#include <string.h>
#include <sys/param.h>
#include "driver/i2c.h"
#define WRITE_BIT 0x00 /*!< I2C master write */
#define READ_BIT 0x01 /*!< I2C master read */
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave */
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
#define ACK_VAL 0x0 /*!< I2C ack value */
#define NACK_VAL 0x1 /*!< I2C nack value */
#if 0
#define IIC_CTRL_NUM I2C_NUM_1 /*!< I2C port number */
#define SDA_PIN_NUM 25 /*!< gpio number for I2C data */
#define SCL_PIN_NUM 26 /*!< gpio number for I2C clock */
#else
#define IIC_CTRL_NUM I2C_NUM_0 /*!< I2C port number */
#define SDA_PIN_NUM 18 /*!< gpio number for I2C data */
#define SCL_PIN_NUM 19 /*!< gpio number for I2C clock */
#endif
#define SHT3X_DeviceAddr (0x44<<1) /* SHT3X的器件地址 */
/* 枚举SHT3x命令列表 */
typedef enum
{
/* 软件复位命令 */
SOFT_RESET_CMD = 0x30A2,
/* 单次测量模式
命名格式:Repeatability_CS_CMD
CS: Clock stretching */
HIGH_ENABLED_CMD = 0x2C06,
MEDIUM_ENABLED_CMD = 0x2C0D,
LOW_ENABLED_CMD = 0x2C10,
HIGH_DISABLED_CMD = 0x2400,
MEDIUM_DISABLED_CMD = 0x240B,
LOW_DISABLED_CMD = 0x2416,
/* 周期测量模式
命名格式:Repeatability_MPS_CMD
MPS:measurement per second */
HIGH_0_5_CMD = 0x2032,
MEDIUM_0_5_CMD = 0x2024,
LOW_0_5_CMD = 0x202F,
HIGH_1_CMD = 0x2130,
MEDIUM_1_CMD = 0x2126,
LOW_1_CMD = 0x212D,
HIGH_2_CMD = 0x2236,
MEDIUM_2_CMD = 0x2220,
LOW_2_CMD = 0x222B,
HIGH_4_CMD = 0x2334,
MEDIUM_4_CMD = 0x2322,
LOW_4_CMD = 0x2329,
HIGH_10_CMD = 0x2737,
MEDIUM_10_CMD = 0x2721,
LOW_10_CMD = 0x272A,
/* 周期测量模式读取数据命令 */
READOUT_FOR_PERIODIC_MODE = 0xE000,
/* 读取传感器编号命令 */
READ_SERIAL_NUMBER = 0x3780,
} sht3x_cmd_t;
/**
* @brief i2c master initialization
*/
static esp_err_t i2c_master_init(void)
{
int i2c_master_port = IIC_CTRL_NUM;
i2c_config_t conf;
conf.mode = I2C_MODE_MASTER;
conf.sda_io_num = SDA_PIN_NUM;
conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
conf.scl_io_num = SCL_PIN_NUM;
conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
conf.master.clk_speed = 100000; /* 标准模式(100 kbit/s) */
i2c_param_config(i2c_master_port, &conf);
return i2c_driver_install(i2c_master_port, conf.mode, 0, 0, 0);
}
/* 描述:向SHT30发送一条16bit指令
* 参数cmd:SHT30指令(在SHT30_MODE中枚举定义)
* 返回值:成功返回ESP_OK */
static esp_err_t SHT3x_Send_Cmd(sht3x_cmd_t sht3x_cmd)
{
uint8_t cmd_buffer[2];
cmd_buffer[0] = sht3x_cmd >> 8;
cmd_buffer[1] = sht3x_cmd;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, SHT3X_DeviceAddr | WRITE_BIT, ACK_CHECK_EN);
i2c_master_write_byte(cmd, cmd_buffer[0], ACK_CHECK_EN);
i2c_master_write_byte(cmd, cmd_buffer[1], ACK_CHECK_EN);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(IIC_CTRL_NUM, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
return ret;
}
/* 描述:从SHT3x读取数据
* 参数data_len:读取多少个字节数据
* 参数data_arr:读取的数据存放在一个数组里
* 返回值:读取成功返回ESP_OK
*/
static esp_err_t SHT3x_Recv_Data(size_t data_len, uint8_t* data_arr)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, SHT3X_DeviceAddr | READ_BIT, ACK_CHECK_EN);
if (data_len > 1) {
i2c_master_read(cmd, data_arr, data_len - 1, ACK_VAL);
}
i2c_master_read_byte(cmd, data_arr + data_len - 1, NACK_VAL);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(IIC_CTRL_NUM, cmd, 1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
return ret;
}
/* 描述:读取传感器编号
* 参数:存储编号数据的指针
* 返回值:0-读取成功,1-读取失败 */
uint8_t SHT3x_ReadSerialNumber(uint32_t* serialNumber)
{
uint8_t Num_buf[4] = {0xFF,0xFF,0xFF,0xFF};
SHT3x_Send_Cmd(READ_SERIAL_NUMBER);
vTaskDelay(500 / portTICK_PERIOD_MS); /* 延时50ms,有问题时需要适当延长!!!!!!*/
esp_err_t ret = SHT3x_Recv_Data(4,Num_buf);
*serialNumber = ((Num_buf[0] << 24) | (Num_buf[1] << 16) |(Num_buf[2] << 8) |(Num_buf[3]));
if(0xFFFFFFFF == *serialNumber) return 1;
else if(ret == ESP_OK) return 0;
printf("SHT3x_ReadSerialNumber ERR :%s\n",esp_err_to_name(ret));
return ret;
}
/* 描述:SHT3x初始化函数,并将其设置为周期测量模式
* 参数:无
* 返回值:初始化成功返回ESP_OK */
esp_err_t sht3x_mode_init(void)
{
/* 初始化IIC控制器 */
ESP_ERROR_CHECK(i2c_master_init());
/* 设置为周期测量模式 */
esp_err_t ret = SHT3x_Send_Cmd(MEDIUM_2_CMD);
return ret;
}
/* 描述:数据CRC校验
* 参数message:需要校验的数据
* 参数initial_value:crc初始值
* 返回值:计算得到的CRC码 */
#define CRC8_POLYNOMIAL 0x131
static uint8_t CheckCrc8(uint8_t* const message, uint8_t initial_value)
{
uint8_t remainder; //余数
uint8_t i = 0, j = 0; //循环变量
/* 初始化 */
remainder = initial_value;
for(j = 0; j < 2;j++)
{
remainder ^= message[j];
/* 从最高位开始依次计算 */
for (i = 0; i < 8; i++)
{
if (remainder & 0x80)
remainder = (remainder << 1)^CRC8_POLYNOMIAL;
else
remainder = (remainder << 1);
}
}
/* 返回计算的CRC码 */
return remainder;
}
/* 描述:温湿度数据获取函数,周期读取,注意,需要提前设置周期模式
* 参数Tem_val:存储温度数据的指针, 温度单位为°C
* 参数Hum_val:存储湿度数据的指针, 温度单位为%
* 返回值:0-读取成功,1-读取失败 **********************************/
uint8_t sht3x_get_humiture_periodic(double *Tem_val,double *Hum_val)
{
uint8_t ret=0;
uint8_t buff[6];
uint16_t tem,hum;
double Temperature=0;
double Humidity=0;
ret = SHT3x_Send_Cmd(READOUT_FOR_PERIODIC_MODE);
ret = SHT3x_Recv_Data(6,buff);
/* 校验温度数据和湿度数据是否接收正确 */
if(CheckCrc8(buff, 0xFF) != buff[2] || CheckCrc8(&buff[3], 0xFF) != buff[5])
{
printf("CRC_ERROR,ret = 0x%x\r\n",ret);
return 1;
}
/* 转换温度数据 */
tem = (((uint16_t)buff[0]<<8) | buff[1]);//温度数据拼接
Temperature= (175.0*(double)tem/65535.0-45.0) ; // T = -45 + 175 * tem / (2^16-1)
/* 转换湿度数据 */
hum = (((uint16_t)buff[3]<<8) | buff[4]);//湿度数据拼接
Humidity= (100.0*(double)hum/65535.0); // RH = hum*100 / (2^16-1)
/* 过滤错误数据 */
if((Temperature>=-20)&&(Temperature<=125)&&(Humidity>=0)&&(Humidity<=100))
{
*Tem_val = Temperature;
*Hum_val = Humidity;
return 0;
}
else
return 1;
}