Files
Smart-granary-code/main/main.cpp

288 lines
8.7 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 <stdio.h>
#include <time.h>
#include <string.h>
#include "wifi-connect.h"
#include "esp_lvgl_port.h"
#include "lvgl_st7789_use.h"
#include "ui.h"
#include "vars.h"
#include "relay_ctrl.h"
#include "esp_err.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "sntp_time.h"
#include "esp_mac.h"
#include "esp_system.h"
#include "esp_efuse.h"
#include "esp_efuse_table.h"
#include "sntp_time.h"
#include "bh1750_use.h"
#include "aht30.h"
#include "MQ-2.h"
#include "JW01.h"
#include "human_door.h"
#include "fire_sensor.h"
#define TAG "MAIN"
#define CO2_SPOILAGE_THRESHOLD_PPM 1500.0f
#define FIRE_DANGER_THRESHOLD_PERCENT 35.0f
typedef struct
{
char time_str[32];
float lux;
float temp;
float humidity;
float gas_percent;
float tvoc;
float hcho;
float co2;
float fire_percent;
bool fire_danger;
bool human_present;
bool door_closed;
} env_data_t;
static env_data_t s_env_data;
static SemaphoreHandle_t s_env_data_lock = NULL;
static volatile bool s_ui_ready = false;
static bool wait_for_wifi_connected(TickType_t timeout_ticks)
{
const TickType_t start_ticks = xTaskGetTickCount();
while ((xTaskGetTickCount() - start_ticks) < timeout_ticks)
{
if (wifi_connect_get_status() == WIFI_CONNECT_STATUS_CONNECTED)
return true;
vTaskDelay(pdMS_TO_TICKS(200));
}
return false;
}
static void env_data_update_system_info(void)
{
if (s_env_data_lock == NULL)
return;
time_t now;
struct tm timeinfo;
time(&now);
localtime_r(&now, &timeinfo);
char time_buf[32];
strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", &timeinfo);
xSemaphoreTake(s_env_data_lock, portMAX_DELAY);
strncpy(s_env_data.time_str, time_buf, sizeof(s_env_data.time_str));
xSemaphoreGive(s_env_data_lock);
set_var_local_time(s_env_data.time_str);
}
static void ui_task(void *arg)
{
for (;;)
{
if (!s_ui_ready)
{
vTaskDelay(pdMS_TO_TICKS(20));
continue;
}
env_data_update_system_info();
lvgl_port_lock(0);
ui_tick();
lvgl_port_unlock();
vTaskDelay(pdMS_TO_TICKS(30));
}
}
static void ui_init_task(void *arg)
{
(void)arg;
lvgl_port_lock(100 / portTICK_PERIOD_MS);
ui_init();
lvgl_port_unlock();
s_ui_ready = true;
vTaskDelete(NULL);
}
static void status_task(void *arg)
{
(void)arg;
for (;;)
{
human_door_state_t io_state{};
if (human_door_read(&io_state) == ESP_OK)
{
set_var_hum_status(io_state.human_present ? "有人" : "无人");
set_var_door_status(io_state.door_closed ? "关闭" : "开启");
if (s_env_data_lock)
{
xSemaphoreTake(s_env_data_lock, portMAX_DELAY);
s_env_data.human_present = io_state.human_present;
s_env_data.door_closed = io_state.door_closed;
xSemaphoreGive(s_env_data_lock);
}
}
vTaskDelay(pdMS_TO_TICKS(200));
}
}
static void sntp_task(void *arg)
{
(void)arg;
if (wait_for_wifi_connected(pdMS_TO_TICKS(15000))) {
esp_err_t sntp_ret = sntp_timp_sync_time(10000);
if (sntp_ret != ESP_OK) {
ESP_LOGW(TAG, "SNTP sync failed: %s", esp_err_to_name(sntp_ret));
}
}
vTaskDelete(NULL);
}
extern "C" void app_main(void)
{
vTaskDelay(pdMS_TO_TICKS(100));
ESP_LOGI(TAG, "--- APP STARTING ---");
s_env_data_lock = xSemaphoreCreateMutex();
// 1. 初始化 Wi-Fi
ESP_ERROR_CHECK(wifi_connect_init());
// 2. 初始化显示屏和 LVGL
start_lvgl_demo();
// 3. 在 CPU1 上执行 UI 初始化,避免 app_main 长时间占用 CPU0
xTaskCreatePinnedToCore(ui_init_task, "ui_init_task", 8192, NULL, 8, NULL, 1);
set_var_food_status("良好");
set_var_fire_status("安全");
// 7. 创建 UI 刷新任务并固定在 CPU1与 ui_init_task 同核)
xTaskCreatePinnedToCore(ui_task, "ui_task", 8192, NULL, 5, NULL, 1);
if (wait_for_wifi_connected(pdMS_TO_TICKS(15000)))
{
set_var_system_ip(wifi_connect_get_ip());
}
// 4. 独立 SNTP 对时任务
xTaskCreate(sntp_task, "sntp_task", 4096, NULL, 4, NULL);
// 初始化继电器 (独立配置每个通道)
const relay_config_t relay_cfg[RELAY_CTRL_ID_MAX] = {
{.pin = GPIO_NUM_9, .active_high = false},
{.pin = GPIO_NUM_10, .active_high = false},
{.pin = GPIO_NUM_11, .active_high = true},
{.pin = GPIO_NUM_12, .active_high = true},
};
ESP_ERROR_CHECK(relay_ctrl_init(relay_cfg));
// 5. 初始化 I2C 总线并注册传感器 (共享总线)
ESP_ERROR_CHECK(bh1750_user_init());
i2c_master_bus_handle_t i2c_bus = bh1750_get_i2c_bus_handle();
// AHT30 挂载到同一条 I2C 总线上
aht30_handle_t aht30_dev = NULL;
ESP_ERROR_CHECK(aht30_create(i2c_bus, AHT30_I2C_ADDRESS, &aht30_dev));
// MQ-2 使用 ADC(GPIO8)
ESP_ERROR_CHECK(mq2_init());
// JW01 使用 UART0(GPIO43/44)
ESP_ERROR_CHECK(jw01_init());
// 火焰传感器使用 ADC(GPIO3)
ESP_ERROR_CHECK(fire_sensor_init());
// GPIO16: HC-SR312, GPIO17: Door switch(低电平=关门)
ESP_ERROR_CHECK(human_door_init());
set_var_hum_status("无人");
set_var_door_status("关闭");
// 6. 创建传感器读取任务
xTaskCreate([](void *arg)
{
aht30_handle_t aht30 = (aht30_handle_t)arg;
uint32_t log_cnt = 0;
for (;;) {
float lux = 0.0f, temp = 0.0f, hum = 0.0f, gas_percent = 0.0f, fire_percent = 0.0f;
jw01_data_t jw01{};
esp_err_t bh_ret = ESP_FAIL;
esp_err_t aht_ret = ESP_FAIL;
esp_err_t mq2_ret = ESP_FAIL;
esp_err_t jw_ret = ESP_FAIL;
esp_err_t fire_ret = ESP_FAIL;
// 读取 BH1750
bh_ret = bh1750_user_read(&lux);
if (bh_ret == ESP_OK) {
set_var_light_val(lux);
}
// 读取 AHT30
aht_ret = aht30_get_temperature_humidity_value(aht30, &temp, &hum);
if (aht_ret == ESP_OK) {
set_var_temp(temp);
set_var_humity_val(hum);
}
// 读取 MQ-2更新空气质量变量
mq2_ret = mq2_read_percent(&gas_percent);
if (mq2_ret == ESP_OK) {
set_var_air_quity(gas_percent);
}
// 读取火焰传感器,更新火焰状态
fire_ret = fire_sensor_read_percent(&fire_percent);
if (fire_ret == ESP_OK) {
set_var_fire_status(fire_sensor_is_danger(fire_percent, FIRE_DANGER_THRESHOLD_PERCENT) ? "危险" : "安全");
}
// 读取 JW01TVOC/HCHO/CO2
jw_ret = jw01_read(&jw01, 200);
if (jw_ret == ESP_OK) {
if (jw01.co2_valid) {
if (jw01.co2 >= CO2_SPOILAGE_THRESHOLD_PPM) {
set_var_food_status("变质");
} else {
set_var_food_status("良好");
}
}
}
// 每 5 次打印一次综合状态,避免日志刷屏
if ((log_cnt++ % 10) == 0) {
ESP_LOGI(TAG,
"SENS bh=%s lux=%.1f | aht=%s t=%.1f h=%.1f | mq2=%s gas=%.1f | fire=%s fp=%.1f | jw01=%s co2_valid=%d co2=%.1f",
esp_err_to_name(bh_ret), lux,
esp_err_to_name(aht_ret), temp, hum,
esp_err_to_name(mq2_ret), gas_percent,
esp_err_to_name(fire_ret), fire_percent,
esp_err_to_name(jw_ret), jw01.co2_valid ? 1 : 0, jw01.co2);
}
// 数据存入共享结构体
if (s_env_data_lock) {
xSemaphoreTake(s_env_data_lock, portMAX_DELAY);
s_env_data.lux = lux;
s_env_data.temp = temp;
s_env_data.humidity = hum;
s_env_data.gas_percent = gas_percent;
s_env_data.fire_percent = fire_percent;
s_env_data.fire_danger = fire_sensor_is_danger(fire_percent, FIRE_DANGER_THRESHOLD_PERCENT);
if (jw01.tvoc_valid) s_env_data.tvoc = jw01.tvoc;
if (jw01.hcho_valid) s_env_data.hcho = jw01.hcho;
if (jw01.co2_valid) s_env_data.co2 = jw01.co2;
xSemaphoreGive(s_env_data_lock);
}
vTaskDelay(pdMS_TO_TICKS(1000));
} }, "sensor_task", 4096 * 3, (void *)aht30_dev, 6, NULL);
// 独立任务:人体与门状态检测
xTaskCreate(status_task, "status_task", 4096, NULL, 6, NULL);
}