mirror of
https://git.beihong.wang/wangbeihong/iot-bedroom-environment-controller.git
synced 2026-04-23 16:53:05 +08:00
316 lines
9.9 KiB
C
316 lines
9.9 KiB
C
#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;
|
||
} |