Compare commits

...

4 Commits

Author SHA1 Message Date
670b86d200 修改一下模型相关 2025-09-24 12:42:13 +08:00
41e14ec828 重构了红外接收 2025-09-23 23:58:49 +08:00
9f663aed0b revert b3c6b4cde9
revert 修复红外的兼容性问题
2025-09-23 14:09:49 +08:00
21d21f8247 revert e1a749a8cd
revert Merge branch 'main' of ssh://gitea.spdis.top/spdis/Dorm-Air-Conditioner-Smart-Controller
2025-09-23 14:09:36 +08:00
5 changed files with 248 additions and 290 deletions

View File

@@ -872,6 +872,12 @@ void handleSetSettings() {
prefs.putFloat("min_temp", minTemp);
prefs.putFloat("max_temp", maxTemp);
Serial.printf("温度设置已更新 - 最低: %.1f°C, 最高: %.1f°C\n", minTemp, maxTemp);
// 温度设置更改后立即执行一次判断
Serial.println("温度设置已更改,立即执行核心判断...");
executeJudgeLogic();
server.send(200, "application/json", "{\"success\":true}");
}

View File

@@ -37,7 +37,7 @@ extern "C" {
// 预处理参数
#define MEL_FMIN 0.0f // Mel滤波器最低频率
#define MEL_FMAX 8000.0f // Mel滤波器最高频率
#define WINDOW_TYPE_HANN 1 // 宁窗
#define WINDOW_TYPE_HANN 1 // 宁窗
#define ENERGY_THRESHOLD 0.01f // 音频活动检测阈值
#define CONFIDENCE_THRESHOLD 0.6f // 预测置信度阈值
@@ -100,6 +100,8 @@ 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);
const char* get_class_name_en(AudioClassType class_id);
const char* get_class_name_cn(AudioClassType class_id);
// ==================== 核心API函数 ====================
@@ -124,7 +126,7 @@ int audio_model_init(void) {
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)));
}
@@ -185,7 +187,7 @@ int audio_model_predict(const int16_t* audio_data, int audio_length, AudioPredic
strcpy(g_last_error, "无效参数");
return -1;
}
uint32_t start_time = micros();
// 添加看门狗喂狗
@@ -193,128 +195,73 @@ int audio_model_predict(const int16_t* audio_data, int audio_length, AudioPredic
yield();
#endif
// 使用栈上的小缓冲区替代大数组,减少内存使用
const int REDUCED_FEATURES = 32; // 减少特征数量
float mel_features[REDUCED_FEATURES];
// TODO: 集成TensorFlow Lite模型进行真实的音频识别
// 当前使用audio_model_data.h中的TensorFlow Lite模型数据
// 需要实现以下步骤:
// 1. 初始化TensorFlow Lite解释器
// 2. 加载模型数据 (audio_model_data)
// 3. 预处理音频数据为模型输入格式
// 4. 执行推理
// 5. 解析输出结果
// 简化的音频特征提取避免复杂的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();
Serial.println("警告当前使用临时实现等待TensorFlow Lite模型集成");
#endif
// 使用简化的特征分析替代复杂的TensorFlow Lite推理
// 计算基本统计特征
float mean_energy = 0.0f;
float energy_variance = 0.0f;
float max_energy = -1000.0f;
float min_energy = 1000.0f;
// 计算音频能量来做简单的分类判断
float rms_energy = calculate_rms_energy(audio_data, audio_length);
// 计算平均能量和能量范围
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
// 添加调试信息:检查音频数据的实际值
#ifdef ARDUINO
int non_zero_count = 0;
int16_t min_val = 32767, max_val = -32768;
long long sum_abs = 0;
for (int i = 0; i < min(100, audio_length); i++) { // 检查前100个样本
if (audio_data[i] != 0) non_zero_count++;
if (audio_data[i] < min_val) min_val = audio_data[i];
if (audio_data[i] > max_val) max_val = audio_data[i];
sum_abs += abs(audio_data[i]);
}
Serial.printf("音频数据调试: 非零样本=%d/100, 最小值=%d, 最大值=%d, 平均绝对值=%lld\n",
non_zero_count, min_val, max_val, sum_abs/100);
#endif
// 基于能量水平进行简单分类
AudioClassType predicted_class;
float confidence;
if (rms_energy > 0.1f) {
// 高能量:可能是关门声或钥匙声
if (rms_energy > 0.3f) {
predicted_class = AUDIO_CLASS_DOOR_CLOSING;
confidence = 0.75f;
} else {
predicted_class = AUDIO_CLASS_KEY_JINGLING;
confidence = 0.65f;
}
}
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;
} else if (rms_energy > 0.02f) {
// 中等能量:室内有人
predicted_class = AUDIO_CLASS_PERSON_PRESENT;
confidence = 0.70f;
} else {
// 低能量:室内无人
predicted_class = AUDIO_CLASS_PERSON_ABSENT;
confidence = 0.80f;
}
// 关门声特征:高能量,低方差
float door_score = 0.0f;
if (mean_energy > -2.0f && energy_variance < 1.0f) {
door_score = 0.5f;
}
// 设置结果
result->predicted_class = predicted_class;
result->confidence = confidence;
// 人员活动声特征:中等能量,中等方差
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;
if (i == (int)predicted_class) {
result->class_probabilities[i] = confidence;
} else {
result->class_probabilities[i] = (1.0f - confidence) / (NUM_CLASSES - 1);
}
}
@@ -329,6 +276,11 @@ int audio_model_predict(const int16_t* audio_data, int audio_length, AudioPredic
g_total_confidence += result->confidence;
}
#ifdef ARDUINO
Serial.printf("音频能量: %.4f, 预测类别: %s, 置信度: %.2f\n",
rms_energy, get_class_name_cn(predicted_class), confidence);
#endif
return 0;
}
@@ -406,22 +358,16 @@ int preprocess_audio_to_mel_simple(const int16_t* audio_data, int audio_length,
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;
float sample = (float)audio_data[i] / 32768.0f;
energy += sample * sample;
// 零交叉率计算 - 使用放大后的音频数据
// 零交叉率计算
if (i > start_idx &&
((amplified_sample >= 0 && prev_sample < 0) ||
(amplified_sample < 0 && prev_sample >= 0))) {
((audio_data[i] >= 0 && prev_sample < 0) ||
(audio_data[i] < 0 && prev_sample >= 0))) {
zero_crossings += 1.0f;
}
prev_sample = (int16_t)amplified_sample;
prev_sample = audio_data[i];
// 添加看门狗喂狗,防止长时间计算
#ifdef ARDUINO
@@ -479,22 +425,16 @@ int preprocess_audio_to_mel(const int16_t* audio_data, int audio_length, float*
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;
float sample = (float)audio_data[i] / 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;
// 零交叉率计算
if (i > start_idx &&
((audio_data[i] >= 0 && prev_sample < 0) ||
(audio_data[i] < 0 && prev_sample >= 0))) {
zero_crossings += 1.0f;
}
prev_sample = audio_data[i];
// 添加看门狗喂狗,防止长时间计算
#ifdef ARDUINO
@@ -670,13 +610,7 @@ float calculate_rms_energy(const int16_t* audio_data, int length) {
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]
float sample = (float)audio_data[i] / 32768.0f; // 归一化到[-1,1]
sum += sample * sample;
}
return sqrtf(sum / length);

7
core.h
View File

@@ -188,10 +188,13 @@ int judge() {
// 获取用户设置的温度范围 (从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
float min_temp = prefs.getFloat("min_temp", 10.0); // 默认最低温度10°C
float max_temp = prefs.getFloat("max_temp", 28.0); // 默认最高温度28°C
prefs.end();
// 打印当前使用的温度设置
Serial.printf("当前温度设置 - 最低: %.1f°C, 最高: %.1f°C\n", min_temp, max_temp);
// 判断逻辑:基于节假日、音频识别、时间、温度和湿度的智能控制
// 规则1节假日规则优先级最高

View File

@@ -1,5 +1,9 @@
#include "ir_control.h"
// IRremoteESP8266库的接收器对象
// 增加缓冲区大小到500以支持长信号超时设置为15ms
IRrecv irrecv(IR_RECEIVE_PIN, 500, 15);
void initIRControl() {
// 配置RMT发送通道
rmt_config_t rmt_tx_config = RMT_DEFAULT_CONFIG_TX((gpio_num_t)IR_SEND_PIN, RMT_TX_CHANNEL);
@@ -24,62 +28,30 @@ void initIRControl() {
return;
}
// 配置RMT接收通道
rmt_config_t rmt_rx_config = RMT_DEFAULT_CONFIG_RX((gpio_num_t)IR_RECEIVE_PIN, RMT_RX_CHANNEL);
rmt_rx_config.clk_div = RMT_CLK_DIV;
rmt_rx_config.mem_block_num = RMT_MEM_BLOCK_NUM;
rmt_rx_config.rx_config.filter_en = RMT_RX_FILTER_EN;
rmt_rx_config.rx_config.filter_ticks_thresh = RMT_RX_FILTER_THRESH_US;
rmt_rx_config.rx_config.idle_threshold = RMT_RX_IDLE_THRESH_US;
err = rmt_config(&rmt_rx_config);
if (err != ESP_OK) {
Serial.printf("RMT接收通道配置失败: %s\n", esp_err_to_name(err));
return;
}
err = rmt_driver_install(RMT_RX_CHANNEL, 1000, 0);
if (err != ESP_OK) {
Serial.printf("RMT接收驱动安装失败: %s\n", esp_err_to_name(err));
return;
}
Serial.println("红外控制模块初始化完成 (使用RMT)");
Serial.println("红外控制模块初始化完成 (使用RMT发送 + IRremoteESP8266接收)");
Serial.printf("发送引脚: %d (RMT通道: %d)\n", IR_SEND_PIN, RMT_TX_CHANNEL);
Serial.printf("接收引脚: %d (RMT通道: %d)\n", IR_RECEIVE_PIN, RMT_RX_CHANNEL);
Serial.printf("接收引脚: %d (IRremoteESP8266)\n", IR_RECEIVE_PIN);
Serial.printf("载波频率: %d Hz\n", RMT_TX_CARRIER_FREQ_HZ);
Serial.printf("时钟分辨率: %d us\n", RMT_CLK_DIV);
Serial.printf("接收缓冲区大小: 500, 超时: 15ms\n");
// 初始化IRremoteESP8266接收器
irrecv.enableIRIn(); // 启用红外接收
Serial.println("IRremoteESP8266接收器已启用");
}
bool checkIRSignalStart() {
// 使用RMT检查是否有信号开始
// 启动接收并检查是否立即有数据
rmt_rx_start(RMT_RX_CHANNEL, true);
// 使用IRremoteESP8266检查是否有信号开始
decode_results results;
// 等待短时间检查是否有信号
vTaskDelay(pdMS_TO_TICKS(1));
// 获取RMT环形缓冲区句柄
RingbufHandle_t rb = NULL;
esp_err_t err = rmt_get_ringbuf_handle(RMT_RX_CHANNEL, &rb);
if (err != ESP_OK) {
rmt_rx_stop(RMT_RX_CHANNEL);
return false;
// 尝试解码,但不等待完整信号
if (irrecv.decode(&results, nullptr, 0, false)) {
// 有信号开始,恢复接收状态
irrecv.resume();
return true;
}
// 检查RMT接收缓冲区是否有数据
size_t rx_size = 0;
rmt_item32_t* items = (rmt_item32_t*)xRingbufferReceive(rb, &rx_size, 0);
bool hasSignal = (items != nullptr && rx_size > 0);
if (items) {
vRingbufferReturnItem(rb, (void*)items);
}
rmt_rx_stop(RMT_RX_CHANNEL);
return hasSignal;
return false;
}
IRSignal receiveIRSignal() {
@@ -91,113 +63,149 @@ IRSignal receiveIRSignal() {
signal.carrierFreq = IR_CARRIER_FREQ;
signal.isValid = false;
Serial.println("开始接收红外信号 (使用RMT)...");
Serial.println("开始接收红外信号 (使用IRremoteESP8266)...");
Serial.println("请按下遥控器按键...");
// 启动RMT接收
rmt_rx_start(RMT_RX_CHANNEL, true);
decode_results results;
// 获取RMT环形缓冲区句柄
RingbufHandle_t rb = NULL;
esp_err_t err = rmt_get_ringbuf_handle(RMT_RX_CHANNEL, &rb);
if (err != ESP_OK) {
Serial.printf("获取RMT缓冲区句柄失败: %s\n", esp_err_to_name(err));
rmt_rx_stop(RMT_RX_CHANNEL);
return signal;
}
// 清空接收缓冲区
irrecv.resume();
// 等待接收数据
size_t rx_size = 0;
rmt_item32_t* items = (rmt_item32_t*)xRingbufferReceive(rb, &rx_size, pdMS_TO_TICKS(RECEIVE_TIMEOUT_US / 1000));
if (items == nullptr || rx_size == 0) {
Serial.println("RMT接收超时或无数据");
rmt_rx_stop(RMT_RX_CHANNEL);
return signal;
}
// 计算接收到的项目数量
size_t item_count = rx_size / sizeof(rmt_item32_t);
Serial.printf("接收到 %d 个RMT项目\n", item_count);
if (item_count == 0) {
Serial.println("接收到的数据为空");
vRingbufferReturnItem(rb, (void*)items);
rmt_rx_stop(RMT_RX_CHANNEL);
return signal;
}
// 分配临时数组存储时序数据
unsigned int* tempData = (unsigned int*)malloc(item_count * 2 * sizeof(unsigned int));
if (tempData == nullptr) {
Serial.println("临时数据内存分配失败");
vRingbufferReturnItem(rb, (void*)items);
rmt_rx_stop(RMT_RX_CHANNEL);
return signal;
}
int tempLength = 0;
// 解析RMT数据项
for (size_t i = 0; i < item_count; i++) {
// 每个RMT项目包含两个时间段level0的持续时间和level1的持续时间
if (items[i].duration0 > 0 && tempLength < item_count * 2) {
tempData[tempLength++] = items[i].duration0;
}
if (items[i].duration1 > 0 && tempLength < item_count * 2) {
tempData[tempLength++] = items[i].duration1;
}
}
// 释放RMT缓冲区
vRingbufferReturnItem(rb, (void*)items);
rmt_rx_stop(RMT_RX_CHANNEL);
if (tempLength > 0) {
// 分离mark和space时间
signal.markCount = (tempLength + 1) / 2; // 奇数索引是mark
signal.spaceCount = tempLength / 2; // 偶数索引是space
// 分配mark时间数组
if (signal.markCount > 0) {
signal.markTimes = (unsigned int*)malloc(signal.markCount * sizeof(unsigned int));
if (signal.markTimes == nullptr) {
Serial.println("mark时间数组内存分配失败");
free(tempData);
return signal;
// 等待接收完整的红外信号
unsigned long startTime = millis();
while (millis() - startTime < (RECEIVE_TIMEOUT_US / 1000)) {
if (irrecv.decode(&results)) {
Serial.printf("检测到红外信号!原始数据长度: %d\n", results.rawlen);
Serial.printf("Raw数组总长度: %d\n", results.rawlen);
// 检查缓冲区溢出
if (results.overflow) {
Serial.println("警告: 接收缓冲区溢出!信号可能不完整");
Serial.println("建议: 增加缓冲区大小或检查信号长度");
}
// 复制mark时间从索引0开始每隔2个取一个
for (int i = 0; i < signal.markCount; i++) {
signal.markTimes[i] = tempData[i * 2];
// 检查信号长度是否合理(典型红外信号应该有足够的数据
if (results.rawlen < 10) {
Serial.printf("警告: 信号长度过短 (%d),可能不是完整信号\n", results.rawlen);
} else if (results.rawlen >= 490) { // 接近缓冲区上限
Serial.printf("警告: 信号长度接近缓冲区上限 (%d/500),可能被截断\n", results.rawlen);
}
}
// 分配space时间数组
if (signal.spaceCount > 0) {
signal.spaceTimes = (unsigned int*)malloc(signal.spaceCount * sizeof(unsigned int));
if (signal.spaceTimes == nullptr) {
Serial.println("space时间数组内存分配失败");
if (signal.markTimes != nullptr) {
free(signal.markTimes);
signal.markTimes = nullptr;
// 打印接收到的协议信息
Serial.printf("协议: %s\n", typeToString(results.decode_type).c_str());
Serial.printf("值: 0x%08X\n", results.value);
Serial.printf("位数: %d\n", results.bits);
if (results.rawlen > 1) {
// IRremoteESP8266的rawbuf包含原始时序数据
// rawbuf[0]是未使用的实际数据从rawbuf[1]开始
// 数据格式mark, space, mark, space, ...
int dataLength = results.rawlen - 1; // 减去未使用的第一个元素
// 计算mark和space的数量
signal.markCount = (dataLength + 1) / 2; // 奇数位置是mark
signal.spaceCount = dataLength / 2; // 偶数位置是space
Serial.printf("数据长度: %d, mark数量: %d, space数量: %d\n",
dataLength, signal.markCount, signal.spaceCount);
// 分配mark时间数组
if (signal.markCount > 0) {
signal.markTimes = (unsigned int*)malloc(signal.markCount * sizeof(unsigned int));
if (signal.markTimes == nullptr) {
Serial.println("mark时间数组内存分配失败");
irrecv.resume();
return signal;
}
// 提取mark时间奇数索引1, 3, 5, ...
for (int i = 0; i < signal.markCount; i++) {
int rawIndex = i * 2 + 1; // 1, 3, 5, ...
if (rawIndex < results.rawlen) {
// IRremoteESP8266的时间单位是tick需要转换为微秒
// 默认tick = 50us
signal.markTimes[i] = results.rawbuf[rawIndex] * kRawTick;
}
}
}
free(tempData);
return signal;
// 分配space时间数组
if (signal.spaceCount > 0) {
signal.spaceTimes = (unsigned int*)malloc(signal.spaceCount * sizeof(unsigned int));
if (signal.spaceTimes == nullptr) {
Serial.println("space时间数组内存分配失败");
if (signal.markTimes != nullptr) {
free(signal.markTimes);
signal.markTimes = nullptr;
}
irrecv.resume();
return signal;
}
// 提取space时间偶数索引2, 4, 6, ...
for (int i = 0; i < signal.spaceCount; i++) {
int rawIndex = i * 2 + 2; // 2, 4, 6, ...
if (rawIndex < results.rawlen) {
// IRremoteESP8266的时间单位是tick需要转换为微秒
signal.spaceTimes[i] = results.rawbuf[rawIndex] * kRawTick;
}
}
}
signal.isValid = true;
Serial.printf("IRremoteESP8266信号接收成功mark数量: %d, space数量: %d\n",
signal.markCount, signal.spaceCount);
// 检查信号头部特征典型的9000us mark + 4500us space
if (signal.markCount > 0 && signal.spaceCount > 0) {
unsigned int firstMark = signal.markTimes[0];
unsigned int firstSpace = signal.spaceTimes[0];
Serial.printf("信号头部: mark=%dus, space=%dus\n", firstMark, firstSpace);
// 检查是否有典型的信号头
if (firstMark > 8000 && firstMark < 10000 && firstSpace > 3500 && firstSpace < 5500) {
Serial.println("检测到标准信号头 (约9ms mark + 4.5ms space)");
} else {
Serial.println("警告: 信号头不符合标准格式,可能丢失或不完整");
}
}
// 打印前几个时序数据用于调试
Serial.print("前几个时序数据(us): ");
for (int i = 0; i < min(10, (int)results.rawlen - 1); i++) {
Serial.printf("%d ", results.rawbuf[i + 1] * kRawTick);
}
Serial.println();
// 打印原始数据的十六进制表示
Serial.print("原始数据(tick): ");
for (int i = 1; i < min(11, (int)results.rawlen); i++) {
Serial.printf("%d ", results.rawbuf[i]);
}
Serial.println();
} else {
Serial.println("接收到的信号数据长度不足");
}
// 复制space时间从索引1开始每隔2个取一个
for (int i = 0; i < signal.spaceCount; i++) {
signal.spaceTimes[i] = tempData[i * 2 + 1];
}
// 恢复接收状态
irrecv.resume();
break;
}
signal.isValid = true;
Serial.printf("RMT信号接收成功mark数量: %d, space数量: %d\n", signal.markCount, signal.spaceCount);
} else {
Serial.println("未接收到有效的RMT信号数据");
// 短暂延时避免CPU占用过高
delay(10);
}
if (!signal.isValid) {
Serial.println("IRremoteESP8266接收超时或无有效信号");
Serial.println("请检查:");
Serial.println("1. 遥控器是否有电");
Serial.println("2. 红外接收器是否正确连接到引脚 " + String(IR_RECEIVE_PIN));
Serial.println("3. 遥控器是否对准红外接收器");
}
free(tempData);
return signal;
}
@@ -217,6 +225,11 @@ bool sendIRSignal(const IRSignal& signal) {
int maxCount = max(signal.markCount, signal.spaceCount);
size_t item_count = maxCount;
// 计算并显示raw数组总长度mark + space的总数
int totalRawLength = signal.markCount + signal.spaceCount;
Serial.printf("发送Raw数组总长度: %d (mark: %d + space: %d)\n",
totalRawLength, signal.markCount, signal.spaceCount);
// 分配RMT项目数组
rmt_item32_t* items = (rmt_item32_t*)malloc(item_count * sizeof(rmt_item32_t));
if (items == nullptr) {

View File

@@ -4,6 +4,8 @@
#include <Arduino.h>
#include "driver/rmt.h"
#include "driver/gpio.h"
#include <IRrecv.h>
#include <IRutils.h>
// 红外信号结构体
struct IRSignal {