Files
iot-bedroom-environment-con…/main/sensors.c

316 lines
9.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "sensors.h"
#include "esp_log.h"
#include "ahtxx.h"
#include "bh1750.h"
#include "sgp30/sgp30.h"
#include "driver/i2c_master.h"
#include "vars.h"
static const char *TAG = "sensors";
// 实际全局变量定义(从 main.c 抽取到这里)
sensor_data_t g_sensor_data = {0};
i2c_master_bus_handle_t bus_handle = NULL;
i2c_master_dev_handle_t dev_handle = NULL;
esp_err_t i2c_master_init(void)
{
/* 如果本地已有句柄,直接返回(避免重复初始化) */
if (bus_handle != NULL)
{
ESP_LOGI(TAG, "I2C bus already initialized (local handle)");
return ESP_OK;
}
/* 优先尝试获取已有的总线句柄(其它模块可能已初始化) */
esp_err_t ret = i2c_master_get_bus_handle(I2C_MASTER_NUM, &bus_handle);
if (ret == ESP_OK)
{
ESP_LOGI(TAG, "I2C bus already initialized (existing handle)");
return ESP_OK;
}
/* 如果不是未初始化的常见返回ESP_ERR_INVALID_STATE记录警告并尝试创建 */
if (ret != ESP_ERR_INVALID_STATE && ret != ESP_ERR_INVALID_ARG)
{
ESP_LOGW(TAG, "i2c_master_get_bus_handle returned %s, will try to create new bus", esp_err_to_name(ret));
}
i2c_master_bus_config_t i2c_mst_config = {
.clk_source = I2C_CLK_SRC_DEFAULT,
.i2c_port = I2C_MASTER_NUM,
.scl_io_num = I2C_MASTER_SCL_IO,
.sda_io_num = I2C_MASTER_SDA_IO,
.glitch_ignore_cnt = 7,
.flags.enable_internal_pullup = true,
};
ret = i2c_new_master_bus(&i2c_mst_config, &bus_handle);
if (ret == ESP_OK)
{
ESP_LOGI(TAG, "I2C master bus initialized successfully");
return ESP_OK;
}
/* 若创建失败且是已被占用的状态,再次尝试取句柄(防止竞态) */
if (ret == ESP_ERR_INVALID_STATE)
{
esp_err_t get_ret = i2c_master_get_bus_handle(I2C_MASTER_NUM, &bus_handle);
if (get_ret == ESP_OK)
{
ESP_LOGW(TAG, "I2C bus already acquired by other module; obtained existing handle");
return ESP_OK;
}
ESP_LOGE(TAG, "i2c_new_master_bus failed and get_bus_handle also failed: %s / %s", esp_err_to_name(ret), esp_err_to_name(get_ret));
return get_ret;
}
ESP_LOGE(TAG, "i2c_new_master_bus failed: %s", esp_err_to_name(ret));
return ret;
}
void i2c0_ahtxx_task(void *pvParameters)
{
(void)pvParameters;
ahtxx_config_t ahtxx_config = I2C_AHT30_CONFIG_DEFAULT;
ahtxx_handle_t ahtxx_handle = NULL;
esp_err_t ret = ahtxx_init(bus_handle, &ahtxx_config, &ahtxx_handle);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "AHTxx init failed, sensor invalid, err=0x%x", ret);
while (1)
{
if (xSensorDataMutex != NULL && xSemaphoreTake(xSensorDataMutex, portMAX_DELAY) == pdTRUE)
{
g_sensor_data.ahtxx_valid = false;
xSemaphoreGive(xSensorDataMutex);
}
vTaskDelay(pdMS_TO_TICKS(5000));
}
}
float temperature, humidity;
while (1)
{
ret = ahtxx_get_measurement(ahtxx_handle, &temperature, &humidity);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "AHTxx read failed, err=0x%x", ret);
if (xSensorDataMutex != NULL && xSemaphoreTake(xSensorDataMutex, portMAX_DELAY) == pdTRUE)
{
g_sensor_data.ahtxx_valid = false;
xSemaphoreGive(xSensorDataMutex);
}
}
else
{
if (xSensorDataMutex != NULL && xSemaphoreTake(xSensorDataMutex, portMAX_DELAY) == pdTRUE)
{
g_sensor_data.temperature = temperature;
g_sensor_data.humidity = humidity;
g_sensor_data.ahtxx_valid = true;
xSemaphoreGive(xSensorDataMutex);
}
// ESP_LOGI(TAG, "AHTxx: temperature=%.1f°C, humidity=%.1f%%", temperature, humidity);
// 更新屏幕显示变量
char temp_str[16];
char humidity_str[16];
snprintf(temp_str, sizeof(temp_str), "%.1f", temperature);
snprintf(humidity_str, sizeof(humidity_str), "%.1f", humidity);
set_var_tempture(temp_str);
set_var_humity(humidity_str);
}
vTaskDelay(pdMS_TO_TICKS(5000));
}
}
void i2c0_bh1750_task(void *pvParameters)
{
(void)pvParameters;
bh1750_handle_t bh1750_handle = NULL;
// 使用默认地址创建 BH1750 设备
esp_err_t ret = bh1750_create(bus_handle, BH1750_I2C_ADDRESS_DEFAULT, &bh1750_handle);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "BH1750 create failed, sensor invalid, err=0x%x", ret);
while (1)
{
if (xSensorDataMutex != NULL && xSemaphoreTake(xSensorDataMutex, portMAX_DELAY) == pdTRUE)
{
g_sensor_data.bh1750_valid = false;
xSemaphoreGive(xSensorDataMutex);
}
vTaskDelay(pdMS_TO_TICKS(5000));
}
}
// 先上电
ret = bh1750_power_on(bh1750_handle);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "BH1750 power on failed, err=0x%x", ret);
}
// 设置测量模式为连续高分辨率模式必须在power_on之后
ret = bh1750_set_measure_mode(bh1750_handle, BH1750_CONTINUE_1LX_RES);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "BH1750 set measure mode failed, err=0x%x", ret);
}
float lux;
while (1)
{
ret = bh1750_get_data(bh1750_handle, &lux);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "BH1750 read failed, err=0x%x", ret);
if (xSensorDataMutex != NULL && xSemaphoreTake(xSensorDataMutex, portMAX_DELAY) == pdTRUE)
{
g_sensor_data.bh1750_valid = false;
xSemaphoreGive(xSensorDataMutex);
}
}
else
{
if (xSensorDataMutex != NULL && xSemaphoreTake(xSensorDataMutex, portMAX_DELAY) == pdTRUE)
{
g_sensor_data.lux = lux;
g_sensor_data.bh1750_valid = true;
xSemaphoreGive(xSensorDataMutex);
}
// ESP_LOGI(TAG, "BH1750: illuminance=%.1f lux", lux);
// 更新屏幕显示变量
char light_str[16];
snprintf(light_str, sizeof(light_str), "%.1f", lux);
set_var_light_value(light_str);
}
vTaskDelay(pdMS_TO_TICKS(5000));
}
}
void i2c0_sgp30_task(void *pvParameters)
{
(void)pvParameters;
// SGP30 配置
sgp30_config_t sgp30_config = {
.i2c_master_port = I2C_MASTER_NUM,
.i2c_address = 0x58, // SGP30 默认地址
};
// 初始化 SGP30
esp_err_t ret = sgp30_init(&sgp30_config);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "SGP30 init failed, err=0x%x", ret);
while (1)
{
if (xSensorDataMutex != NULL && xSemaphoreTake(xSensorDataMutex, portMAX_DELAY) == pdTRUE)
{
g_sensor_data.sgp30_valid = false;
xSemaphoreGive(xSensorDataMutex);
}
vTaskDelay(pdMS_TO_TICKS(5000));
}
}
ESP_LOGI(TAG, "SGP30 initialized successfully");
uint16_t co2_ppm, tvoc_ppb;
while (1)
{
ret = sgp30_read_measurements(&co2_ppm, &tvoc_ppb);
if (ret != ESP_OK)
{
if (xSensorDataMutex != NULL && xSemaphoreTake(xSensorDataMutex, portMAX_DELAY) == pdTRUE)
{
g_sensor_data.sgp30_valid = false;
xSemaphoreGive(xSensorDataMutex);
}
}
else
{
if (xSensorDataMutex != NULL && xSemaphoreTake(xSensorDataMutex, portMAX_DELAY) == pdTRUE)
{
g_sensor_data.co2_ppm = co2_ppm;
g_sensor_data.tvoc_ppb = tvoc_ppb;
g_sensor_data.sgp30_valid = true;
xSemaphoreGive(xSensorDataMutex);
}
// ESP_LOGI(TAG, "SGP30: CO2=%d ppm, TVOC=%d ppb", co2_ppm, tvoc_ppb);
// 更新屏幕显示变量
char co2_str[16];
char voc_str[16];
snprintf(co2_str, sizeof(co2_str), "%d", co2_ppm);
snprintf(voc_str, sizeof(voc_str), "%d", tvoc_ppb);
set_var_co2_value(co2_str);
set_var_voc_value(voc_str);
}
// SGP30 建议每秒读取一次
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
void get_sensor_data(sensor_data_t *data)
{
if (data != NULL)
{
if (xSensorDataMutex != NULL && xSemaphoreTake(xSensorDataMutex, portMAX_DELAY) == pdTRUE)
{
*data = g_sensor_data;
xSemaphoreGive(xSensorDataMutex);
}
}
}
esp_err_t sensors_init(void)
{
// esp_err_t err = i2c_master_init();
// if (err != ESP_OK)
// {
// ESP_LOGW(TAG, "i2c_master_init failed: %s", esp_err_to_name(err));
// return err;
// }
ESP_LOGI(TAG, "创建传感器任务前堆: %d", heap_caps_get_free_size(MALLOC_CAP_8BIT));
/* i2c0_ahtxx_task 需要较大栈,防止栈溢出(观察到任务崩溃),恢复为 4096 */
BaseType_t ok = xTaskCreate(i2c0_ahtxx_task, "i2c0_ahtxx_task", 3048, NULL, 5, NULL);
ESP_LOGI(TAG, "创建 i2c0_ahtxx_task 后堆: %d", heap_caps_get_free_size(MALLOC_CAP_8BIT));
if (ok != pdPASS)
{
return ESP_ERR_NO_MEM;
}
// 降低光照/SGP30任务栈到 1536释放堆空间如遇溢出可再调大
ok = xTaskCreate(i2c0_bh1750_task, "i2c0_bh1750_task", 3072, NULL, 5, NULL);
ESP_LOGI(TAG, "创建 i2c0_bh1750_task 后堆: %d", heap_caps_get_free_size(MALLOC_CAP_8BIT));
if (ok != pdPASS)
{
return ESP_ERR_NO_MEM;
}
ok = xTaskCreate(i2c0_sgp30_task, "i2c0_sgp30_task", 2048, NULL, 5, NULL);
ESP_LOGI(TAG, "创建 i2c0_sgp30_task 后堆: %d", heap_caps_get_free_size(MALLOC_CAP_8BIT));
if (ok != pdPASS)
{
return ESP_ERR_NO_MEM;
}
return ESP_OK;
}
esp_err_t sensors_read(void)
{
// 保留占位,如需单次读可以实现
return ESP_OK;
}