diff --git a/Dorm-Air-Conditioner-Smart-Controller.ino b/Dorm-Air-Conditioner-Smart-Controller.ino index 2f61844..22ee1fd 100644 --- a/Dorm-Air-Conditioner-Smart-Controller.ino +++ b/Dorm-Air-Conditioner-Smart-Controller.ino @@ -190,8 +190,11 @@ void sendStoredIRSignal(String buttonName) { // 从字符串解析红外信号 IRSignal parseIRSignalFromString(String dataStr) { 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; if (dataStr.length() == 0) { @@ -203,11 +206,11 @@ IRSignal parseIRSignalFromString(String dataStr) { for (int i = 0; i < dataStr.length(); i++) { if (dataStr[i] == ',') commaCount++; } - signal.length = commaCount + 1; + int totalLength = commaCount + 1; - // 分配内存 - signal.data = (unsigned int*)malloc(signal.length * sizeof(unsigned int)); - if (signal.data == nullptr) { + // 分配临时内存存储原始数据 + unsigned int* tempData = (unsigned int*)malloc(totalLength * sizeof(unsigned int)); + if (tempData == nullptr) { return signal; } @@ -217,27 +220,78 @@ IRSignal parseIRSignalFromString(String dataStr) { for (int i = 0; i <= dataStr.length(); i++) { if (i == dataStr.length() || dataStr[i] == ',') { String valueStr = dataStr.substring(startPos, i); - signal.data[index] = valueStr.toInt(); + tempData[index] = valueStr.toInt(); index++; startPos = i + 1; } } + // 分离mark和space数据 + signal.markCount = (totalLength + 1) / 2; + signal.spaceCount = totalLength / 2; + + // 分配mark时间数组 + if (signal.markCount > 0) { + signal.markTimes = (unsigned int*)malloc(signal.markCount * sizeof(unsigned int)); + if (signal.markTimes == nullptr) { + free(tempData); + return signal; + } + + for (int i = 0; i < signal.markCount; i++) { + signal.markTimes[i] = tempData[i * 2]; + } + } + + // 分配space时间数组 + if (signal.spaceCount > 0) { + signal.spaceTimes = (unsigned int*)malloc(signal.spaceCount * sizeof(unsigned int)); + if (signal.spaceTimes == nullptr) { + if (signal.markTimes != nullptr) { + free(signal.markTimes); + signal.markTimes = nullptr; + signal.markCount = 0; + } + free(tempData); + return signal; + } + + for (int i = 0; i < signal.spaceCount; i++) { + signal.spaceTimes[i] = tempData[i * 2 + 1]; + } + } + + free(tempData); signal.isValid = true; return signal; } // 将红外信号转换为字符串 String irSignalToString(const IRSignal& signal) { - if (!signal.isValid || signal.data == nullptr || signal.length == 0) { + if (!signal.isValid || + (signal.markTimes == nullptr && signal.spaceTimes == nullptr) || + (signal.markCount == 0 && signal.spaceCount == 0)) { return ""; } String result = ""; - for (int i = 0; i < signal.length; i++) { - if (i > 0) result += ","; - result += String(signal.data[i]); + int maxCount = max(signal.markCount, signal.spaceCount); + + // 重建原始时序数据:交替输出mark和space + for (int i = 0; i < maxCount; i++) { + // 添加mark时间 + if (i < signal.markCount) { + if (result.length() > 0) result += ","; + result += String(signal.markTimes[i]); + } + + // 添加space时间 + if (i < signal.spaceCount) { + if (result.length() > 0) result += ","; + result += String(signal.spaceTimes[i]); + } } + return result; } @@ -751,7 +805,7 @@ void handleStartRecord() { prefs.putString(("ir_" + buttonName).c_str(), irDataStr); Serial.println("红外信号录入成功: " + buttonName); - Serial.println("信号长度: " + String(signal.length)); + Serial.println("mark数量: " + String(signal.markCount) + ", space数量: " + String(signal.spaceCount)); freeIRSignal(signal); server.send(200, "application/json", "{\"success\":true}"); @@ -768,7 +822,7 @@ void handleSendIR() { return; } - DynamicJsonDocument doc(200); + DynamicJsonDocument doc(26700); deserializeJson(doc, server.arg("plain")); String buttonName = doc["button"]; diff --git a/LunarCalendar.h b/LunarCalendar.h deleted file mode 100644 index 1ac696a..0000000 --- a/LunarCalendar.h +++ /dev/null @@ -1,370 +0,0 @@ -#ifndef LUNAR_CALENDAR_H -#define LUNAR_CALENDAR_H - -#include -#include - -class LunarCalendar { -private: - // 农历数据表 (1900-2100年) - static const uint32_t lunarInfo[]; - static const char* lunarMonthNames[]; - static const char* lunarDayNames[]; - static const char* tianGan[]; - static const char* diZhi[]; - static const char* animals[]; - - // 辅助函数 - static int getDaysInYear(int year); - static int getLeapMonth(int year); - static int getDaysInMonth(int year, int month, bool isLeap = false); - static bool isLeapYear(int year); - static int getDayOfYear(int year, int month, int day); - static void calculateLunar(int solarYear, int solarMonth, int solarDay, - int& lunarYear, int& lunarMonth, int& lunarDay, bool& isLeap); - -public: - // 主要转换函数 - static String solarToLunar(String solarDate); - static String getLunarYearName(int year); - static String getLunarMonthName(int month, bool isLeap = false); - static String getLunarDayName(int day); - - // 节假日判断函数 - static bool isHoliday(int year, int month, int day); - static bool isSolarHoliday(int month, int day); - static bool isLunarHoliday(int lunarYear, int lunarMonth, int lunarDay, bool isLeap); -}; - -// 农历数据表 (1900-2100年,每年用32位表示) -// 前4位表示闰月月份,后12位表示每月天数(0=29天,1=30天) -const uint32_t LunarCalendar::lunarInfo[] = { - 0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, - 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, - 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, - 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, - 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, - 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, - 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, - 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, - 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, - 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0, - 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, - 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, - 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, - 0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, - 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, - 0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, - 0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, - 0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, - 0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, - 0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, - 0x0d520 -}; - -// 农历月份名称 -const char* LunarCalendar::lunarMonthNames[] = { - "正月", "二月", "三月", "四月", "五月", "六月", - "七月", "八月", "九月", "十月", "冬月", "腊月" -}; - -// 农历日期名称 -const char* LunarCalendar::lunarDayNames[] = { - "初一", "初二", "初三", "初四", "初五", "初六", "初七", "初八", "初九", "初十", - "十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九", "二十", - "廿一", "廿二", "廿三", "廿四", "廿五", "廿六", "廿七", "廿八", "廿九", "三十" -}; - -// 天干 -const char* LunarCalendar::tianGan[] = { - "甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸" -}; - -// 地支 -const char* LunarCalendar::diZhi[] = { - "子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥" -}; - -// 生肖 -const char* LunarCalendar::animals[] = { - "鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪" -}; - -// 获取农历年总天数 -int LunarCalendar::getDaysInYear(int year) { - // 边界检查 - if (year < 1900 || year > 2100) return 365; - - int sum = 348; - for (int i = 0x8000; i > 0x8; i >>= 1) { - if ((lunarInfo[year - 1900] & i) != 0) sum += 1; - } - return sum + getDaysInMonth(year, getLeapMonth(year), true); -} - -// 获取农历年闰月月份 -int LunarCalendar::getLeapMonth(int year) { - // 边界检查 - if (year < 1900 || year > 2100) return 0; - return lunarInfo[year - 1900] & 0xf; -} - -// 获取农历月天数 -int LunarCalendar::getDaysInMonth(int year, int month, bool isLeap) { - // 边界检查 - if (year < 1900 || year > 2100 || month < 1 || month > 12) return 29; - - if (isLeap && month != getLeapMonth(year)) return 0; - if (isLeap) { - return (lunarInfo[year - 1900] & 0x10000) ? 30 : 29; - } else { - return (lunarInfo[year - 1900] & (0x10000 >> month)) ? 30 : 29; - } -} - -// 判断公历年是否为闰年 -bool LunarCalendar::isLeapYear(int year) { - return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); -} - -// 获取公历日期在当年的天数 -int LunarCalendar::getDayOfYear(int year, int month, int day) { - int daysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - if (isLeapYear(year)) daysInMonth[1] = 29; - - int dayOfYear = day; - for (int i = 0; i < month - 1; i++) { - dayOfYear += daysInMonth[i]; - } - return dayOfYear; -} - -// 核心转换算法 -void LunarCalendar::calculateLunar(int solarYear, int solarMonth, int solarDay, - int& lunarYear, int& lunarMonth, int& lunarDay, bool& isLeap) { - // 1900年1月31日为农历1900年正月初一 - int baseYear = 1900; - int baseMonth = 1; - int baseDay = 31; - - // 计算距离基准日期的天数 - int offset = 0; - - // 计算年份差 - for (int i = baseYear; i < solarYear; i++) { - offset += isLeapYear(i) ? 366 : 365; - } - - // 加上当年已过天数 - offset += getDayOfYear(solarYear, solarMonth, solarDay) - getDayOfYear(baseYear, baseMonth, baseDay); - - // 计算农历日期 - lunarYear = baseYear; - int daysInLunarYear = getDaysInYear(lunarYear); - - while (offset >= daysInLunarYear) { - offset -= daysInLunarYear; - lunarYear++; - daysInLunarYear = getDaysInYear(lunarYear); - } - - // 计算月份 - lunarMonth = 1; - isLeap = false; - int leapMonth = getLeapMonth(lunarYear); - - while (offset > 0 && lunarMonth <= 12) { - int daysInCurrentMonth; - - if (lunarMonth == leapMonth && !isLeap && leapMonth > 0) { - // 闰月 - daysInCurrentMonth = getDaysInMonth(lunarYear, lunarMonth, true); - if (offset >= daysInCurrentMonth) { - offset -= daysInCurrentMonth; - isLeap = true; - } else { - isLeap = true; - break; - } - } - - // 正常月份 - daysInCurrentMonth = getDaysInMonth(lunarYear, lunarMonth, false); - if (offset >= daysInCurrentMonth) { - offset -= daysInCurrentMonth; - if (isLeap) { - isLeap = false; - } else { - lunarMonth++; - } - } else { - break; - } - } - - // 确保月份在有效范围内 - if (lunarMonth > 12) lunarMonth = 12; - if (lunarMonth < 1) lunarMonth = 1; - - lunarDay = offset + 1; -} - -// 主要转换函数 -String LunarCalendar::solarToLunar(String solarDate) { - // 解析输入日期 "2025/5/6" - int year, month, day; - int firstSlash = solarDate.indexOf('/'); - int secondSlash = solarDate.indexOf('/', firstSlash + 1); - - if (firstSlash == -1 || secondSlash == -1) { - return "日期格式错误"; - } - - year = solarDate.substring(0, firstSlash).toInt(); - month = solarDate.substring(firstSlash + 1, secondSlash).toInt(); - day = solarDate.substring(secondSlash + 1).toInt(); - - // 验证日期范围 - if (year < 1900 || year > 2100 || month < 1 || month > 12 || day < 1 || day > 31) { - return "日期超出范围(1900-2100)"; - } - - // 转换为农历 - int lunarYear, lunarMonth, lunarDay; - bool isLeap; - calculateLunar(year, month, day, lunarYear, lunarMonth, lunarDay, isLeap); - - // 格式化输出 - String result = getLunarYearName(lunarYear) + "年"; - result += getLunarMonthName(lunarMonth, isLeap); - result += getLunarDayName(lunarDay); - - return result; -} - -// 获取农历年份名称(天干地支+生肖) -String LunarCalendar::getLunarYearName(int year) { - int tianGanIndex = (year - 4) % 10; - int diZhiIndex = (year - 4) % 12; - - // 确保索引为正数 - if (tianGanIndex < 0) tianGanIndex += 10; - if (diZhiIndex < 0) diZhiIndex += 12; - - String yearName = String(tianGan[tianGanIndex]) + String(diZhi[diZhiIndex]); - yearName += "(" + String(animals[diZhiIndex]) + ")"; - - return yearName; -} - -// 获取农历月份名称 -String LunarCalendar::getLunarMonthName(int month, bool isLeap) { - String monthName = ""; - if (isLeap) { - monthName = "闰"; - } - // 添加边界检查 - if (month >= 1 && month <= 12) { - monthName += lunarMonthNames[month - 1]; - } else { - monthName += "未知月"; - } - return monthName; -} - -// 获取农历日期名称 -String LunarCalendar::getLunarDayName(int day) { - // 添加边界检查 - if (day >= 1 && day <= 30) { - return String(lunarDayNames[day - 1]); - } else { - return "未知日"; - } -} - -// 节假日数据结构 -struct SolarHoliday { - int month; - int day; - const char* name; -}; - -struct LunarHoliday { - int month; - int day; - const char* name; -}; - -// 公历法定节假日列表 -static const SolarHoliday solarHolidays[] = { - {1, 1, "元旦"}, - {3, 8, "妇女节"}, - {5, 1, "劳动节"}, - {6, 1, "儿童节"}, - {10, 1, "国庆节"}, - {10, 2, "国庆节"}, - {10, 3, "国庆节"} -}; - -// 农历法定节假日列表 -static const LunarHoliday lunarHolidays[] = { - {1, 1, "春节"}, - {1, 2, "春节"}, - {1, 3, "春节"}, - {1, 15, "元宵节"}, - {5, 5, "端午节"}, - {8, 15, "中秋节"}, - {9, 9, "重阳节"}, - {12, 30, "除夕"}, // 大月 - {12, 29, "除夕"} // 小月 -}; - -// 判断是否为公历节假日 -bool LunarCalendar::isSolarHoliday(int month, int day) { - int numSolarHolidays = sizeof(solarHolidays) / sizeof(solarHolidays[0]); - for (int i = 0; i < numSolarHolidays; i++) { - if (solarHolidays[i].month == month && solarHolidays[i].day == day) { - return true; - } - } - return false; -} - -// 判断是否为农历节假日 -bool LunarCalendar::isLunarHoliday(int lunarYear, int lunarMonth, int lunarDay, bool isLeap) { - // 闰月不算节假日 - if (isLeap) return false; - - int numLunarHolidays = sizeof(lunarHolidays) / sizeof(lunarHolidays[0]); - for (int i = 0; i < numLunarHolidays; i++) { - if (lunarHolidays[i].month == lunarMonth && lunarHolidays[i].day == lunarDay) { - // 特殊处理除夕:需要判断是否为该年最后一天 - if (lunarMonth == 12 && (lunarDay == 29 || lunarDay == 30)) { - int daysInLastMonth = getDaysInMonth(lunarYear, 12, false); - if (lunarDay == daysInLastMonth) { - return true; // 是除夕 - } - } else { - return true; - } - } - } - return false; -} - -// 主要节假日判断函数 -bool LunarCalendar::isHoliday(int year, int month, int day) { - // 首先检查公历节假日 - if (isSolarHoliday(month, day)) { - return true; - } - - // 检查农历节假日 - int lunarYear, lunarMonth, lunarDay; - bool isLeap; - calculateLunar(year, month, day, lunarYear, lunarMonth, lunarDay, isLeap); - - return isLunarHoliday(lunarYear, lunarMonth, lunarDay, isLeap); -} - -#endif // LUNAR_CALENDAR_H \ No newline at end of file diff --git a/ir_control.cpp b/ir_control.cpp index 0a46ac9..cfd6a5e 100644 --- a/ir_control.cpp +++ b/ir_control.cpp @@ -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); } \ No newline at end of file diff --git a/ir_control.h b/ir_control.h index ba50bfa..dd5d840 100644 --- a/ir_control.h +++ b/ir_control.h @@ -2,12 +2,19 @@ #define IR_CONTROL_H #include +#include "driver/rmt.h" +#include "driver/gpio.h" +#include +#include // 红外信号结构体 struct IRSignal { - unsigned int* data; // 原始信号数据数组(微秒) - int length; // 信号长度 - bool isValid; // 信号是否有效 + unsigned int* markTimes; // mark时间数组(微秒) + unsigned int* spaceTimes; // space时间数组(微秒) + int markCount; // mark数量 + int spaceCount; // space数量 + unsigned int carrierFreq; // 载波频率(Hz) + bool isValid; // 信号是否有效 }; // 配置参数 @@ -16,6 +23,22 @@ struct IRSignal { #define MAX_SIGNAL_LENGTH 1000 #define RECEIVE_TIMEOUT_US 1000000 // 1秒接收超时 #define SIGNAL_END_TIMEOUT_US 50000 // 50ms信号结束判断 +#define IR_CARRIER_FREQ 38000 // 38kHz载波频率 +#define CARRIER_PERIOD_US 26 // 38kHz载波周期(微秒) + +// RMT配置参数 +#define RMT_TX_CHANNEL RMT_CHANNEL_0 // RMT发送通道 +#define RMT_RX_CHANNEL RMT_CHANNEL_1 // RMT接收通道 +#define RMT_CLK_DIV 80 // RMT时钟分频器 (80MHz / 80 = 1MHz, 1us分辨率) +#define RMT_MEM_BLOCK_NUM 1 // RMT内存块数量 +#define RMT_TX_CARRIER_EN true // 启用发送载波 +#define RMT_TX_CARRIER_FREQ_HZ 38000 // 发送载波频率 +#define RMT_TX_CARRIER_DUTY_PERCENT 33 // 载波占空比 +#define RMT_TX_CARRIER_LEVEL RMT_CARRIER_LEVEL_HIGH // 载波电平 +#define RMT_IDLE_LEVEL RMT_IDLE_LEVEL_LOW // 空闲电平 +#define RMT_RX_FILTER_EN true // 启用接收滤波器 +#define RMT_RX_FILTER_THRESH_US 100 // 接收滤波器阈值(微秒) +#define RMT_RX_IDLE_THRESH_US 10000 // 接收空闲阈值(微秒) /** * 初始化红外控制模块 @@ -33,9 +56,12 @@ bool checkIRSignalStart(); /** * 接收红外信号 - * @return IRSignal 包含原始红外信号数据的结构体 - * - data: 指向信号数据数组的指针 - * - length: 信号数据长度 + * @return IRSignal 包含红外信号mark/space时序和载波频率的结构体 + * - markTimes: 指向mark时间数组的指针 + * - spaceTimes: 指向space时间数组的指针 + * - markCount: mark数量 + * - spaceCount: space数量 + * - carrierFreq: 载波频率 * - isValid: 是否成功接收到有效信号 * * 注意:调用者需要在使用完毕后调用 freeIRSignal() 释放内存