sht30.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /**
  2. * @Author: 李建
  3. * @Date: 2025/4/22 13:48
  4. * Description: 温湿度传感器
  5. * Copyright: Copyright (©) 2025 永续绿建. All rights reserved.
  6. */
  7. #include <esp_log.h>
  8. #include "sht30.h"
  9. #include "driver/i2c.h"
  10. #include "main.h"
  11. #include "lvgl_port.h"
  12. #include "wifi.h"
  13. #include "esp_wifi.h"
  14. #include "lcd_st7701.h"
  15. sht30_data_t sht30Data = {0};
  16. static const char* TAG = "SHT30";
  17. #define DRIFT_RATE 0.1524 // 根据历史数据计算的漂移率 (°C/hour)
  18. #define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
  19. #define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
  20. #define ACK_VAL 0x0 /*!< I2C ack value */
  21. #define NACK_VAL 0x1 /*!< I2C nack value */
  22. int current_seconds;
  23. // 温度修正函数
  24. static double correct_temperature(double current_temp) {
  25. static const int start_seconds = 0;
  26. // 计算经过时间(秒)
  27. double elapsed_seconds = (double) (current_seconds - start_seconds);
  28. // 计算修正量(漂移率 × 经过分钟数)
  29. double correction = DRIFT_RATE * (elapsed_seconds / 60.0);
  30. // 应用修正
  31. return current_temp - correction;
  32. }
  33. static esp_err_t i2c_master_init(void)
  34. {
  35. int i2c_master_port = I2C_MASTER_NUM;
  36. i2c_config_t conf = {
  37. .mode = I2C_MODE_MASTER,
  38. .sda_io_num = I2C_MASTER_SDA_IO,
  39. .scl_io_num = I2C_MASTER_SCL_IO,
  40. .sda_pullup_en = GPIO_PULLUP_ENABLE,
  41. .scl_pullup_en = GPIO_PULLUP_ENABLE,
  42. .master.clk_speed = I2C_MASTER_FREQ_HZ,
  43. };
  44. i2c_param_config(i2c_master_port, &conf);
  45. return i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
  46. }
  47. 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)
  48. {
  49. i2c_cmd_handle_t cmd = i2c_cmd_link_create();
  50. i2c_master_start(cmd);
  51. i2c_master_write_byte(cmd, (slave_addr << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN);
  52. i2c_master_write_byte(cmd, reg_addr, ACK_CHECK_EN);
  53. i2c_master_write(cmd, data_wr, size, ACK_CHECK_EN);
  54. i2c_master_stop(cmd);
  55. esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, ticks_to_wait);
  56. i2c_cmd_link_delete(cmd);
  57. return ret;
  58. }
  59. 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)
  60. {
  61. if (size == 0) {
  62. return ESP_OK;
  63. }
  64. i2c_cmd_handle_t cmd = i2c_cmd_link_create();
  65. i2c_master_start(cmd);
  66. i2c_master_write_byte(cmd, (slave_addr << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN);
  67. i2c_master_write_byte(cmd, reg_addr>>8, ACK_CHECK_EN);
  68. i2c_master_write_byte(cmd, reg_addr, ACK_CHECK_EN);
  69. i2c_master_start(cmd);
  70. i2c_master_write_byte(cmd, (slave_addr << 1) | I2C_MASTER_READ, ACK_CHECK_EN);
  71. if (size > 1) {
  72. i2c_master_read(cmd, data_rd, size - 1, ACK_VAL);
  73. }
  74. i2c_master_read_byte(cmd, data_rd + size - 1, NACK_VAL);
  75. i2c_master_stop(cmd);
  76. esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, ticks_to_wait);
  77. i2c_cmd_link_delete(cmd);
  78. return ret;
  79. }
  80. /**
  81. * @brief 向SHT3x发送一条指令(16bit)
  82. *
  83. * @param cmd —— SHT3x指令(在SHT3x_MODE中枚举定义)
  84. *
  85. * @retval 成功返回HAL_OK(ESP_OK)
  86. */
  87. static uint8_t SHT3X_Send_Cmd(SHT30_CMD cmd)
  88. {
  89. uint8_t cmd_buffer[2];
  90. cmd_buffer[0] = cmd >> 8;
  91. cmd_buffer[1] = cmd;
  92. return i2c_master_write_slave_reg(SHT3X_I2C_BUS, SHT3X_SLAVE_ADDRESS, cmd_buffer[0], cmd_buffer+1, 1, SHT3X_TICKS_TO_WAIT);
  93. }
  94. void sht3x_reset(void)
  95. {
  96. SHT3X_Send_Cmd(SOFT_RESET_CMD);
  97. vTaskDelay(20 / portTICK_PERIOD_MS);
  98. }
  99. esp_err_t sht3x_init(void)
  100. {
  101. return SHT3X_Send_Cmd(MEDIUM_2_CMD);
  102. }
  103. esp_err_t sht3x_read_th_raw_dat(uint8_t* dat)
  104. {
  105. return i2c_master_read_slave_reg_16bit(SHT3X_I2C_BUS, SHT3X_SLAVE_ADDRESS, READOUT_FOR_PERIODIC_MODE, dat, 6, SHT3X_TICKS_TO_WAIT);
  106. }
  107. #define CRC8_POLYNOMIAL 0x31
  108. static uint8_t SHT3X_CheckCrc8(uint8_t* const message, uint8_t initial_value)
  109. {
  110. uint8_t remainder; //余数
  111. uint8_t i = 0, j = 0; //循环变量
  112. /* 初始化 */
  113. remainder = initial_value;
  114. for(j = 0; j < 2;j++)
  115. {
  116. remainder ^= message[j];
  117. /* 从最高位开始依次计算 */
  118. for (i = 0; i < 8; i++)
  119. {
  120. if (remainder & 0x80)
  121. {
  122. remainder = (remainder << 1)^CRC8_POLYNOMIAL;
  123. }
  124. else
  125. {
  126. remainder = (remainder << 1);
  127. }
  128. }
  129. }
  130. /* 返回计算的CRC码 */
  131. return remainder;
  132. }
  133. /**
  134. * @brief 将SHT30接收的6个字节数据进行CRC校验,并转换为温度值和湿度值
  135. *
  136. * @param dat —— 存储接收数据的地址(6个字节数组)
  137. *
  138. * @retval 校验成功 —— 返回0
  139. * 校验失败 —— 返回1,并设置温度值和湿度值为0
  140. */
  141. uint8_t sht3x_dat2float(uint8_t* const dat, float* temperature, float* humidity)
  142. {
  143. uint16_t recv_temperature = 0;
  144. uint16_t recv_humidity = 0;
  145. /* 校验温度数据和湿度数据是否接收正确 */
  146. if(SHT3X_CheckCrc8(dat, 0xFF) != dat[2] || SHT3X_CheckCrc8(&dat[3], 0xFF) != dat[5])
  147. return 1;
  148. /* 转换温度数据 */
  149. recv_temperature = ((uint16_t)dat[0]<<8)|dat[1];
  150. *temperature = -45 + 175*((float)recv_temperature/65535);
  151. /* 转换湿度数据 */
  152. recv_humidity = ((uint16_t)dat[3]<<8)|dat[4];
  153. *humidity = 100 * ((float)recv_humidity / 65535);
  154. return 0;
  155. }
  156. LV_IMAGE_DECLARE(_WiFiqiang_RGB565A8_30x30);
  157. LV_IMAGE_DECLARE(_WiFizhong_RGB565A8_30x30);
  158. void ui_wifi_lab_refresh() {
  159. int rssi=0;
  160. if(get_wifi_status())
  161. {
  162. esp_err_t ret= esp_wifi_sta_get_rssi(&rssi);
  163. ESP_LOGE(TAG,"rssi %d",rssi);
  164. if(ret==ESP_OK)
  165. {
  166. lvgl_port_lock(-1);
  167. if(rssi>=-50)
  168. {
  169. lv_obj_set_style_bg_image_src(guider_ui.screen_main_label_wifi, &_WiFiqiang_RGB565A8_30x30,
  170. LV_PART_MAIN | LV_STATE_DEFAULT);
  171. }else if( (rssi<-50)&&(rssi>=-80))
  172. {
  173. lv_obj_set_style_bg_image_src(guider_ui.screen_main_label_wifi, &_WiFizhong_RGB565A8_30x30,
  174. LV_PART_MAIN | LV_STATE_DEFAULT);
  175. } else if (rssi<-80)
  176. {
  177. lv_obj_set_style_bg_image_src(guider_ui.screen_main_label_wifi, &_WiFiruo_RGB565A8_30x30,
  178. LV_PART_MAIN | LV_STATE_DEFAULT);
  179. }
  180. lvgl_port_unlock();
  181. }
  182. }else{
  183. }
  184. }
  185. void i2c_sht3x_task(void* arg)
  186. {
  187. // 配置I2C0-主机模式,400K,指定 SCL-5,SDA-4
  188. //ESP_ERROR_CHECK(i2c_master_init());
  189. uint8_t recv_dat[6] = {0};
  190. ESP_LOGI(TAG, "esp32 sht3x project starting ……");
  191. sht3x_reset(); // 复位SHT3X
  192. if(sht3x_init() == ESP_OK) // 初始化SHT3X(周期测量模式)
  193. ESP_LOGI(TAG, "sht3x init ok.\n");
  194. else
  195. ESP_LOGE(TAG, "sht3x init fail.\n");
  196. vTaskDelay(1000 / portTICK_PERIOD_MS); //延时1s 等待SHT3X传感器内部采样完成
  197. char temp_buf[32];
  198. char hum_buf[32];
  199. for (;;)
  200. {
  201. if(sht3x_read_th_raw_dat(recv_dat) == ESP_OK) // 从SHT3X读取一次数据(周期测量模式下)
  202. {
  203. // 将SHT3X接收的6个字节数据进行CRC校验,并转换为温度值和湿度值
  204. if(sht3x_dat2float(recv_dat, &sht30Data.temperature, &sht30Data.humidity) == 0)
  205. {
  206. //ESP_LOGI(TAG, "temperature = %.2f ℃,humidity = %.2f %%RH", temperature, humidity);
  207. sht30Data.temperature = correct_temperature(sht30Data.temperature);
  208. snprintf(temp_buf, sizeof(temp_buf), "%.1f", sht30Data.temperature);
  209. snprintf(hum_buf, sizeof(hum_buf), "%.1f", sht30Data.humidity);
  210. ui_wifi_lab_refresh();
  211. if(guider_ui.screen_main) {
  212. lvgl_port_lock(-1);
  213. lv_label_set_text_static(guider_ui.screen_main_label_temp, temp_buf);
  214. lv_label_set_text_static(guider_ui.screen_main_label_hum_vul2,hum_buf);
  215. lvgl_port_unlock();
  216. }
  217. }
  218. else
  219. {
  220. ESP_LOGE(TAG, "crc check fail.\n");
  221. }
  222. }
  223. else
  224. {
  225. ESP_LOGE(TAG, "read data from sht3x fail.\n");
  226. }
  227. if (lcd_st7701_get_blacklight() == false) // 如果当前背光引脚为低电平
  228. {
  229. current_seconds -= 3;
  230. if (current_seconds < 0) current_seconds = 0;
  231. } else
  232. current_seconds += 3;
  233. ui_wifi_lab_refresh();
  234. vTaskDelay(5000 / portTICK_PERIOD_MS);
  235. vTaskDelay(5000 / portTICK_PERIOD_MS);
  236. }
  237. }
  238. void sht30_init() {
  239. xTaskCreate(i2c_sht3x_task, "i2c_sht3x_task", 6 * 1024, NULL, 3, NULL);
  240. }