重构了红外接收

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

@@ -190,8 +190,11 @@ void sendStoredIRSignal(String buttonName) {
// 从字符串解析红外信号 // 从字符串解析红外信号
IRSignal parseIRSignalFromString(String dataStr) { IRSignal parseIRSignalFromString(String dataStr) {
IRSignal signal; IRSignal signal;
signal.data = nullptr; signal.markTimes = nullptr;
signal.length = 0; signal.spaceTimes = nullptr;
signal.markCount = 0;
signal.spaceCount = 0;
signal.carrierFreq = IR_CARRIER_FREQ;
signal.isValid = false; signal.isValid = false;
if (dataStr.length() == 0) { if (dataStr.length() == 0) {
@@ -203,11 +206,11 @@ IRSignal parseIRSignalFromString(String dataStr) {
for (int i = 0; i < dataStr.length(); i++) { for (int i = 0; i < dataStr.length(); i++) {
if (dataStr[i] == ',') commaCount++; if (dataStr[i] == ',') commaCount++;
} }
signal.length = commaCount + 1; int totalLength = commaCount + 1;
// 分配内存 // 分配临时内存存储原始数据
signal.data = (unsigned int*)malloc(signal.length * sizeof(unsigned int)); unsigned int* tempData = (unsigned int*)malloc(totalLength * sizeof(unsigned int));
if (signal.data == nullptr) { if (tempData == nullptr) {
return signal; return signal;
} }
@@ -217,27 +220,78 @@ IRSignal parseIRSignalFromString(String dataStr) {
for (int i = 0; i <= dataStr.length(); i++) { for (int i = 0; i <= dataStr.length(); i++) {
if (i == dataStr.length() || dataStr[i] == ',') { if (i == dataStr.length() || dataStr[i] == ',') {
String valueStr = dataStr.substring(startPos, i); String valueStr = dataStr.substring(startPos, i);
signal.data[index] = valueStr.toInt(); tempData[index] = valueStr.toInt();
index++; index++;
startPos = i + 1; 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; signal.isValid = true;
return signal; return signal;
} }
// 将红外信号转换为字符串 // 将红外信号转换为字符串
String irSignalToString(const IRSignal& 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 ""; return "";
} }
String result = ""; String result = "";
for (int i = 0; i < signal.length; i++) { int maxCount = max(signal.markCount, signal.spaceCount);
if (i > 0) result += ",";
result += String(signal.data[i]); // 重建原始时序数据交替输出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; return result;
} }
@@ -751,7 +805,7 @@ void handleStartRecord() {
prefs.putString(("ir_" + buttonName).c_str(), irDataStr); prefs.putString(("ir_" + buttonName).c_str(), irDataStr);
Serial.println("红外信号录入成功: " + buttonName); Serial.println("红外信号录入成功: " + buttonName);
Serial.println("信号长度: " + String(signal.length)); Serial.println("mark数量: " + String(signal.markCount) + ", space数量: " + String(signal.spaceCount));
freeIRSignal(signal); freeIRSignal(signal);
server.send(200, "application/json", "{\"success\":true}"); server.send(200, "application/json", "{\"success\":true}");
@@ -768,7 +822,7 @@ void handleSendIR() {
return; return;
} }
DynamicJsonDocument doc(200); DynamicJsonDocument doc(26700);
deserializeJson(doc, server.arg("plain")); deserializeJson(doc, server.arg("plain"));
String buttonName = doc["button"]; String buttonName = doc["button"];

View File

@@ -1,370 +0,0 @@
#ifndef LUNAR_CALENDAR_H
#define LUNAR_CALENDAR_H
#include <Arduino.h>
#include <WiFi.h>
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

View File

@@ -1,169 +1,336 @@
#include "ir_control.h" #include "ir_control.h"
void initIRControl() { // IRremoteESP8266库的接收器对象
pinMode(IR_RECEIVE_PIN, INPUT); // 增加缓冲区大小到500以支持长信号超时设置为15ms
pinMode(IR_SEND_PIN, OUTPUT); IRrecv irrecv(IR_RECEIVE_PIN, 500, 15);
digitalWrite(IR_SEND_PIN, LOW);
Serial.println("红外控制模块初始化完成"); void initIRControl() {
Serial.print("接收引脚: "); // 配置RMT发送通道
Serial.println(IR_RECEIVE_PIN); rmt_config_t rmt_tx_config = RMT_DEFAULT_CONFIG_TX((gpio_num_t)IR_SEND_PIN, RMT_TX_CHANNEL);
Serial.print("发送引脚: "); rmt_tx_config.clk_div = RMT_CLK_DIV;
Serial.println(IR_SEND_PIN); 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() { 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 receiveIRSignal() {
IRSignal signal; IRSignal signal;
signal.data = nullptr; signal.markTimes = nullptr;
signal.length = 0; signal.spaceTimes = nullptr;
signal.markCount = 0;
signal.spaceCount = 0;
signal.carrierFreq = IR_CARRIER_FREQ;
signal.isValid = false; signal.isValid = false;
Serial.println("开始接收红外信号..."); Serial.println("开始接收红外信号 (使用IRremoteESP8266)...");
Serial.println("请按下遥控器按键...");
// 分配内存存储信号数据 decode_results results;
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; irrecv.resume();
bool currentState = HIGH;
bool lastState = HIGH;
// 等待信号开始(第一个低电平) // 等待接收完整的红外信号
while (digitalRead(IR_RECEIVE_PIN) == HIGH && (micros() - startTime) < RECEIVE_TIMEOUT_US) { unsigned long startTime = millis();
delayMicroseconds(10); 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 ((micros() - startTime) >= RECEIVE_TIMEOUT_US) { // 检查缓冲区溢出
Serial.println("等待信号超时"); if (results.overflow) {
free(signal.data); Serial.println("警告: 接收缓冲区溢出!信号可能不完整");
signal.data = nullptr; Serial.println("建议: 增加缓冲区大小或检查信号长度");
return signal; }
}
Serial.println("检测到信号开始"); // 检查信号长度是否合理(典型红外信号应该有足够的数据)
lastChange = micros(); if (results.rawlen < 10) {
lastState = LOW; Serial.printf("警告: 信号长度过短 (%d),可能不是完整信号\n", results.rawlen);
signal.length = 0; } else if (results.rawlen >= 490) { // 接近缓冲区上限
Serial.printf("警告: 信号长度接近缓冲区上限 (%d/500),可能被截断\n", results.rawlen);
}
// 接收信号数据 // 打印接收到的协议信息
while (signal.length < MAX_SIGNAL_LENGTH) { Serial.printf("协议: %s\n", typeToString(results.decode_type).c_str());
currentState = digitalRead(IR_RECEIVE_PIN); Serial.printf("值: 0x%08X\n", results.value);
Serial.printf("位数: %d\n", results.bits);
if (currentState != lastState) { if (results.rawlen > 1) {
// 状态改变,记录持续时间 // IRremoteESP8266的rawbuf包含原始时序数据
unsigned long duration = micros() - lastChange; // rawbuf[0]是未使用的实际数据从rawbuf[1]开始
signal.data[signal.length] = duration; // 数据格式mark, space, mark, space, ...
signal.length++;
lastState = currentState; int dataLength = results.rawlen - 1; // 减去未使用的第一个元素
lastChange = micros();
}
// 检查是否信号结束(高电平持续时间过长) // 计算mark和space的数量
if (currentState == HIGH && (micros() - lastChange) > SIGNAL_END_TIMEOUT_US) { signal.markCount = (dataLength + 1) / 2; // 奇数位置是mark
Serial.println("检测到信号结束"); 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; break;
} }
// 总体超时检查 // 短暂延时避免CPU占用过高
if ((micros() - startTime) > RECEIVE_TIMEOUT_US) { delay(10);
Serial.println("接收总体超时");
break;
}
} }
if (signal.length > 0) { if (!signal.isValid) {
signal.isValid = true; Serial.println("IRremoteESP8266接收超时或无有效信号");
Serial.print("信号接收成功,长度: "); Serial.println("请检查:");
Serial.println(signal.length); Serial.println("1. 遥控器是否有电");
Serial.println("2. 红外接收器是否正确连接到引脚 " + String(IR_RECEIVE_PIN));
// 重新分配内存以节省空间 Serial.println("3. 遥控器是否对准红外接收器");
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; return signal;
} }
// generateCarrier函数已被RMT模块替代不再需要
bool sendIRSignal(const IRSignal& signal) { bool sendIRSignal(const IRSignal& signal) {
if (!isValidIRSignal(signal)) { if (!isValidIRSignal(signal)) {
Serial.println("信号无效,无法发送"); Serial.println("信号无效,无法发送");
return false; return false;
} }
Serial.println("开始发送红外信号..."); Serial.println("开始发送红外信号 (使用RMT)...");
Serial.print("信号长度: "); Serial.printf("载波频率: %d Hz\n", signal.carrierFreq);
Serial.println(signal.length); Serial.printf("mark数量: %d, space数量: %d\n", signal.markCount, signal.spaceCount);
// 禁用中断以确保精确的时序 // 计算需要的RMT项目数量
noInterrupts(); 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++) { // 分配RMT项目数组
digitalWrite(IR_SEND_PIN, state); rmt_item32_t* items = (rmt_item32_t*)malloc(item_count * sizeof(rmt_item32_t));
delayMicroseconds(signal.data[i]); if (items == nullptr) {
state = !state; // 切换状态 Serial.println("RMT项目内存分配失败");
return false;
} }
// 确保最后是低电平 // 构建RMT数据项
digitalWrite(IR_SEND_PIN, LOW); 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期间为低电平无载波
interrupts(); items[i].duration1 = (i < signal.spaceCount) ? signal.spaceTimes[i] : 0;
}
Serial.println("信号发送完成"); // 发送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; return true;
} }
void freeIRSignal(IRSignal& signal) { void freeIRSignal(IRSignal& signal) {
if (signal.data != nullptr) { if (signal.markTimes != nullptr) {
free(signal.data); free(signal.markTimes);
signal.data = nullptr; 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; signal.isValid = false;
} }
IRSignal copyIRSignal(const IRSignal& source) { IRSignal copyIRSignal(const IRSignal& source) {
IRSignal copy; IRSignal copy;
copy.data = nullptr; copy.markTimes = nullptr;
copy.length = 0; copy.spaceTimes = nullptr;
copy.markCount = 0;
copy.spaceCount = 0;
copy.carrierFreq = 0;
copy.isValid = false; copy.isValid = false;
if (!isValidIRSignal(source)) { if (!isValidIRSignal(source)) {
return copy; return copy;
} }
// 分配内存 // 复制mark时间数组
copy.data = (unsigned int*)malloc(source.length * sizeof(unsigned int)); if (source.markCount > 0 && source.markTimes != nullptr) {
if (copy.data == nullptr) { copy.markTimes = (unsigned int*)malloc(source.markCount * sizeof(unsigned int));
Serial.println("复制信号时内存分配失败"); if (copy.markTimes == nullptr) {
return copy; Serial.println("复制mark时间数组时内存分配失败");
return copy;
}
for (int i = 0; i < source.markCount; i++) {
copy.markTimes[i] = source.markTimes[i];
}
copy.markCount = source.markCount;
} }
// 复制数据 // 复制space时间数组
for (int i = 0; i < source.length; i++) { if (source.spaceCount > 0 && source.spaceTimes != nullptr) {
copy.data[i] = source.data[i]; 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; copy.isValid = true;
return copy; return copy;
@@ -176,33 +343,56 @@ void printIRSignal(const IRSignal& signal, int maxPrint) {
} }
Serial.println("=== 红外信号数据 ==="); Serial.println("=== 红外信号数据 ===");
Serial.print("信号长度: "); Serial.print("载波频率: ");
Serial.println(signal.length); Serial.print(signal.carrierFreq);
Serial.println(" Hz");
Serial.print("mark数量: ");
Serial.println(signal.markCount);
Serial.print("space数量: ");
Serial.println(signal.spaceCount);
Serial.print("信号有效: "); Serial.print("信号有效: ");
Serial.println(signal.isValid ? "" : ""); Serial.println(signal.isValid ? "" : "");
int printCount = (maxPrint == 0) ? signal.length : min(maxPrint, signal.length); // 打印mark时间
int markPrintCount = (maxPrint == 0) ? signal.markCount : min(maxPrint, signal.markCount);
Serial.println("原始数据 (微秒):"); Serial.println("Mark时间 (微秒):");
for (int i = 0; i < printCount; i++) { for (int i = 0; i < markPrintCount; i++) {
Serial.print("Index "); Serial.print("Mark ");
Serial.print(i); Serial.print(i);
Serial.print(": "); Serial.print(": ");
Serial.print(signal.data[i]); Serial.print(signal.markTimes[i]);
Serial.print(" us ("); Serial.println(" us");
Serial.print((i % 2 == 0) ? "LOW" : "HIGH");
Serial.println(")");
} }
if (printCount < signal.length) { if (markPrintCount < signal.markCount) {
Serial.print("... 还有 "); Serial.print("... 还有 ");
Serial.print(signal.length - printCount); Serial.print(signal.markCount - markPrintCount);
Serial.println(" 个数据点未显示"); 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("=================="); Serial.println("==================");
} }
bool isValidIRSignal(const IRSignal& signal) { 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);
} }

View File

@@ -2,12 +2,19 @@
#define IR_CONTROL_H #define IR_CONTROL_H
#include <Arduino.h> #include <Arduino.h>
#include "driver/rmt.h"
#include "driver/gpio.h"
#include <IRrecv.h>
#include <IRutils.h>
// 红外信号结构体 // 红外信号结构体
struct IRSignal { struct IRSignal {
unsigned int* data; // 原始信号数据数组(微秒) unsigned int* markTimes; // mark时间数组(微秒)
int length; // 信号长度 unsigned int* spaceTimes; // space时间数组微秒
bool isValid; // 信号是否有效 int markCount; // mark数量
int spaceCount; // space数量
unsigned int carrierFreq; // 载波频率Hz
bool isValid; // 信号是否有效
}; };
// 配置参数 // 配置参数
@@ -16,6 +23,22 @@ struct IRSignal {
#define MAX_SIGNAL_LENGTH 1000 #define MAX_SIGNAL_LENGTH 1000
#define RECEIVE_TIMEOUT_US 1000000 // 1秒接收超时 #define RECEIVE_TIMEOUT_US 1000000 // 1秒接收超时
#define SIGNAL_END_TIMEOUT_US 50000 // 50ms信号结束判断 #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 包含原始红外信号数据的结构体 * @return IRSignal 包含红外信号mark/space时序和载波频率的结构体
* - data: 指向信号数据数组的指针 * - markTimes: 指向mark时间数组的指针
* - length: 信号数据长度 * - spaceTimes: 指向space时间数组的指针
* - markCount: mark数量
* - spaceCount: space数量
* - carrierFreq: 载波频率
* - isValid: 是否成功接收到有效信号 * - isValid: 是否成功接收到有效信号
* *
* 注意:调用者需要在使用完毕后调用 freeIRSignal() 释放内存 * 注意:调用者需要在使用完毕后调用 freeIRSignal() 释放内存