ESP32/main/temp_humidity_monitor.c
2026-06-23 20:38:23 +08:00

160 lines
4.5 KiB
C

#include "temp_humidity_monitor.h"
#include <stdint.h>
#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;
}