1. 项目缘起当智能家居遇上“老古董”空调去年夏天我租住的房子里那台服役超过十年的美的空调成了我智能家居改造计划里最顽固的“钉子户”。它没有Wi-Fi模块没有蓝牙只有一个孤零零的红外遥控器。每次想提前开空调都得满屋子找遥控器或者忍受回家后那半小时的闷热。市面上那些万能红外遥控插座要么协议支持不全要么延迟感人最关键的是它们剥夺了我这个技术爱好者“亲手搞定”的乐趣。于是一个想法冒了出来用我手边闲置的几块ESP8266开发板自己写个库让这台“老古董”空调接入我的Home Assistant。听起来简单不就是发个红外信号吗但真正动手后才发现从解析遥控器信号到稳定控制中间隔着一片名为“协议细节”的沼泽。市面上通用的红外库比如IRremoteESP8266功能强大但对于美的空调这种品牌定制化程度高的设备其内置的解码和发送逻辑往往不够精准导致控制失灵比如温度调了没反应或者模式乱跳。这正是我动手封装这个“ESP8266美的空调红外控制库”的直接原因。它不是一个从零造轮子的项目而是在成熟生态Arduino ESP8266之上针对特定品牌美的空调的红外协议进行深度适配、封装和优化的结果。目标是让后来者无论是想快速实现一个空调遥控网页还是想将其深度集成到开源智能家居平台中都能避开我踩过的那些坑用最少的代码、最稳定的方式控制家里那台“不听话”的美的空调。这个库的核心价值在于“精准”和“易用”。它不试图做一个包罗万象的万能遥控器而是聚焦于美的空调的红外指令集确保每一条指令的波形、时序、编码都尽可能还原原装遥控器的效果。同时它提供了高度抽象的Arduino API让你无需关心底层红外载波频率是38kHz还是40kHz也无需手动拼接那些冗长的十六进制码只需关心“设定26度制冷模式”这样的业务逻辑。2. 核心原理拆解红外遥控的“摩尔斯电码”在开始写代码之前我们必须先弄明白当我们按下空调遥控器时究竟发生了什么。你可以把红外通信想象成一种光波的“摩尔斯电码”。2.1 红外通信的基础框架空调遥控器前端有一个红外发光二极管IR LED它不会持续发光而是以某个特定频率通常是38kHz快速闪烁。这个频率称为载波频率。为什么是38kHz这是一个行业通用频率既能有效传输数据又能避开大多数日常光源如日光灯、太阳光的干扰。真正的控制信息是通过对这个38kHz的载波进行“开关”调制来传递的。也就是说我们让IR LED以38kHz的频率闪烁一段时间表示逻辑“1”或“0”然后关闭一段时间表示间隔。不同的“开-关”时间组合就构成了不同的编码协议。2.2 美的空调协议的特点美的空调的红外协议属于脉冲宽度调制PWM类型并且是非标准协议。这与常见的NEC、Sony协议有很大不同。经过对原装遥控器的抓取和分析我发现其协议结构大致如下引导码一个较长的低电平关闭载波后跟随一个较长的低电平。这个独特的头用于告诉接收端“注意有数据来了”美的的引导码时长非常固定是判断协议的关键。用户码也称为地址码用于区分不同厂家的设备。美的空调的用户码通常是固定的一个或两个字节。命令码这才是真正的控制指令比如开关、模式、温度、风速、扫风等。关键点来了美的空调的命令码通常是反码形式发送的。即发送完命令码后会紧接着发送该命令码的按位取反值。接收端会校验这两部分如果命令码 ^ 反码 0xFF则认为数据有效。这是一种简单的容错机制。结束码一个短暂的低电平脉冲标志一帧数据发送完毕。连发码当你长按遥控器按键时遥控器不会重复发送完整的数据帧而是发送一个简短的“连发码”用于告知设备“保持上一个指令”。我们的库也需要模拟这个行为以实现类似“温度连续上调”的功能。更复杂的是美的空调的状态是组合编码的。一帧数据里包含了模式、温度、风速、扫风等所有信息。这意味着当你只想把温度从26度调到27度时你需要发送的并不是一个单纯的“温度1”指令而是一帧包含了当前模式、27度、当前风速、当前扫风状态的完整数据。如果你只发送温度部分空调很可能无法识别或执行错误动作。这是很多自制控制器失败的主要原因——误以为红外指令是“增量式”的而实际上它是“全量式”的。2.3 ESP8266如何扮演“遥控器”ESP8266在这里的角色就是一个可编程的红外信号发射器。过程如下生成波形数据我们的库根据用户设定的参数温度、模式等按照上述协议规则生成一长串代表“载波开”和“载波关”时长单位通常是微秒的数组。硬件定时器调制ESP8266的CPU主频很高80MHz或160MHz直接用循环控制IO口开关无法产生精准的38kHz载波。因此我们需要借助硬件定时器中断。库内部会配置一个硬件定时器使其以76kHz2倍于38kHz的频率触发中断。在中断服务程序里我们翻转IO口电平从而产生出非常精确的38kHz方波。信号发送在需要发送“载波开”的时段我们启动这个定时器IO口就会输出38kHz的方波驱动IR LED发光。在“载波关”的时段则关闭定时器让IO口保持低电平。通过精确控制这些“开”和“关”时段的长度我们就拼接出了完整的、符合美的协议的红外指令波形。3. 环境搭建与库的集成理论说得再多不如动手接线。这一部分我会带你从零开始搭建一个可以编译和测试我们库的完整环境。3.1 硬件清单与连接你需要准备以下材料ESP8266开发板NodeMCU、Wemos D1 mini 或任何基于ESP-12F模组的板子都可以。它们通常自带USB转串口芯片方便调试。红外发射管建议使用940nm波长的这是最常见的型号。注意一个可能功率不够特别是如果你的空调距离较远3米或者有遮挡。NPN三极管如8050和电阻ESP8266的GPIO口驱动能力有限最大12mA不足以直接让IR LED发出足够强的光。我们需要用三极管做一个简单的开关电路来放大电流。杜邦线和面包板用于连接。电路连接图以NodeMCU的D2引脚为例ESP8266 (NodeMCU) D2 (GPIO4) ---[1kΩ电阻]--- 三极管基极(B) 三极管发射极(E) --- GND 三极管集电极(C) --- IR LED负极短脚 IR LED正极长脚 --- VCC (3.3V)注意务必确认你的开发板是3.3V逻辑电平。直接将5V接到ESP8266的GPIO会烧毁芯片IR LED的工作电压可以是3.3V但为了提高发射距离你也可以串联一个限流电阻后接5V如果开发板有5V引脚此时三极管集电极接5V。计算限流电阻R (5V - IR LED压降约1.2V) / 所需电流。IR LED瞬间电流可达100mA选择电阻时注意功率。3.2 软件环境配置安装Arduino IDE从官网下载并安装。建议使用较新版本如2.3.x它对ESP8266的支持更好。添加ESP8266开发板支持打开Arduino IDE进入“文件”-“首选项”。在“附加开发板管理器网址”中填入http://arduino.esp8266.com/stable/package_esp8266com_index.json点击“确定”然后进入“工具”-“开发板”-“开发板管理器”。搜索“esp8266”找到并安装“ESP8266 by ESP8266 Community”。安装过程可能需要一些时间。安装依赖库我们的美的空调库底层需要依赖一个强大的红外收发库。在“项目”-“加载库”-“管理库”中搜索并安装IRremoteESP8266。这个库由Sebastien Warin等人维护提供了非常完善的底层红外驱动和众多通用协议支持是我们项目的基石。获取并安装本库你可以从GitHub仓库下载我们的“MideaAC”库。下载后在Arduino IDE中选择“项目”-“加载库”-“添加.ZIP库”然后选择你下载的ZIP文件。安装成功后你可以在示例中找到“MideaAC”的相关例程。3.3 第一个测试程序让空调“滴”一声环境搭好了我们来点个“灯”——让空调响一声证明通信链路是通的。通常发送任何有效指令空调都会用蜂鸣器“滴”一声回应。#include Arduino.h #include MideaAC.h // 定义红外发射引脚这里使用NodeMCU的D2 (GPIO4) const uint16_t kIrLedPin 4; // 创建空调控制对象 MideaAC ac(kIrLedPin); void setup() { Serial.begin(115200); Serial.println(Midea AC IR Controller Boot...); // 初始化空调控制器 ac.begin(); // 发送一个默认的“开机”指令通常包含默认的24度制冷 // 即使空调已经是开机状态它也会“滴”一声回应 Serial.println(Sending ON command...); ac.send(); delay(5000); // 等待5秒让你能听到响声 } void loop() { // 主循环暂时空置 }将代码上传到ESP8266把红外发射管对准空调距离最好在1米内无遮挡打开串口监视器。你应该能看到“Sending ON command...”的打印信息同时空调会发出一声提示音。如果没反应请按以下步骤排查查硬件IR LED极性接反了吗三极管电路正确吗用手机摄像头对准IR LED发送指令时能看到它发出微弱的紫光手机CMOS对红外光敏感。查引脚确认代码中的kIrLedPin与实际连接的GPIO号一致。NodeMCU的D2对应GPIO4。查距离与角度红外是直线传播且易被阻挡。确保发射管直接对准空调的红外接收窗通常是一个深色的小塑料片。4. 库API详解与实战应用让空调“滴”一声只是开始。我们的库提供了完整的API让你能精细控制空调的每一个状态。4.1 核心对象与初始化库的核心是MideaAC类。初始化时你需要传入红外发射引脚号。MideaAC ac(4); // 使用GPIO4在setup()函数中调用ac.begin()进行初始化。这个函数内部会设置红外发射的底层驱动。4.2 状态设置像操作遥控器一样思考设置状态时务必记住前面提到的“全量式”编码原则。库提供了一系列set方法它们只修改内存中的状态对象并不会立即发送红外信号。// 1. 开关机 ac.on(); // 开机状态设置为开 ac.off(); // 关机 // 2. 模式设置 ac.setMode(MideaAC::MODE_COOL); // 制冷 ac.setMode(MideaAC::MODE_HEAT); // 制热 ac.setMode(MideaAC::MODE_DRY); // 除湿 ac.setMode(MideaAC::MODE_FAN); // 送风 ac.setMode(MideaAC::MODE_AUTO); // 自动 // 3. 温度设置单位摄氏度 ac.setTemp(25); // 设置为25度 // 注意不同模式温度范围不同。制冷通常在16-30度制热可能16-30或更高。 // 4. 风速设置 ac.setFan(MideaAC::FAN_AUTO); // 自动风 ac.setFan(MideaAC::FAN_LOW); // 低风 ac.setFan(MideaAC::FAN_MEDIUM); // 中风 ac.setFan(MideaAC::FAN_HIGH); // 高风 // 5. 扫风设置部分型号支持 ac.setSwingVertical(true); // 垂直扫风开 ac.setSwingVertical(false); // 垂直扫风关 ac.setSwingHorizontal(true); // 水平扫风开部分高端型号 // 6. 其他功能 ac.setEco(true); // 开启ECO节能模式 ac.setTurbo(true); // 开启强劲/快速冷热模式 ac.setLight(true); // 开启显示屏灯光若有4.3 发送指令将状态变为行动所有设置完成后你需要调用ac.send()方法。这个方法会做三件事将当前设置的所有状态模式、温度、风速等按照美的协议编码成一帧数据。驱动红外发射管将这帧数据发送出去。可选如果开启了调试会通过串口打印出这帧数据的十六进制码方便你排查问题。一个完整的控制流程示例实现“开机并设定为26度制冷、自动风”。void setCool26Auto() { ac.on(); // 1. 开机 ac.setMode(MideaAC::MODE_COOL); // 2. 设为制冷模式 ac.setTemp(26); // 3. 温度设为26度 ac.setFan(MideaAC::FAN_AUTO); // 4. 风速设为自动 // 注意我们没有设置扫风库会使用默认值通常为关闭 Serial.println(Setting to Cool 26C, Auto Fan...); ac.send(); // 5. 发送这是最关键的一步。 }4.4 状态查询与记忆库内部维护了一个状态对象。你可以随时查询当前的设置注意这不是从空调读回的而是你上次设置并发送的状态。Serial.print(Power: ); Serial.println(ac.getPower() ? ON : OFF); Serial.print(Mode: ); Serial.println(ac.getMode()); Serial.print(Temperature: ); Serial.println(ac.getTemp()); Serial.print(Fan Speed: ); Serial.println(ac.getFan());这个特性非常有用。例如你想做一个Web界面需要显示空调的当前状态虽然这个状态是控制器“认为”的而非空调实际状态但对于用户界面来说通常够用了。5. 进阶集成构建Web服务器与对接Home Assistant让ESP8266独立工作只是第一步让它联网并融入智能家居生态才是终极目标。5.1 构建一个简单的控制Web服务器我们可以利用ESP8266强大的Wi-Fi和网络功能创建一个内网Web服务器通过浏览器就能控制空调。#include ESP8266WiFi.h #include ESP8266WebServer.h #include MideaAC.h const char* ssid 你的Wi-Fi名称; const char* password 你的Wi-Fi密码; ESP8266WebServer server(80); // 在80端口创建服务器 MideaAC ac(4); void handleRoot() { // 返回一个简单的HTML控制页面 String html htmlbody; html h1美的空调控制器/h1; html pa href/onbutton开机/button/a/p; html pa href/offbutton关机/button/a/p; html pa href/cool26button制冷26度/button/a/p; html pa href/heat24button制热24度/button/a/p; html /body/html; server.send(200, text/html, html); } void handleOn() { ac.on(); ac.send(); server.send(200, text/plain, Command: ON sent); } void handleOff() { ac.off(); ac.send(); server.send(200, text/plain, Command: OFF sent); } void handleCool26() { ac.on(); ac.setMode(MideaAC::MODE_COOL); ac.setTemp(26); ac.setFan(MideaAC::FAN_AUTO); ac.send(); server.send(200, text/plain, Command: Cool 26C sent); } void setup() { Serial.begin(115200); ac.begin(); // 连接Wi-Fi WiFi.begin(ssid, password); while (WiFi.status() ! WL_CONNECTED) { delay(500); Serial.print(.); } Serial.println(); Serial.print(Connected! IP address: ); Serial.println(WiFi.localIP()); // 设置服务器路由 server.on(/, handleRoot); server.on(/on, handleOn); server.on(/off, handleOff); server.on(/cool26, handleCool26); server.begin(); Serial.println(HTTP server started); } void loop() { server.handleClient(); // 处理客户端请求 }上传代码后在串口监视器查看ESP8266获取到的IP地址在浏览器中输入这个IP你就能看到一个简单的控制面板了。5.2 对接Home Assistant实现语音与自动化控制Home Assistant (HA) 是当前最流行的开源智能家居平台。要让我们的ESP8266空调控制器被HA识别和控制通常有两种方式方式一通过MQTT推荐更灵活在ESP8266上集成PubSubClient库使其成为一个MQTT客户端。在代码中连接到你家庭网络中的MQTT服务器如Mosquitto。定义MQTT主题例如home/bedroom/ac/power。当ESP8266收到该主题下ON的消息时就执行ac.on(); ac.send();。在Home Assistant中添加MQTT集成并手动配置一个空调实体其power_command_topic指向home/bedroom/ac/power。这样HA的界面就能直接发送开关指令了。方式二模拟成ESPHome设备更简单如果你熟悉ESPHome可以借鉴其与HA的通信协议API。但更简单的方法是直接使用ESPHome固件。幸运的是IRremoteESP8266库的作者已经为许多空调品牌包括美的编写了ESPHome组件。你可以在ESPHome的配置文件中直接使用climate:组件并指定品牌为midea然后刷入ESP8266。这样ESP8266启动后会自动被HA发现无需任何复杂配置。这是对用户最友好的方式但可定制性稍逊于自己写代码。5.3 状态反馈的“伪实现”红外是单向通信ESP8266无法知道空调是否真的执行了指令。但在智能家居场景中我们往往需要状态反馈。一个实用的“伪实现”方案是状态缓存在ESP8266的内存中严格缓存最后一次成功发送的完整状态。超时重置当HA或Web界面查询状态时返回这个缓存的状态。同时可以设置一个“心跳”或“状态过期”机制。例如如果超过24小时没有发送过任何指令则自动将缓存状态设为“关机”因为空调很可能已经被物理遥控器关闭了。人工同步在Web界面或HA里提供一个“同步”按钮。当用户用物理遥控器操作后可以点击这个按钮手动将ESP8266的缓存状态更新为遥控器操作后的预期状态例如如果遥控器关了空调就点一下“同步关机”。6. 深度排坑与优化指南在实际部署中你会遇到各种各样的问题。下面是我踩过坑后总结出的经验。6.1 信号发送失败或不稳定症状偶尔能控制经常失灵或者近距离可以远一点就不行。排查与解决供电不足这是最常见的原因。ESP8266在发射Wi-Fi信号时峰值电流可达200mA红外发射也需要100mA左右。使用电脑USB口或劣质手机充电器供电可能力不从心。务必使用输出能力在5V/2A以上的电源适配器并确保USB线质量良好。发射管功率与数量单个5mm IR LED的发射功率有限。可以并联2-3个IR LED共用限流电阻来增强信号。或者直接购买专用的“红外发射模块”它通常集成了三极管放大电路和多个LED。发射角度与反射红外光类似手电筒有指向性。确保发射管直接对准空调接收窗。如果无法对准可以尝试利用墙壁、天花板的漫反射但这样会大幅衰减信号。有时稍微偏一个角度反而因为反射效果更好。环境光干扰强烈的日光或某些LED灯可能包含红外光谱造成干扰。尝试在夜间或拉上窗帘测试。代码时序问题确保在发送红外信号ac.send()时没有其他高优先级的中断如Wi-Fi事件长时间阻塞CPU。ac.send()函数本身是阻塞的发送一帧数据可能需要几十毫秒在此期间最好关闭其他中断。6.2 指令错乱空调执行了非预期操作症状发送制冷指令空调却开了制热或者温度调节紊乱。排查与解决协议版本不匹配美的空调不同年份、不同系列的型号其红外协议可能有细微差别。我们的库通常基于某几个主流型号的协议。如果你的空调控制异常首先需要抓取原装遥控器的红外码进行比对。抓取方法使用一个红外接收头VS1838B连接ESP8266的另一个GPIO利用IRremoteESP8266库的示例代码IRrecvDumpV2将遥控器对准接收头按键从串口监视器读取完整的原始时序和解析出的编码。将这个编码与库中生成的编码对比找出差异点如引导码时长、用户码、校验方式。数据位顺序有些协议是高位在前MSB first有些是低位在前LSB first。如果顺序反了解码出来的命令码就完全不对。这需要在库的编码函数中调整。“全量发送”原则再次强调确保每次发送的都是完整状态。不要只调用ac.setTemp()就ac.send()而应该先设置好所有参数至少包括开关、模式、温度、风速。6.3 与Wi-Fi功能的冲突ESP8266的Wi-Fi和红外发射都依赖硬件定时器和中断不当使用会导致系统不稳定甚至崩溃。经验红外发送函数ac.send()内部会短暂关闭所有中断以确保波形时序精确。这会导致Wi-Fi通信短暂中断。在频繁发送红外指令如做一个温度渐变功能时可能会引起Wi-Fi断连。解决方案是避免在高频循环中发送红外指令如果必须则每次发送后延迟至少100ms并加入Wi-Fi连接状态检查与重连逻辑。6.4 功耗与长期运行如果你希望控制器7x24小时运行功耗和稳定性就很重要。降低功耗在loop()函数中如果没有任务使用delay()或yield()让CPU休眠。更进阶的做法是使用深度睡眠Deep Sleep但这就需要外部信号如按钮来唤醒了不适合需要随时响应网络请求的场景。看门狗与异常重启ESP8266内置看门狗但复杂的逻辑可能使其触发。在loop()中定期调用ESP.wdtFeed()喂狗。对于长期运行的项目可以在代码中监控关键指标如Wi-Fi连接状态如果异常超过一定时间主动调用ESP.restart()重启设备这是一个简单粗暴但有效的恢复手段。7. 从项目到产品封装与扩展思考当你成功让一个ESP8266控制了一台空调后这个项目就可以告一段落了吗对于爱好者来说是的。但如果你想把它做得更“产品化”或者应用于更多场景这里还有一些思路。7.1 硬件封装与美化选择合适的外壳3D打印一个小巧的外壳将ESP8266、三极管、电阻和IR LED集成进去只留出USB供电口和一个小孔让红外光透出。这能有效保护电路也更美观。供电方案长期使用不建议一直插着USB线。可以找一个旧的手机充电器搭配一个美观的USB电源座固定在插座上。或者如果空调插座附近有空间甚至可以考虑设计一个“空调插座伴侣”直接取电。多路发射如果你需要控制客厅和卧室的多台空调可以一个ESP8266连接多个IR LED分别指向不同方向用代码控制不同的LED点亮实现“一控多”。注意GPIO驱动能力可能需要更多的三极管放大电路。7.2 软件功能扩展定时与预约利用ESP8266的RTC实时时钟或从网络获取时间NTP实现“每天晚7点自动打开空调”、“运行3小时后自动关闭”等复杂定时任务。温度联动接入一个DHT11或DHT22温湿度传感器。编写逻辑实现“当室内温度高于28度且有人在家时自动开启制冷到26度”的简单自动化。这需要结合人体传感器如红外热释电或地理围栏通过手机APP判断是否到家来实现。语音控制集成除了Home Assistant你还可以让ESP8266直接对接百度DuerOS、阿里云IoT等平台的语音技能实现“小度小度打开卧室空调”这样的直接语音控制。这需要在这些云平台创建产品并让ESP8266通过SDK接入复杂度较高但体验最好。协议学习与分享为你的库增加一个“学习模式”。通过红外接收头记录下未知品牌或型号空调的遥控器编码并将其保存到文件系统如SPIFFS或上传到服务器。甚至可以构建一个社区共享的协议数据库让用户贡献自己家的空调编码。7.3 应对固件更新的挑战一个库发布后随着Arduino核心库、IRremoteESP8266依赖库的更新可能会出现兼容性问题。版本锁定在你的库的library.properties文件和文档中明确声明所依赖的IRremoteESP8266库的版本号例如dependsIRremoteESP82662.8.5。这能最大程度避免用户因自动更新到不兼容的新版本而无法使用。提供清晰的错误信息在代码中对于可能因版本不匹配导致的编译错误或运行时错误提供尽可能清晰的提示信息引导用户检查依赖库版本。回过头看这个项目始于一个简单的个人需求但深入下去却涉及了硬件电路、通信协议、嵌入式编程、网络通信乃至产品化思维等多个层面。它完美地诠释了“软硬结合”的魅力。最大的收获不是最终控制了空调而是在解决一个个具体问题信号弱、指令错乱、Wi-Fi冲突的过程中对底层原理的理解变得无比透彻。现在每当我用手机App远程打开空调感受到凉风袭来的那一刻都会想起那个对着示波器其实是串口打印的波形数组和红外接收头调试的下午。技术带来的便利最终都源于这些看似枯燥的细节被一一攻克。如果你也有一台老空调不妨试试这个过程本身就是一种乐趣。
ESP8266红外控制美的空调:从协议解析到Home Assistant集成
1. 项目缘起当智能家居遇上“老古董”空调去年夏天我租住的房子里那台服役超过十年的美的空调成了我智能家居改造计划里最顽固的“钉子户”。它没有Wi-Fi模块没有蓝牙只有一个孤零零的红外遥控器。每次想提前开空调都得满屋子找遥控器或者忍受回家后那半小时的闷热。市面上那些万能红外遥控插座要么协议支持不全要么延迟感人最关键的是它们剥夺了我这个技术爱好者“亲手搞定”的乐趣。于是一个想法冒了出来用我手边闲置的几块ESP8266开发板自己写个库让这台“老古董”空调接入我的Home Assistant。听起来简单不就是发个红外信号吗但真正动手后才发现从解析遥控器信号到稳定控制中间隔着一片名为“协议细节”的沼泽。市面上通用的红外库比如IRremoteESP8266功能强大但对于美的空调这种品牌定制化程度高的设备其内置的解码和发送逻辑往往不够精准导致控制失灵比如温度调了没反应或者模式乱跳。这正是我动手封装这个“ESP8266美的空调红外控制库”的直接原因。它不是一个从零造轮子的项目而是在成熟生态Arduino ESP8266之上针对特定品牌美的空调的红外协议进行深度适配、封装和优化的结果。目标是让后来者无论是想快速实现一个空调遥控网页还是想将其深度集成到开源智能家居平台中都能避开我踩过的那些坑用最少的代码、最稳定的方式控制家里那台“不听话”的美的空调。这个库的核心价值在于“精准”和“易用”。它不试图做一个包罗万象的万能遥控器而是聚焦于美的空调的红外指令集确保每一条指令的波形、时序、编码都尽可能还原原装遥控器的效果。同时它提供了高度抽象的Arduino API让你无需关心底层红外载波频率是38kHz还是40kHz也无需手动拼接那些冗长的十六进制码只需关心“设定26度制冷模式”这样的业务逻辑。2. 核心原理拆解红外遥控的“摩尔斯电码”在开始写代码之前我们必须先弄明白当我们按下空调遥控器时究竟发生了什么。你可以把红外通信想象成一种光波的“摩尔斯电码”。2.1 红外通信的基础框架空调遥控器前端有一个红外发光二极管IR LED它不会持续发光而是以某个特定频率通常是38kHz快速闪烁。这个频率称为载波频率。为什么是38kHz这是一个行业通用频率既能有效传输数据又能避开大多数日常光源如日光灯、太阳光的干扰。真正的控制信息是通过对这个38kHz的载波进行“开关”调制来传递的。也就是说我们让IR LED以38kHz的频率闪烁一段时间表示逻辑“1”或“0”然后关闭一段时间表示间隔。不同的“开-关”时间组合就构成了不同的编码协议。2.2 美的空调协议的特点美的空调的红外协议属于脉冲宽度调制PWM类型并且是非标准协议。这与常见的NEC、Sony协议有很大不同。经过对原装遥控器的抓取和分析我发现其协议结构大致如下引导码一个较长的低电平关闭载波后跟随一个较长的低电平。这个独特的头用于告诉接收端“注意有数据来了”美的的引导码时长非常固定是判断协议的关键。用户码也称为地址码用于区分不同厂家的设备。美的空调的用户码通常是固定的一个或两个字节。命令码这才是真正的控制指令比如开关、模式、温度、风速、扫风等。关键点来了美的空调的命令码通常是反码形式发送的。即发送完命令码后会紧接着发送该命令码的按位取反值。接收端会校验这两部分如果命令码 ^ 反码 0xFF则认为数据有效。这是一种简单的容错机制。结束码一个短暂的低电平脉冲标志一帧数据发送完毕。连发码当你长按遥控器按键时遥控器不会重复发送完整的数据帧而是发送一个简短的“连发码”用于告知设备“保持上一个指令”。我们的库也需要模拟这个行为以实现类似“温度连续上调”的功能。更复杂的是美的空调的状态是组合编码的。一帧数据里包含了模式、温度、风速、扫风等所有信息。这意味着当你只想把温度从26度调到27度时你需要发送的并不是一个单纯的“温度1”指令而是一帧包含了当前模式、27度、当前风速、当前扫风状态的完整数据。如果你只发送温度部分空调很可能无法识别或执行错误动作。这是很多自制控制器失败的主要原因——误以为红外指令是“增量式”的而实际上它是“全量式”的。2.3 ESP8266如何扮演“遥控器”ESP8266在这里的角色就是一个可编程的红外信号发射器。过程如下生成波形数据我们的库根据用户设定的参数温度、模式等按照上述协议规则生成一长串代表“载波开”和“载波关”时长单位通常是微秒的数组。硬件定时器调制ESP8266的CPU主频很高80MHz或160MHz直接用循环控制IO口开关无法产生精准的38kHz载波。因此我们需要借助硬件定时器中断。库内部会配置一个硬件定时器使其以76kHz2倍于38kHz的频率触发中断。在中断服务程序里我们翻转IO口电平从而产生出非常精确的38kHz方波。信号发送在需要发送“载波开”的时段我们启动这个定时器IO口就会输出38kHz的方波驱动IR LED发光。在“载波关”的时段则关闭定时器让IO口保持低电平。通过精确控制这些“开”和“关”时段的长度我们就拼接出了完整的、符合美的协议的红外指令波形。3. 环境搭建与库的集成理论说得再多不如动手接线。这一部分我会带你从零开始搭建一个可以编译和测试我们库的完整环境。3.1 硬件清单与连接你需要准备以下材料ESP8266开发板NodeMCU、Wemos D1 mini 或任何基于ESP-12F模组的板子都可以。它们通常自带USB转串口芯片方便调试。红外发射管建议使用940nm波长的这是最常见的型号。注意一个可能功率不够特别是如果你的空调距离较远3米或者有遮挡。NPN三极管如8050和电阻ESP8266的GPIO口驱动能力有限最大12mA不足以直接让IR LED发出足够强的光。我们需要用三极管做一个简单的开关电路来放大电流。杜邦线和面包板用于连接。电路连接图以NodeMCU的D2引脚为例ESP8266 (NodeMCU) D2 (GPIO4) ---[1kΩ电阻]--- 三极管基极(B) 三极管发射极(E) --- GND 三极管集电极(C) --- IR LED负极短脚 IR LED正极长脚 --- VCC (3.3V)注意务必确认你的开发板是3.3V逻辑电平。直接将5V接到ESP8266的GPIO会烧毁芯片IR LED的工作电压可以是3.3V但为了提高发射距离你也可以串联一个限流电阻后接5V如果开发板有5V引脚此时三极管集电极接5V。计算限流电阻R (5V - IR LED压降约1.2V) / 所需电流。IR LED瞬间电流可达100mA选择电阻时注意功率。3.2 软件环境配置安装Arduino IDE从官网下载并安装。建议使用较新版本如2.3.x它对ESP8266的支持更好。添加ESP8266开发板支持打开Arduino IDE进入“文件”-“首选项”。在“附加开发板管理器网址”中填入http://arduino.esp8266.com/stable/package_esp8266com_index.json点击“确定”然后进入“工具”-“开发板”-“开发板管理器”。搜索“esp8266”找到并安装“ESP8266 by ESP8266 Community”。安装过程可能需要一些时间。安装依赖库我们的美的空调库底层需要依赖一个强大的红外收发库。在“项目”-“加载库”-“管理库”中搜索并安装IRremoteESP8266。这个库由Sebastien Warin等人维护提供了非常完善的底层红外驱动和众多通用协议支持是我们项目的基石。获取并安装本库你可以从GitHub仓库下载我们的“MideaAC”库。下载后在Arduino IDE中选择“项目”-“加载库”-“添加.ZIP库”然后选择你下载的ZIP文件。安装成功后你可以在示例中找到“MideaAC”的相关例程。3.3 第一个测试程序让空调“滴”一声环境搭好了我们来点个“灯”——让空调响一声证明通信链路是通的。通常发送任何有效指令空调都会用蜂鸣器“滴”一声回应。#include Arduino.h #include MideaAC.h // 定义红外发射引脚这里使用NodeMCU的D2 (GPIO4) const uint16_t kIrLedPin 4; // 创建空调控制对象 MideaAC ac(kIrLedPin); void setup() { Serial.begin(115200); Serial.println(Midea AC IR Controller Boot...); // 初始化空调控制器 ac.begin(); // 发送一个默认的“开机”指令通常包含默认的24度制冷 // 即使空调已经是开机状态它也会“滴”一声回应 Serial.println(Sending ON command...); ac.send(); delay(5000); // 等待5秒让你能听到响声 } void loop() { // 主循环暂时空置 }将代码上传到ESP8266把红外发射管对准空调距离最好在1米内无遮挡打开串口监视器。你应该能看到“Sending ON command...”的打印信息同时空调会发出一声提示音。如果没反应请按以下步骤排查查硬件IR LED极性接反了吗三极管电路正确吗用手机摄像头对准IR LED发送指令时能看到它发出微弱的紫光手机CMOS对红外光敏感。查引脚确认代码中的kIrLedPin与实际连接的GPIO号一致。NodeMCU的D2对应GPIO4。查距离与角度红外是直线传播且易被阻挡。确保发射管直接对准空调的红外接收窗通常是一个深色的小塑料片。4. 库API详解与实战应用让空调“滴”一声只是开始。我们的库提供了完整的API让你能精细控制空调的每一个状态。4.1 核心对象与初始化库的核心是MideaAC类。初始化时你需要传入红外发射引脚号。MideaAC ac(4); // 使用GPIO4在setup()函数中调用ac.begin()进行初始化。这个函数内部会设置红外发射的底层驱动。4.2 状态设置像操作遥控器一样思考设置状态时务必记住前面提到的“全量式”编码原则。库提供了一系列set方法它们只修改内存中的状态对象并不会立即发送红外信号。// 1. 开关机 ac.on(); // 开机状态设置为开 ac.off(); // 关机 // 2. 模式设置 ac.setMode(MideaAC::MODE_COOL); // 制冷 ac.setMode(MideaAC::MODE_HEAT); // 制热 ac.setMode(MideaAC::MODE_DRY); // 除湿 ac.setMode(MideaAC::MODE_FAN); // 送风 ac.setMode(MideaAC::MODE_AUTO); // 自动 // 3. 温度设置单位摄氏度 ac.setTemp(25); // 设置为25度 // 注意不同模式温度范围不同。制冷通常在16-30度制热可能16-30或更高。 // 4. 风速设置 ac.setFan(MideaAC::FAN_AUTO); // 自动风 ac.setFan(MideaAC::FAN_LOW); // 低风 ac.setFan(MideaAC::FAN_MEDIUM); // 中风 ac.setFan(MideaAC::FAN_HIGH); // 高风 // 5. 扫风设置部分型号支持 ac.setSwingVertical(true); // 垂直扫风开 ac.setSwingVertical(false); // 垂直扫风关 ac.setSwingHorizontal(true); // 水平扫风开部分高端型号 // 6. 其他功能 ac.setEco(true); // 开启ECO节能模式 ac.setTurbo(true); // 开启强劲/快速冷热模式 ac.setLight(true); // 开启显示屏灯光若有4.3 发送指令将状态变为行动所有设置完成后你需要调用ac.send()方法。这个方法会做三件事将当前设置的所有状态模式、温度、风速等按照美的协议编码成一帧数据。驱动红外发射管将这帧数据发送出去。可选如果开启了调试会通过串口打印出这帧数据的十六进制码方便你排查问题。一个完整的控制流程示例实现“开机并设定为26度制冷、自动风”。void setCool26Auto() { ac.on(); // 1. 开机 ac.setMode(MideaAC::MODE_COOL); // 2. 设为制冷模式 ac.setTemp(26); // 3. 温度设为26度 ac.setFan(MideaAC::FAN_AUTO); // 4. 风速设为自动 // 注意我们没有设置扫风库会使用默认值通常为关闭 Serial.println(Setting to Cool 26C, Auto Fan...); ac.send(); // 5. 发送这是最关键的一步。 }4.4 状态查询与记忆库内部维护了一个状态对象。你可以随时查询当前的设置注意这不是从空调读回的而是你上次设置并发送的状态。Serial.print(Power: ); Serial.println(ac.getPower() ? ON : OFF); Serial.print(Mode: ); Serial.println(ac.getMode()); Serial.print(Temperature: ); Serial.println(ac.getTemp()); Serial.print(Fan Speed: ); Serial.println(ac.getFan());这个特性非常有用。例如你想做一个Web界面需要显示空调的当前状态虽然这个状态是控制器“认为”的而非空调实际状态但对于用户界面来说通常够用了。5. 进阶集成构建Web服务器与对接Home Assistant让ESP8266独立工作只是第一步让它联网并融入智能家居生态才是终极目标。5.1 构建一个简单的控制Web服务器我们可以利用ESP8266强大的Wi-Fi和网络功能创建一个内网Web服务器通过浏览器就能控制空调。#include ESP8266WiFi.h #include ESP8266WebServer.h #include MideaAC.h const char* ssid 你的Wi-Fi名称; const char* password 你的Wi-Fi密码; ESP8266WebServer server(80); // 在80端口创建服务器 MideaAC ac(4); void handleRoot() { // 返回一个简单的HTML控制页面 String html htmlbody; html h1美的空调控制器/h1; html pa href/onbutton开机/button/a/p; html pa href/offbutton关机/button/a/p; html pa href/cool26button制冷26度/button/a/p; html pa href/heat24button制热24度/button/a/p; html /body/html; server.send(200, text/html, html); } void handleOn() { ac.on(); ac.send(); server.send(200, text/plain, Command: ON sent); } void handleOff() { ac.off(); ac.send(); server.send(200, text/plain, Command: OFF sent); } void handleCool26() { ac.on(); ac.setMode(MideaAC::MODE_COOL); ac.setTemp(26); ac.setFan(MideaAC::FAN_AUTO); ac.send(); server.send(200, text/plain, Command: Cool 26C sent); } void setup() { Serial.begin(115200); ac.begin(); // 连接Wi-Fi WiFi.begin(ssid, password); while (WiFi.status() ! WL_CONNECTED) { delay(500); Serial.print(.); } Serial.println(); Serial.print(Connected! IP address: ); Serial.println(WiFi.localIP()); // 设置服务器路由 server.on(/, handleRoot); server.on(/on, handleOn); server.on(/off, handleOff); server.on(/cool26, handleCool26); server.begin(); Serial.println(HTTP server started); } void loop() { server.handleClient(); // 处理客户端请求 }上传代码后在串口监视器查看ESP8266获取到的IP地址在浏览器中输入这个IP你就能看到一个简单的控制面板了。5.2 对接Home Assistant实现语音与自动化控制Home Assistant (HA) 是当前最流行的开源智能家居平台。要让我们的ESP8266空调控制器被HA识别和控制通常有两种方式方式一通过MQTT推荐更灵活在ESP8266上集成PubSubClient库使其成为一个MQTT客户端。在代码中连接到你家庭网络中的MQTT服务器如Mosquitto。定义MQTT主题例如home/bedroom/ac/power。当ESP8266收到该主题下ON的消息时就执行ac.on(); ac.send();。在Home Assistant中添加MQTT集成并手动配置一个空调实体其power_command_topic指向home/bedroom/ac/power。这样HA的界面就能直接发送开关指令了。方式二模拟成ESPHome设备更简单如果你熟悉ESPHome可以借鉴其与HA的通信协议API。但更简单的方法是直接使用ESPHome固件。幸运的是IRremoteESP8266库的作者已经为许多空调品牌包括美的编写了ESPHome组件。你可以在ESPHome的配置文件中直接使用climate:组件并指定品牌为midea然后刷入ESP8266。这样ESP8266启动后会自动被HA发现无需任何复杂配置。这是对用户最友好的方式但可定制性稍逊于自己写代码。5.3 状态反馈的“伪实现”红外是单向通信ESP8266无法知道空调是否真的执行了指令。但在智能家居场景中我们往往需要状态反馈。一个实用的“伪实现”方案是状态缓存在ESP8266的内存中严格缓存最后一次成功发送的完整状态。超时重置当HA或Web界面查询状态时返回这个缓存的状态。同时可以设置一个“心跳”或“状态过期”机制。例如如果超过24小时没有发送过任何指令则自动将缓存状态设为“关机”因为空调很可能已经被物理遥控器关闭了。人工同步在Web界面或HA里提供一个“同步”按钮。当用户用物理遥控器操作后可以点击这个按钮手动将ESP8266的缓存状态更新为遥控器操作后的预期状态例如如果遥控器关了空调就点一下“同步关机”。6. 深度排坑与优化指南在实际部署中你会遇到各种各样的问题。下面是我踩过坑后总结出的经验。6.1 信号发送失败或不稳定症状偶尔能控制经常失灵或者近距离可以远一点就不行。排查与解决供电不足这是最常见的原因。ESP8266在发射Wi-Fi信号时峰值电流可达200mA红外发射也需要100mA左右。使用电脑USB口或劣质手机充电器供电可能力不从心。务必使用输出能力在5V/2A以上的电源适配器并确保USB线质量良好。发射管功率与数量单个5mm IR LED的发射功率有限。可以并联2-3个IR LED共用限流电阻来增强信号。或者直接购买专用的“红外发射模块”它通常集成了三极管放大电路和多个LED。发射角度与反射红外光类似手电筒有指向性。确保发射管直接对准空调接收窗。如果无法对准可以尝试利用墙壁、天花板的漫反射但这样会大幅衰减信号。有时稍微偏一个角度反而因为反射效果更好。环境光干扰强烈的日光或某些LED灯可能包含红外光谱造成干扰。尝试在夜间或拉上窗帘测试。代码时序问题确保在发送红外信号ac.send()时没有其他高优先级的中断如Wi-Fi事件长时间阻塞CPU。ac.send()函数本身是阻塞的发送一帧数据可能需要几十毫秒在此期间最好关闭其他中断。6.2 指令错乱空调执行了非预期操作症状发送制冷指令空调却开了制热或者温度调节紊乱。排查与解决协议版本不匹配美的空调不同年份、不同系列的型号其红外协议可能有细微差别。我们的库通常基于某几个主流型号的协议。如果你的空调控制异常首先需要抓取原装遥控器的红外码进行比对。抓取方法使用一个红外接收头VS1838B连接ESP8266的另一个GPIO利用IRremoteESP8266库的示例代码IRrecvDumpV2将遥控器对准接收头按键从串口监视器读取完整的原始时序和解析出的编码。将这个编码与库中生成的编码对比找出差异点如引导码时长、用户码、校验方式。数据位顺序有些协议是高位在前MSB first有些是低位在前LSB first。如果顺序反了解码出来的命令码就完全不对。这需要在库的编码函数中调整。“全量发送”原则再次强调确保每次发送的都是完整状态。不要只调用ac.setTemp()就ac.send()而应该先设置好所有参数至少包括开关、模式、温度、风速。6.3 与Wi-Fi功能的冲突ESP8266的Wi-Fi和红外发射都依赖硬件定时器和中断不当使用会导致系统不稳定甚至崩溃。经验红外发送函数ac.send()内部会短暂关闭所有中断以确保波形时序精确。这会导致Wi-Fi通信短暂中断。在频繁发送红外指令如做一个温度渐变功能时可能会引起Wi-Fi断连。解决方案是避免在高频循环中发送红外指令如果必须则每次发送后延迟至少100ms并加入Wi-Fi连接状态检查与重连逻辑。6.4 功耗与长期运行如果你希望控制器7x24小时运行功耗和稳定性就很重要。降低功耗在loop()函数中如果没有任务使用delay()或yield()让CPU休眠。更进阶的做法是使用深度睡眠Deep Sleep但这就需要外部信号如按钮来唤醒了不适合需要随时响应网络请求的场景。看门狗与异常重启ESP8266内置看门狗但复杂的逻辑可能使其触发。在loop()中定期调用ESP.wdtFeed()喂狗。对于长期运行的项目可以在代码中监控关键指标如Wi-Fi连接状态如果异常超过一定时间主动调用ESP.restart()重启设备这是一个简单粗暴但有效的恢复手段。7. 从项目到产品封装与扩展思考当你成功让一个ESP8266控制了一台空调后这个项目就可以告一段落了吗对于爱好者来说是的。但如果你想把它做得更“产品化”或者应用于更多场景这里还有一些思路。7.1 硬件封装与美化选择合适的外壳3D打印一个小巧的外壳将ESP8266、三极管、电阻和IR LED集成进去只留出USB供电口和一个小孔让红外光透出。这能有效保护电路也更美观。供电方案长期使用不建议一直插着USB线。可以找一个旧的手机充电器搭配一个美观的USB电源座固定在插座上。或者如果空调插座附近有空间甚至可以考虑设计一个“空调插座伴侣”直接取电。多路发射如果你需要控制客厅和卧室的多台空调可以一个ESP8266连接多个IR LED分别指向不同方向用代码控制不同的LED点亮实现“一控多”。注意GPIO驱动能力可能需要更多的三极管放大电路。7.2 软件功能扩展定时与预约利用ESP8266的RTC实时时钟或从网络获取时间NTP实现“每天晚7点自动打开空调”、“运行3小时后自动关闭”等复杂定时任务。温度联动接入一个DHT11或DHT22温湿度传感器。编写逻辑实现“当室内温度高于28度且有人在家时自动开启制冷到26度”的简单自动化。这需要结合人体传感器如红外热释电或地理围栏通过手机APP判断是否到家来实现。语音控制集成除了Home Assistant你还可以让ESP8266直接对接百度DuerOS、阿里云IoT等平台的语音技能实现“小度小度打开卧室空调”这样的直接语音控制。这需要在这些云平台创建产品并让ESP8266通过SDK接入复杂度较高但体验最好。协议学习与分享为你的库增加一个“学习模式”。通过红外接收头记录下未知品牌或型号空调的遥控器编码并将其保存到文件系统如SPIFFS或上传到服务器。甚至可以构建一个社区共享的协议数据库让用户贡献自己家的空调编码。7.3 应对固件更新的挑战一个库发布后随着Arduino核心库、IRremoteESP8266依赖库的更新可能会出现兼容性问题。版本锁定在你的库的library.properties文件和文档中明确声明所依赖的IRremoteESP8266库的版本号例如dependsIRremoteESP82662.8.5。这能最大程度避免用户因自动更新到不兼容的新版本而无法使用。提供清晰的错误信息在代码中对于可能因版本不匹配导致的编译错误或运行时错误提供尽可能清晰的提示信息引导用户检查依赖库版本。回过头看这个项目始于一个简单的个人需求但深入下去却涉及了硬件电路、通信协议、嵌入式编程、网络通信乃至产品化思维等多个层面。它完美地诠释了“软硬结合”的魅力。最大的收获不是最终控制了空调而是在解决一个个具体问题信号弱、指令错乱、Wi-Fi冲突的过程中对底层原理的理解变得无比透彻。现在每当我用手机App远程打开空调感受到凉风袭来的那一刻都会想起那个对着示波器其实是串口打印的波形数组和红外接收头调试的下午。技术带来的便利最终都源于这些看似枯燥的细节被一一攻克。如果你也有一台老空调不妨试试这个过程本身就是一种乐趣。