重构了红外接收

This commit is contained in:
2025-09-23 23:58:49 +08:00
parent 9f663aed0b
commit 41e14ec828
4 changed files with 410 additions and 510 deletions

View File

@@ -1,169 +1,336 @@
#include "ir_control.h"
// IRremoteESP8266库的接收器对象
// 增加缓冲区大小到500以支持长信号超时设置为15ms
IRrecv irrecv(IR_RECEIVE_PIN, 500, 15);
void initIRControl() {
pinMode(IR_RECEIVE_PIN, INPUT);
pinMode(IR_SEND_PIN, OUTPUT);
digitalWrite(IR_SEND_PIN, LOW);
// 配置RMT发送通道
rmt_config_t rmt_tx_config = RMT_DEFAULT_CONFIG_TX((gpio_num_t)IR_SEND_PIN, RMT_TX_CHANNEL);
rmt_tx_config.clk_div = RMT_CLK_DIV;
rmt_tx_config.mem_block_num = RMT_MEM_BLOCK_NUM;
rmt_tx_config.tx_config.carrier_en = RMT_TX_CARRIER_EN;
rmt_tx_config.tx_config.carrier_freq_hz = RMT_TX_CARRIER_FREQ_HZ;
rmt_tx_config.tx_config.carrier_duty_percent = RMT_TX_CARRIER_DUTY_PERCENT;
rmt_tx_config.tx_config.carrier_level = RMT_TX_CARRIER_LEVEL;
rmt_tx_config.tx_config.idle_level = RMT_IDLE_LEVEL;
rmt_tx_config.tx_config.idle_output_en = true;
Serial.println("红外控制模块初始化完成");
Serial.print("接收引脚: ");
Serial.println(IR_RECEIVE_PIN);
Serial.print("发送引脚: ");
Serial.println(IR_SEND_PIN);
esp_err_t err = rmt_config(&rmt_tx_config);
if (err != ESP_OK) {
Serial.printf("RMT发送通道配置失败: %s\n", esp_err_to_name(err));
return;
}
err = rmt_driver_install(RMT_TX_CHANNEL, 0, 0);
if (err != ESP_OK) {
Serial.printf("RMT发送驱动安装失败: %s\n", esp_err_to_name(err));
return;
}
Serial.println("红外控制模块初始化完成 (使用RMT发送 + IRremoteESP8266接收)");
Serial.printf("发送引脚: %d (RMT通道: %d)\n", IR_SEND_PIN, RMT_TX_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() {
return (digitalRead(IR_RECEIVE_PIN) == LOW);
// 使用IRremoteESP8266检查是否有信号开始
decode_results results;
// 尝试解码,但不等待完整信号
if (irrecv.decode(&results, nullptr, 0, false)) {
// 有信号开始,恢复接收状态
irrecv.resume();
return true;
}
return false;
}
IRSignal receiveIRSignal() {
IRSignal signal;
signal.data = nullptr;
signal.length = 0;
signal.markTimes = nullptr;
signal.spaceTimes = nullptr;
signal.markCount = 0;
signal.spaceCount = 0;
signal.carrierFreq = IR_CARRIER_FREQ;
signal.isValid = false;
Serial.println("开始接收红外信号...");
Serial.println("开始接收红外信号 (使用IRremoteESP8266)...");
Serial.println("请按下遥控器按键...");
// 分配内存存储信号数据
signal.data = (unsigned int*)malloc(MAX_SIGNAL_LENGTH * sizeof(unsigned int));
if (signal.data == nullptr) {
Serial.println("内存分配失败");
return signal;
}
decode_results results;
unsigned long startTime = micros();
unsigned long lastChange = startTime;
bool currentState = HIGH;
bool lastState = HIGH;
// 清空接收缓冲区
irrecv.resume();
// 等待信号开始(第一个低电平)
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++;
// 等待接收完整的红外信号
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);
lastState = currentState;
lastChange = micros();
}
// 检查是否信号结束(高电平持续时间过长)
if (currentState == HIGH && (micros() - lastChange) > SIGNAL_END_TIMEOUT_US) {
Serial.println("检测到信号结束");
// 检查缓冲区溢出
if (results.overflow) {
Serial.println("警告: 接收缓冲区溢出!信号可能不完整");
Serial.println("建议: 增加缓冲区大小或检查信号长度");
}
// 检查信号长度是否合理(典型红外信号应该有足够的数据)
if (results.rawlen < 10) {
Serial.printf("警告: 信号长度过短 (%d),可能不是完整信号\n", results.rawlen);
} else if (results.rawlen >= 490) { // 接近缓冲区上限
Serial.printf("警告: 信号长度接近缓冲区上限 (%d/500),可能被截断\n", results.rawlen);
}
// 打印接收到的协议信息
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;
}
}
}
// 分配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("接收到的信号数据长度不足");
}
// 恢复接收状态
irrecv.resume();
break;
}
// 总体超时检查
if ((micros() - startTime) > RECEIVE_TIMEOUT_US) {
Serial.println("接收总体超时");
break;
}
// 短暂延时避免CPU占用过高
delay(10);
}
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;
if (!signal.isValid) {
Serial.println("IRremoteESP8266接收超时或无有效信号");
Serial.println("请检查:");
Serial.println("1. 遥控器是否有电");
Serial.println("2. 红外接收器是否正确连接到引脚 " + String(IR_RECEIVE_PIN));
Serial.println("3. 遥控器是否对准红外接收器");
}
return signal;
}
// generateCarrier函数已被RMT模块替代不再需要
bool sendIRSignal(const IRSignal& signal) {
if (!isValidIRSignal(signal)) {
Serial.println("信号无效,无法发送");
return false;
}
Serial.println("开始发送红外信号...");
Serial.print("信号长度: ");
Serial.println(signal.length);
Serial.println("开始发送红外信号 (使用RMT)...");
Serial.printf("载波频率: %d Hz\n", signal.carrierFreq);
Serial.printf("mark数量: %d, space数量: %d\n", signal.markCount, signal.spaceCount);
// 禁用中断以确保精确的时序
noInterrupts();
// 计算需要的RMT项目数量
int maxCount = max(signal.markCount, signal.spaceCount);
size_t item_count = maxCount;
bool state = LOW; // 红外信号通常以低电平开始
// 计算并显示raw数组总长度mark + space的总数
int totalRawLength = signal.markCount + signal.spaceCount;
Serial.printf("发送Raw数组总长度: %d (mark: %d + space: %d)\n",
totalRawLength, signal.markCount, signal.spaceCount);
for (int i = 0; i < signal.length; i++) {
digitalWrite(IR_SEND_PIN, state);
delayMicroseconds(signal.data[i]);
state = !state; // 切换状态
// 分配RMT项目数组
rmt_item32_t* items = (rmt_item32_t*)malloc(item_count * sizeof(rmt_item32_t));
if (items == nullptr) {
Serial.println("RMT项目内存分配失败");
return false;
}
// 确保最后是低电平
digitalWrite(IR_SEND_PIN, LOW);
// 构建RMT数据项
for (int i = 0; i < maxCount; i++) {
items[i].level0 = 1; // mark期间为高电平载波调制
items[i].duration0 = (i < signal.markCount) ? signal.markTimes[i] : 0;
items[i].level1 = 0; // space期间为低电平无载波
items[i].duration1 = (i < signal.spaceCount) ? signal.spaceTimes[i] : 0;
}
// 重新启用中断
interrupts();
// 发送RMT数据
esp_err_t err = rmt_write_items(RMT_TX_CHANNEL, items, item_count, true);
Serial.println("信号发送完成");
if (err != ESP_OK) {
Serial.printf("RMT发送失败: %s\n", esp_err_to_name(err));
free(items);
return false;
}
// 等待发送完成
err = rmt_wait_tx_done(RMT_TX_CHANNEL, pdMS_TO_TICKS(1000));
if (err != ESP_OK) {
Serial.printf("RMT发送等待超时: %s\n", esp_err_to_name(err));
free(items);
return false;
}
free(items);
Serial.println("RMT信号发送完成");
return true;
}
void freeIRSignal(IRSignal& signal) {
if (signal.data != nullptr) {
free(signal.data);
signal.data = nullptr;
if (signal.markTimes != nullptr) {
free(signal.markTimes);
signal.markTimes = nullptr;
}
signal.length = 0;
if (signal.spaceTimes != nullptr) {
free(signal.spaceTimes);
signal.spaceTimes = nullptr;
}
signal.markCount = 0;
signal.spaceCount = 0;
signal.carrierFreq = 0;
signal.isValid = false;
}
IRSignal copyIRSignal(const IRSignal& source) {
IRSignal copy;
copy.data = nullptr;
copy.length = 0;
copy.markTimes = nullptr;
copy.spaceTimes = nullptr;
copy.markCount = 0;
copy.spaceCount = 0;
copy.carrierFreq = 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;
// 复制mark时间数组
if (source.markCount > 0 && source.markTimes != nullptr) {
copy.markTimes = (unsigned int*)malloc(source.markCount * sizeof(unsigned int));
if (copy.markTimes == nullptr) {
Serial.println("复制mark时间数组时内存分配失败");
return copy;
}
for (int i = 0; i < source.markCount; i++) {
copy.markTimes[i] = source.markTimes[i];
}
copy.markCount = source.markCount;
}
// 复制数据
for (int i = 0; i < source.length; i++) {
copy.data[i] = source.data[i];
// 复制space时间数组
if (source.spaceCount > 0 && source.spaceTimes != nullptr) {
copy.spaceTimes = (unsigned int*)malloc(source.spaceCount * sizeof(unsigned int));
if (copy.spaceTimes == nullptr) {
Serial.println("复制space时间数组时内存分配失败");
if (copy.markTimes != nullptr) {
free(copy.markTimes);
copy.markTimes = nullptr;
copy.markCount = 0;
}
return copy;
}
for (int i = 0; i < source.spaceCount; i++) {
copy.spaceTimes[i] = source.spaceTimes[i];
}
copy.spaceCount = source.spaceCount;
}
copy.length = source.length;
copy.carrierFreq = source.carrierFreq;
copy.isValid = true;
return copy;
@@ -176,33 +343,56 @@ void printIRSignal(const IRSignal& signal, int maxPrint) {
}
Serial.println("=== 红外信号数据 ===");
Serial.print("信号长度: ");
Serial.println(signal.length);
Serial.print("载波频率: ");
Serial.print(signal.carrierFreq);
Serial.println(" Hz");
Serial.print("mark数量: ");
Serial.println(signal.markCount);
Serial.print("space数量: ");
Serial.println(signal.spaceCount);
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 ");
// 打印mark时间
int markPrintCount = (maxPrint == 0) ? signal.markCount : min(maxPrint, signal.markCount);
Serial.println("Mark时间 (微秒):");
for (int i = 0; i < markPrintCount; i++) {
Serial.print("Mark ");
Serial.print(i);
Serial.print(": ");
Serial.print(signal.data[i]);
Serial.print(" us (");
Serial.print((i % 2 == 0) ? "LOW" : "HIGH");
Serial.println(")");
Serial.print(signal.markTimes[i]);
Serial.println(" us");
}
if (printCount < signal.length) {
if (markPrintCount < signal.markCount) {
Serial.print("... 还有 ");
Serial.print(signal.length - printCount);
Serial.println(" 个数据点未显示");
Serial.print(signal.markCount - markPrintCount);
Serial.println("mark数据点未显示");
}
// 打印space时间
int spacePrintCount = (maxPrint == 0) ? signal.spaceCount : min(maxPrint, signal.spaceCount);
Serial.println("Space时间 (微秒):");
for (int i = 0; i < spacePrintCount; i++) {
Serial.print("Space ");
Serial.print(i);
Serial.print(": ");
Serial.print(signal.spaceTimes[i]);
Serial.println(" us");
}
if (spacePrintCount < signal.spaceCount) {
Serial.print("... 还有 ");
Serial.print(signal.spaceCount - spacePrintCount);
Serial.println(" 个space数据点未显示");
}
Serial.println("==================");
}
bool isValidIRSignal(const IRSignal& signal) {
return (signal.data != nullptr && signal.length > 0 && signal.isValid);
return (signal.isValid &&
((signal.markTimes != nullptr && signal.markCount > 0) ||
(signal.spaceTimes != nullptr && signal.spaceCount > 0)) &&
signal.carrierFreq > 0);
}