281 lines
10 KiB
C
281 lines
10 KiB
C
#ifndef CORE_H
|
||
#define CORE_H
|
||
|
||
#include <Arduino.h>
|
||
#include <Preferences.h>
|
||
|
||
// 包含所有必要的模块头文件
|
||
#include "DHT11control.h"
|
||
#include "RTC_Module.h"
|
||
#include "LunarCalendarAndHolidayJudge.h"
|
||
#include "audio_model_esp32.h"
|
||
#include "microphone.h"
|
||
|
||
// 核心判断结果枚举
|
||
typedef enum {
|
||
JUDGE_NO_ACTION = 0, // 无需操作
|
||
JUDGE_TURN_ON_COOLING = 1, // 开启制冷
|
||
JUDGE_TURN_ON_HEATING = 2, // 开启制暖
|
||
JUDGE_TURN_OFF_AC = 3, // 关闭空调
|
||
JUDGE_ADJUST_TEMP = 4, // 除湿
|
||
JUDGE_ERROR = -1 // 错误状态
|
||
} JudgeResult;
|
||
|
||
// 环境数据结构
|
||
typedef struct {
|
||
float temperature; // 当前温度
|
||
float humidity; // 当前湿度
|
||
int year; // 当前年份
|
||
int month; // 当前月份
|
||
int day; // 当前日期
|
||
int hour; // 当前小时
|
||
int minute; // 当前分钟
|
||
int second; // 当前秒数
|
||
bool is_holiday; // 是否为节假日
|
||
AudioClassType audio_class; // AI音频识别结果
|
||
float audio_confidence; // 音频识别置信度
|
||
bool data_valid; // 数据是否有效
|
||
} EnvironmentData;
|
||
|
||
// 全局麦克风对象
|
||
static INMP441Microphone microphone(14, 15, 32, 16000, 1024);
|
||
|
||
// 初始化核心模块
|
||
bool initializeCore() {
|
||
Serial.println("正在初始化核心模块...");
|
||
|
||
// 初始化音频模型
|
||
if (audio_model_init() != 0) {
|
||
Serial.println("音频模型初始化失败!");
|
||
return false;
|
||
}
|
||
|
||
// 初始化麦克风
|
||
if (!microphone.begin()) {
|
||
Serial.println("麦克风初始化失败!");
|
||
return false;
|
||
}
|
||
|
||
Serial.println("核心模块初始化完成");
|
||
return true;
|
||
}
|
||
|
||
// 获取环境数据
|
||
bool getEnvironmentData(EnvironmentData* env_data) {
|
||
if (env_data == NULL) {
|
||
return false;
|
||
}
|
||
|
||
// 初始化数据结构
|
||
memset(env_data, 0, sizeof(EnvironmentData));
|
||
env_data->data_valid = false;
|
||
|
||
// 获取温湿度数据
|
||
float* temp_humidity = getTempAndHumidity();
|
||
if (temp_humidity[0] == -999 || temp_humidity[1] == -999) {
|
||
Serial.println("温湿度传感器读取失败!");
|
||
return false;
|
||
}
|
||
env_data->temperature = temp_humidity[0];
|
||
env_data->humidity = temp_humidity[1];
|
||
|
||
// 获取RTC时间
|
||
RtcDateTime current_time = getRTCTime();
|
||
if (!current_time.IsValid()) {
|
||
Serial.println("RTC时间读取失败!");
|
||
return false;
|
||
}
|
||
|
||
env_data->year = current_time.Year();
|
||
env_data->month = current_time.Month();
|
||
env_data->day = current_time.Day();
|
||
env_data->hour = current_time.Hour();
|
||
env_data->minute = current_time.Minute();
|
||
env_data->second = current_time.Second();
|
||
|
||
// 判断是否为节假日
|
||
env_data->is_holiday = LunarCalendar::isHoliday(env_data->year, env_data->month, env_data->day);
|
||
|
||
env_data->data_valid = true;
|
||
return true;
|
||
}
|
||
|
||
// 执行AI音频识别
|
||
bool performAudioRecognition(EnvironmentData* env_data) {
|
||
if (env_data == NULL) {
|
||
return false;
|
||
}
|
||
|
||
Serial.println("开始音频识别...");
|
||
|
||
// 分配音频缓冲区 (2秒 * 16000Hz = 32000 samples)
|
||
const size_t audio_buffer_size = 32000;
|
||
int16_t* audio_buffer = (int16_t*)malloc(audio_buffer_size * sizeof(int16_t));
|
||
if (audio_buffer == NULL) {
|
||
Serial.println("音频缓冲区分配失败!");
|
||
return false;
|
||
}
|
||
|
||
// 采集2秒音频数据
|
||
size_t samples_read = 0;
|
||
unsigned long start_time = millis();
|
||
|
||
while (samples_read < audio_buffer_size && (millis() - start_time) < 2100) {
|
||
size_t chunk_size = microphone.readAudioData(
|
||
audio_buffer + samples_read,
|
||
min(1024, (int)(audio_buffer_size - samples_read))
|
||
);
|
||
samples_read += chunk_size;
|
||
delay(1); // 短暂延时避免过度占用CPU
|
||
}
|
||
|
||
Serial.printf("采集到 %d 个音频样本\n", samples_read);
|
||
|
||
// 执行AI预测
|
||
AudioPredictionResult prediction_result;
|
||
int predict_status = audio_model_predict(audio_buffer, samples_read, &prediction_result);
|
||
|
||
// 释放音频缓冲区
|
||
free(audio_buffer);
|
||
|
||
if (predict_status != 0 || !prediction_result.is_valid) {
|
||
Serial.println("音频预测失败!");
|
||
env_data->audio_class = AUDIO_CLASS_PERSON_ABSENT;
|
||
env_data->audio_confidence = 0.0f;
|
||
return false;
|
||
}
|
||
|
||
env_data->audio_class = prediction_result.predicted_class;
|
||
env_data->audio_confidence = prediction_result.confidence;
|
||
|
||
Serial.printf("音频识别结果: %s (置信度: %.2f)\n",
|
||
get_class_name_cn(env_data->audio_class),
|
||
env_data->audio_confidence);
|
||
|
||
return true;
|
||
}
|
||
|
||
// 核心判断函数
|
||
int judge() {
|
||
Serial.println("=== 开始核心判断流程 ===");
|
||
|
||
// 获取环境数据
|
||
EnvironmentData env_data;
|
||
if (!getEnvironmentData(&env_data)) {
|
||
Serial.println("环境数据获取失败!");
|
||
return JUDGE_ERROR;
|
||
}
|
||
|
||
// 执行音频识别
|
||
if (!performAudioRecognition(&env_data)) {
|
||
Serial.println("音频识别失败,使用默认值");
|
||
// 继续执行,但音频数据可能不准确
|
||
}
|
||
|
||
// 打印当前环境状态
|
||
Serial.println("=== 当前环境状态 ===");
|
||
Serial.printf("时间: %04d-%02d-%02d %02d:%02d:%02d\n",
|
||
env_data.year, env_data.month, env_data.day,
|
||
env_data.hour, env_data.minute, env_data.second);
|
||
Serial.printf("温度: %.1f°C, 湿度: %.1f%%\n", env_data.temperature, env_data.humidity);
|
||
Serial.printf("节假日: %s\n", env_data.is_holiday ? "是" : "否");
|
||
Serial.printf("音频识别: %s (置信度: %.2f)\n",
|
||
get_class_name_cn(env_data.audio_class), env_data.audio_confidence);
|
||
|
||
// 核心判断逻辑
|
||
JudgeResult result = JUDGE_NO_ACTION;
|
||
|
||
// 获取用户设置的温度范围 (从Preferences读取)
|
||
Preferences prefs;
|
||
prefs.begin("DACSC", true); // 只读模式
|
||
float min_temp = prefs.getFloat("min_temp", 5.0); // 默认最低温度22°C
|
||
float max_temp = prefs.getFloat("max_temp", 28.0); // 默认最高温度26°C
|
||
prefs.end();
|
||
|
||
// 判断逻辑:基于节假日、音频识别、时间、温度和湿度的智能控制
|
||
|
||
// 规则1:节假日规则(优先级最高)
|
||
if (env_data.is_holiday) {
|
||
result = JUDGE_NO_ACTION;
|
||
Serial.println("判断结果: 节假日,系统不进行任何操作");
|
||
}
|
||
// 规则2-9:非节假日规则
|
||
else {
|
||
// 检查是否有人在室内(包括"室内有人"和"有人进门")
|
||
bool person_present = (env_data.audio_class == AUDIO_CLASS_PERSON_PRESENT ||
|
||
env_data.audio_class == AUDIO_CLASS_KEY_JINGLING) &&
|
||
env_data.audio_confidence > 0.6;
|
||
|
||
// 检查是否无人在室内(包括"室内无人"和"有人出门")
|
||
bool person_absent = (env_data.audio_class == AUDIO_CLASS_PERSON_ABSENT ||
|
||
env_data.audio_class == AUDIO_CLASS_DOOR_CLOSING) &&
|
||
env_data.audio_confidence > 0.6;
|
||
|
||
// 检查是否为白天时间(8:00-22:00)
|
||
bool is_daytime = (env_data.hour >= 8 && env_data.hour <= 22);
|
||
|
||
if (person_absent) {
|
||
// 规则10:无人时关闭空调(优先级高)
|
||
result = JUDGE_TURN_OFF_AC;
|
||
Serial.println("判断结果: 检测到无人或有人出门,关闭空调以节能");
|
||
}
|
||
else if (person_present) {
|
||
if (is_daytime) {
|
||
// 白天有人的规则(规则2-5)
|
||
if (env_data.temperature < min_temp) {
|
||
result = JUDGE_TURN_ON_HEATING;
|
||
Serial.println("判断结果: 白天有人且温度过低,开启制热");
|
||
}
|
||
else if (env_data.temperature > max_temp) {
|
||
result = JUDGE_TURN_ON_COOLING;
|
||
Serial.println("判断结果: 白天有人且温度过高,开启制冷");
|
||
}
|
||
else if (env_data.humidity > 70.0) {
|
||
result = JUDGE_ADJUST_TEMP;
|
||
Serial.println("判断结果: 白天有人、温度舒适但湿度过高,开启除湿");
|
||
}
|
||
else {
|
||
result = JUDGE_NO_ACTION;
|
||
Serial.println("判断结果: 白天有人、温度舒适且湿度正常,无需操作");
|
||
}
|
||
}
|
||
else {
|
||
// 晚上有人的规则(规则6-9)
|
||
if (env_data.temperature < min_temp) {
|
||
result = JUDGE_TURN_ON_HEATING;
|
||
Serial.println("判断结果: 晚上有人但温度过低,开启制热");
|
||
}
|
||
else if (env_data.temperature > max_temp) {
|
||
result = JUDGE_TURN_ON_COOLING;
|
||
Serial.println("判断结果: 晚上有人但温度过高,开启制冷");
|
||
}
|
||
else if (env_data.humidity > 70.0) {
|
||
result = JUDGE_ADJUST_TEMP;
|
||
Serial.println("判断结果: 晚上有人、温度舒适但湿度过高,开启除湿");
|
||
}
|
||
else {
|
||
result = JUDGE_NO_ACTION;
|
||
Serial.println("判断结果: 晚上有人、温度舒适且湿度正常,无需操作以节能");
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
// 音频识别不确定或置信度不够时的默认处理
|
||
result = JUDGE_NO_ACTION;
|
||
Serial.println("判断结果: 音频识别不确定,保持当前状态");
|
||
}
|
||
}
|
||
|
||
Serial.printf("=== 判断完成,返回值: %d ===\n", result);
|
||
return result;
|
||
}
|
||
|
||
// 清理核心模块资源
|
||
void cleanupCore() {
|
||
Serial.println("清理核心模块资源...");
|
||
microphone.end();
|
||
audio_model_cleanup();
|
||
Serial.println("核心模块资源清理完成");
|
||
}
|
||
|
||
#endif // CORE_H
|