#include "temp_humidity_monitor.h" #include #include "driver/i2c_master.h" #include "esp_check.h" #include "esp_err.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/portmacro.h" #include "freertos/task.h" static const char *TAG = "shtc3"; #define SHTC3_I2C_PORT I2C_NUM_0 #define SHTC3_I2C_SDA GPIO_NUM_13 #define SHTC3_I2C_SCL GPIO_NUM_14 #define SHTC3_I2C_ADDRESS 0x70 #define SHTC3_I2C_HZ 100000 #define SHTC3_WAKEUP_CMD 0x3517 #define SHTC3_SLEEP_CMD 0xB098 #define SHTC3_MEASURE_CMD 0x7866 static i2c_master_bus_handle_t s_i2c_bus; static i2c_master_dev_handle_t s_shtc3_dev; static portMUX_TYPE s_env_mux = portMUX_INITIALIZER_UNLOCKED; static int s_latest_temperature_c10; static int s_latest_humidity_percent; static bool s_has_latest; static uint8_t shtc3_crc(const uint8_t *data) { uint8_t crc = 0xFF; for (int i = 0; i < 2; i++) { crc ^= data[i]; for (int bit = 0; bit < 8; bit++) { if ((crc & 0x80) != 0) { crc = (uint8_t)((crc << 1) ^ 0x31); } else { crc <<= 1; } } } return crc; } static esp_err_t shtc3_send_command(uint16_t command) { uint8_t data[2] = { (uint8_t)(command >> 8), (uint8_t)(command & 0xFF), }; return i2c_master_transmit(s_shtc3_dev, data, sizeof(data), 1000); } static esp_err_t shtc3_read_once(int *temperature_c10, int *humidity_percent) { uint8_t data[6] = { 0 }; ESP_RETURN_ON_ERROR(shtc3_send_command(SHTC3_WAKEUP_CMD), TAG, "wake failed"); vTaskDelay(pdMS_TO_TICKS(2)); ESP_RETURN_ON_ERROR(shtc3_send_command(SHTC3_MEASURE_CMD), TAG, "measure failed"); vTaskDelay(pdMS_TO_TICKS(15)); ESP_RETURN_ON_ERROR(i2c_master_receive(s_shtc3_dev, data, sizeof(data), 1000), TAG, "read failed"); (void)shtc3_send_command(SHTC3_SLEEP_CMD); if (shtc3_crc(&data[0]) != data[2] || shtc3_crc(&data[3]) != data[5]) { return ESP_ERR_INVALID_CRC; } uint16_t raw_temp = ((uint16_t)data[0] << 8) | data[1]; uint16_t raw_humidity = ((uint16_t)data[3] << 8) | data[4]; *temperature_c10 = -450 + (int)((1750L * raw_temp + 32767) / 65535); *humidity_percent = (int)((100L * raw_humidity + 32767) / 65535); if (*humidity_percent < 0) { *humidity_percent = 0; } else if (*humidity_percent > 100) { *humidity_percent = 100; } return ESP_OK; } static esp_err_t shtc3_init(void) { i2c_master_bus_config_t bus_config = { .i2c_port = SHTC3_I2C_PORT, .sda_io_num = SHTC3_I2C_SDA, .scl_io_num = SHTC3_I2C_SCL, .clk_source = I2C_CLK_SRC_DEFAULT, .glitch_ignore_cnt = 7, .flags.enable_internal_pullup = true, }; ESP_RETURN_ON_ERROR(i2c_new_master_bus(&bus_config, &s_i2c_bus), TAG, "i2c bus init failed"); i2c_device_config_t device_config = { .dev_addr_length = I2C_ADDR_BIT_LEN_7, .device_address = SHTC3_I2C_ADDRESS, .scl_speed_hz = SHTC3_I2C_HZ, }; ESP_RETURN_ON_ERROR(i2c_master_bus_add_device(s_i2c_bus, &device_config, &s_shtc3_dev), TAG, "shtc3 device add failed"); return ESP_OK; } static void temp_humidity_task(void *arg) { (void)arg; while (1) { int temperature_c10 = 0; int humidity_percent = 0; esp_err_t ret = shtc3_read_once(&temperature_c10, &humidity_percent); if (ret == ESP_OK) { portENTER_CRITICAL(&s_env_mux); s_latest_temperature_c10 = temperature_c10; s_latest_humidity_percent = humidity_percent; s_has_latest = true; portEXIT_CRITICAL(&s_env_mux); } else { ESP_LOGW(TAG, "read disabled this round: %s", esp_err_to_name(ret)); } vTaskDelay(pdMS_TO_TICKS(10000)); } } void temp_humidity_monitor_start(void) { esp_err_t ret = shtc3_init(); if (ret != ESP_OK) { ESP_LOGW(TAG, "temperature/humidity monitor disabled: %s", esp_err_to_name(ret)); return; } xTaskCreate(temp_humidity_task, "shtc3", 4096, NULL, 2, NULL); } bool temp_humidity_monitor_take_latest(int *temperature_c10, int *humidity_percent) { if (temperature_c10 == NULL || humidity_percent == NULL) { return false; } bool has_latest = false; portENTER_CRITICAL(&s_env_mux); if (s_has_latest) { *temperature_c10 = s_latest_temperature_c10; *humidity_percent = s_latest_humidity_percent; s_has_latest = false; has_latest = true; } portEXIT_CRITICAL(&s_env_mux); return has_latest; }