refactor(display): migrate from ST7735S to ST7789 driver

- Rename component directory from lvgl_st7735s_use to lvgl_st7789_use
- Update CMakeLists.txt to register new source files
- Add comprehensive README documentation for ST7789 configuration
- Add time_alarm module with SNTP synchronization and alarm management
- Add sensors header for sensor abstraction layer
This commit is contained in:
Wang Beihong
2026-03-29 02:31:29 +08:00
parent 8c33fe7411
commit f0ac50642d
50 changed files with 15494 additions and 3282 deletions

View File

@@ -1,6 +1,13 @@
idf_component_register(SRCS "main.c"
idf_component_register(SRCS "main.cpp"
"app_state.c"
"wifi_manager.c"
"sensors.c"
"mqtt_manager.c"
"time_alarm.c"
"peripherals.c"
INCLUDE_DIRS "."
PRIV_REQUIRES wifi-connect cjson nvs_flash lvgl_st7735s_use esp_driver_i2c esp_type_utils esp_timer espressif__servo esp_event esp_netif serial_mcu mqtt
PRIV_REQUIRES wifi-connect ui cjson nvs_flash lvgl_st7789_use esp_driver_i2c esp_type_utils esp_timer espressif__servo esp_event esp_netif serial_mcu mqtt chiehmin__sgp30
WHOLE_ARCHIVE
)

19
main/app_state.c Normal file
View File

@@ -0,0 +1,19 @@
#include "app_state.h"
/* Definitions of shared application state */
SemaphoreHandle_t xSensorDataMutex = NULL;
SemaphoreHandle_t xMqttMessageMutex = NULL;
SemaphoreHandle_t xTimePeriodMutex = NULL;
SemaphoreHandle_t xControlFlagMutex = NULL;
bool servo_control_flag = false;
bool fan_control_flag = false;
bool buzzer_control_flag = false;
bool light_source_control_flag = false;
uint8_t led_brightness_value = 0;
bool led_backlight_on = false;
device_message_t g_device_message;
float g_temperature_threshold = 28.0f;
bool g_cooling_mode_enabled = false;
bool g_high_temp_alerted = false;

35
main/app_state.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef APP_STATE_H
#define APP_STATE_H
#include "common.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
extern SemaphoreHandle_t xSensorDataMutex;
extern SemaphoreHandle_t xMqttMessageMutex;
extern SemaphoreHandle_t xTimePeriodMutex;
extern SemaphoreHandle_t xControlFlagMutex;
extern bool servo_control_flag;
extern bool fan_control_flag;
extern bool buzzer_control_flag;
extern bool light_source_control_flag;
extern uint8_t led_brightness_value;
extern bool led_backlight_on;
extern device_message_t g_device_message;
extern float g_temperature_threshold;
extern bool g_cooling_mode_enabled;
extern bool g_high_temp_alerted;
#ifdef __cplusplus
}
#endif
#endif // APP_STATE_H

65
main/common.h Normal file
View File

@@ -0,0 +1,65 @@
// Common types shared across modules
#ifndef COMMON_H
#define COMMON_H
#include <stdint.h>
#include <stdbool.h>
// 时间段类型
typedef enum
{
TIME_PERIOD_DAY = 0,
TIME_PERIOD_NIGHT = 1,
} time_period_type_t;
// 设备状态结构体
typedef struct
{
bool online;
char current_mode[20];
bool home_status;
bool standby_mode;
uint8_t error_code;
bool auto_mode; // true=自动模式, false=手动模式
} device_state_t;
// 遥测数据结构体
typedef struct
{
float temperature;
float humidity;
float light_intensity;
uint16_t co2_ppm;
uint16_t tvoc_ppb;
char curtain_state[10];
char led_state[10];
uint8_t led_power;
char fan_state[10];
char buzzer_state[10];
} telemetry_data_t;
// 设备消息结构体
typedef struct
{
char device_id[32];
char device_type[32];
uint64_t timestamp;
device_state_t state;
telemetry_data_t telemetry;
} device_message_t;
#ifdef __cplusplus
extern "C" {
#endif
// 时间段与降温模式函数声明
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);
void cooling_mode_save_to_nvs(void);
void cooling_mode_load_from_nvs(void);
#ifdef __cplusplus
}
#endif
#endif // COMMON_H

File diff suppressed because it is too large Load Diff

593
main/main.cpp Normal file
View File

@@ -0,0 +1,593 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <math.h>
#include <time.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_err.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_sntp.h"
#include "esp_timer.h"
#include "mqtt_client.h"
#include "cJSON.h"
#include "esp_mac.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "driver/ledc.h"
#include "common.h"
#include "sensors.h"
#include "mqtt_manager.h"
#include "wifi_manager.h"
#include "peripherals.h"
#include "lvgl_st7789_use.h"
#include "iot_servo.h"
#include "time_alarm.h"
#include "wifi-connect.h"
#include "serial_mcu.h"
#include "app_state.h"
#include "ui.h" // 使用EEZStudio提供的ui组件便于后续扩展
#include "esp_lvgl_port.h"
#include "vars.h" // 定义全局变量接口
// 添加 extern "C" 包裹 C 头文件声明
#ifdef __cplusplus
extern "C"
{
#endif
// C 函数声明
#ifdef __cplusplus
}
#endif
/* Forward declarations */
static void mqtt_app_start(void);
static void time_period_check_task(void *pvParameters);
static void cooling_mode_task(void *pvParameters);
static const char *TAG = "main";
/* 其他子模块在各自文件中定义 TAG避免在 main 中重复定义未使用的 TAG */
/* 共享应用状态由 main/app_state.c 提供定义,使用 app_state.h 中的 extern 访问 */
/* 校准常量已迁移到 peripherals 模块 */
/* 简单遥测上报封装(暂为适配层) */
static void update_telemetry_and_report(void)
{
mqtt_manager_publish_feedback();
}
#define I2C_MASTER_SCL_IO 5 // GPIO number for I2C master clock
#define I2C_MASTER_SDA_IO 4 // GPIO number for I2C master data
#define I2C_MASTER_NUM I2C_NUM_0 // I2C port number for master dev
#define I2C_MASTER_FREQ_HZ 100000 // I2C master clock frequency
// SERVO_GPIO defined in peripherals.h
// ========================= MQTT配置 =========================
#define MQTT_BROKER_URL "mqtt://beihong.wang:1883" // MQTT代理URL从menuconfig配置获取
#define MQTT_USERNAME "esp_mqtt_client" // MQTT用户名
#define MQTT_CLIENT_ID "esp_mqtt_client" // MQTT客户端ID
#define MQTT_PASSWORD "664hd78gas97" // MQTT密码
// MQTT主题配置
#define MQTT_PUBLISH_TOPIC_QOS0 "topic/sensor/esp32_iothome_001" // QoS0发布的主题
#define MQTT_NOTIFY_TOPIC "topic/control/esp32_iothome_001" // 上层通知主题
#define MQTT_UNSUBSCRIBE_TOPIC "topic/control/esp32_iothome_001" // 取消订阅的主题
// mqtt_publish_task moved to mqtt_manager.c
void mqtt_publish_feedback(void)
{
mqtt_manager_publish_feedback();
}
// mqtt_event_handler moved to mqtt_manager.c
// mqtt_app_start now delegates to mqtt_manager_start
static void mqtt_app_start(void)
{
ESP_LOGI(TAG, "mqtt_app_start: delegating to mqtt_manager_start");
esp_err_t err = mqtt_manager_start();
if (err != ESP_OK)
{
ESP_LOGE(TAG, "mqtt_manager_start failed: %s", esp_err_to_name(err));
}
}
// ========================= HTTP服务器相关函数 =========================
// I2C句柄已在前面声明
extern i2c_master_bus_handle_t bus_handle;
extern i2c_master_dev_handle_t dev_handle;
// 舵机初始化函数
// 舵机与外设控制由 peripherals 模块提供
static void rx_task(void *arg)
{
static const char *RX_TASK_TAG = "RX_TASK";
esp_log_level_set(RX_TASK_TAG, ESP_LOG_INFO);
uint8_t *data = (uint8_t *)malloc(1024 + 1);
static uint8_t frame_buffer[256]; // 帧缓冲区
static int frame_index = 0; // 帧索引
while (1)
{
const int rxBytes = uart_read_bytes(UART_NUM_1, data, 1024, 1000 / portTICK_PERIOD_MS);
if (rxBytes > 0)
{
// 处理接收到的数据
for (int i = 0; i < rxBytes; i++)
{
// 查找帧头 AA
if (frame_index == 0 && data[i] == 0xAA)
{
frame_buffer[frame_index++] = data[i];
}
// 检查数据部分是否在01到06范围内
else if (frame_index == 1 && data[i] >= 0x01 && data[i] <= 0x06)
{
frame_buffer[frame_index++] = data[i];
}
// 检查帧尾 55
else if (frame_index == 2 && data[i] == 0x55)
{
frame_buffer[frame_index++] = data[i];
// 接收到完整帧 AA [01-06] 55
ESP_LOGI(RX_TASK_TAG, "Valid frame received: AA 0x%02X 55", frame_buffer[1]);
// 解析按键状态
uint8_t data_value = frame_buffer[1];
// 根据数据值解析按键状态
switch (data_value)
{
case 0x01:
ESP_LOGI(RX_TASK_TAG, "Key 1 pressed - LED toggle");
if (xControlFlagMutex != NULL && xSemaphoreTake(xControlFlagMutex, portMAX_DELAY) == pdTRUE)
{
light_source_control_flag = !light_source_control_flag;
led_brightness_value = light_source_control_flag ? 100 : 0;
xSemaphoreGive(xControlFlagMutex);
}
ESP_LOGI(RX_TASK_TAG, "LED light: %s (brightness=%d)",
light_source_control_flag ? "ON" : "OFF", led_brightness_value);
mqtt_manager_publish_feedback();
break;
case 0x02:
ESP_LOGI(RX_TASK_TAG, "Key 2 pressed - Fan toggle");
if (xControlFlagMutex != NULL && xSemaphoreTake(xControlFlagMutex, portMAX_DELAY) == pdTRUE)
{
fan_control_flag = !fan_control_flag;
xSemaphoreGive(xControlFlagMutex);
}
ESP_LOGI(RX_TASK_TAG, "Fan: %s", fan_control_flag ? "ON" : "OFF");
mqtt_manager_publish_feedback();
break;
case 0x03:
ESP_LOGI(RX_TASK_TAG, "Key 3 pressed - Curtain toggle");
if (xControlFlagMutex != NULL && xSemaphoreTake(xControlFlagMutex, portMAX_DELAY) == pdTRUE)
{
servo_control_flag = !servo_control_flag;
xSemaphoreGive(xControlFlagMutex);
}
ESP_LOGI(RX_TASK_TAG, "Curtain: %s", servo_control_flag ? "OPEN" : "CLOSE");
mqtt_manager_publish_feedback();
break;
case 0x04:
ESP_LOGI(RX_TASK_TAG, "Key 4 pressed - Buzzer toggle");
if (xControlFlagMutex != NULL && xSemaphoreTake(xControlFlagMutex, portMAX_DELAY) == pdTRUE)
{
buzzer_control_flag = !buzzer_control_flag;
xSemaphoreGive(xControlFlagMutex);
}
ESP_LOGI(RX_TASK_TAG, "Buzzer: %s", buzzer_control_flag ? "ON" : "OFF");
mqtt_manager_publish_feedback();
break;
case 0x05:
ESP_LOGI(RX_TASK_TAG, "Key 5 pressed - Backlight toggle");
if (xControlFlagMutex != NULL && xSemaphoreTake(xControlFlagMutex, portMAX_DELAY) == pdTRUE)
{
led_backlight_on = !led_backlight_on;
xSemaphoreGive(xControlFlagMutex);
}
ESP_LOGI(RX_TASK_TAG, "LCD Backlight: %s", led_backlight_on ? "ON" : "OFF");
mqtt_manager_publish_feedback();
break;
case 0x06:
ESP_LOGI(RX_TASK_TAG, "Key 6 pressed");
// ui_toggle_page(); // 翻页
break;
default:
ESP_LOGI(RX_TASK_TAG, "Unknown key value: 0x%02X", data_value);
break;
}
frame_index = 0;
}
else
{
frame_index = 0;
if (data[i] == 0xAA)
{
frame_buffer[frame_index++] = data[i];
}
}
}
}
}
free(data);
}
// void initialize_nvs()
// {
// esp_err_t ret = nvs_flash_init(); // 初始化NVS, 并检查是否需要擦除NVS
// if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
// {
// ESP_ERROR_CHECK(nvs_flash_erase());
// ret = nvs_flash_init();
// }
// ESP_ERROR_CHECK(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));
}
}
/* 外设控制逻辑已迁移到 peripherals 模块main/peripherals.c
在此文件中不再实现 `peripheral_control_task` 与 `init_gpio_output`。
main.c 通过包含 peripherals.h 并在 app_main 中调用 `peripherals_init()`
与 `xTaskCreate(peripheral_control_task, ...)` 来使用该任务。 */
/* 闹钟逻辑已迁移到 time_alarm 模块 (main/time_alarm.c) */
// 添加 extern "C" 包裹 app_main 函数
#ifdef __cplusplus
extern "C"
{
#endif
void app_main(void)
{
// esp_log_level_set("*", ESP_LOG_INFO);
// esp_log_level_set("mqtt_client", ESP_LOG_VERBOSE);
// esp_log_level_set("mqtt_example", ESP_LOG_VERBOSE);
// esp_log_level_set("transport_base", ESP_LOG_VERBOSE);
// esp_log_level_set("esp-tls", ESP_LOG_VERBOSE);
// esp_log_level_set("transport", ESP_LOG_VERBOSE);
// esp_log_level_set("outbox", ESP_LOG_VERBOSE);
// esp_log_level_set("dhcps", ESP_LOG_DEBUG);
// esp_log_level_set("esp_netif", ESP_LOG_DEBUG);
// esp_log_level_set("esp_netif_lwip", ESP_LOG_DEBUG);
// esp_log_level_set("wifi", ESP_LOG_DEBUG);
esp_log_level_set("mqtt_manager", ESP_LOG_DEBUG);
// 设置时区为北京时间 (CST-8)
setenv("TZ", "CST-8", 1);
tzset();
// 初始化 Wi-Fi 配网组件,支持长按按键进入配网
ESP_ERROR_CHECK(wifi_connect_init());
printf("设备启动完成:进入配网模式,手机连接 ESP32-* 后访问 http://192.168.4.1\n");
// 初始化模块骨架stub保持与现有流程兼容
ESP_ERROR_CHECK(wifi_manager_init());
// mqtt_manager_init 为骨架占位,实际 MQTT 启动仍由原有 mqtt_app_start
ESP_ERROR_CHECK(mqtt_manager_init());
// 启动 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);
// 连接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());
// 创建互斥锁
xSensorDataMutex = xSemaphoreCreateMutex();
if (xSensorDataMutex == NULL)
{
ESP_LOGE(TAG, "创建传感器数据互斥锁失败");
}
// 添加MQTT消息互斥锁
xMqttMessageMutex = xSemaphoreCreateMutex();
if (xMqttMessageMutex == NULL)
{
ESP_LOGE(TAG, "创建MQTT消息互斥锁失败");
}
// 创建时间段配置互斥锁
xTimePeriodMutex = xSemaphoreCreateMutex();
if (xTimePeriodMutex == NULL)
{
ESP_LOGE(TAG, "创建时间段配置互斥锁失败");
}
// 创建控制标志互斥锁
xControlFlagMutex = xSemaphoreCreateMutex();
if (xControlFlagMutex == NULL)
{
ESP_LOGE(TAG, "创建控制标志互斥锁失败");
}
// 初始化外设与模块stubsensor 需在 I2C 与互斥锁初始化后进行
ESP_ERROR_CHECK(peripherals_init());
ESP_ERROR_CHECK(sensors_init());
ESP_ERROR_CHECK(time_alarm_init());
// 初始化舵机
servo_init();
// GPIO输出初始化(风扇控制)
init_gpio_output();
// 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(peripheral_control_task, "peripheral_control_task", 4096, NULL, 5, NULL);
// 传感器任务由 sensors_init() 启动
// 创建接收任务
xTaskCreate(rx_task, "uart_rx_task", 4096, NULL, configMAX_PRIORITIES - 1, NULL);
while (1)
{
// 定期打印传感器数据
print_sensor_data();
vTaskDelay(5000 / portTICK_PERIOD_MS);
}
}
#ifdef __cplusplus
}
#endif
/**
* @brief 降温模式任务
* 监测温度,当温度超过阈值时自动开启风扇
* 当温度超过35°C时触发高温提醒
*/
static void cooling_mode_task(void *pvParameters)
{
ESP_LOGI(TAG, "Cooling mode task started");
// 从NVS加载配置
cooling_mode_load_from_nvs();
// 高温阈值
const float HIGH_TEMP_THRESHOLD = 48.0f;
while (1)
{
if (g_cooling_mode_enabled)
{
float current_temp = 0;
bool temp_valid = false;
// 获取当前温度
if (xSensorDataMutex != NULL && xSemaphoreTake(xSensorDataMutex, portMAX_DELAY) == pdTRUE)
{
current_temp = g_sensor_data.temperature;
temp_valid = g_sensor_data.ahtxx_valid;
xSemaphoreGive(xSensorDataMutex);
}
if (temp_valid)
{
// 降温模式:温度超过阈值,开启风扇
if (current_temp > g_temperature_threshold)
{
if (xControlFlagMutex != NULL && xSemaphoreTake(xControlFlagMutex, portMAX_DELAY) == pdTRUE)
{
if (!fan_control_flag)
{
fan_control_flag = true;
ESP_LOGI(TAG, "Temperature %.1f°C > %.1f°C, cooling mode: Fan ON",
current_temp, g_temperature_threshold);
update_telemetry_and_report();
}
xSemaphoreGive(xControlFlagMutex);
}
}
// 温度恢复到阈值以下,关闭风扇
else if (current_temp < (g_temperature_threshold - 1.0f)) // 添加1°C的滞后防止频繁切换
{
if (xControlFlagMutex != NULL && xSemaphoreTake(xControlFlagMutex, portMAX_DELAY) == pdTRUE)
{
if (fan_control_flag)
{
fan_control_flag = false;
ESP_LOGI(TAG, "Temperature %.1f°C < %.1f°C, cooling mode: Fan OFF",
current_temp, g_temperature_threshold - 1.0f);
update_telemetry_and_report();
}
xSemaphoreGive(xControlFlagMutex);
}
}
// 高温提醒温度超过35°C
if (current_temp > HIGH_TEMP_THRESHOLD)
{
if (!g_high_temp_alerted)
{
g_high_temp_alerted = true;
// 蜂鸣器发出高温提示音
if (xControlFlagMutex != NULL && xSemaphoreTake(xControlFlagMutex, portMAX_DELAY) == pdTRUE)
{
buzzer_control_flag = true;
xSemaphoreGive(xControlFlagMutex);
}
ESP_LOGW(TAG, "High temperature alert: %.1f°C (>40°C)", current_temp);
// 发送MQTT提醒消息
if (g_mqtt_client != NULL)
{
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "type", "device_message");
cJSON_AddStringToObject(root, "device_id", g_device_message.device_id);
cJSON_AddStringToObject(root, "device_type", g_device_message.device_type);
cJSON_AddStringToObject(root, "message_type", "high_temp_alert");
cJSON *data_obj = cJSON_CreateObject();
cJSON_AddStringToObject(data_obj, "alert", "温度过高请注意通风");
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)
{
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);
}
update_telemetry_and_report();
}
}
else
{
// 温度恢复正常,重置高温提醒标志
if (g_high_temp_alerted)
{
g_high_temp_alerted = false;
// 关闭高温提醒蜂鸣器
if (xControlFlagMutex != NULL && xSemaphoreTake(xControlFlagMutex, portMAX_DELAY) == pdTRUE)
{
buzzer_control_flag = false;
xSemaphoreGive(xControlFlagMutex);
}
ESP_LOGI(TAG, "Temperature normalized: %.1f°C, reset high temp alert", current_temp);
update_telemetry_and_report();
}
}
}
}
// 每5秒检查一次
vTaskDelay(pdMS_TO_TICKS(5000));
}
}
/**
* @brief 自动通风控制模式任务
* 监测空气质量当空气质量大于50时自动开启风扇并发送提醒
*/
/* ventilation_mode_task 已移除,保留 README 文档说明,不再在代码中保留未使用的静态函数 */
// MQ135 task removed; provide a short stub to avoid undefined references
void mq135_task(void *pvParameters)
{
ESP_LOGI("mq135_task", "mq135 task removed");
vTaskDelete(NULL);
}
// Remaining MQ135 implementation removed
/* ----------------- 时间段与降温模式的最小存根实现 ----------------- */
#ifdef __cplusplus
extern "C"
{
#endif
void cooling_mode_save_to_nvs(void)
{
// TODO: 实现 NVS 存储,目前为最小存根
}
void cooling_mode_load_from_nvs(void)
{
// TODO: 从 NVS 加载设置。目前使用默认值
g_cooling_mode_enabled = false;
g_temperature_threshold = 28.0f;
}
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;
}
#ifdef __cplusplus
}
#endif
static void time_period_check_task(void *pvParameters)
{
(void)pvParameters;
while (1)
{
// 占位:以后在此检查时间段并执行对应操作
vTaskDelay(pdMS_TO_TICKS(10000));
}
}

1030
main/mqtt_manager.c Normal file

File diff suppressed because it is too large Load Diff

23
main/mqtt_manager.h Normal file
View File

@@ -0,0 +1,23 @@
#ifndef MQTT_MANAGER_H
#define MQTT_MANAGER_H
#include "esp_err.h"
#include "mqtt_client.h"
#ifdef __cplusplus
extern "C" {
#endif
extern esp_mqtt_client_handle_t g_mqtt_client;
esp_err_t mqtt_manager_init(void);
esp_err_t mqtt_manager_start(void);
esp_err_t mqtt_manager_stop(void);
void mqtt_manager_publish_feedback(void);
void mqtt_manager_publish_alarm_status(int alarm_idx, const char *alarm_status, bool triggered);
#ifdef __cplusplus
}
#endif
#endif // MQTT_MANAGER_H

204
main/peripherals.c Normal file
View File

@@ -0,0 +1,204 @@
#include "peripherals.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "driver/gpio.h"
#include "driver/ledc.h"
#include "iot_servo.h"
#include "app_state.h"
#include "lvgl_st7789_use.h"
#include "mqtt_manager.h"
#include "serial_mcu.h"
static const char *TAG = "peripherals";
void init_gpio_output(void)
{
gpio_config_t io_conf = {
.pin_bit_mask = 1ULL << GPIO_NUM_1,
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = 0,
.pull_down_en = 1,
.intr_type = GPIO_INTR_DISABLE};
gpio_config(&io_conf);
}
static const uint16_t calibration_value_0 = 0;
static const uint16_t calibration_value_180 = 180;
void servo_init(void)
{
ESP_LOGI(TAG, "初始化舵机控制");
servo_config_t servo_cfg = {
.max_angle = 180,
.min_width_us = 500,
.max_width_us = 2500,
.freq = 50,
.timer_number = LEDC_TIMER_0,
.channels = {
.servo_pin = {
SERVO_GPIO,
},
.ch = {
LEDC_CHANNEL_0,
},
},
.channel_number = 1,
};
esp_err_t ret = iot_servo_init(LEDC_LOW_SPEED_MODE, &servo_cfg);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "舵机初始化失败: %s", esp_err_to_name(ret));
}
else
{
ESP_LOGI(TAG, "舵机初始化成功");
}
}
void servo_set_angle(uint16_t angle)
{
if (angle < calibration_value_0)
{
angle = calibration_value_0;
}
else if (angle > calibration_value_180)
{
angle = calibration_value_180;
}
iot_servo_write_angle(LEDC_LOW_SPEED_MODE, 0, angle);
ESP_LOGI(TAG, "舵机角度设置为: %d", angle);
}
void peripheral_control_task(void *pvParameters)
{
(void)pvParameters;
for (;;)
{
static bool last_led_backlight_on = false;
static bool backlight_first_run = true;
if (backlight_first_run)
{
last_led_backlight_on = !led_backlight_on;
backlight_first_run = false;
}
if (led_backlight_on != last_led_backlight_on)
{
if (led_backlight_on == false)
{
gpio_set_level(EXAMPLE_LCD_GPIO_BL, 0);
}
else
{
gpio_set_level(EXAMPLE_LCD_GPIO_BL, 1);
}
last_led_backlight_on = led_backlight_on;
}
static bool last_servo_control_flag = false;
static bool first_run = true;
if (first_run)
{
last_servo_control_flag = !servo_control_flag;
first_run = false;
}
if (servo_control_flag != last_servo_control_flag)
{
if (servo_control_flag == false)
{
servo_set_angle(0);
}
else
{
servo_set_angle(180);
}
mqtt_manager_publish_feedback();
last_servo_control_flag = servo_control_flag;
}
static bool last_fan_control_flag = false;
static bool fan_first_run = true;
if (fan_first_run)
{
last_fan_control_flag = !fan_control_flag;
fan_first_run = false;
}
if (fan_control_flag != last_fan_control_flag)
{
if (fan_control_flag == false)
{
gpio_set_level(GPIO_NUM_1, 0);
}
else
{
gpio_set_level(GPIO_NUM_1, 1);
}
mqtt_manager_publish_feedback();
last_fan_control_flag = fan_control_flag;
}
static bool last_buzzer_control_flag = false;
static bool buzzer_first_run = true;
if (buzzer_first_run)
{
last_buzzer_control_flag = !buzzer_control_flag;
buzzer_first_run = false;
}
if (buzzer_control_flag != last_buzzer_control_flag)
{
if (buzzer_control_flag == false)
{
sendControlFrame(0x02, 0);
}
else
{
sendControlFrame(0x02, 1);
}
mqtt_manager_publish_feedback();
last_buzzer_control_flag = buzzer_control_flag;
}
static uint8_t last_led_brightness_value = 0;
static bool led_first_run = true;
if (led_first_run)
{
last_led_brightness_value = !led_brightness_value;
led_first_run = false;
}
if (led_brightness_value != last_led_brightness_value)
{
sendControlFrame(0x01, led_brightness_value);
ESP_LOGI(TAG, "LED brightness updated to %d", led_brightness_value);
mqtt_manager_publish_feedback();
last_led_brightness_value = led_brightness_value;
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
esp_err_t peripherals_init(void)
{
ESP_LOGI(TAG, "peripherals_init");
/* 初始化 GPIO 输出(风扇、背光等)和舵机 */
init_gpio_output();
servo_init();
/* 未来可在此添加其他外设初始化(如 ADC、PWM、RMT 等) */
return ESP_OK;
}

25
main/peripherals.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef PERIPHERALS_H
#define PERIPHERALS_H
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SERVO_GPIO (10)
esp_err_t peripherals_init(void);
/* Peripheral control task (created from main.c) */
void peripheral_control_task(void *pvParameters);
/* GPIO and servo helpers */
void init_gpio_output(void);
void servo_init(void);
void servo_set_angle(uint16_t angle);
#ifdef __cplusplus
}
#endif
#endif // PERIPHERALS_H

298
main/sensors.c Normal file
View File

@@ -0,0 +1,298 @@
#include "sensors.h"
#include "esp_log.h"
#include "ahtxx.h"
#include "bh1750.h"
#include "sgp30/sgp30.h"
#include "driver/i2c_master.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);
}
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_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);
}
// 上电
ret = bh1750_power_on(bh1750_handle);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "BH1750 power on 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);
}
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)
{
ESP_LOGE(TAG, "SGP30 read failed, err=0x%x", ret);
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);
}
// 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);
}
}
}
void print_sensor_data(void)
{
if (xSensorDataMutex != NULL && xSemaphoreTake(xSensorDataMutex, portMAX_DELAY) == pdTRUE)
{
// 传感器日志已移除,保持占位
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;
}
BaseType_t ok = xTaskCreate(i2c0_ahtxx_task, "i2c0_ahtxx_task", 4096, NULL, 5, NULL);
if (ok != pdPASS)
{
return ESP_ERR_NO_MEM;
}
ok = xTaskCreate(i2c0_bh1750_task, "i2c0_bh1750_task", 4096, NULL, 5, NULL);
if (ok != pdPASS)
{
return ESP_ERR_NO_MEM;
}
ok = xTaskCreate(i2c0_sgp30_task, "i2c0_sgp30_task", 4096, NULL, 5, NULL);
if (ok != pdPASS)
{
return ESP_ERR_NO_MEM;
}
return ESP_OK;
}
esp_err_t sensors_read(void)
{
// 保留占位,如需单次读可以实现
return ESP_OK;
}

65
main/sensors.h Normal file
View File

@@ -0,0 +1,65 @@
#ifndef SENSORS_H
#define SENSORS_H
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "driver/i2c_master.h"
#ifdef __cplusplus
extern "C" {
#endif
// 默认 I2C 引脚/参数(如果上层未定义,则使用这些默认值)
#ifndef I2C_MASTER_SCL_IO
#define I2C_MASTER_SCL_IO 5
#endif
#ifndef I2C_MASTER_SDA_IO
#define I2C_MASTER_SDA_IO 4
#endif
#ifndef I2C_MASTER_NUM
#define I2C_MASTER_NUM I2C_NUM_0
#endif
#ifndef I2C_MASTER_FREQ_HZ
#define I2C_MASTER_FREQ_HZ 100000
#endif
// 传感器数据结构体(从 main.c 抽取)
typedef struct
{
float temperature;
float humidity;
float lux;
uint16_t co2_ppm;
uint16_t tvoc_ppb;
bool ahtxx_valid;
bool bh1750_valid;
bool sgp30_valid;
} sensor_data_t;
// 全局传感器数据(在 sensors.c 定义)
extern sensor_data_t g_sensor_data;
// 互斥锁(由 main.c 创建sensors 使用 extern 引用)
extern SemaphoreHandle_t xSensorDataMutex;
// I2C 句柄(在 sensors.c 中定义)
extern i2c_master_bus_handle_t bus_handle;
extern i2c_master_dev_handle_t dev_handle;
// 模块对外接口
esp_err_t sensors_init(void);
esp_err_t sensors_read(void);
// 低层接口 / 任务
esp_err_t i2c_master_init(void);
void i2c0_ahtxx_task(void *pvParameters);
void i2c0_bh1750_task(void *pvParameters);
void i2c0_sgp30_task(void *pvParameters);
void get_sensor_data(sensor_data_t *data);
void print_sensor_data(void);
#ifdef __cplusplus
}
#endif
#endif // SENSORS_H

303
main/time_alarm.c Normal file
View File

@@ -0,0 +1,303 @@
#include "time_alarm.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_sntp.h"
#include <time.h>
#include "mqtt_manager.h"
#include "common.h"
#include "app_state.h"
static const char *TAG = "time_alarm";
typedef struct {
uint8_t hour;
uint8_t minute;
uint8_t second;
bool enable;
bool triggered;
} alarm_t;
static alarm_t g_alarms[ALARM_MAX_NUM];
static TaskHandle_t alarm_task_handle = NULL;
// 来自 main.c 的外部符号
extern SemaphoreHandle_t xControlFlagMutex;
extern bool buzzer_control_flag;
extern bool servo_control_flag;
extern bool light_source_control_flag;
extern uint8_t led_brightness_value;
// 来自 app_state.h 的外部符号
extern SemaphoreHandle_t xMqttMessageMutex;
extern device_message_t g_device_message;
// mqtt_manager 提供的反馈接口
extern void mqtt_manager_publish_feedback(void);
static void local_sntp_init(void)
{
esp_sntp_setoperatingmode(SNTP_OPMODE_POLL);
esp_sntp_setservername(0, "cn.pool.ntp.org");
esp_sntp_setservername(1, "ntp1.aliyun.com");
esp_sntp_init();
}
static void wait_for_time_sync(void)
{
time_t now = 0;
struct tm timeinfo = {0};
int retry = 0;
const int max_retry = 30; // 增加重试次数到30次30秒
ESP_LOGI(TAG, "等待SNTP时间同步...");
while (1)
{
time(&now);
localtime_r(&now, &timeinfo);
// 检查时间是否有效(年份 > 2016
if (timeinfo.tm_year > (2016 - 1900))
{
ESP_LOGI(TAG, "SNTP时间同步成功当前时间: %04d-%02d-%02d %02d:%02d:%02d",
timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
break;
}
if (retry % 5 == 0) // 每5秒打印一次状态
{
ESP_LOGI(TAG, "等待SNTP同步... 已等待 %d 秒", retry);
}
vTaskDelay(pdMS_TO_TICKS(1000));
retry++;
if (retry >= max_retry)
{
ESP_LOGW(TAG, "SNTP时间同步失败使用系统默认时间");
// 设置一个默认时间,避免卡住
now = 1704067200; // 2024-01-01 00:00:00
localtime_r(&now, &timeinfo);
ESP_LOGI(TAG, "使用默认时间: %04d-%02d-%02d %02d:%02d:%02d",
timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
break;
}
}
}
void alarm_set_time(uint8_t alarm_idx, uint8_t hour, uint8_t minute, uint8_t second)
{
if (alarm_idx >= ALARM_MAX_NUM)
return;
g_alarms[alarm_idx].hour = hour;
g_alarms[alarm_idx].minute = minute;
g_alarms[alarm_idx].second = second;
g_alarms[alarm_idx].triggered = false;
ESP_LOGI(TAG, "Alarm[%d] time set to %02d:%02d:%02d, enable=%d", alarm_idx, hour, minute, second, g_alarms[alarm_idx].enable ? 1 : 0);
}
void alarm_set_enable(uint8_t alarm_idx, bool enable)
{
if (alarm_idx >= ALARM_MAX_NUM)
return;
g_alarms[alarm_idx].enable = enable;
g_alarms[alarm_idx].triggered = false;
ESP_LOGI(TAG, "Alarm[%d] enable=%d, time=%02d:%02d:%02d", alarm_idx, enable ? 1 : 0,
g_alarms[alarm_idx].hour, g_alarms[alarm_idx].minute, g_alarms[alarm_idx].second);
}
void alarm_disable_all(void)
{
for (int i = 0; i < ALARM_MAX_NUM; i++)
{
g_alarms[i].enable = false;
g_alarms[i].triggered = false;
}
}
// 获取闹钟状态信息
void alarm_get_status(int alarm_idx, uint8_t *hour, uint8_t *minute, uint8_t *second, bool *enabled, bool *triggered)
{
if (alarm_idx >= ALARM_MAX_NUM)
return;
if (hour) *hour = g_alarms[alarm_idx].hour;
if (minute) *minute = g_alarms[alarm_idx].minute;
if (second) *second = g_alarms[alarm_idx].second;
if (enabled) *enabled = g_alarms[alarm_idx].enable;
if (triggered) *triggered = g_alarms[alarm_idx].triggered;
}
// 自动停止任务函数
static void alarm_auto_stop_task(void *arg)
{
int alarm_idx = (int)arg;
vTaskDelay(pdMS_TO_TICKS(10000)); // 等待10秒
ESP_LOGI(TAG, "Auto-stopping alarm %d actions after 10 seconds", alarm_idx);
if (xControlFlagMutex != NULL && xSemaphoreTake(xControlFlagMutex, portMAX_DELAY) == pdTRUE)
{
buzzer_control_flag = false;
servo_control_flag = false;
light_source_control_flag = false;
led_brightness_value = 0;
xSemaphoreGive(xControlFlagMutex);
}
// 更新设备消息中的设备状态
if (xMqttMessageMutex != NULL && xSemaphoreTake(xMqttMessageMutex, portMAX_DELAY) == pdTRUE)
{
strcpy(g_device_message.telemetry.buzzer_state, "close");
strcpy(g_device_message.telemetry.curtain_state, "close");
strcpy(g_device_message.telemetry.led_state, "close");
g_device_message.telemetry.led_power = 0;
xSemaphoreGive(xMqttMessageMutex);
}
// 发布闹钟关闭状态消息
mqtt_manager_publish_alarm_status(alarm_idx, "stopped", false);
vTaskDelete(NULL);
}
static void alarm_trigger_action(int idx)
{
ESP_LOGI(TAG, "Alarm %d triggered", idx);
if (xControlFlagMutex != NULL && xSemaphoreTake(xControlFlagMutex, portMAX_DELAY) == pdTRUE)
{
buzzer_control_flag = true;
servo_control_flag = true;
light_source_control_flag = true;
led_brightness_value = 100;
xSemaphoreGive(xControlFlagMutex);
}
// 更新设备消息中的设备状态
if (xMqttMessageMutex != NULL && xSemaphoreTake(xMqttMessageMutex, portMAX_DELAY) == pdTRUE)
{
strcpy(g_device_message.telemetry.buzzer_state, "open");
strcpy(g_device_message.telemetry.curtain_state, "open");
strcpy(g_device_message.telemetry.led_state, "open");
g_device_message.telemetry.led_power = 100;
xSemaphoreGive(xMqttMessageMutex);
}
// 发布闹钟触发状态消息
mqtt_manager_publish_alarm_status(idx, "triggered", true);
// 创建自动停止任务延迟10秒后停止
TaskHandle_t stop_task_handle = NULL;
// 增加栈大小到4096因为cJSON操作需要较多栈空间
BaseType_t result = xTaskCreate(alarm_auto_stop_task, "alarm_stop_task", 4096, (void *)idx, 3, &stop_task_handle);
if (result == pdPASS) {
ESP_LOGI(TAG, "Auto-stop task created for alarm %d", idx);
} else {
ESP_LOGE(TAG, "Failed to create auto-stop task for alarm %d", idx);
}
}
static void alarm_task(void *pvParameters)
{
(void)pvParameters;
ESP_LOGI(TAG, "alarm task started");
local_sntp_init();
wait_for_time_sync();
// 打印当前设置的闹钟状态
ESP_LOGI(TAG, "当前闹钟设置:");
for (int i = 0; i < ALARM_MAX_NUM; i++)
{
ESP_LOGI(TAG, "Alarm[%d]: %02d:%02d:%02d, enable=%d, triggered=%d",
i, g_alarms[i].hour, g_alarms[i].minute, g_alarms[i].second,
g_alarms[i].enable ? 1 : 0, g_alarms[i].triggered ? 1 : 0);
}
while (1)
{
time_t now = time(NULL);
struct tm tm_now;
localtime_r(&now, &tm_now);
// 每5分钟打印一次当前时间
if (tm_now.tm_min % 5 == 0 && tm_now.tm_sec == 0)
{
ESP_LOGI(TAG, "当前系统时间: %02d:%02d:%02d", tm_now.tm_hour, tm_now.tm_min, tm_now.tm_sec);
}
for (int i = 0; i < ALARM_MAX_NUM; i++)
{
if (g_alarms[i].enable)
{
// 如果闹钟已经触发,检查是否已经过了触发时间,以便重置状态
if (g_alarms[i].triggered)
{
// 如果当前时间已经过了闹钟时间至少1分钟重置触发状态
if (tm_now.tm_hour > g_alarms[i].hour ||
(tm_now.tm_hour == g_alarms[i].hour && tm_now.tm_min > g_alarms[i].minute))
{
g_alarms[i].triggered = false;
ESP_LOGI(TAG, "闹钟[%d]触发状态已重置,准备明天再次触发", i);
}
}
else // 闹钟未触发,检查是否应该触发
{
// 只检查小时和分钟,秒数不检查,这样更加可靠
if (tm_now.tm_hour == g_alarms[i].hour && tm_now.tm_min == g_alarms[i].minute)
{
ESP_LOGI(TAG, "闹钟[%d]触发! 设置时间: %02d:%02d:%02d, 当前时间: %02d:%02d:%02d",
i, g_alarms[i].hour, g_alarms[i].minute, g_alarms[i].second,
tm_now.tm_hour, tm_now.tm_min, tm_now.tm_sec);
g_alarms[i].triggered = true;
alarm_trigger_action(i);
}
}
}
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
esp_err_t time_alarm_init(void)
{
for (int i = 0; i < ALARM_MAX_NUM; i++)
{
g_alarms[i].hour = 0;
g_alarms[i].minute = 0;
g_alarms[i].second = 0;
g_alarms[i].enable = false;
g_alarms[i].triggered = false;
}
ESP_LOGI(TAG, "time_alarm initialized");
return ESP_OK;
}
esp_err_t time_alarm_start(void)
{
if (alarm_task_handle != NULL)
return ESP_OK;
BaseType_t ok = xTaskCreate(alarm_task, "alarm_clock", 4096, NULL, 5, &alarm_task_handle);
if (ok != pdPASS)
{
ESP_LOGE(TAG, "Failed to create alarm task");
return ESP_ERR_NO_MEM;
}
return ESP_OK;
}
esp_err_t time_alarm_stop(void)
{
if (alarm_task_handle != NULL)
{
vTaskDelete(alarm_task_handle);
alarm_task_handle = NULL;
}
return ESP_OK;
}

28
main/time_alarm.h Normal file
View File

@@ -0,0 +1,28 @@
#ifndef TIME_ALARM_H
#define TIME_ALARM_H
#include "esp_err.h"
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#define ALARM_MAX_NUM 4
// 控制闹钟时间与使能的外部接口
void alarm_set_time(uint8_t alarm_idx, uint8_t hour, uint8_t minute, uint8_t second);
void alarm_set_enable(uint8_t alarm_idx, bool enable);
void alarm_disable_all(void);
void alarm_get_status(int alarm_idx, uint8_t *hour, uint8_t *minute, uint8_t *second, bool *enabled, bool *triggered);
esp_err_t time_alarm_init(void);
esp_err_t time_alarm_start(void);
esp_err_t time_alarm_stop(void);
#ifdef __cplusplus
}
#endif
#endif // TIME_ALARM_H

22
main/wifi_manager.c Normal file
View File

@@ -0,0 +1,22 @@
#include "wifi_manager.h"
#include "esp_log.h"
static const char *TAG = "wifi_manager";
esp_err_t wifi_manager_init(void)
{
ESP_LOGI(TAG, "wifi_manager_init (stub)");
return ESP_OK;
}
esp_err_t wifi_manager_start(void)
{
ESP_LOGI(TAG, "wifi_manager_start (stub)");
return ESP_OK;
}
esp_err_t wifi_manager_stop(void)
{
ESP_LOGI(TAG, "wifi_manager_stop (stub)");
return ESP_OK;
}

17
main/wifi_manager.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef WIFI_MANAGER_H
#define WIFI_MANAGER_H
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
esp_err_t wifi_manager_init(void);
esp_err_t wifi_manager_start(void);
esp_err_t wifi_manager_stop(void);
#ifdef __cplusplus
}
#endif
#endif // WIFI_MANAGER_H