/** * @Author: 李建 * @Date: 2025/4/22 13:48 * Description: 温湿度传感器 * Copyright: Copyright (©) 2025 永续绿建. All rights reserved. */ #include #include "sht30.h" #include "driver/i2c.h" #include "main.h" #include "lvgl_port.h" #include "wifi.h" #include "esp_wifi.h" #include "lcd_st7701.h" sht30_data_t sht30Data = {0}; static const char* TAG = "SHT30"; #define DRIFT_RATE 0.1524 // 根据历史数据计算的漂移率 (°C/hour) #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 */ int current_seconds; // 温度修正函数 static double correct_temperature(double current_temp) { static const int start_seconds = 0; // 计算经过时间(秒) double elapsed_seconds = (double) (current_seconds - start_seconds); // 计算修正量(漂移率 × 经过分钟数) double correction = DRIFT_RATE * (elapsed_seconds / 60.0); // 应用修正 return current_temp - correction; } static esp_err_t i2c_master_init(void) { int i2c_master_port = I2C_MASTER_NUM; i2c_config_t conf = { .mode = I2C_MODE_MASTER, .sda_io_num = I2C_MASTER_SDA_IO, .scl_io_num = I2C_MASTER_SCL_IO, .sda_pullup_en = GPIO_PULLUP_ENABLE, .scl_pullup_en = GPIO_PULLUP_ENABLE, .master.clk_speed = I2C_MASTER_FREQ_HZ, }; i2c_param_config(i2c_master_port, &conf); return i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0); } esp_err_t i2c_master_write_slave_reg(i2c_port_t i2c_num, uint8_t slave_addr, uint8_t reg_addr, uint8_t *data_wr, size_t size, TickType_t ticks_to_wait) { i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (slave_addr << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN); i2c_master_write_byte(cmd, reg_addr, ACK_CHECK_EN); i2c_master_write(cmd, data_wr, size, ACK_CHECK_EN); i2c_master_stop(cmd); esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, ticks_to_wait); i2c_cmd_link_delete(cmd); return ret; } esp_err_t i2c_master_read_slave_reg_16bit(i2c_port_t i2c_num, uint8_t slave_addr, uint16_t reg_addr, uint8_t *data_rd, size_t size, TickType_t ticks_to_wait) { if (size == 0) { return ESP_OK; } i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_master_start(cmd); i2c_master_write_byte(cmd, (slave_addr << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN); i2c_master_write_byte(cmd, reg_addr>>8, ACK_CHECK_EN); i2c_master_write_byte(cmd, reg_addr, ACK_CHECK_EN); i2c_master_start(cmd); i2c_master_write_byte(cmd, (slave_addr << 1) | I2C_MASTER_READ, ACK_CHECK_EN); if (size > 1) { i2c_master_read(cmd, data_rd, size - 1, ACK_VAL); } i2c_master_read_byte(cmd, data_rd + size - 1, NACK_VAL); i2c_master_stop(cmd); esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, ticks_to_wait); i2c_cmd_link_delete(cmd); return ret; } /** * @brief 向SHT3x发送一条指令(16bit) * * @param cmd —— SHT3x指令(在SHT3x_MODE中枚举定义) * * @retval 成功返回HAL_OK(ESP_OK) */ static uint8_t SHT3X_Send_Cmd(SHT30_CMD cmd) { uint8_t cmd_buffer[2]; cmd_buffer[0] = cmd >> 8; cmd_buffer[1] = cmd; return i2c_master_write_slave_reg(SHT3X_I2C_BUS, SHT3X_SLAVE_ADDRESS, cmd_buffer[0], cmd_buffer+1, 1, SHT3X_TICKS_TO_WAIT); } void sht3x_reset(void) { SHT3X_Send_Cmd(SOFT_RESET_CMD); vTaskDelay(20 / portTICK_PERIOD_MS); } esp_err_t sht3x_init(void) { return SHT3X_Send_Cmd(MEDIUM_2_CMD); } esp_err_t sht3x_read_th_raw_dat(uint8_t* dat) { return i2c_master_read_slave_reg_16bit(SHT3X_I2C_BUS, SHT3X_SLAVE_ADDRESS, READOUT_FOR_PERIODIC_MODE, dat, 6, SHT3X_TICKS_TO_WAIT); } #define CRC8_POLYNOMIAL 0x31 static uint8_t SHT3X_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; } /** * @brief 将SHT30接收的6个字节数据进行CRC校验,并转换为温度值和湿度值 * * @param dat —— 存储接收数据的地址(6个字节数组) * * @retval 校验成功 —— 返回0 * 校验失败 —— 返回1,并设置温度值和湿度值为0 */ uint8_t sht3x_dat2float(uint8_t* const dat, float* temperature, float* humidity) { uint16_t recv_temperature = 0; uint16_t recv_humidity = 0; /* 校验温度数据和湿度数据是否接收正确 */ if(SHT3X_CheckCrc8(dat, 0xFF) != dat[2] || SHT3X_CheckCrc8(&dat[3], 0xFF) != dat[5]) return 1; /* 转换温度数据 */ recv_temperature = ((uint16_t)dat[0]<<8)|dat[1]; *temperature = -45 + 175*((float)recv_temperature/65535); /* 转换湿度数据 */ recv_humidity = ((uint16_t)dat[3]<<8)|dat[4]; *humidity = 100 * ((float)recv_humidity / 65535); return 0; } LV_IMAGE_DECLARE(_WiFiqiang_RGB565A8_30x30); LV_IMAGE_DECLARE(_WiFizhong_RGB565A8_30x30); void ui_wifi_lab_refresh() { int rssi=0; if(get_wifi_status()) { esp_err_t ret= esp_wifi_sta_get_rssi(&rssi); ESP_LOGE(TAG,"rssi %d",rssi); if(ret==ESP_OK) { lvgl_port_lock(-1); if(rssi>=-50) { lv_obj_set_style_bg_image_src(guider_ui.screen_main_label_wifi, &_WiFiqiang_RGB565A8_30x30, LV_PART_MAIN | LV_STATE_DEFAULT); }else if( (rssi<-50)&&(rssi>=-80)) { lv_obj_set_style_bg_image_src(guider_ui.screen_main_label_wifi, &_WiFizhong_RGB565A8_30x30, LV_PART_MAIN | LV_STATE_DEFAULT); } else if (rssi<-80) { lv_obj_set_style_bg_image_src(guider_ui.screen_main_label_wifi, &_WiFiruo_RGB565A8_30x30, LV_PART_MAIN | LV_STATE_DEFAULT); } lvgl_port_unlock(); } }else{ } } void i2c_sht3x_task(void* arg) { // 配置I2C0-主机模式,400K,指定 SCL-5,SDA-4 //ESP_ERROR_CHECK(i2c_master_init()); uint8_t recv_dat[6] = {0}; ESP_LOGI(TAG, "esp32 sht3x project starting ……"); sht3x_reset(); // 复位SHT3X if(sht3x_init() == ESP_OK) // 初始化SHT3X(周期测量模式) ESP_LOGI(TAG, "sht3x init ok.\n"); else ESP_LOGE(TAG, "sht3x init fail.\n"); vTaskDelay(1000 / portTICK_PERIOD_MS); //延时1s 等待SHT3X传感器内部采样完成 char temp_buf[32]; char hum_buf[32]; for (;;) { if(sht3x_read_th_raw_dat(recv_dat) == ESP_OK) // 从SHT3X读取一次数据(周期测量模式下) { // 将SHT3X接收的6个字节数据进行CRC校验,并转换为温度值和湿度值 if(sht3x_dat2float(recv_dat, &sht30Data.temperature, &sht30Data.humidity) == 0) { //ESP_LOGI(TAG, "temperature = %.2f ℃,humidity = %.2f %%RH", temperature, humidity); sht30Data.temperature = correct_temperature(sht30Data.temperature); snprintf(temp_buf, sizeof(temp_buf), "%.1f", sht30Data.temperature); snprintf(hum_buf, sizeof(hum_buf), "%.1f", sht30Data.humidity); ui_wifi_lab_refresh(); if(guider_ui.screen_main) { lvgl_port_lock(-1); lv_label_set_text_static(guider_ui.screen_main_label_temp, temp_buf); lv_label_set_text_static(guider_ui.screen_main_label_hum_vul2,hum_buf); lvgl_port_unlock(); } } else { ESP_LOGE(TAG, "crc check fail.\n"); } } else { ESP_LOGE(TAG, "read data from sht3x fail.\n"); } if (lcd_st7701_get_blacklight() == false) // 如果当前背光引脚为低电平 { current_seconds -= 3; if (current_seconds < 0) current_seconds = 0; } else current_seconds += 3; ui_wifi_lab_refresh(); vTaskDelay(5000 / portTICK_PERIOD_MS); vTaskDelay(5000 / portTICK_PERIOD_MS); } } void sht30_init() { xTaskCreate(i2c_sht3x_task, "i2c_sht3x_task", 6 * 1024, NULL, 3, NULL); }