Files
Dorm-Air-Conditioner-Smart-…/ir_control.cpp
2025-09-23 23:58:49 +08:00

398 lines
15 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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);
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;
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() {
// 使用IRremoteESP8266检查是否有信号开始
decode_results results;
// 尝试解码,但不等待完整信号
if (irrecv.decode(&results, nullptr, 0, false)) {
// 有信号开始,恢复接收状态
irrecv.resume();
return true;
}
return false;
}
IRSignal receiveIRSignal() {
IRSignal signal;
signal.markTimes = nullptr;
signal.spaceTimes = nullptr;
signal.markCount = 0;
signal.spaceCount = 0;
signal.carrierFreq = IR_CARRIER_FREQ;
signal.isValid = false;
Serial.println("开始接收红外信号 (使用IRremoteESP8266)...");
Serial.println("请按下遥控器按键...");
decode_results results;
// 清空接收缓冲区
irrecv.resume();
// 等待接收完整的红外信号
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("建议: 增加缓冲区大小或检查信号长度");
}
// 检查信号长度是否合理(典型红外信号应该有足够的数据)
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;
}
// 短暂延时避免CPU占用过高
delay(10);
}
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("开始发送红外信号 (使用RMT)...");
Serial.printf("载波频率: %d Hz\n", signal.carrierFreq);
Serial.printf("mark数量: %d, space数量: %d\n", signal.markCount, signal.spaceCount);
// 计算需要的RMT项目数量
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) {
Serial.println("RMT项目内存分配失败");
return false;
}
// 构建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;
}
// 发送RMT数据
esp_err_t err = rmt_write_items(RMT_TX_CHANNEL, items, item_count, true);
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.markTimes != nullptr) {
free(signal.markTimes);
signal.markTimes = nullptr;
}
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.markTimes = nullptr;
copy.spaceTimes = nullptr;
copy.markCount = 0;
copy.spaceCount = 0;
copy.carrierFreq = 0;
copy.isValid = false;
if (!isValidIRSignal(source)) {
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;
}
// 复制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.carrierFreq = source.carrierFreq;
copy.isValid = true;
return copy;
}
void printIRSignal(const IRSignal& signal, int maxPrint) {
if (!isValidIRSignal(signal)) {
Serial.println("信号无效,无法打印");
return;
}
Serial.println("=== 红外信号数据 ===");
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 ? "" : "");
// 打印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.markTimes[i]);
Serial.println(" us");
}
if (markPrintCount < signal.markCount) {
Serial.print("... 还有 ");
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.isValid &&
((signal.markTimes != nullptr && signal.markCount > 0) ||
(signal.spaceTimes != nullptr && signal.spaceCount > 0)) &&
signal.carrierFreq > 0);
}