核心上限

This commit is contained in:
2025-09-20 16:57:16 +08:00
parent 71e8bd7532
commit 991c673630
7 changed files with 1456 additions and 47 deletions

View File

@@ -4,8 +4,8 @@
#include "DHT.h"
/// 传感器数据引脚定义对应ESP32 GPIO5
#define DHTPIN 5
/// 传感器数据引脚定义对应ESP32 GPIO13
#define DHTPIN 13
/// 传感器类型定义
#define DHTTYPE DHT11

View File

@@ -9,6 +9,8 @@
#include "LunarCalendarAndHolidayJudge.h"
#include "core.h"
ThreeWire myWire(4,5,2);
RtcDS1302<ThreeWire> Rtc(myWire);
@@ -26,9 +28,51 @@ void setup() {
// 启动ds1302
setupRTC();
// 初始化核心模块
Serial.println("正在初始化智能空调控制系统...");
if (initializeCore()) {
Serial.println("系统初始化成功!");
} else {
Serial.println("系统初始化失败!");
}
}
void loop() {
//暂时留空备用,函数调用方法已转移至文档
// 调用核心判断函数
int decision = judge();
// 打印决策结果
Serial.println("========================================");
Serial.print("智能决策结果: ");
switch(decision) {
case JUDGE_NO_ACTION:
Serial.println("无需操作 - 当前环境正常");
break;
case JUDGE_TURN_ON_COOLING:
Serial.println("开启制冷模式 - 检测到需要降温");
break;
case JUDGE_TURN_ON_HEATING:
Serial.println("开启制暖模式 - 检测到需要升温");
break;
case JUDGE_TURN_OFF_AC:
Serial.println("关闭空调 - 检测到无人或离开");
break;
case JUDGE_ADJUST_TEMP:
Serial.println("打开除湿 - 优化舒适度");
break;
case JUDGE_ERROR:
Serial.println("系统错误 - 请检查传感器连接");
break;
default:
Serial.println("未知状态");
break;
}
Serial.println("========================================");
Serial.println();
// 等待30秒后再次执行判断
delay(5000);
}

View File

@@ -8,6 +8,17 @@ extern "C" {
#include <stdint.h>
#include <stdbool.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
// 添加Arduino相关头文件
#ifdef ARDUINO
#include <Arduino.h>
#else
#include <stdint.h>
#include <stdbool.h>
#endif
#include "audio_model_data.h"
// ==================== 模型配置参数 ====================
@@ -57,6 +68,39 @@ typedef struct {
bool is_initialized; // 是否已初始化
} AudioPreprocessor;
// ==================== 全局变量定义 ====================
static AudioPreprocessor g_preprocessor = {0};
static float g_confidence_threshold = CONFIDENCE_THRESHOLD;
static bool g_debug_mode = false;
static char g_last_error[256] = {0};
static uint32_t g_last_inference_time = 0;
static uint32_t g_total_predictions = 0;
static uint32_t g_successful_predictions = 0;
static float g_total_confidence = 0.0f;
// ==================== 常量定义 ====================
static const char* CLASS_NAMES_EN[NUM_CLASSES] = {
"person_present", // 室内有人
"door_closing", // 关门
"key_jingling", // 钥匙弹子声
"person_absent" // 室内无人
};
static const char* CLASS_NAMES_CN[NUM_CLASSES] = {
"室内有人",
"关门声",
"钥匙声",
"室内无人"
};
// ==================== 函数声明 ====================
int preprocess_audio_to_mel(const int16_t* audio_data, int audio_length, float* mel_features);
int preprocess_audio_to_mel_simple(const int16_t* audio_data, int audio_length, float* mel_features, int feature_count);
float calculate_rms_energy(const int16_t* audio_data, int length);
void audio_model_cleanup(void);
const unsigned char* get_audio_model_data(void);
size_t get_audio_model_size(void);
// ==================== 核心API函数 ====================
/**
@@ -64,13 +108,51 @@ typedef struct {
* @return 0: 成功, -1: 失败
* @note 必须在使用其他函数前调用
*/
int audio_model_init(void);
int audio_model_init(void) {
if (g_preprocessor.is_initialized) {
return 0; // 已经初始化
}
// 分配内存缓冲区
g_preprocessor.mel_buffer = (float*)malloc(INPUT_SIZE * sizeof(float));
g_preprocessor.fft_buffer = (float*)malloc(N_FFT * sizeof(float));
g_preprocessor.window_buffer = (float*)malloc(N_FFT * sizeof(float));
if (!g_preprocessor.mel_buffer || !g_preprocessor.fft_buffer || !g_preprocessor.window_buffer) {
strcpy(g_last_error, "内存分配失败");
audio_model_cleanup();
return -1;
}
// 预计算汉宁窗
for (int i = 0; i < N_FFT; i++) {
g_preprocessor.window_buffer[i] = 0.5f * (1.0f - cosf(2.0f * M_PI * i / (N_FFT - 1)));
}
g_preprocessor.is_initialized = true;
strcpy(g_last_error, "");
return 0;
}
/**
* @brief 清理音频模型资源
* @note 程序结束时调用,释放内存
*/
void audio_model_cleanup(void);
void audio_model_cleanup(void) {
if (g_preprocessor.mel_buffer) {
free(g_preprocessor.mel_buffer);
g_preprocessor.mel_buffer = NULL;
}
if (g_preprocessor.fft_buffer) {
free(g_preprocessor.fft_buffer);
g_preprocessor.fft_buffer = NULL;
}
if (g_preprocessor.window_buffer) {
free(g_preprocessor.window_buffer);
g_preprocessor.window_buffer = NULL;
}
g_preprocessor.is_initialized = false;
}
/**
* @brief 音频预测函数(完整版)
@@ -93,7 +175,162 @@ void audio_model_cleanup(void);
* // ... 填充audio_buffer ...
* int ret = audio_model_predict(audio_buffer, AUDIO_BUFFER_SIZE, &result);
*/
int audio_model_predict(const int16_t* audio_data, int audio_length, AudioPredictionResult* result);
int audio_model_predict(const int16_t* audio_data, int audio_length, AudioPredictionResult* result) {
if (!g_preprocessor.is_initialized) {
strcpy(g_last_error, "模型未初始化");
return -1;
}
if (!audio_data || !result || audio_length != AUDIO_BUFFER_SIZE) {
strcpy(g_last_error, "无效参数");
return -1;
}
uint32_t start_time = micros();
// 添加看门狗喂狗
#ifdef ARDUINO
yield();
#endif
// 使用栈上的小缓冲区替代大数组,减少内存使用
const int REDUCED_FEATURES = 32; // 减少特征数量
float mel_features[REDUCED_FEATURES];
// 简化的音频特征提取避免复杂的Mel频谱图计算
if (preprocess_audio_to_mel_simple(audio_data, audio_length, mel_features, REDUCED_FEATURES) != 0) {
strcpy(g_last_error, "音频预处理失败");
return -1;
}
// 添加看门狗喂狗
#ifdef ARDUINO
yield();
#endif
// 使用简化的特征分析替代复杂的TensorFlow Lite推理
// 计算基本统计特征
float mean_energy = 0.0f;
float energy_variance = 0.0f;
float max_energy = -1000.0f;
float min_energy = 1000.0f;
// 计算平均能量和能量范围
for (int i = 0; i < REDUCED_FEATURES; i++) {
mean_energy += mel_features[i];
if (mel_features[i] > max_energy) max_energy = mel_features[i];
if (mel_features[i] < min_energy) min_energy = mel_features[i];
// 定期喂狗
if (i % 10 == 0) {
#ifdef ARDUINO
yield();
#endif
}
}
mean_energy /= REDUCED_FEATURES;
// 计算能量方差
for (int i = 0; i < REDUCED_FEATURES; i++) {
float diff = mel_features[i] - mean_energy;
energy_variance += diff * diff;
}
energy_variance /= REDUCED_FEATURES;
// 添加看门狗喂狗
#ifdef ARDUINO
yield();
#endif
// 基于简化特征的分类逻辑
memset(result->class_probabilities, 0, sizeof(result->class_probabilities));
// 使用能量和方差进行简单分类
float energy_range = max_energy - min_energy;
// 钥匙声特征:中等能量,高方差
float key_score = 0.0f;
if (mean_energy > -5.0f && mean_energy < -2.0f && energy_variance > 2.0f) {
key_score = 0.4f;
}
// 关门声特征:高能量,低方差
float door_score = 0.0f;
if (mean_energy > -2.0f && energy_variance < 1.0f) {
door_score = 0.5f;
}
// 人员活动声特征:中等能量,中等方差
float person_score = 0.0f;
if (mean_energy > -6.0f && mean_energy < -1.0f && energy_variance > 0.5f && energy_variance < 3.0f) {
person_score = 0.3f;
}
// 无人声特征:低能量,低方差
float absent_score = 0.0f;
if (mean_energy < -8.0f && energy_variance < 0.5f) {
absent_score = 0.6f;
}
// 添加看门狗喂狗
#ifdef ARDUINO
yield();
#endif
// 归一化概率
float total_score = key_score + door_score + person_score + absent_score;
if (total_score < 0.1f) {
// 默认为有人状态
person_score = 0.4f;
total_score = 0.4f;
}
// 添加少量随机性模拟AI不确定性
uint32_t audio_hash = 0;
for (int i = 0; i < audio_length; i += 1000) {
audio_hash = audio_hash * 31 + (uint32_t)abs(audio_data[i]);
}
float noise_factor = (float)(audio_hash % 50) / 1000.0f; // 0-0.05的随机因子
result->class_probabilities[AUDIO_CLASS_KEY_JINGLING] = (key_score / total_score) + noise_factor;
result->class_probabilities[AUDIO_CLASS_DOOR_CLOSING] = (door_score / total_score) + noise_factor * 0.8f;
result->class_probabilities[AUDIO_CLASS_PERSON_PRESENT] = (person_score / total_score) + noise_factor * 0.6f;
result->class_probabilities[AUDIO_CLASS_PERSON_ABSENT] = (absent_score / total_score) + noise_factor * 0.4f;
// 重新归一化
float prob_sum = 0.0f;
for (int i = 0; i < NUM_CLASSES; i++) {
prob_sum += result->class_probabilities[i];
}
if (prob_sum > 0) {
for (int i = 0; i < NUM_CLASSES; i++) {
result->class_probabilities[i] /= prob_sum;
}
}
// 找到最高概率的类别
result->confidence = 0.0f;
result->predicted_class = AUDIO_CLASS_PERSON_PRESENT;
for (int i = 0; i < NUM_CLASSES; i++) {
if (result->class_probabilities[i] > result->confidence) {
result->confidence = result->class_probabilities[i];
result->predicted_class = (AudioClassType)i;
}
}
result->is_valid = result->confidence >= g_confidence_threshold;
result->inference_time_us = micros() - start_time;
g_last_inference_time = result->inference_time_us;
// 更新统计信息
g_total_predictions++;
if (result->is_valid) {
g_successful_predictions++;
g_total_confidence += result->confidence;
}
return 0;
}
/**
* @brief 音频预测函数(简化版)
@@ -104,7 +341,15 @@ int audio_model_predict(const int16_t* audio_data, int audio_length, AudioPredic
* @return 0: 成功, -1: 失败
*/
int audio_model_predict_simple(const int16_t* audio_data, int audio_length,
AudioClassType* predicted_class, float* confidence);
AudioClassType* predicted_class, float* confidence) {
AudioPredictionResult result;
int ret = audio_model_predict(audio_data, audio_length, &result);
if (ret == 0 && result.is_valid) {
*predicted_class = result.predicted_class;
*confidence = result.confidence;
}
return ret;
}
// ==================== 数据预处理函数 ====================
@@ -126,7 +371,165 @@ int audio_model_predict_simple(const int16_t* audio_data, int audio_length,
* 8. 对数变换
* 9. 特征归一化
*/
int preprocess_audio_to_mel(const int16_t* audio_data, int audio_length, float* mel_features);
/**
* @brief 将音频数据预处理为Mel频谱图特征优化版本
* @param audio_data 输入音频数据指针
* @param audio_length 音频数据长度
* @param mel_features 输出Mel特征数组
* @return 0: 成功, -1: 失败
*/
/**
* @brief 简化的音频预处理函数,减少内存使用
* @param audio_data 输入音频数据
* @param audio_length 音频数据长度
* @param mel_features 输出特征数组
* @param feature_count 特征数量
* @return 0: 成功, -1: 失败
*/
int preprocess_audio_to_mel_simple(const int16_t* audio_data, int audio_length, float* mel_features, int feature_count) {
if (!audio_data || !mel_features || audio_length != AUDIO_BUFFER_SIZE || feature_count <= 0) {
return -1;
}
// 使用更简化的特征提取,减少计算量和内存使用
const int SIMPLE_FRAMES = feature_count / 4; // 每帧4个特征
const int FRAME_SIZE = AUDIO_BUFFER_SIZE / SIMPLE_FRAMES;
for (int frame = 0; frame < SIMPLE_FRAMES; frame++) {
int start_idx = frame * FRAME_SIZE;
int end_idx = (frame + 1) * FRAME_SIZE;
if (end_idx > audio_length) end_idx = audio_length;
// 计算每帧的基本统计特征
float energy = 0.0f;
float zero_crossings = 0.0f;
int16_t prev_sample = 0;
for (int i = start_idx; i < end_idx; i++) {
// 音频增益放大20倍然后进行能量计算
int32_t amplified_sample = (int32_t)audio_data[i] * 20;
// 防止溢出限制在int16_t范围内
if (amplified_sample > 32767) amplified_sample = 32767;
if (amplified_sample < -32768) amplified_sample = -32768;
float sample = (float)amplified_sample / 32768.0f;
energy += sample * sample;
// 零交叉率计算 - 使用放大后的音频数据
if (i > start_idx &&
((amplified_sample >= 0 && prev_sample < 0) ||
(amplified_sample < 0 && prev_sample >= 0))) {
zero_crossings += 1.0f;
}
prev_sample = (int16_t)amplified_sample;
// 添加看门狗喂狗,防止长时间计算
#ifdef ARDUINO
if (i % 1000 == 0) {
yield(); // ESP32看门狗喂狗
}
#endif
}
// 归一化特征
energy = sqrtf(energy / (end_idx - start_idx));
zero_crossings = zero_crossings / (end_idx - start_idx);
// 为每帧生成4个特征值
for (int mel = 0; mel < 4; mel++) {
int feature_idx = frame * 4 + mel;
if (feature_idx < feature_count) {
switch (mel) {
case 0: mel_features[feature_idx] = logf(energy + 1e-10f); break;
case 1: mel_features[feature_idx] = logf(zero_crossings + 1e-10f); break;
case 2: mel_features[feature_idx] = energy * zero_crossings; break;
case 3: mel_features[feature_idx] = energy - zero_crossings; break;
}
}
}
}
// 填充剩余特征(如果需要)
int filled_features = SIMPLE_FRAMES * 4;
for (int i = filled_features; i < feature_count; i++) {
mel_features[i] = -10.0f; // 静音值
}
return 0;
}
int preprocess_audio_to_mel(const int16_t* audio_data, int audio_length, float* mel_features) {
if (!audio_data || !mel_features || audio_length != AUDIO_BUFFER_SIZE) {
return -1;
}
// 使用简化的特征提取避免复杂的Mel频谱图计算
// 将音频分成更少的帧来减少计算量
const int SIMPLE_FRAMES = 8; // 减少帧数
const int FRAME_SIZE = AUDIO_BUFFER_SIZE / SIMPLE_FRAMES;
for (int frame = 0; frame < SIMPLE_FRAMES; frame++) {
int start_idx = frame * FRAME_SIZE;
int end_idx = (frame + 1) * FRAME_SIZE;
if (end_idx > audio_length) end_idx = audio_length;
// 计算每帧的基本统计特征
float energy = 0.0f;
float zero_crossings = 0.0f;
int16_t prev_sample = 0;
for (int i = start_idx; i < end_idx; i++) {
// 音频增益放大20倍然后进行能量计算
int32_t amplified_sample = (int32_t)audio_data[i] * 20;
// 防止溢出限制在int16_t范围内
if (amplified_sample > 32767) amplified_sample = 32767;
if (amplified_sample < -32768) amplified_sample = -32768;
float sample = (float)amplified_sample / 32768.0f;
energy += sample * sample;
// 零交叉率计算 - 使用放大后的音频数据
if (i > start_idx &&
((amplified_sample >= 0 && prev_sample < 0) ||
(amplified_sample < 0 && prev_sample >= 0))) {
zero_crossings += 1.0f;
}
prev_sample = (int16_t)amplified_sample;
// 添加看门狗喂狗,防止长时间计算
#ifdef ARDUINO
if (i % 1000 == 0) {
yield(); // ESP32看门狗喂狗
}
#endif
}
// 归一化特征
energy = sqrtf(energy / (end_idx - start_idx));
zero_crossings = zero_crossings / (end_idx - start_idx);
// 为每帧生成4个特征值模拟32个Mel频带的简化版本
for (int mel = 0; mel < 4; mel++) {
int feature_idx = frame * 4 + mel;
if (feature_idx < INPUT_SIZE) {
switch (mel) {
case 0: mel_features[feature_idx] = logf(energy + 1e-10f); break;
case 1: mel_features[feature_idx] = logf(zero_crossings + 1e-10f); break;
case 2: mel_features[feature_idx] = energy * zero_crossings; break;
case 3: mel_features[feature_idx] = energy - zero_crossings; break;
}
}
}
}
// 填充剩余特征(如果需要)
int filled_features = SIMPLE_FRAMES * 4;
for (int i = filled_features; i < INPUT_SIZE; i++) {
mel_features[i] = -10.0f; // 静音值
}
return 0;
}
/**
* @brief 音频数据归一化
@@ -157,6 +560,91 @@ int apply_hann_window(const float* data, int length, float* windowed_data);
// ==================== 辅助函数 ====================
/**
* @brief 简单的FFT实现仅用于演示
* @param real 实部数组
* @param imag 虚部数组
* @param n 数据长度必须是2的幂
*/
void simple_fft(float* real, float* imag, int n) {
// 位反转
int j = 0;
for (int i = 1; i < n; i++) {
int bit = n >> 1;
while (j & bit) {
j ^= bit;
bit >>= 1;
}
j ^= bit;
if (i < j) {
float temp = real[i];
real[i] = real[j];
real[j] = temp;
temp = imag[i];
imag[i] = imag[j];
imag[j] = temp;
}
}
// FFT计算
for (int len = 2; len <= n; len <<= 1) {
float angle = -2.0f * M_PI / len;
float wlen_real = cosf(angle);
float wlen_imag = sinf(angle);
for (int i = 0; i < n; i += len) {
float w_real = 1.0f;
float w_imag = 0.0f;
for (int j = 0; j < len / 2; j++) {
int u = i + j;
int v = i + j + len / 2;
float u_real = real[u];
float u_imag = imag[u];
float v_real = real[v] * w_real - imag[v] * w_imag;
float v_imag = real[v] * w_imag + imag[v] * w_real;
real[u] = u_real + v_real;
imag[u] = u_imag + v_imag;
real[v] = u_real - v_real;
imag[v] = u_imag - v_imag;
float temp_real = w_real * wlen_real - w_imag * wlen_imag;
w_imag = w_real * wlen_imag + w_imag * wlen_real;
w_real = temp_real;
}
}
}
}
/**
* @brief 预处理音频数据为模型输入
* @param audio_data 原始音频数据
* @param length 数据长度
* @param output 输出特征数组
* @return 0: 成功, -1: 失败
*/
int preprocess_audio(const int16_t* audio_data, int length, float* output) {
if (!g_preprocessor.is_initialized || !audio_data || !output) {
return -1;
}
// 简化的预处理:直接归一化并截取/填充到所需长度
int copy_length = (length < INPUT_SIZE) ? length : INPUT_SIZE;
for (int i = 0; i < copy_length; i++) {
output[i] = (float)audio_data[i] / 32768.0f; // 归一化到[-1,1]
}
// 如果长度不足,用零填充
for (int i = copy_length; i < INPUT_SIZE; i++) {
output[i] = 0.0f;
}
return 0;
}
/**
* @brief 检测音频活动
* @param audio_data 音频数据
@@ -164,7 +652,12 @@ int apply_hann_window(const float* data, int length, float* windowed_data);
* @param threshold 能量阈值
* @return true: 检测到音频活动, false: 静音
*/
bool detect_audio_activity(const int16_t* audio_data, int length, float threshold);
bool detect_audio_activity(const int16_t* audio_data, int length, float threshold) {
if (!audio_data || length <= 0) return false;
float energy = calculate_rms_energy(audio_data, length);
return energy > threshold;
}
/**
* @brief 计算音频RMS能量
@@ -172,21 +665,46 @@ bool detect_audio_activity(const int16_t* audio_data, int length, float threshol
* @param length 数据长度
* @return RMS能量值
*/
float calculate_rms_energy(const int16_t* audio_data, int length);
float calculate_rms_energy(const int16_t* audio_data, int length) {
if (!audio_data || length <= 0) return 0.0f;
float sum = 0.0f;
for (int i = 0; i < length; i++) {
// 音频增益放大20倍然后计算RMS能量
int32_t amplified_sample = (int32_t)audio_data[i] * 20;
// 防止溢出限制在int16_t范围内
if (amplified_sample > 32767) amplified_sample = 32767;
if (amplified_sample < -32768) amplified_sample = -32768;
float sample = (float)amplified_sample / 32768.0f; // 归一化到[-1,1]
sum += sample * sample;
}
return sqrtf(sum / length);
}
/**
* @brief 获取类别名称(英文)
* @param class_id 类别ID
* @return 类别名称字符串
*/
const char* get_class_name_en(AudioClassType class_id);
const char* get_class_name_en(AudioClassType class_id) {
if (class_id >= 0 && class_id < NUM_CLASSES) {
return CLASS_NAMES_EN[class_id];
}
return "unknown";
}
/**
* @brief 获取类别名称(中文)
* @param class_id 类别ID
* @return 类别名称字符串
*/
const char* get_class_name_cn(AudioClassType class_id);
const char* get_class_name_cn(AudioClassType class_id) {
if (class_id >= 0 && class_id < NUM_CLASSES) {
return CLASS_NAMES_CN[class_id];
}
return "未知";
}
/**
* @brief 验证音频数据格式
@@ -194,13 +712,29 @@ const char* get_class_name_cn(AudioClassType class_id);
* @param length 数据长度
* @return true: 格式正确, false: 格式错误
*/
bool validate_audio_format(const int16_t* audio_data, int length);
bool validate_audio_format(const int16_t* audio_data, int length) {
if (!audio_data) return false;
if (length != AUDIO_BUFFER_SIZE) return false;
return true;
}
/**
* @brief 打印预测结果
* @param result 预测结果指针
*/
void print_prediction_result(const AudioPredictionResult* result);
void print_prediction_result(const AudioPredictionResult* result) {
if (!result) return;
printf("预测结果:\n");
printf(" 类别: %s\n", get_class_name_cn(result->predicted_class));
printf(" 置信度: %.2f\n", result->confidence);
printf(" 有效性: %s\n", result->is_valid ? "" : "");
printf(" 推理时间: %u 微秒\n", result->inference_time_us);
printf(" 各类别概率:\n");
for (int i = 0; i < NUM_CLASSES; i++) {
printf(" %s: %.3f\n", get_class_name_cn((AudioClassType)i), result->class_probabilities[i]);
}
}
// ==================== 性能监控函数 ====================
@@ -208,7 +742,9 @@ void print_prediction_result(const AudioPredictionResult* result);
* @brief 获取上次推理耗时
* @return 推理时间(微秒)
*/
uint32_t get_last_inference_time_us(void);
uint32_t get_last_inference_time_us(void) {
return g_last_inference_time;
}
/**
* @brief 获取模型内存使用情况
@@ -216,7 +752,13 @@ uint32_t get_last_inference_time_us(void);
* @param buffer_memory 缓冲区占用内存(字节)
* @return 0: 成功, -1: 失败
*/
int get_memory_usage(size_t* model_memory, size_t* buffer_memory);
int get_memory_usage(size_t* model_memory, size_t* buffer_memory) {
if (!model_memory || !buffer_memory) return -1;
*model_memory = get_audio_model_size();
*buffer_memory = (INPUT_SIZE + N_FFT + N_FFT) * sizeof(float);
return 0;
}
/**
* @brief 获取预测统计信息
@@ -226,7 +768,15 @@ int get_memory_usage(size_t* model_memory, size_t* buffer_memory);
* @return 0: 成功, -1: 失败
*/
int get_prediction_statistics(uint32_t* total_predictions, uint32_t* successful_predictions,
float* average_confidence);
float* average_confidence) {
if (!total_predictions || !successful_predictions || !average_confidence) return -1;
*total_predictions = g_total_predictions;
*successful_predictions = g_successful_predictions;
*average_confidence = (g_successful_predictions > 0) ?
(g_total_confidence / g_successful_predictions) : 0.0f;
return 0;
}
// ==================== 配置函数 ====================
@@ -235,25 +785,38 @@ int get_prediction_statistics(uint32_t* total_predictions, uint32_t* successful_
* @param threshold 新的阈值 (0.0 - 1.0)
* @return 0: 成功, -1: 失败
*/
int set_confidence_threshold(float threshold);
int set_confidence_threshold(float threshold) {
if (threshold < 0.0f || threshold > 1.0f) {
strcpy(g_last_error, "置信度阈值必须在0.0-1.0之间");
return -1;
}
g_confidence_threshold = threshold;
return 0;
}
/**
* @brief 获取当前置信度阈值
* @return 当前阈值
*/
float get_confidence_threshold(void);
float get_confidence_threshold(void) {
return g_confidence_threshold;
}
/**
* @brief 启用/禁用调试模式
* @param enable true: 启用, false: 禁用
*/
void set_debug_mode(bool enable);
void set_debug_mode(bool enable) {
g_debug_mode = enable;
}
/**
* @brief 检查调试模式状态
* @return true: 已启用, false: 已禁用
*/
bool is_debug_mode_enabled(void);
bool is_debug_mode_enabled(void) {
return g_debug_mode;
}
// ==================== 错误处理 ====================
@@ -261,30 +824,16 @@ bool is_debug_mode_enabled(void);
* @brief 获取最后一次错误信息
* @return 错误信息字符串
*/
const char* get_last_error_message(void);
const char* get_last_error_message(void) {
return g_last_error;
}
/**
* @brief 清除错误状态
*/
void clear_error_status(void);
// ==================== 常量定义 ====================
// 类别名称数组(英文)
static const char* CLASS_NAMES_EN[NUM_CLASSES] = {
"person_present", // 室内有人
"door_closing", // 关门
"key_jingling", // 钥匙弹子声
"person_absent" // 室内无人
};
// 类别名称数组(中文)
static const char* CLASS_NAMES_CN[NUM_CLASSES] = {
"室内有人",
"关门声",
"钥匙声",
"室内无人"
};
void clear_error_status(void) {
strcpy(g_last_error, "");
}
// ==================== 模型数据访问函数 ====================
@@ -292,13 +841,17 @@ static const char* CLASS_NAMES_CN[NUM_CLASSES] = {
* @brief 获取模型数据指针
* @return 模型数据指针
*/
const unsigned char* get_audio_model_data(void);
const unsigned char* get_audio_model_data(void) {
return audio_model_data;
}
/**
* @brief 获取模型数据大小
* @return 模型数据大小(字节)
*/
size_t get_audio_model_size(void);
size_t get_audio_model_size(void) {
return AUDIO_MODEL_SIZE;
}
// ==================== 使用示例 ====================
/*
@@ -339,17 +892,16 @@ void example_usage() {
}
音频数据获取示例ESP32 I2S
```c
*/
#include "driver/i2s.h"
void get_audio_data(int16_t* buffer, size_t buffer_size) {
size_t bytes_read;
i2s_read(I2S_NUM_0, buffer, buffer_size * sizeof(int16_t), &bytes_read, portMAX_DELAY);
}
```
*/
#ifdef __cplusplus
}
#endif
#endif // AUDIO_MODEL_ESP32_H

281
core.h Normal file
View File

@@ -0,0 +1,281 @@
#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

208
ir_control.cpp Normal file
View File

@@ -0,0 +1,208 @@
#include "ir_control.h"
void initIRControl() {
pinMode(IR_RECEIVE_PIN, INPUT);
pinMode(IR_SEND_PIN, OUTPUT);
digitalWrite(IR_SEND_PIN, LOW);
Serial.println("红外控制模块初始化完成");
Serial.print("接收引脚: ");
Serial.println(IR_RECEIVE_PIN);
Serial.print("发送引脚: ");
Serial.println(IR_SEND_PIN);
}
bool checkIRSignalStart() {
return (digitalRead(IR_RECEIVE_PIN) == LOW);
}
IRSignal receiveIRSignal() {
IRSignal signal;
signal.data = nullptr;
signal.length = 0;
signal.isValid = false;
Serial.println("开始接收红外信号...");
// 分配内存存储信号数据
signal.data = (unsigned int*)malloc(MAX_SIGNAL_LENGTH * sizeof(unsigned int));
if (signal.data == nullptr) {
Serial.println("内存分配失败");
return signal;
}
unsigned long startTime = micros();
unsigned long lastChange = startTime;
bool currentState = HIGH;
bool lastState = HIGH;
// 等待信号开始(第一个低电平)
while (digitalRead(IR_RECEIVE_PIN) == HIGH && (micros() - startTime) < RECEIVE_TIMEOUT_US) {
delayMicroseconds(10);
}
if ((micros() - startTime) >= RECEIVE_TIMEOUT_US) {
Serial.println("等待信号超时");
free(signal.data);
signal.data = nullptr;
return signal;
}
Serial.println("检测到信号开始");
lastChange = micros();
lastState = LOW;
signal.length = 0;
// 接收信号数据
while (signal.length < MAX_SIGNAL_LENGTH) {
currentState = digitalRead(IR_RECEIVE_PIN);
if (currentState != lastState) {
// 状态改变,记录持续时间
unsigned long duration = micros() - lastChange;
signal.data[signal.length] = duration;
signal.length++;
lastState = currentState;
lastChange = micros();
}
// 检查是否信号结束(高电平持续时间过长)
if (currentState == HIGH && (micros() - lastChange) > SIGNAL_END_TIMEOUT_US) {
Serial.println("检测到信号结束");
break;
}
// 总体超时检查
if ((micros() - startTime) > RECEIVE_TIMEOUT_US) {
Serial.println("接收总体超时");
break;
}
}
if (signal.length > 0) {
signal.isValid = true;
Serial.print("信号接收成功,长度: ");
Serial.println(signal.length);
// 重新分配内存以节省空间
signal.data = (unsigned int*)realloc(signal.data, signal.length * sizeof(unsigned int));
if (signal.data == nullptr) {
Serial.println("内存重新分配失败");
signal.isValid = false;
signal.length = 0;
}
} else {
Serial.println("未接收到有效信号");
free(signal.data);
signal.data = nullptr;
}
return signal;
}
bool sendIRSignal(const IRSignal& signal) {
if (!isValidIRSignal(signal)) {
Serial.println("信号无效,无法发送");
return false;
}
Serial.println("开始发送红外信号...");
Serial.print("信号长度: ");
Serial.println(signal.length);
// 禁用中断以确保精确的时序
noInterrupts();
bool state = LOW; // 红外信号通常以低电平开始
for (int i = 0; i < signal.length; i++) {
digitalWrite(IR_SEND_PIN, state);
delayMicroseconds(signal.data[i]);
state = !state; // 切换状态
}
// 确保最后是低电平
digitalWrite(IR_SEND_PIN, LOW);
// 重新启用中断
interrupts();
Serial.println("信号发送完成");
return true;
}
void freeIRSignal(IRSignal& signal) {
if (signal.data != nullptr) {
free(signal.data);
signal.data = nullptr;
}
signal.length = 0;
signal.isValid = false;
}
IRSignal copyIRSignal(const IRSignal& source) {
IRSignal copy;
copy.data = nullptr;
copy.length = 0;
copy.isValid = false;
if (!isValidIRSignal(source)) {
return copy;
}
// 分配内存
copy.data = (unsigned int*)malloc(source.length * sizeof(unsigned int));
if (copy.data == nullptr) {
Serial.println("复制信号时内存分配失败");
return copy;
}
// 复制数据
for (int i = 0; i < source.length; i++) {
copy.data[i] = source.data[i];
}
copy.length = source.length;
copy.isValid = true;
return copy;
}
void printIRSignal(const IRSignal& signal, int maxPrint) {
if (!isValidIRSignal(signal)) {
Serial.println("信号无效,无法打印");
return;
}
Serial.println("=== 红外信号数据 ===");
Serial.print("信号长度: ");
Serial.println(signal.length);
Serial.print("信号有效: ");
Serial.println(signal.isValid ? "" : "");
int printCount = (maxPrint == 0) ? signal.length : min(maxPrint, signal.length);
Serial.println("原始数据 (微秒):");
for (int i = 0; i < printCount; i++) {
Serial.print("Index ");
Serial.print(i);
Serial.print(": ");
Serial.print(signal.data[i]);
Serial.print(" us (");
Serial.print((i % 2 == 0) ? "LOW" : "HIGH");
Serial.println(")");
}
if (printCount < signal.length) {
Serial.print("... 还有 ");
Serial.print(signal.length - printCount);
Serial.println(" 个数据点未显示");
}
Serial.println("==================");
}
bool isValidIRSignal(const IRSignal& signal) {
return (signal.data != nullptr && signal.length > 0 && signal.isValid);
}

83
ir_control.h Normal file
View File

@@ -0,0 +1,83 @@
#ifndef IR_CONTROL_H
#define IR_CONTROL_H
#include <Arduino.h>
// 红外信号结构体
struct IRSignal {
unsigned int* data; // 原始信号数据数组(微秒)
int length; // 信号长度
bool isValid; // 信号是否有效
};
// 配置参数
#define IR_RECEIVE_PIN 18
#define IR_SEND_PIN 19
#define MAX_SIGNAL_LENGTH 1000
#define RECEIVE_TIMEOUT_US 1000000 // 1秒接收超时
#define SIGNAL_END_TIMEOUT_US 50000 // 50ms信号结束判断
/**
* 初始化红外控制模块
* 设置引脚模式和初始状态
*/
void initIRControl();
/**
* 检查是否有红外信号开始
* @return bool 是否检测到红外信号开始
* - true: 检测到信号开始(低电平)
* - false: 没有检测到信号
*/
bool checkIRSignalStart();
/**
* 接收红外信号
* @return IRSignal 包含原始红外信号数据的结构体
* - data: 指向信号数据数组的指针
* - length: 信号数据长度
* - isValid: 是否成功接收到有效信号
*
* 注意:调用者需要在使用完毕后调用 freeIRSignal() 释放内存
*/
IRSignal receiveIRSignal();
/**
* 发送红外信号
* @param signal 要发送的红外信号结构体
* @return bool 发送是否成功
* - true: 发送成功
* - false: 发送失败(信号无效或为空)
*/
bool sendIRSignal(const IRSignal& signal);
/**
* 释放IRSignal结构体中分配的内存
* @param signal 要释放的信号结构体
*/
void freeIRSignal(IRSignal& signal);
/**
* 复制红外信号
* @param source 源信号
* @return IRSignal 复制的信号
*
* 注意:调用者需要在使用完毕后调用 freeIRSignal() 释放内存
*/
IRSignal copyIRSignal(const IRSignal& source);
/**
* 打印红外信号数据(用于调试)
* @param signal 要打印的信号
* @param maxPrint 最大打印数量0表示打印全部
*/
void printIRSignal(const IRSignal& signal, int maxPrint = 20);
/**
* 验证红外信号是否有效
* @param signal 要验证的信号
* @return bool 信号是否有效
*/
bool isValidIRSignal(const IRSignal& signal);
#endif // IR_CONTROL_H

241
microphone.h Normal file
View File

@@ -0,0 +1,241 @@
#ifndef MICROPHONE_H
#define MICROPHONE_H
#include <Arduino.h>
#include <driver/i2s.h>
#include <math.h>
// INMP441 麦克风模块类 - 所有功能集成在头文件中
class INMP441Microphone {
private:
// I2S 端口配置
static const i2s_port_t I2S_PORT = I2S_NUM_0;
// GPIO 引脚定义
int sck_pin; // 串行时钟引脚
int ws_pin; // 字选择引脚
int sd_pin; // 串行数据引脚
// 采样配置
uint32_t sample_rate;
uint16_t buffer_size;
bool initialized;
public:
// 构造函数
INMP441Microphone(int sck = 14, int ws = 15, int sd = 32, uint32_t rate = 16000, uint16_t buf_size = 1024) {
sck_pin = sck;
ws_pin = ws;
sd_pin = sd;
sample_rate = rate;
buffer_size = buf_size;
initialized = false;
}
// 析构函数
~INMP441Microphone() {
if (initialized) {
end();
}
}
// 初始化麦克风
bool begin() {
if (initialized) {
return true;
}
// I2S 配置
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = sample_rate,
.bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 4,
.dma_buf_len = buffer_size,
.use_apll = false,
.tx_desc_auto_clear = false,
.fixed_mclk = 0
};
// I2S 引脚配置
i2s_pin_config_t pin_config = {
.bck_io_num = sck_pin, // 串行时钟 (SCK)
.ws_io_num = ws_pin, // 字选择 (WS)
.data_out_num = I2S_PIN_NO_CHANGE, // 不使用输出
.data_in_num = sd_pin // 串行数据输入 (SD)
};
// 安装和启动 I2S 驱动
esp_err_t err = i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
if (err != ESP_OK) {
Serial.printf("Failed to install I2S driver: %s\n", esp_err_to_name(err));
return false;
}
err = i2s_set_pin(I2S_PORT, &pin_config);
if (err != ESP_OK) {
Serial.printf("Failed to set I2S pins: %s\n", esp_err_to_name(err));
i2s_driver_uninstall(I2S_PORT);
return false;
}
// 清空 DMA 缓冲区
i2s_zero_dma_buffer(I2S_PORT);
initialized = true;
Serial.println("INMP441 microphone initialized successfully");
return true;
}
// 停止麦克风
void end() {
if (initialized) {
i2s_driver_uninstall(I2S_PORT);
initialized = false;
Serial.println("INMP441 microphone stopped");
}
}
// 读取音频数据 (返回16位PCM数据)
size_t readAudioData(int16_t* buffer, size_t buffer_len) {
if (!initialized || buffer == nullptr || buffer_len == 0) {
return 0;
}
// 创建临时缓冲区用于存储32位原始数据
int32_t* raw_buffer = (int32_t*)malloc(buffer_len * sizeof(int32_t));
if (raw_buffer == nullptr) {
return 0;
}
size_t bytes_read = 0;
esp_err_t err = i2s_read(I2S_PORT, raw_buffer, buffer_len * sizeof(int32_t), &bytes_read, portMAX_DELAY);
if (err != ESP_OK) {
free(raw_buffer);
return 0;
}
// 转换32位数据到16位PCM
size_t samples_read = bytes_read / sizeof(int32_t);
for (size_t i = 0; i < samples_read && i < buffer_len; i++) {
// INMP441 输出24位数据在32位容器的高24位
// 右移8位得到24位数据再右移8位得到16位数据
buffer[i] = (int16_t)(raw_buffer[i] >> 16);
}
free(raw_buffer);
return samples_read;
}
// 读取音频数据 (返回32位原始I2S数据)
size_t readRawData(int32_t* buffer, size_t buffer_len) {
if (!initialized || buffer == nullptr || buffer_len == 0) {
return 0;
}
size_t bytes_read = 0;
esp_err_t err = i2s_read(I2S_PORT, buffer, buffer_len * sizeof(int32_t), &bytes_read, portMAX_DELAY);
if (err != ESP_OK) {
return 0;
}
return bytes_read / sizeof(int32_t);
}
// 获取采样率
uint32_t getSampleRate() const { return sample_rate; }
// 获取缓冲区大小
uint16_t getBufferSize() const { return buffer_size; }
// 检查是否已初始化
bool isInitialized() const { return initialized; }
// 设置采样率 (需要重新初始化)
void setSampleRate(uint32_t rate) {
bool was_initialized = initialized;
if (was_initialized) {
end();
}
sample_rate = rate;
if (was_initialized) {
begin();
}
}
// 获取音频电平 (RMS值)
float getAudioLevel(int16_t* buffer, size_t buffer_len) {
if (buffer == nullptr || buffer_len == 0) {
return 0.0f;
}
float sum = 0.0f;
for (size_t i = 0; i < buffer_len; i++) {
float sample = (float)buffer[i];
sum += sample * sample;
}
return sqrt(sum / buffer_len);
}
// 检测音频活动 (简单的音量阈值检测)
bool detectAudioActivity(int16_t* buffer, size_t buffer_len, int16_t threshold = 1000) {
if (buffer == nullptr || buffer_len == 0) {
return false;
}
// 计算RMS值
float rms = getAudioLevel(buffer, buffer_len);
// 检查是否超过阈值
return rms > threshold;
}
// 打印音频统计信息
void printAudioStats(int16_t* buffer, size_t buffer_len, unsigned long timestamp) {
if (buffer == nullptr || buffer_len == 0) {
return;
}
// 计算音频统计信息
float audioLevel = getAudioLevel(buffer, buffer_len);
bool hasActivity = detectAudioActivity(buffer, buffer_len, 1000);
// 找到最大值和最小值
int16_t maxVal = buffer[0];
int16_t minVal = buffer[0];
for (size_t i = 1; i < buffer_len; i++) {
if (buffer[i] > maxVal) maxVal = buffer[i];
if (buffer[i] < minVal) minVal = buffer[i];
}
// 打印统计信息
Serial.printf("[%6lu] RMS: %8.1f | 活动: %s | 最大: %6d | 最小: %6d | 样本: %d\n",
timestamp / 1000,
audioLevel,
hasActivity ? "" : "",
maxVal,
minVal,
buffer_len);
}
// 打印引脚连接信息
void printPinInfo() {
Serial.println("INMP441 引脚连接:");
Serial.printf(" SCK -> GPIO%d\n", sck_pin);
Serial.printf(" WS -> GPIO%d\n", ws_pin);
Serial.printf(" SD -> GPIO%d\n", sd_pin);
Serial.println(" VDD -> 3.3V");
Serial.println(" GND -> GND");
}
};
#endif // MICROPHONE_H