能用,自动没测,不想改了,崩溃了

This commit is contained in:
Wang Beihong
2026-03-31 21:52:29 +08:00
parent f0ac50642d
commit f8c7706c11
32 changed files with 15324 additions and 12035 deletions

View File

@@ -58,9 +58,17 @@ extern "C"
/* Forward declarations */
static void mqtt_app_start(void);
static void time_period_check_task(void *pvParameters);
static void cooling_mode_task(void *pvParameters);
#ifdef __cplusplus
extern "C"
{
#endif
void time_period_check_task(void *pvParameters);
#ifdef __cplusplus
}
#endif
static const char *TAG = "main";
/* 其他子模块在各自文件中定义 TAG避免在 main 中重复定义未使用的 TAG */
@@ -101,14 +109,81 @@ void mqtt_publish_feedback(void)
// mqtt_event_handler moved to mqtt_manager.c
// mqtt_app_start now delegates to mqtt_manager_start
static void mqtt_app_start(void)
// 优化后的代码
static void safe_mqtt_start_task(void *pvParameters)
{
ESP_LOGI(TAG, "mqtt_app_start: delegating to mqtt_manager_start");
(void)pvParameters;
ESP_LOGI(TAG, "安全MQTT启动任务开始等待WiFi连接...");
int retry_count = 0;
const int max_retries = 60; // 60秒超时
bool wifi_connected = false;
while (retry_count < max_retries)
{
wifi_connect_status_t wifi_status = wifi_connect_get_status();
if (wifi_status == WIFI_CONNECT_STATUS_CONNECTED)
{
wifi_connected = true;
ESP_LOGI(TAG, "WiFi连接成功 (IP获取可能需要更多时间)准备启动MQTT");
break;
}
// 增加对 FAILED 的处理,避免死等
if (wifi_status == WIFI_CONNECT_STATUS_FAILED) {
ESP_LOGW(TAG, "WiFi连接明确失败等待重连...");
}
vTaskDelay(pdMS_TO_TICKS(1000));
retry_count++;
}
if (!wifi_connected)
{
ESP_LOGW(TAG, "WiFi连接超时 (%d秒)但仍尝试启动MQTT", max_retries);
}
// 等待 SNTP 同步
// 注意:如果 WiFi 没连上SNTP 也会超时,这里依赖 time_alarm 内部的超时机制
ESP_LOGI(TAG, "等待 SNTP 时间同步...");
time_alarm_wait_for_sntp_sync();
// 启动 MQTT
ESP_LOGI(TAG, "启动MQTT管理器");
esp_err_t err = mqtt_manager_start();
if (err != ESP_OK)
{
ESP_LOGE(TAG, "mqtt_manager_start failed: %s", esp_err_to_name(err));
ESP_LOGE(TAG, "MQTT启动失败: %s", esp_err_to_name(err));
// 可选:这里可以添加重启逻辑,或者进入配网模式
}
vTaskDelete(NULL);
}
static void mqtt_app_start(void)
{
ESP_LOGI(TAG, "mqtt_app_start: 创建安全MQTT启动任务");
// 【关键修改】将栈大小从 3072 改为 2048 (8KB) 甚至 1536 (6KB)
// 这个任务只是等待,不需要大栈空间
BaseType_t task_ok = xTaskCreate(safe_mqtt_start_task,
"safe_mqtt_start",
2048, // 修改这里:从 3072 降为 2048
NULL,
5,
NULL);
if (task_ok != pdPASS)
{
ESP_LOGE(TAG, "创建安全启动任务失败(内存不足)直接启动MQTT");
// 如果连任务都创建不了,说明内存极度紧张,直接启动可能会再次失败
esp_err_t err = mqtt_manager_start();
if (err != ESP_OK)
{
ESP_LOGE(TAG, "直接启动MQTT也失败: %s", esp_err_to_name(err));
}
}
}
@@ -320,17 +395,6 @@ extern "C"
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);
// 连接WIFI
// ESP_ERROR_CHECK(example_connect());
// // Print out Access Point Information
// wifi_ap_record_t ap_info;
// ESP_ERROR_CHECK(esp_wifi_sta_get_ap_info(&ap_info));
// ESP_LOGI(TAG, "--- Access Point Information ---");
// ESP_LOG_BUFFER_HEX("MAC Address", ap_info.bssid, sizeof(ap_info.bssid));
// ESP_LOG_BUFFER_CHAR("SSID", ap_info.ssid, sizeof(ap_info.ssid));
// ESP_LOGI(TAG, "Primary Channel: %d", ap_info.primary);
// ESP_LOGI(TAG, "RSSI: %d", ap_info.rssi);
// 初始化I2C总线
ESP_ERROR_CHECK(i2c_master_init());
@@ -377,26 +441,24 @@ extern "C"
// MCU间的串口通信初始化
serial_mcu_init();
mqtt_app_start(); // 启动 MQTT 客户端
ESP_ERROR_CHECK(time_alarm_start());
// 创建时间段检查任务
xTaskCreate(time_period_check_task, "time_period_task", 4096, NULL, 5, NULL);
// 创建降温模式任务
xTaskCreate(cooling_mode_task, "cooling_mode_task", 4096, NULL, 5, NULL);
// 自动通风与 MQ135 相关任务已移除
// xTaskCreate(ventilation_mode_task, "ventilation_mode_task", 4096, NULL, 5, NULL);
// xTaskCreate(mq135_task, "mq135_task", 4096, NULL, 5, NULL);
xTaskCreate(cooling_mode_task, "cooling_task", 3072, NULL, 5, NULL);
// 创建时间段检查任务
xTaskCreate(time_period_check_task, "time_period_task", 3072, NULL, 5, NULL);
// 创建外设控制任务
xTaskCreate(peripheral_control_task, "peripheral_control_task", 4096, NULL, 5, NULL);
xTaskCreate(peripheral_control_task, "periph_ctrl_task", 3072, NULL, 5, NULL);
// 传感器任务由 sensors_init() 启动
// 创建接收任务
xTaskCreate(rx_task, "uart_rx_task", 4096, NULL, configMAX_PRIORITIES - 1, NULL);
xTaskCreate(rx_task, "uart_rx_task", 3072, NULL, configMAX_PRIORITIES - 1, NULL);
while (1)
{
// 定期打印传感器数据
print_sensor_data();
vTaskDelay(5000 / portTICK_PERIOD_MS);
}
}
@@ -418,7 +480,6 @@ static void cooling_mode_task(void *pvParameters)
cooling_mode_load_from_nvs();
// 高温阈值
const float HIGH_TEMP_THRESHOLD = 48.0f;
while (1)
{
@@ -468,8 +529,8 @@ static void cooling_mode_task(void *pvParameters)
}
}
// 高温提醒:温度超过35°C
if (current_temp > HIGH_TEMP_THRESHOLD)
// 高温提醒:温度超过阈值
if (current_temp > g_temperature_threshold)
{
if (!g_high_temp_alerted)
{
@@ -482,7 +543,7 @@ static void cooling_mode_task(void *pvParameters)
xSemaphoreGive(xControlFlagMutex);
}
ESP_LOGW(TAG, "High temperature alert: %.1f°C (>40°C)", current_temp);
ESP_LOGW(TAG, "High temperature alert: %.1f°C (>%.1f°C)", current_temp, g_temperature_threshold);
// 发送MQTT提醒消息
if (g_mqtt_client != NULL)
@@ -498,12 +559,23 @@ static void cooling_mode_task(void *pvParameters)
cJSON_AddNumberToObject(data_obj, "temperature", roundf(current_temp * 10) / 10);
cJSON_AddItemToObject(root, "data", data_obj);
char *json_message = cJSON_Print(root);
if (json_message)
// 尝试使用预分配缓冲区避免堆分配
static char temp_json_buf[512];
bool printed_temp = cJSON_PrintPreallocated(root, temp_json_buf, sizeof(temp_json_buf), 0);
if (printed_temp)
{
esp_mqtt_client_publish(g_mqtt_client, MQTT_PUBLISH_TOPIC_QOS0, json_message, 0, 0, 0);
ESP_LOGI(TAG, "High temp alert sent: %s", json_message);
free(json_message);
esp_mqtt_client_publish(g_mqtt_client, MQTT_PUBLISH_TOPIC_QOS0, temp_json_buf, 0, 0, 0);
ESP_LOGI(TAG, "High temp alert sent: %s", temp_json_buf);
}
else
{
char *json_message = cJSON_Print(root);
if (json_message)
{
esp_mqtt_client_publish(g_mqtt_client, MQTT_PUBLISH_TOPIC_QOS0, json_message, 0, 0, 0);
ESP_LOGI(TAG, "High temp alert sent: %s", json_message);
free(json_message);
}
}
cJSON_Delete(root);
}
@@ -557,37 +629,285 @@ extern "C"
void cooling_mode_save_to_nvs(void)
{
// TODO: 实现 NVS 存储,目前为最小存根
nvs_handle_t nvs_handle;
esp_err_t err = nvs_open("cooling_config", NVS_READWRITE, &nvs_handle);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Failed to open NVS for cooling config: %s", esp_err_to_name(err));
return;
}
err = nvs_set_u8(nvs_handle, "cooling_enabled", g_cooling_mode_enabled ? 1 : 0);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Failed to save cooling enabled: %s", esp_err_to_name(err));
}
err = nvs_set_u32(nvs_handle, "temperature_threshold", (uint32_t)(g_temperature_threshold * 10));
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Failed to save temperature threshold: %s", esp_err_to_name(err));
}
nvs_close(nvs_handle);
ESP_LOGI(TAG, "Cooling mode config saved to NVS");
}
void cooling_mode_load_from_nvs(void)
{
// TODO: 从 NVS 加载设置。目前使用默认值
g_cooling_mode_enabled = false;
g_temperature_threshold = 28.0f;
nvs_handle_t nvs_handle;
esp_err_t err = nvs_open("cooling_config", NVS_READONLY, &nvs_handle);
if (err != ESP_OK)
{
ESP_LOGI(TAG, "No cooling config found in NVS, using defaults");
g_cooling_mode_enabled = false;
g_temperature_threshold = 28.0f;
return;
}
uint8_t cooling_enabled = 0;
err = nvs_get_u8(nvs_handle, "cooling_enabled", &cooling_enabled);
if (err == ESP_OK)
{
g_cooling_mode_enabled = (cooling_enabled == 1);
}
else
{
g_cooling_mode_enabled = false;
}
uint32_t temp_threshold = 280; // 28.0°C * 10
err = nvs_get_u32(nvs_handle, "temperature_threshold", &temp_threshold);
if (err == ESP_OK)
{
g_temperature_threshold = temp_threshold / 10.0f;
}
else
{
g_temperature_threshold = 28.0f;
}
nvs_close(nvs_handle);
ESP_LOGI(TAG, "Cooling mode config loaded from NVS: enabled=%d, threshold=%.1f°C",
g_cooling_mode_enabled, g_temperature_threshold);
}
void time_period_set(time_period_type_t period_type, uint8_t start_hour, uint8_t start_minute,
uint8_t end_hour, uint8_t end_minute)
{
// TODO: 保存时间段设置或通知其他模块。当前为占位实现。
(void)period_type;
(void)start_hour;
(void)start_minute;
(void)end_hour;
(void)end_minute;
ESP_LOGI(TAG, "设置时间段: type=%d, start=%02d:%02d, end=%02d:%02d",
period_type, start_hour, start_minute, end_hour, end_minute);
if (xTimePeriodMutex != NULL && xSemaphoreTake(xTimePeriodMutex, portMAX_DELAY) == pdTRUE)
{
if (period_type == TIME_PERIOD_DAY)
{
g_day_period.start_hour = start_hour;
g_day_period.start_minute = start_minute;
g_day_period.end_hour = end_hour;
g_day_period.end_minute = end_minute;
}
else if (period_type == TIME_PERIOD_NIGHT)
{
g_night_period.start_hour = start_hour;
g_night_period.start_minute = start_minute;
g_night_period.end_hour = end_hour;
g_night_period.end_minute = end_minute;
}
xSemaphoreGive(xTimePeriodMutex);
}
time_period_save_to_nvs();
}
void time_period_save_to_nvs(void)
{
nvs_handle_t nvs_handle;
esp_err_t err = nvs_open("time_period", NVS_READWRITE, &nvs_handle);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Failed to open NVS for time period: %s", esp_err_to_name(err));
return;
}
// 保存昼间时段
err = nvs_set_u8(nvs_handle, "day_start_hour", g_day_period.start_hour);
err = nvs_set_u8(nvs_handle, "day_start_min", g_day_period.start_minute);
err = nvs_set_u8(nvs_handle, "day_end_hour", g_day_period.end_hour);
err = nvs_set_u8(nvs_handle, "day_end_min", g_day_period.end_minute);
// 保存夜间时段
err = nvs_set_u8(nvs_handle, "night_start_hour", g_night_period.start_hour);
err = nvs_set_u8(nvs_handle, "night_start_min", g_night_period.start_minute);
err = nvs_set_u8(nvs_handle, "night_end_hour", g_night_period.end_hour);
err = nvs_set_u8(nvs_handle, "night_end_min", g_night_period.end_minute);
// 保存是否启用时间段控制
err = nvs_set_u8(nvs_handle, "use_time_period", g_use_time_period ? 1 : 0);
nvs_close(nvs_handle);
ESP_LOGI(TAG, "Time period config saved to NVS");
}
void time_period_load_from_nvs(void)
{
nvs_handle_t nvs_handle;
esp_err_t err = nvs_open("time_period", NVS_READONLY, &nvs_handle);
if (err != ESP_OK)
{
ESP_LOGI(TAG, "No time period config found in NVS, using defaults");
g_day_period.start_hour = 8;
g_day_period.start_minute = 0;
g_day_period.end_hour = 20;
g_day_period.end_minute = 0;
g_night_period.start_hour = 20;
g_night_period.start_minute = 0;
g_night_period.end_hour = 8;
g_night_period.end_minute = 0;
g_use_time_period = false;
return;
}
// 加载昼间时段
uint8_t value;
err = nvs_get_u8(nvs_handle, "day_start_hour", &value);
if (err == ESP_OK)
g_day_period.start_hour = value;
err = nvs_get_u8(nvs_handle, "day_start_min", &value);
if (err == ESP_OK)
g_day_period.start_minute = value;
err = nvs_get_u8(nvs_handle, "day_end_hour", &value);
if (err == ESP_OK)
g_day_period.end_hour = value;
err = nvs_get_u8(nvs_handle, "day_end_min", &value);
if (err == ESP_OK)
g_day_period.end_minute = value;
// 加载夜间时段
err = nvs_get_u8(nvs_handle, "night_start_hour", &value);
if (err == ESP_OK)
g_night_period.start_hour = value;
err = nvs_get_u8(nvs_handle, "night_start_min", &value);
if (err == ESP_OK)
g_night_period.start_minute = value;
err = nvs_get_u8(nvs_handle, "night_end_hour", &value);
if (err == ESP_OK)
g_night_period.end_hour = value;
err = nvs_get_u8(nvs_handle, "night_end_min", &value);
if (err == ESP_OK)
g_night_period.end_minute = value;
// 加载是否启用时间段控制
uint8_t use_time_period = 0;
err = nvs_get_u8(nvs_handle, "use_time_period", &use_time_period);
if (err == ESP_OK)
{
g_use_time_period = (use_time_period == 1);
}
nvs_close(nvs_handle);
ESP_LOGI(TAG, "Time period config loaded from NVS");
ESP_LOGI(TAG, "Day period: %02d:%02d - %02d:%02d",
g_day_period.start_hour, g_day_period.start_minute,
g_day_period.end_hour, g_day_period.end_minute);
ESP_LOGI(TAG, "Night period: %02d:%02d - %02d:%02d",
g_night_period.start_hour, g_night_period.start_minute,
g_night_period.end_hour, g_night_period.end_minute);
ESP_LOGI(TAG, "Use time period: %d", g_use_time_period);
}
static bool is_in_time_period(time_period_config_t *period)
{
time_t now = time(NULL);
if (now == (time_t)-1)
{
return false;
}
struct tm tm_now;
localtime_r(&now, &tm_now);
int current_minutes = tm_now.tm_hour * 60 + tm_now.tm_min;
int start_minutes = period->start_hour * 60 + period->start_minute;
int end_minutes = period->end_hour * 60 + period->end_minute;
// 处理跨越午夜的情况(如夜间时段 20:00-8:00
if (end_minutes < start_minutes)
{
// 时间段跨越午夜
return (current_minutes >= start_minutes || current_minutes < end_minutes);
}
else
{
// 时间段在同一天内
return (current_minutes >= start_minutes && current_minutes < end_minutes);
}
}
void time_period_check_task(void *pvParameters)
{
(void)pvParameters;
ESP_LOGI(TAG, "时间段检查任务启动");
// 从NVS加载时间段配置
time_period_load_from_nvs();
while (1)
{
if (g_use_time_period)
{
bool in_day_period = is_in_time_period(&g_day_period);
bool in_night_period = is_in_time_period(&g_night_period);
ESP_LOGV(TAG, "时间段状态: 昼间=%d, 夜间=%d", in_day_period, in_night_period);
// 时间段控制逻辑
if (xControlFlagMutex != NULL && xSemaphoreTake(xControlFlagMutex, portMAX_DELAY) == pdTRUE)
{
// 夜间时段:关闭灯光,降低亮度
if (in_night_period)
{
if (light_source_control_flag)
{
light_source_control_flag = false;
ESP_LOGI(TAG, "夜间时段:关闭灯光");
}
if (led_brightness_value > 50)
{
led_brightness_value = 50;
ESP_LOGI(TAG, "夜间时段降低亮度到50%");
}
}
// 昼间时段:可以开启灯光,亮度恢复
else if (in_day_period)
{
// 昼间时段不强制开启灯光,但允许用户手动开启
// 亮度可以恢复到较高值
if (led_brightness_value < 80)
{
led_brightness_value = 80;
ESP_LOGI(TAG, "昼间时段亮度恢复到80%");
}
}
xSemaphoreGive(xControlFlagMutex);
}
// 更新MQTT状态
update_telemetry_and_report();
}
vTaskDelay(pdMS_TO_TICKS(10000)); // 每10秒检查一次
}
}
#ifdef __cplusplus
}
#endif
static void time_period_check_task(void *pvParameters)
{
(void)pvParameters;
while (1)
{
// 占位:以后在此检查时间段并执行对应操作
vTaskDelay(pdMS_TO_TICKS(10000));
}
}