1. 从“万能遥控器”到“智能开关”红外遥控的Arduino实现逻辑相信很多朋友和我一样家里总有几个“失踪”的遥控器电视、空调、机顶盒遥控器一多找起来就头疼。你有没有想过用一块小小的Arduino板子自己做一个“万能遥控器”或者更进一步让家里的电器听你的代码指令自动开关这听起来很酷但背后的原理其实并不复杂。红外遥控这个我们每天都在接触的技术其核心就是通过红外光这种不可见光来传递一串特定的“密码”。Arduino要做的就是学会“听”懂这些密码解码然后在自己需要的时候再“说”出同样的密码编码发射从而控制对应的设备。我最初接触Arduino红外遥控是想解决一个很实际的问题工作室的空调太老旧没有智能接口夏天每次进去都像蒸桑拿。手动开空调又总是忘记。于是我琢磨着用Arduino红外接收头录下空调遥控器的开关信号再结合温湿度传感器和实时时钟模块做一个自动温控系统。这个想法听起来简单但实操过程中从接收不稳定到编码协议识别错误踩的坑一个接一个。今天我就把这些从电路连接到代码调试再到协议分析的完整经验梳理出来。无论你是想复刻一个万能遥控器还是想为你的Arduino智能小车、智能家居项目添加红外控制功能这篇内容都能帮你避开我走过的弯路快速上手。整个流程可以概括为三个核心环节首先你需要一套能“收”能“发”的硬件核心是一个红外接收器和一个红外发射二极管其次你得找到一个靠谱的“翻译官”——也就是Arduino库来帮你处理复杂的红外信号编解码最后也是最关键的一步是理解并应对不同设备间千差万别的红外协议。市面上没有统一的红外通信标准不同品牌、甚至同品牌不同型号的设备可能采用完全不同的协议这是项目成功与否的最大变数。接下来我们就从硬件搭建开始一步步拆解。2. 硬件搭建红外收/发模块的选型与电路连接要点动手之前我们先得把“耳朵”和“嘴巴”准备好。红外接收和发射是两套相对独立的电路但通常我们会一起部署以实现完整的遥控与学习功能。2.1 红外接收模块如何选择与正确连接市面上常见的红外接收头是一个三引脚的小黑块比如VS1838B、HS0038、TL1838等。它们内部集成了红外接收管、放大器、带通滤波器和解调器。简单来说它只接收特定频率通常是38kHz的红外信号并把其他杂光过滤掉最后输出一个干净的数字信号给Arduino。注意接收头非常常见且廉价但务必确认其载波频率。绝大多数消费电子遥控器使用38kHz但也有少数使用36kHz、40kHz或56kHz。买错频率会导致完全无法接收信号。包装或产品页面上通常会标明。连接电路极其简单以最常见的VS1838B为例VCC引脚接Arduino的5V引脚。GND引脚接Arduino的任意GND引脚。OUT引脚接Arduino的任意数字引脚例如引脚11。这个引脚将输出解码后的数字信号。这里有一个极易被忽略但至关重要的细节红外接收头对电源噪声非常敏感。如果直接使用开发板的5V当板上其他模块如舵机、电机驱动工作时电源波动可能导致接收头误触发或无法工作。我的经验是务必在接收头的VCC和GND之间并联一个10uF到100uF的电解电容用于滤波。这个小小的电容曾经帮我解决了一个困扰半天的信号间歇性失灵问题。2.2 红外发射电路驱动能力与发射距离的权衡红外发射部分的核心是一个红外发射二极管IRED。它和普通LED类似但发出的是红外光。你不能像点亮一个普通LED那样直接用Arduino的引脚最大输出电流约20mA去驱动它原因有二一是驱动电流不足会导致发射距离极短可能只有几厘米二是Arduino引脚直接承受持续较大的电流可能损坏。因此我们需要一个简单的驱动电路。最经典、最可靠的方法是使用一个NPN三极管如8050、9013、2N2222进行电流放大。具体连接如下红外发射二极管正极较长引脚通过一个100Ω左右的限流电阻连接到Arduino的5V引脚。这个电阻值需要计算假设二极管正向压降约1.2V期望驱动电流为100mA则电阻 R (5V - 1.2V) / 0.1A 38Ω。为了安全留有余量常用100Ω。电流越大发射距离越远但二极管寿命会缩短通常100mA是一个平衡点。三极管部分发射二极管的负极连接到三极管的集电极C。三极管的发射极E接地。三极管的基极B通过一个1kΩ的电阻连接到Arduino的一个数字引脚例如引脚3。这个1kΩ电阻是必须的用于限制基极电流保护Arduino引脚。当Arduino引脚输出高电平时三极管导通电流从5V经电阻、红外二极管、三极管到地形成回路二极管发光。当引脚输出低电平时三极管截止电路断开。这个电路提供了足够的驱动能力可以将有效发射距离提升到5米以上满足大多数室内场景。提示为了增强发射方向性和距离你可以将2-3个红外发射二极管并联每个仍需独立的限流电阻使用。或者使用集成的红外发射模块它内部已经包含了驱动电路使用起来像接收头一样简单VCC, GND, IN但成本稍高。2.3 硬件连接检查清单与常见故障排查在烧录代码前请对照以下清单检查你的连接[ ] 红外接收头OUT引脚连接到Arduino数字引脚如11并在VCC与GND间并联了滤波电容如47uF。[ ] 红外发射电路中三极管基极通过1kΩ电阻连接Arduino数字引脚如3。[ ] 红外发射二极管正极通过100Ω电阻接5V负极接三极管集电极。[ ] 三极管发射极接地。[ ] 确保所有接地GND都共地连接。如果连接后无法工作可以按以下步骤排查接收端无反应先用手机摄像头对准你的红外遥控器按键按下时在手机屏幕上应能看到发射管发出白紫色光手机CMOS能感应到部分红外光。这能证明遥控器是好的。然后检查接收头是否接反滤波电容是否焊牢。发射端控制无效用一个最简易的方法测试将红外接收头的OUT引脚接到Arduino的引脚11用后续章节的接收代码。然后用你的发射电路对准接收头让Arduino发射一个已知的遥控信号如NEC协议的0xFF02FD这是很多遥控器的“电源”键通用码。观察串口监视器是否有正确解码。如果没有首先用万用表测量发射二极管两端在发射时的电压降应在1.1V-1.3V左右如果电压是5V或0V说明二极管未导通或电路断路。3. 软件核心IRremote库的深度解析与使用技巧硬件是躯体软件是灵魂。在Arduino的世界里处理红外信号几乎离不开一个强大的库IRremote。它由Arduino社区大神们维护支持发送、接收和解码数十种常见的红外协议是我们项目的基石。3.1 IRremote库的安装与版本选择在Arduino IDE中你可以通过“项目” - “加载库” - “管理库”搜索“IRremote”进行安装。这里有一个至关重要的坑点库的版本和兼容性。目前主要有两个活跃的分支原版IRremote库作者为Arduino-IRremote。这个版本历史悠久支持广泛但近年来更新放缓。IRremoteESP8266/IRremoteArduino这是一个功能更强大、更新更活跃的分支最初为ESP8266开发但现在完美支持AVR如Uno、ESP32等多种平台。它支持的协议更多解码能力更强。我强烈推荐使用IRremoteArduino这个分支。你可以在库管理器中搜索“IRremote by Armin Joachimsmeyer”找到它版本号通常较高。它的API和原版大部分兼容但功能更稳定。安装后你的代码需要包含#include IRremote.hpp注意是.hpp不是原版的.h。3.2 接收与解码如何准确捕获遥控器信号接收代码的核心是初始化接收对象并在循环中不断检查是否收到信号。以下是基于IRremoteArduino库的示例#include IRremote.hpp // 使用.hpp头文件 #define IR_RECEIVE_PIN 11 // 定义接收引脚 void setup() { Serial.begin(115200); IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); // 初始化接收启用引脚13的LED反馈 } void loop() { if (IrReceiver.decode()) { // 如果接收到数据 Serial.println(IrReceiver.decodedIRData.decodedRawData, HEX); // 以16进制打印原始解码数据 // 更友好的打印方式显示协议和命令码 Serial.print(Protocol: ); Serial.println(getProtocolString(IrReceiver.decodedIRData.protocol)); Serial.print(Command: 0x); Serial.println(IrReceiver.decodedIRData.command, HEX); Serial.println(); // 空行分隔 IrReceiver.resume(); // 必须调用准备接收下一个信号 } }上传这段代码打开串口监视器波特率115200用电视或空调遥控器对准接收头按键。你会看到一串类似0xFF02FD的十六进制数以及协议名称如NEC。这里有几个关键经验和容易踩的坑IrReceiver.resume()必须调用这是最容易被遗忘的一行代码。如果不调用库将一直停留在当前解码状态无法接收下一个信号。解码结果的含义decodedRawData是原始数据对于NEC协议它通常是32位。而command是8位的命令码对于学习型遥控器我们通常需要记录的是decodedRawData。信号干扰日光灯、节能灯、甚至某些LED灯都会发出红外噪声。如果接收不稳定尝试让接收头远离强光源或者用深色热缩管套住接收头只留前端接收窗来减少干扰。协议自动识别IRremoteArduino库的强大之处在于它能自动识别多种协议。如果打印出的协议是UNKNOWN可能意味着你的遥控器使用了非常冷门的协议或者信号受到严重干扰。此时可以尝试打印IrReceiver.decodedIRData.rawDataPtr-rawlen和IrReceiver.decodedIRData.rawDataPtr-rawbuf来查看原始脉冲数据进行手动分析。3.3 发送信号模拟遥控器按键动作发送信号比接收更简单。你需要知道目标设备使用的协议、设备地址如果有和具体的命令码。这些信息通过前面的接收代码就能获取。#include IRremote.hpp #define IR_SEND_PIN 3 // 定义发射引脚需与硬件连接对应 void setup() { Serial.begin(115200); IrSender.begin(IR_SEND_PIN); // 初始化发送禁用LED反馈 } void loop() { // 示例发送一个NEC协议信号地址为0x0命令为0x45假设是某个设备的音量 Serial.println(Sending NEC code 0x45...); IrSender.sendNEC(0x0, 0x45, 0); // 参数地址命令重发次数 delay(1000); // 等待1秒避免信号过快 // 如果你记录的是原始32位数据如0xFF02FD可以使用sendNECRaw // IrSender.sendNECRaw(0xFF02FD, 0); }发送时的注意事项引脚选择对于AVR Arduino如Uno发送引脚必须是支持PWM的引脚如3, 5, 6, 9, 10, 11因为库需要利用PWM生成38kHz的载波。对于ESP32等几乎所有数字引脚都可以。sendNECvssendNECRawsendNEC(地址, 命令, 重发次数)是标准格式。而sendNECRaw(32位原始数据, 重发次数)直接发送你接收到的完整数据。对于大多数学习型应用使用sendNECRaw更可靠因为它包含了完整的校验和避免了你自己计算地址和命令的麻烦。重发次数红外信号容易受干扰实际遥控器在按下按键时会连续发送多次相同的信号。设置重发次数如1或2可以提高可靠性。但注意有些设备如某些品牌的空调可能将快速连续的同信号识别为长按产生不同效果需要根据实际情况测试。4. 红外协议迷宫NEC、RC-5与RAW数据的实战应对策略如果说硬件和库是工具那么红外协议就是你需要破解的“语言”。不同的设备说着不同的“方言”理解它们是项目成功的关键。4.1 常见协议解析NEC、Sony SIRC、RC-5NEC协议这是最常见、最普及的家电遥控协议。其特点是载波频率38kHz。信号结构一个完整的NEC信号由9ms的引导码、4.5ms的空闲间隔开始然后是32位的数据16位地址8位命令8位命令反码。采用脉冲位置调制PPM逻辑0和1由560us的脉冲后跟不同长度的空闲时间区分。优点广泛支持解码容易。缺点地址空间有限65536个且容易受到其他38kHz信号的干扰。应用绝大多数国产电视、机顶盒、DVD播放器。Sony SIRC协议索尼设备常用特点是变长数据12, 15, 20位。载波频率40kHz。信号结构没有引导码直接是数据位。每个位以1.2ms或0.6ms的脉冲开始逻辑0和1通过脉冲后的空闲时间长短区分。注意由于没有引导码和校验SIRC信号在嘈杂环境中更容易误码。RC-5协议飞利浦制定在音响设备中常见。载波频率36kHz。信号结构采用双相编码曼彻斯特编码抗干扰能力强。每个位中间都有跳变逻辑0和1由跳变方向决定。特点具有“Toggle Bit”用于区分是第一次按下还是持续按住这对于实现“按一下开再按一下关”的功能至关重要。4.2 协议识别与兼容性处理实战当你拿到一个未知的遥控器第一步是识别其协议。使用前面3.2节的接收代码库会尽力自动识别。如果识别为已知协议事情就简单了。但现实往往是骨感的。你可能会遇到库识别为UNKNOWN这可能是一种小众协议或者信号质量太差。此时你需要启用库的原始数据捕获功能。// 在setup()中开启原始数据输出 IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK, USE_DEFAULT_FEEDBACK_LED_PIN, true); // 最后一个参数true表示启用RAW数据捕获 // 在loop的decode部分可以打印原始数据 if (IrReceiver.decode()) { if (IrReceiver.decodedIRData.protocol UNKNOWN) { Serial.println(Received unknown protocol, raw data:); // 打印原始脉冲长度和缓冲区数据 Serial.print(Raw len ); Serial.println(IrReceiver.decodedIRData.rawDataPtr-rawlen); for (uint16_t i 1; i IrReceiver.decodedIRData.rawDataPtr-rawlen; i) { Serial.print(i % 2 ? Mark : Space); Serial.print( ); Serial.print(IrReceiver.decodedIRData.rawDataPtr-rawbuf[i] * MICROS_PER_TICK); Serial.println( us); } } IrReceiver.resume(); }这段代码会打印出信号中每个“Mark”高电平红外发射和“Space”低电平空闲的持续时间微秒。通过分析这些时间间隔的模式比如引导码的长脉冲、代表0和1的特定时间对你有可能推断出它属于哪种协议或者至少可以记录下这个“指纹”用于重放。4.3 RAW数据录制与重放应对一切未知协议的终极方案对于库无法识别、或者你不想深究协议的设备最粗暴但有效的方法就是RAW录制与重放。你可以把红外信号看作一系列特定时间长度的高电平和低电平交替序列。我们把这个序列原封不动地录下来再原样播放出去。IRremote库提供了强大的RAW数据支持#include IRremote.hpp #define IR_RECEIVE_PIN 11 #define IR_SEND_PIN 3 #define RAW_DATA_BUFFER_LENGTH 200 // 定义存储原始数据的数组长度根据信号复杂度调整 uint16_t rawData[RAW_DATA_BUFFER_LENGTH]; // 存储原始时间数据 uint16_t rawDataLength 0; // 实际数据长度 void setup() { Serial.begin(115200); IrReceiver.begin(IR_RECEIVE_PIN); IrSender.begin(IR_SEND_PIN); Serial.println(Ready to record RAW IR signal...); } void loop() { if (Serial.available()) { char command Serial.read(); if (command r) { // 输入r开始录制 Serial.println(Recording... Point remote at receiver and press a key.); delay(1000); // 给用户1秒准备时间 IrReceiver.start(); // 确保接收器开启 delay(200); // 短暂等待清空可能存在的旧数据 // 等待并录制信号 while (!IrReceiver.decode()) { // 空循环等待信号 } if (IrReceiver.decodedIRData.protocol UNKNOWN) { rawDataLength IrReceiver.decodedIRData.rawDataPtr-rawlen; // 复制原始数据注意rawbuf[0]是间隙时间通常从1开始 for (uint16_t i 1; i rawDataLength i RAW_DATA_BUFFER_LENGTH; i) { // 将时间单位从“Tick”转换为微秒 rawData[i-1] IrReceiver.decodedIRData.rawDataPtr-rawbuf[i] * MICROS_PER_TICK; } Serial.print(Recorded RAW data length: ); Serial.println(rawDataLength - 1); // 减1是因为跳过了rawbuf[0] Serial.println(RAW data (in microseconds):); for (uint16_t i 0; i rawDataLength - 1; i) { Serial.println(rawData[i]); } } else { Serial.println(Received a known protocol. Use standard send functions.); } IrReceiver.resume(); IrReceiver.stop(); // 停止接收避免干扰 } else if (command p) { // 输入p播放录制的信号 if (rawDataLength 1) { Serial.println(Playing back RAW signal...); // 注意sendRaw函数需要接收的数据以微秒为单位且第一个元素是Mark时间 IrSender.sendRaw(rawData, rawDataLength - 1, 38); // 38代表38kHz载波频率 Serial.println(Playback done.); } else { Serial.println(No RAW data recorded yet.); } } } }这段代码实现了一个简单的RAW信号学习机。打开串口监视器发送字符r然后用遥控器对准接收头按键代码会录下原始时间序列并打印。发送字符pArduino就会通过发射管重放这个序列。重要提示RAW重放非常强大但有两个限制。第一它严重依赖于定时精度。MICROS_PER_TICK这个常量在IRremote库中定义代表了每个“Tick”对应的微秒数这取决于你使用的单片机主频和库的配置。对于16MHz的Arduino Uno通常是50微秒。如果定时不准确重放可能失败。第二RAW数据会占用大量内存。一个复杂的信号可能有几百个时间点每个点占用2字节很容易耗尽Uno的SRAM。因此对于已知协议尽量使用标准发送函数。5. 项目实战构建一个可学习型万能红外遥控器掌握了收发硬件、软件库和协议知识我们就可以将它们组合起来做一个真正实用的项目一个可以通过串口命令学习并重放红外信号的Arduino万能遥控器。这个项目将综合运用前面所有知识点。5.1 系统设计与功能定义我们的目标是制作一个设备学习功能能够接收并存储多个红外遥控器的按键信号。重放功能根据指令重新发射存储的特定红外信号。管理功能能够通过串口查看、删除已学习的信号。持久化存储学习到的信号在断电后不丢失。考虑到Arduino Uno有限的存储空间2KB SRAM, 1KB EEPROM我们需要精心设计数据结构。RAW数据太大不适合大量存储。因此我们优先存储协议类型和命令码对于NEC等标准协议。只有当协议未知时才考虑存储压缩后的RAW数据。5.2 代码实现学习、存储与重放我们将使用EEPROM来存储数据。每个存储条目我们称之为一个“遥控指令”它的结构可以设计如下伪代码struct RemoteCommand { uint8_t protocol; // 协议类型用一个数字代表如0NEC, 1SONY, 255RAW uint32_t address; // 设备地址某些协议有 uint32_t command; // 命令码 // 如果是RAW协议下面两个字段有效 uint16_t rawLength; // RAW数据长度 uint16_t rawDataOffset; // RAW数据在EEPROM中的起始地址索引 };由于EEPROM写入寿命有限约10万次我们不能每次学习都擦写整个结构。一个实用的策略是在SRAM中维护一个指令列表只在需要保存如收到“保存”命令或断电前如果有电池备份RTC或电容可以检测电压跌落才批量写入EEPROM。以下是核心功能模块的简化代码框架#include IRremote.hpp #include EEPROM.h #define MAX_COMMANDS 20 // 最大存储指令数 #define RAW_DATA_EEPROM_START 512 // RAW数据从EEPROM的512地址开始存储 struct RemoteCommand { uint8_t protocol; uint32_t address; uint32_t command; uint16_t rawLength; uint16_t rawDataOffset; char name[16]; // 给指令起个名字如TV_Power }; RemoteCommand commandList[MAX_COMMANDS]; uint8_t commandCount 0; void learnCommand(uint8_t slot) { if (slot MAX_COMMANDS) return; Serial.println(Point remote and press key...); IrReceiver.start(); while (!IrReceiver.decode()) {} // 等待信号 if (IrReceiver.decodedIRData.protocol ! UNKNOWN) { commandList[slot].protocol IrReceiver.decodedIRData.protocol; commandList[slot].address IrReceiver.decodedIRData.address; commandList[slot].command IrReceiver.decodedIRData.command; commandList[slot].rawLength 0; strncpy(commandList[slot].name, LearnedCmd, 16); } else { // 处理RAW数据这里需要将RAW数据压缩并存入EEPROM并记录offset和length // 这是一个复杂操作涉及EEPROM管理此处省略详细代码 commandList[slot].protocol 255; // 代表RAW commandList[slot].rawLength IrReceiver.decodedIRData.rawDataPtr-rawlen - 1; // ... 计算并分配EEPROM空间存储RAW数据 ... } commandCount max(commandCount, slot1); IrReceiver.resume(); Serial.println(Learn OK.); } void sendCommand(uint8_t slot) { if (slot commandCount) return; RemoteCommand cmd commandList[slot]; switch(cmd.protocol) { case NEC: // 假设NEC对应数字0 IrSender.sendNEC(cmd.address, cmd.command, 1); break; case 255: // RAW // 从EEPROM中读取RAW数据并发送 // IrSender.sendRaw(rawDataArray, cmd.rawLength, 38); break; // ... 其他协议 ... } } void setup() { Serial.begin(115200); IrReceiver.begin(IR_RECEIVE_PIN); IrSender.begin(IR_SEND_PIN); // 从EEPROM加载已保存的commandList loadCommandsFromEEPROM(); Serial.println(Infrared Universal Remote Ready); Serial.println(Commands: L[slot] - Learn, S[slot] - Send, P - List); } void loop() { if (Serial.available()) { char c Serial.read(); if (c L) { uint8_t slot Serial.parseInt(); learnCommand(slot); } else if (c S) { uint8_t slot Serial.parseInt(); sendCommand(slot); } else if (c P) { listCommands(); } } }这个框架提供了学习L、发送S、列表P的基本串口命令。你需要完善loadCommandsFromEEPROM、RAW数据的存储与读取以及错误处理。5.3 优化与扩展思路添加状态反馈可以连接一个OLED屏幕实时显示当前模式、学习的槽位、信号强度等信息比串口更方便。物理按键控制接上几个按钮就可以脱离电脑像普通遥控器一样使用。结合旋转编码器还能实现菜单浏览。网络化如果使用ESP8266或ESP32作为主控可以轻松接入家庭Wi-Fi。通过MQTT或HTTP API你就能用手机App、语音助手如Home Assistant来控制任何红外设备实现真正的智能化。逻辑控制结合其他传感器。就像我最初的项目用DHT11温湿度传感器检测环境当温度超过28℃且有人体红外传感器感应到人在房间时自动发送空调开机信号。低功耗设计如果想让遥控器便携可以考虑用电池供电。在待机时让Arduino进入深度睡眠模式只有按下学习键或发射键时才唤醒可以极大延长电池寿命。6. 高频问题排查与进阶调试技巧即使按照步骤操作你也可能会遇到一些奇怪的问题。这里我汇总了几个最常被问到和我自己踩过的坑。6.1 信号接收不稳定时有时无可能原因1电源噪声。这是头号杀手。确保接收头VCC-GND之间有滤波电容47-100uF电解电容并联一个0.1uF瓷片电容效果更好。尝试使用独立的5V稳压模块为接收头供电与电机等大电流负载完全分开。可能原因2环境光干扰。强烈的日光、卤素灯、某些LED灯都包含红外光谱。移开干扰源或给接收头加上一个红外透光的黑色滤光片甚至可以用废旧遥控器的前端滤光片。可能原因3引脚冲突。某些Arduino板卡如Nano的引脚11、12、13与SPI、LED等复用。如果遇到问题尝试换一个不常用的引脚比如引脚2或8。可能原因4库版本或设置问题。确保你使用的IRremote库版本与代码兼容。在IrReceiver.begin()中尝试禁用LED反馈将ENABLE_LED_FEEDBACK改为DISABLE_LED_FEEDBACK因为引脚13的LED闪烁也可能引入微小干扰。6.2 发射距离短无法控制设备可能原因1发射管电流不足。检查你的限流电阻是否太小导致电流过大可能烧管或太大导致电流不足。用万用表测量发射时的电流目标应在50-150mA之间。可以尝试减小限流电阻如从100Ω换到68Ω来增加电流和距离但注意不要超过二极管和三极管的最大额定电流。可能原因2发射管方向性。红外发射二极管有很强的方向性。确保发射管正对目标设备的接收窗并且中间没有障碍物。稍微偏转角度就可能使信号大幅衰减。可能原因3载波频率偏差。虽然大多数设备用38kHz但可能存在微小偏差。IRremote库的IrSender.begin()函数可以指定载波频率默认38。对于某些老旧设备可以尝试微调如IrSender.begin(IR_SEND_PIN, DISABLE_LED_FEEDBACK, 37)试试37kHz。可能原因4协议或数据错误。确保你发送的协议、地址、命令码完全正确。最稳妥的方法是用接收代码先读取原装遥控器的信号然后一字不差地用发送代码重放这个信号。如果原装遥控器能控制而你的Arduino不能问题一定在发射电路或代码上。6.3 如何控制空调等复杂设备空调、投影仪等设备的遥控信号往往更复杂它们可能使用非标准协议如格力、美的空调有自定义协议。发送长数据帧包含模式、温度、风速、摆风等大量信息。有复杂的校验和。对于这类设备RAW录制与重放是最简单粗暴且有效的方法。但要注意空调的一个“开机制冷26度”命令可能包含上百个字节的RAW数据远超Arduino Uno的内存。解决方案有使用内存更大的板卡如Arduino Mega 2560、ESP32。数据压缩分析RAW数据发现它通常由几种固定时间长度的脉冲组成。可以设计一种简单的游程编码RLE只存储时间值和重复次数能大幅压缩数据。使用专门的红外编码芯片如PT2262/2272编解码芯片但这就脱离了Arduino编程的范畴。6.4 与舵机、电机等其他模块同时工作的干扰这是一个经典问题。舵机特别是SG90在工作时会产生巨大的电源噪声和PWM噪声严重干扰红外接收。电源隔离为红外接收头、Arduino控制部分、舵机/电机驱动部分分别供电或者使用独立的稳压模块。至少要在舵机电源入口处加一个大容量电解电容如470uF稳压。软件消抖与屏蔽在舵机动作期间暂时关闭红外接收IrReceiver.stop()动作完成后再开启IrReceiver.start()。或者在接收代码中如果检测到持续的高电平或低电平噪声非正常红外信号特征则忽略本次解码。物理隔离将红外接收头用铝箔或铜箔包裹并接地屏蔽层只留出接收窗可以有效屏蔽电磁干扰。红外遥控作为一项经典技术与Arduino结合后焕发了新的生命力。它不仅仅是复制一个遥控器更是打开了物理世界与数字世界交互的一扇低成本大门。从最初的信号时有时无到后来能稳定控制客厅的所有电器这个过程充满了调试的乐趣和解决问题的成就感。当你第一次用自己编写的代码打开空调时那种感觉是无与伦比的。希望这篇内容能帮你扫清入门路上的障碍更重要的是理解问题背后的原理这样无论遇到什么新设备、新协议你都能有自己的解决思路。