宿舍的空调遥控器又找不到了。
这已经是这学期第三次发生这种事。广州的天气即使到了十一月,中午依然有些闷热。室友们翻遍了桌面的杂物堆和床铺的缝隙,一无所获。作为大四学生,大家的时间基本都消磨在实习、秋招或者毕业设计的开题报告里,生活里这些细碎的麻烦往往会被暂时搁置,最后变成一种将就。但我看着墙上那台毫无动静的空调,觉得这种将就不应该成为常态。
既然找不到遥控器,那就自己做一个,而且要能用手机控制。这是我当时脑子里冒出来的唯一念头。
作为软件工程专业的学生,我平时打交道的多是代码、服务器和数据库。虽然在过去的三年里拿过两次国奖,专业课成绩也维持在4.02,但那些大多停留在纯软件的范畴。面对硬件,我其实是个初学者。这次算是一次打破专业边界的尝试。
我花了一晚上的时间在淘宝上挑选零件。核心主控选了 ESP8266 开发板,它自带 WiFi 模块,价格便宜,非常适合做这种轻量级的物联网原型。除此之外,还需要红外发射 LED、用来学习原有遥控器信号的红外接收头,以及配套的面包板和杜邦线。所有的元件加起来,加上邮费,总成本不到二十块钱。
等待快递的两天里,我大致构思了软件的架构。ESP8266 性能有限,但跑一个基础的 Web Server 绰绰有余。只要让开发板连上宿舍的路由器,手机浏览器访问它的局域网 IP,就能加载出一个控制面板。点击网页上的按钮,ESP8266 接收到请求后,再通过 GPIO 引脚驱动红外发射管,把对应的信号发送给空调。逻辑听起来非常顺理成章。
但实际动手后,我才发现软硬件结合的世界里,到处都是纯软件思维容易忽略的坑。
第一个坑出现在红外信号的录制上。我最初的设想很简单:把红外接收头接到开发板上,拿借来的隔壁宿舍同款遥控器对着它按一下,把读到的代码记录下来,之后原样发射出去就行了。对于电视遥控器,这个逻辑是成立的,因为电视遥控器每个按键发送的都是一个独立的简短指令,比如“音量加”或者“频道减”。
但我低估了空调遥控器的复杂性。
当我把接收头连好,跑起 IRrecvDumpV2 这个示例程序,按下空调遥控器的开机键时,串口监视器里吐出了一大串长得令人发指的数字数组。我查阅了一些资料才明白,空调遥控器是没有“状态记忆”的。你按下的每一次操作,遥控器发送的都不是单一指令,而是一个包含当前所有状态的完整数据包。里面打包了开关机状态、设定温度、风速、运行模式甚至扫风角度。每次按键,它都在向空调发送一份完整的“配置清单”。
这就意味着,如果我要像市面上的智能家居那样,在网页上随意调节温度和模式,我必须逆向解析出这台空调红外协议的每一位代表什么,或者引入极其庞大的红外码库。这显然超出了一个周末原型探索的预期。
为了让项目能快速跑起来,我决定做个妥协。我不再尝试解析协议,而是采用“笨办法”——裸数据录制。我用遥控器设定好几个我们宿舍最常用的状态:26度制冷自动风、27度睡眠模式、以及关机。然后分别记录下这几个状态对应的 Raw Data 数组。虽然不够智能,但对于解决“开不了空调”这个问题,已经足够了。
第二个坑是硬件层面的。代码写好后,我尝试发送信号。
#include <ESP8266WebServer.h>
#include <IRsend.h>
ESP8266WebServer server(80);
IRsend irsend(4); // GPIO4
void handleOn() {
irsend.sendRaw(powerOnSignal, len, 38);
server.send(200, "text/plain", "AC ON");
}
代码逻辑很清晰,sendRaw 函数负责把录制好的数组以 38kHz 的载波频率发送出去。我满怀期待地用手机点击了网页上的“开启”按钮,网页返回了 "AC ON",但空调毫无反应。
我反复检查了杜邦线的连接,确认 GPIO4 引脚没有插错。软件的 Debug 我很在行,但面对一块物理电路板,我一时有些茫然。后来在查阅电子论坛时,我学到了一个硬件调试的土办法:用手机摄像头对准红外发射管。因为手机摄像头的 CMOS 传感器能捕捉到人眼看不见的红外光。
我照做了。再次点击发送时,我在手机屏幕里看到红外 LED 确实闪烁了一下微弱的紫光。信号发出了,但为什么空调不认。
继续深挖资料后,我意识到了问题所在。ESP8266 的 GPIO 引脚输出电压只有 3.3V,且能提供的电流非常小。我把红外 LED 直接串联在引脚上,导致它的发射功率极低,有效距离可能只有不到二十厘米。正常的做法应该是在电路上加一个三极管作为开关,用 5V 电源来驱动发射管,以此放大信号。
但手头并没有三极管。作为初次尝试的原型,我选择了最简单粗暴的物理解决方案:我拿了一根很长的 USB 数据线,把整个面包板举高,直接贴在了空调的红外接收窗旁边。再次点击手机屏幕,“滴”的一声清脆响声,空调的导风板缓缓打开,冷风吹了出来。那一刻,看着简陋的面包板和凌乱的跳线,我感受到了一种与纯粹写代码完全不同的成就感。那是数字世界干涉现实世界的真实触感。
至于 Web 界面,对我来说反而成了最轻松的部分。我手写了一个简单的 HTML 页面,加了一些 CSS 样式,让它看起来像一个现代化的控制面板,有圆角按钮和状态指示。为了避免在 C++ 代码里拼接冗长的 HTML 字符串,我本来想用 SPIFFS 文件系统把网页存进开发板的闪存里,但考虑到目前的控制逻辑还比较简单,最终还是先用 PROGMEM 把它以常量字符串的形式写死在了代码里。这也是原型开发的一种取舍,先让它跑起来,优化是下一步的事情。
现在的宿舍里,只要连着同一个 WiFi,任何人打开那个特定的本地 IP,就能控制空调。它还不是一个完美的产品,发射距离短得可怜,功能也只有预设的几个状态,但它确确实实解决了当下最迫切的问题。
我把这个项目的早期代码整理了一下,开源在了 GitHub 上,仓库名叫 esp-media-ir。回头看这次折腾的过程,从选型、等快递、接线、录制信号到最终调试,踩了不少坑,但也让我摸到了硬件编程的边缘。
程序员解决问题的方式可能就是这样。遥控器找不到,那就自己做一个。哪怕它一开始只是一堆插在面包板上的廉价元件,但只要能“滴”的那一声启动空调,这个探索的过程就是有价值的。