Files
Smart-flower-system/main/main.c
Wang Beihong ce2cf9a9ee 新增:添加 Wi-Fi 和 MQTT 连接状态变量,并更新 UI
- 引入了新的全局变量,用于跟踪 Wi-Fi 和 MQTT 连接状态。
- 实现了 Wi-Fi 和 MQTT 连接状态的 getter 和 setter 函数。
- 增加了管理网络信息和时间同步的功能。
- 更新了主应用程序逻辑,以反映 UI 中的当前连接状态。
- 改进了 Wi-Fi 连接处理,以定期发布网络信息。
-土壤湿度和空气温度的综合阈值变量被整合到 UI 中。
2026-03-14 18:10:43 +08:00

709 lines
23 KiB
C
Executable File
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 <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_check.h"
#include "esp_log.h"
#include "wifi-connect.h"
#include "lvgl_st7789_use.h"
#include "i2c_master_messager.h"
#include "io_device_control.h"
#include "console_simple_init.h" // 提供 console_cmd_user_register 和 console_cmd_all_register
#include "console_user_cmds.h"
#include "capactive_soil_moisture_sensor_V2.0.h"
#include "ui.h" // 使用EEZStudio提供的ui组件便于后续扩展
#include "esp_lvgl_port.h"
#include "vars.h" // 定义全局变量接口
#include "auto_ctrl_thresholds.h"
#include "auto_alerts.h"
#include "mqtt_control.h" // MQTT 控制接口
#include "status_web.h"
#include "sntp_timp.h"
// 配置宏定义BH1750 光照传感器是否启用(默认禁用)
#ifndef CONFIG_I2C_MASTER_MESSAGER_BH1750_ENABLE
#define CONFIG_I2C_MASTER_MESSAGER_BH1750_ENABLE 0
#endif
// 配置宏定义AHT30 温湿度传感器是否启用(默认禁用)
#ifndef CONFIG_I2C_MASTER_MESSAGER_AHT30_ENABLE
#define CONFIG_I2C_MASTER_MESSAGER_AHT30_ENABLE 0
#endif
// 配置宏定义BH1750 读取周期毫秒默认500ms
#ifndef CONFIG_I2C_MASTER_MESSAGER_BH1750_READ_PERIOD_MS
#define CONFIG_I2C_MASTER_MESSAGER_BH1750_READ_PERIOD_MS 500
#endif
// 配置宏定义AHT30 读取周期毫秒默认2000ms
#ifndef CONFIG_I2C_MASTER_MESSAGER_AHT30_READ_PERIOD_MS
#define CONFIG_I2C_MASTER_MESSAGER_AHT30_READ_PERIOD_MS 2000
#endif
// 配置宏定义I2C 是否启用内部上拉电阻(默认启用)
#ifndef CONFIG_I2C_MASTER_MESSAGER_ENABLE_INTERNAL_PULLUP
#define CONFIG_I2C_MASTER_MESSAGER_ENABLE_INTERNAL_PULLUP 1
#endif
// I2C 端口配置
#define BOTANY_I2C_PORT I2C_NUM_0
// I2C SCL 引脚
#define BOTANY_I2C_SCL_GPIO GPIO_NUM_5
// I2C SDA 引脚
#define BOTANY_I2C_SDA_GPIO GPIO_NUM_4
// BH1750 使能标志
#define BOTANY_BH1750_ENABLE CONFIG_I2C_MASTER_MESSAGER_BH1750_ENABLE
// AHT30 使能标志
#define BOTANY_AHT30_ENABLE CONFIG_I2C_MASTER_MESSAGER_AHT30_ENABLE
// BH1750 读取周期
#define BOTANY_BH1750_PERIOD_MS CONFIG_I2C_MASTER_MESSAGER_BH1750_READ_PERIOD_MS
// AHT30 读取周期
#define BOTANY_AHT30_PERIOD_MS CONFIG_I2C_MASTER_MESSAGER_AHT30_READ_PERIOD_MS
// I2C 内部上拉使能
#define BOTANY_I2C_INTERNAL_PULLUP CONFIG_I2C_MASTER_MESSAGER_ENABLE_INTERNAL_PULLUP
// MQTT 告警主题
#define BOTANY_MQTT_ALERT_TOPIC "topic/alert/esp32_iothome_002"
// MQTT 遥测数据上报周期(毫秒)
#define BOTANY_MQTT_TELEMETRY_PERIOD_MS 5000
#define BOTANY_STATUS_WEB_PORT 8080
// 日志标签
static const char *TAG = "main";
// 全局变量:存储空气温度字符串
static char s_air_temp[16];
// 全局变量:存储空气湿度字符串
static char s_air_hum[16];
// 全局变量:存储土壤湿度字符串
static char s_soil[16];
// 全局变量:存储光照强度字符串
static char s_lux[16];
// 全局变量水泵状态true=开启false=关闭)
static bool s_pump_on = false;
// 全局变量补光灯状态true=开启false=关闭)
static bool s_light_on = false;
// 全局变量自动控制模式使能true=自动false=手动)
static bool s_auto_control_enabled = true;
static bool s_i2c_ready = false;
static bool s_soil_sensor_ready = false;
static uint32_t s_main_loop_counter = 0;
/**
* @brief 发布当前完整状态快照(含阈值)到传感器主题
*/
static esp_err_t publish_telemetry_snapshot(void)
{
if (!mqtt_control_is_connected())
{
return ESP_ERR_INVALID_STATE;
}
auto_ctrl_thresholds_t thresholds = {0};
auto_ctrl_thresholds_get(&thresholds);
char telemetry_payload[256] = {0};
int len = snprintf(telemetry_payload,
sizeof(telemetry_payload),
"{\"temp\":\"%s\",\"hum\":\"%s\",\"soil\":\"%s\",\"lux\":\"%s\",\"pump\":\"%s\",\"light\":\"%s\",\"mode\":\"%s\",\"soil_on\":%.1f,\"soil_off\":%.1f,\"light_on\":%.1f,\"light_off\":%.1f}",
s_air_temp,
s_air_hum,
s_soil,
s_lux,
s_pump_on ? "on" : "off",
s_light_on ? "on" : "off",
s_auto_control_enabled ? "auto" : "manual",
thresholds.pump_on_soil_below_pct,
thresholds.pump_off_soil_above_pct,
thresholds.light_on_lux_below,
thresholds.light_off_lux_above);
if (len <= 0 || len >= (int)sizeof(telemetry_payload))
{
return ESP_ERR_INVALID_SIZE;
}
return mqtt_control_publish_sensor(telemetry_payload, 0, 0);
}
static void update_status_web_snapshot(void)
{
status_web_snapshot_t snap = {0};
snprintf(snap.temp, sizeof(snap.temp), "%s", s_air_temp[0] ? s_air_temp : "--");
snprintf(snap.hum, sizeof(snap.hum), "%s", s_air_hum[0] ? s_air_hum : "--");
snprintf(snap.soil, sizeof(snap.soil), "%s", s_soil[0] ? s_soil : "--");
snprintf(snap.lux, sizeof(snap.lux), "%s", s_lux[0] ? s_lux : "--");
snap.pump_on = s_pump_on;
snap.light_on = s_light_on;
snap.auto_mode = s_auto_control_enabled;
auto_ctrl_thresholds_t thresholds = {0};
auto_ctrl_thresholds_get(&thresholds);
snap.soil_on_threshold = thresholds.pump_on_soil_below_pct;
snap.soil_off_threshold = thresholds.pump_off_soil_above_pct;
snap.light_on_threshold = thresholds.light_on_lux_below;
snap.light_off_threshold = thresholds.light_off_lux_above;
snap.i2c_ready = s_i2c_ready;
snap.soil_sensor_ready = s_soil_sensor_ready;
snap.loop_counter = s_main_loop_counter;
esp_err_t ret = status_web_update(&snap);
if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE)
{
ESP_LOGW(TAG, "status web update failed: %s", esp_err_to_name(ret));
}
}
static void update_ui_threshold_vars(const auto_ctrl_thresholds_t *thresholds)
{
if (thresholds == NULL)
{
return;
}
char soil_threshold_text[16] = {0};
char air_threshold_text[16] = {0};
snprintf(soil_threshold_text,
sizeof(soil_threshold_text),
"%.0f~%.0f",
thresholds->pump_on_soil_below_pct,
thresholds->pump_off_soil_above_pct);
snprintf(air_threshold_text,
sizeof(air_threshold_text),
"%.0f~%.0f",
thresholds->light_on_lux_below,
thresholds->light_off_lux_above);
set_var_soil_mois_num(soil_threshold_text);
set_var_air_temp_num(air_threshold_text);
}
/**
* @brief MQTT 控制命令处理函数
*
* 处理来自 MQTT 的控制命令,包括模式切换、阈值更新、水泵和补光灯控制。
*
* @param cmd 指向 MQTT 控制命令结构体的指针
* @param user_ctx 用户上下文(未使用)
* @return esp_err_t 处理结果
*/
static esp_err_t mqtt_control_command_handler(const mqtt_control_command_t *cmd, void *user_ctx)
{
(void)user_ctx;
ESP_RETURN_ON_FALSE(cmd != NULL, ESP_ERR_INVALID_ARG, TAG, "cmd is null");
esp_err_t final_ret = ESP_OK;
bool has_any_control = cmd->has_mode || cmd->has_thresholds || cmd->has_pump || cmd->has_light;
// 处理模式切换命令
if (cmd->has_mode)
{
s_auto_control_enabled = cmd->auto_mode;
ESP_LOGI(TAG, "MQTT 控制模式切换: %s", s_auto_control_enabled ? "auto" : "manual");
}
// 处理阈值更新命令
if (cmd->has_thresholds)
{
esp_err_t ret = auto_ctrl_thresholds_set_values(cmd->soil_on_pct,
cmd->soil_off_pct,
cmd->light_on_lux,
cmd->light_off_lux);
if (ret == ESP_OK)
{
ESP_LOGI(TAG,
"MQTT 更新阈值: soil_on=%.1f soil_off=%.1f light_on=%.1f light_off=%.1f",
cmd->soil_on_pct,
cmd->soil_off_pct,
cmd->light_on_lux,
cmd->light_off_lux);
}
else
{
ESP_LOGE(TAG, "设置阈值失败: %s", esp_err_to_name(ret));
final_ret = ret;
}
}
// 处理水泵控制命令
if (cmd->has_pump)
{
esp_err_t ret = io_device_control_set_pump(cmd->pump_on);
if (ret == ESP_OK)
{
s_pump_on = cmd->pump_on;
ESP_LOGI(TAG, "MQTT 控制水泵: %s", cmd->pump_on ? "on" : "off");
}
else
{
ESP_LOGE(TAG, "MQTT 控制水泵失败: %s", esp_err_to_name(ret));
final_ret = ret;
}
}
// 处理补光灯控制命令
if (cmd->has_light)
{
esp_err_t ret = io_device_control_set_light(cmd->light_on);
if (ret == ESP_OK)
{
s_light_on = cmd->light_on;
ESP_LOGI(TAG, "MQTT 控制补光灯: %s", cmd->light_on ? "on" : "off");
}
else
{
ESP_LOGE(TAG, "MQTT 控制补光灯失败: %s", esp_err_to_name(ret));
final_ret = ret;
}
}
// 任何控制指令处理后都立即上报最新状态(含阈值)作为回复。
if (has_any_control)
{
esp_err_t pub_ret = publish_telemetry_snapshot();
if (pub_ret != ESP_OK)
{
ESP_LOGW(TAG, "控制后立即上报失败: %s", esp_err_to_name(pub_ret));
if (final_ret == ESP_OK)
{
final_ret = pub_ret;
}
}
}
return final_ret;
}
/**
* @brief 将告警指标类型转换为字符串
*
* @param metric 告警指标类型
* @return const char* 对应的字符串表示
*/
static const char *alert_metric_text(auto_alert_metric_t metric)
{
switch (metric)
{
case AUTO_ALERT_METRIC_SOIL_MOISTURE:
return "soil";
case AUTO_ALERT_METRIC_LIGHT_INTENSITY:
return "light";
default:
return "unknown";
}
}
/**
* @brief 将告警状态转换为字符串
*
* @param state 告警状态
* @return const char* 对应的字符串表示
*/
static const char *alert_state_text(auto_alert_state_t state)
{
switch (state)
{
case AUTO_ALERT_STATE_NORMAL:
return "normal";
case AUTO_ALERT_STATE_ALARM:
return "alarm";
default:
return "unknown";
}
}
/**
* @brief 自动告警 MQTT 回调函数
*
* 当自动告警模块触发事件时,通过此函数将告警信息以 JSON 格式发布到 MQTT。
*
* @param event 指向告警事件结构体的指针
* @param user_ctx 用户上下文(未使用)
*/
static void auto_alert_mqtt_callback(const auto_alert_event_t *event, void *user_ctx)
{
(void)user_ctx;
if (event == NULL)
{
return;
}
// 使用明文发送报警简单的 JSON 字符串,格式示例:{"metric":"soil","state":"alarm"}
char payload[64] = {0};
int len = snprintf(payload,
sizeof(payload),
"{\"metric\":\"%s\",\"state\":\"%s\"}",
alert_metric_text(event->metric),
alert_state_text(event->state));
if (len <= 0 || len >= (int)sizeof(payload))
{
return;
}
if (!mqtt_control_is_connected())
{
return;
}
esp_err_t ret = mqtt_control_publish(BOTANY_MQTT_ALERT_TOPIC, payload, 1, 0);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "告警 MQTT 发布失败: %s", esp_err_to_name(ret));
}
}
/**
* @brief 自动控制逻辑更新函数
*
* 根据当前传感器数据和阈值,决定是否需要开启或关闭水泵和补光灯。
*
* @param soil_valid 土壤湿度数据是否有效
* @param soil_moisture_pct 当前土壤湿度百分比
* @param light_valid 光照数据是否有效
* @param light_lux 当前光照强度lux
* @param thresholds 指向阈值配置结构体的指针
* @param pump_on 指向当前水泵状态的指针(输入/输出)
* @param light_on 指向当前补光灯状态的指针(输入/输出)
*/
static void auto_control_update(bool soil_valid,
float soil_moisture_pct,
bool light_valid,
float light_lux,
const auto_ctrl_thresholds_t *thresholds,
bool *pump_on,
bool *light_on)
{
bool desired_pump = *pump_on;
bool desired_light = *light_on;
// 根据土壤湿度决定水泵状态
if (soil_valid)
{
if (!desired_pump && soil_moisture_pct < thresholds->pump_on_soil_below_pct)
{
desired_pump = true;
}
else if (desired_pump && soil_moisture_pct > thresholds->pump_off_soil_above_pct)
{
desired_pump = false;
}
}
// 根据光照强度决定补光灯状态
if (light_valid)
{
if (!desired_light && light_lux < thresholds->light_on_lux_below)
{
desired_light = true;
}
else if (desired_light && light_lux > thresholds->light_off_lux_above)
{
desired_light = false;
}
}
// 如果水泵状态需要改变,则执行控制
if (desired_pump != *pump_on)
{
esp_err_t ret = io_device_control_set_pump(desired_pump);
if (ret == ESP_OK)
{
*pump_on = desired_pump;
ESP_LOGI(TAG,
"自动控制: 水泵%s (土壤湿度=%.1f%%)",
desired_pump ? "开启" : "关闭",
soil_moisture_pct);
}
else
{
ESP_LOGE(TAG, "自动控制: 水泵控制失败: %s", esp_err_to_name(ret));
}
}
// 如果补光灯状态需要改变,则执行控制
if (desired_light != *light_on)
{
esp_err_t ret = io_device_control_set_light(desired_light);
if (ret == ESP_OK)
{
*light_on = desired_light;
ESP_LOGI(TAG,
"自动控制: 补光灯%s (光照=%.1f lux)",
desired_light ? "开启" : "关闭",
light_lux);
}
else
{
ESP_LOGE(TAG, "自动控制: 补光灯控制失败: %s", esp_err_to_name(ret));
}
}
}
/**
* @brief UI 任务函数
*
* 负责定期驱动 UI 刷新。
*
* @param arg 任务参数(未使用)
*/
static void ui_task(void *arg)
{
(void)arg;
for (;;)
{
lvgl_port_lock(0);
ui_tick();
lvgl_port_unlock();
vTaskDelay(pdMS_TO_TICKS(20));
}
}
/**
* @brief 等待 Wi-Fi 连接成功
*
* 在初始化 console 之前,确保 Wi-Fi 已连接成功最多等待120秒。
*/
static void wait_for_wifi_connected(void)
{
set_var_wifi_disconnected(true);
set_var_wifi_connected(false);
const uint32_t timeout_s = 120;
uint32_t elapsed_half_s = 0;
ESP_LOGI(TAG, "等待 Wi-Fi 连接成功后再初始化 console...");
while (wifi_connect_get_status() != WIFI_CONNECT_STATUS_CONNECTED)
{
if (elapsed_half_s >= (timeout_s * 2))
{
ESP_LOGW(TAG, "等待 Wi-Fi 超时(%" PRIu32 "s继续初始化 console", timeout_s);
return;
}
vTaskDelay(pdMS_TO_TICKS(500));
elapsed_half_s++;
// 每 5 秒打印一次等待状态,避免日志刷屏。
if ((elapsed_half_s % 10) == 0)
{
ESP_LOGI(TAG, "仍在等待 Wi-Fi 连接(%" PRIu32 "s", elapsed_half_s / 2);
}
}
set_var_wifi_disconnected(false);
set_var_wifi_connected(true);
ESP_LOGI(TAG, "Wi-Fi 已连接,开始初始化 console");
}
/**
* @brief 同步 MQTT 连接状态到 UI 变量
*/
static void update_mqtt_ui_state(void)
{
bool mqtt_connected = mqtt_control_is_connected();
set_var_mqtt_connected(mqtt_connected);
set_var_mqtt_disconnected(!mqtt_connected);
}
/**
* @brief 主函数
*
* 系统启动入口,初始化所有组件并进入主循环。
*/
void app_main(void)
{
// 初始化 Wi-Fi 配网组件,支持长按按键进入配网
ESP_ERROR_CHECK(wifi_connect_init());
printf("设备启动完成:长按按键进入配网模式,手机连接 ESP32-* 后访问 http://192.168.4.1\n");
// 启动 LVGL 演示程序,显示简单的界面
ESP_ERROR_CHECK(start_lvgl_demo());
// 初始化 UI 组件(需在 LVGL 锁内进行对象创建)
lvgl_port_lock(0);
ui_init();
lvgl_port_unlock();
BaseType_t ui_task_ok = xTaskCreate(ui_task, "ui_task", 4096, NULL, 5, NULL);
ESP_ERROR_CHECK(ui_task_ok == pdPASS ? ESP_OK : ESP_FAIL);
// 初始化 IO 设备控制组件GPIO1 水泵GPIO10 光照,高电平有效)
ESP_ERROR_CHECK(io_device_control_init());
i2c_master_messager_config_t i2c_cfg = {
.i2c_port = BOTANY_I2C_PORT,
.scl_io_num = BOTANY_I2C_SCL_GPIO,
.sda_io_num = BOTANY_I2C_SDA_GPIO,
.i2c_enable_internal_pullup = BOTANY_I2C_INTERNAL_PULLUP,
.bh1750_enable = BOTANY_BH1750_ENABLE,
.aht30_enable = BOTANY_AHT30_ENABLE,
.bh1750_read_period_ms = BOTANY_BH1750_PERIOD_MS,
.aht30_read_period_ms = BOTANY_AHT30_PERIOD_MS,
.bh1750_mode = BH1750_CONTINUE_1LX_RES,
};
bool i2c_ready = false;
esp_err_t ret = i2c_master_messager_init(&i2c_cfg);
if (ret == ESP_OK)
{
ret = i2c_master_messager_start();
}
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "I2C 传感器管理启动失败: %s", esp_err_to_name(ret));
ESP_LOGW(TAG, "请检查 I2C 引脚/上拉电阻/端口占用情况,系统将继续运行但不采集传感器");
ESP_ERROR_CHECK(lvgl_st7789_set_center_text("I2C init failed"));
}
else
{
i2c_ready = true;
s_i2c_ready = true;
}
// 初始化电容式土壤湿度传感器GPIO0 / ADC1_CH0
bool soil_ready = false;
cap_soil_sensor_config_t soil_cfg = {
.unit = CAP_SOIL_SENSOR_DEFAULT_UNIT,
.channel = CAP_SOIL_SENSOR_DEFAULT_CHANNEL,
.atten = ADC_ATTEN_DB_12,
.bitwidth = ADC_BITWIDTH_DEFAULT,
// 标定值来自当前实测:空气中约 3824水中约 1463。
.air_raw = 3824,
.water_raw = 1463,
};
ret = cap_soil_sensor_init(&soil_cfg);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "土壤湿度传感器初始化失败: %s", esp_err_to_name(ret));
}
else
{
soil_ready = true;
s_soil_sensor_ready = true;
}
// 按需求:仅在 Wi-Fi 确认连通后再初始化 MQTT和console。
wait_for_wifi_connected();
// Wi-Fi 连通后做一次 SNTP 对时,失败不阻断后续业务。
esp_err_t sntp_ret = sntp_timp_sync_time(12000);
if (sntp_ret != ESP_OK)
{
ESP_LOGW(TAG, "SNTP 对时未完成: %s", esp_err_to_name(sntp_ret));
}
// 独立状态网页(端口 8080与配网页面端口 80互不干扰。
ESP_ERROR_CHECK(status_web_start(BOTANY_STATUS_WEB_PORT));
// MQTT 启动前,先给 UI 一个明确的初始状态。
set_var_mqtt_connected(false);
set_var_mqtt_disconnected(true);
ESP_ERROR_CHECK(mqtt_control_register_command_handler(mqtt_control_command_handler, NULL));
ESP_ERROR_CHECK(mqtt_control_start()); // 启动 MQTT 客户端
ESP_ERROR_CHECK(console_cmd_init());
ESP_ERROR_CHECK(console_user_cmds_register());
ESP_ERROR_CHECK(console_cmd_all_register()); // 可选:自动注册插件命令
ESP_ERROR_CHECK(console_cmd_start());
auto_ctrl_thresholds_init_defaults();
auto_alerts_init();
ESP_ERROR_CHECK(auto_alerts_register_callback(auto_alert_mqtt_callback, NULL));
auto_ctrl_thresholds_t thresholds = {0};
auto_ctrl_thresholds_get(&thresholds);
update_ui_threshold_vars(&thresholds);
uint32_t telemetry_elapsed_ms = 0;
ESP_LOGI(TAG,
"自动控制阈值: pump_on<%.1f%%, pump_off>%.1f%%, light_on<%.1flux, light_off>%.1flux",
thresholds.pump_on_soil_below_pct,
thresholds.pump_off_soil_above_pct,
thresholds.light_on_lux_below,
thresholds.light_off_lux_above);
for (;;)
{
s_main_loop_counter++;
update_mqtt_ui_state();
// 预留给 MQTT 回调动态更新阈值:每个周期读取最新配置。
auto_ctrl_thresholds_get(&thresholds);
update_ui_threshold_vars(&thresholds);
bool soil_valid = false;
float soil_moisture_pct = 0.0f;
cap_soil_sensor_data_t soil_data = {0};
if (soil_ready && cap_soil_sensor_read(&soil_data) == ESP_OK)
{
// 读取成功
soil_valid = true;
soil_moisture_pct = soil_data.moisture_percent;
snprintf(s_soil, sizeof(s_soil), "%.0f", soil_data.moisture_percent);
set_var_soil_moisture(s_soil);
}
bool light_valid = false;
float light_lux = 0.0f;
i2c_master_messager_data_t sensor_data = {0};
if (i2c_ready && i2c_master_messager_get_data(&sensor_data) == ESP_OK)
{
// 读取成功
if (sensor_data.aht30.valid)
{
snprintf(s_air_temp, sizeof(s_air_temp), "%.1f", sensor_data.aht30.temperature_c);
set_var_air_temperature(s_air_temp);
snprintf(s_air_hum, sizeof(s_air_hum), "%.1f", sensor_data.aht30.humidity_rh);
set_var_air_humidity(s_air_hum);
}
if (sensor_data.bh1750.valid)
{
light_valid = true;
light_lux = sensor_data.bh1750.lux;
snprintf(s_lux, sizeof(s_lux), "%.0f", sensor_data.bh1750.lux);
set_var_light_intensity(s_lux);
}
}
if (s_auto_control_enabled)
{
auto_control_update(soil_valid,
soil_moisture_pct,
light_valid,
light_lux,
&thresholds,
&s_pump_on,
&s_light_on);
}
// 预留给 MQTT回调注册后可在此处收到边沿告警事件并发布。
auto_alerts_evaluate(soil_valid,
soil_moisture_pct,
light_valid,
light_lux,
&thresholds);
update_status_web_snapshot();
telemetry_elapsed_ms += 1000;
if (telemetry_elapsed_ms >= BOTANY_MQTT_TELEMETRY_PERIOD_MS)
{
telemetry_elapsed_ms = 0;
esp_err_t pub_ret = publish_telemetry_snapshot();
if (pub_ret != ESP_OK && pub_ret != ESP_ERR_INVALID_STATE)
{
ESP_LOGW(TAG, "周期状态上报失败: %s", esp_err_to_name(pub_ret));
}
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}