1. 项目概述当电磁炉与抽油烟机“对话”厨房里的自动化听起来像是未来智能家居的专属但其实很多乐趣和便利就藏在身边已有的设备里。我最近给家里的厨房换上了一台新的电磁炉在翻阅说明书时偶然发现了一个名为“hob2hood”的功能。这个功能听起来很酷它能根据电磁炉的烹饪状态自动控制抽油烟机的开关和风速。想象一下当你开始爆炒油烟刚起抽油烟机就自动调至高挡位当你转为小火慢炖它又能自动降低风速或关闭既智能又节能。然而现实往往比理想骨感。我的抽油烟机是个老型号根本不支持这个“高级”功能。难道为了这个自动化就得把整套厨电都换新吗作为一个电子爱好者我的第一反应是能不能自己动手让它们“联姻”这个想法催生了整个“Hob2hood接口研究”项目。本质上这是一个逆向工程和桥接项目目标是破解电磁炉发出的控制信号并用一个自制的控制器来“翻译”这些信号驱动我那台“哑巴”抽油烟机工作。这个项目非常适合喜欢动手改造家居、对嵌入式系统和信号解码感兴趣的朋友。它不要求你拥有顶级的电子工程学位但需要你具备焊接、使用万用表示波器等基础技能以及最重要的——解决问题的耐心。整个过程就像在解一个有趣的谜题信号是什么格式如何可靠地接收怎样设计一个稳定且安全的控制器接下来我将详细拆解我的探索过程、实现方案以及一路踩过的坑希望能为你提供一份详实的参考。2. 核心思路与方案选型面对“让旧抽油烟机听懂新电磁炉指令”这个问题市面上其实有一些现成的思路但经过分析我决定走一条更底层、更可控的自研道路。2.1 为什么放弃现成的Arduino方案在项目初期我确实在网上找到了一些基于Arduino和红外接收库如IRremote的解决方案。这些方案看起来简单快捷似乎能快速实现功能。但我深入研究了它们的代码后发现了一个潜在的风险这些方案的成功很大程度上依赖于库函数对特定红外协议的解码而这种解码可能具有偶然性。电磁炉制造商使用的红外通信协议往往是私有的、非标准的。现有的通用红外库在解析这些非标准信号时可能因为时序容差、编码方式判断等原因在某个版本能工作换一个库版本就可能失效。这种不确定性是我无法接受的。我希望构建的系统是稳定、可靠的其解码逻辑基于我亲自验证过的信号特征而不是依赖一个可能变化的黑盒库。因此我决定从最底层开始——用示波器直接观察和分析原始信号。2.2 自研解码方案的优势与挑战选择自研意味着我们需要亲自充当“协议分析仪”。这带来了几个核心优势绝对可控从信号捕捉、波形分析到解码逻辑编写全程自主彻底理解通信机制。高可靠性解码程序完全针对目标信号定制排除了通用库可能引入的误判和版本兼容问题。灵活性一旦掌握解码方法可以轻松适配不同品牌或型号的设备只需调整解码逻辑即可。当然挑战也随之而来信号捕获需要示波器来可视化红外接收头输出的数字波形。协议逆向需要从一堆脉冲中找出起始位、数据位、停止位判断编码方式如PWM、Manchester、NRZ等和逻辑定义高电平代表1还是0。容错设计实际环境中可能存在红外干扰解码程序需要一定的容错能力。我的整体方案架构因此确定信号分析 → 协议破解 → 构建测试发射器验证 → 开发测试接收器 → 制作最终控制器。这是一个经典的硬件逆向工程流程确保了每一步都有扎实的依据。3. 红外信号解码全解析这是整个项目的基石也是最考验耐心和细心的环节。一切从连接示波器开始。3.1 信号捕获与初步观察我使用了一个通用的38kHz红外接收头如VS1838B将其信号输出引脚连接到数字示波器的探头地线共用。然后将接收头对准电磁炉的疑似红外发射窗口通常在玻璃面板下方边缘。操作电磁炉分别触发“开机”、“关机”、“风速0-4”等指令同时捕获示波器上的波形。注意红外接收头输出的是解调后的数字信号。即它已经过滤掉了38kHz的载波直接输出了代表原始数据的高低电平序列。这是我们分析的对象。捕获到的波形并非标准的UART串口信号。它没有固定的波特率时钟线看起来像是一串长短不一的高低电平脉冲。一个关键的发现是每次按键电磁炉都会连续发送三次完全相同的码组这显然是厂家为了增强抗干扰能力而设计的。3.2 协议深度逆向与位流提取通过对比不同指令如“开机”和“风速1”的波形我找到了规律。忽略掉每次发送之间的长空闲间隔专注于单个码组的波形。我发现每个码组由一个显著的长低电平约数毫秒开始这很像一个“起始位”。起始位之后跟随的是24个明显的数据位脉冲。接下来是判断编码方式。常见的红外编码有NEC脉冲宽度编码、RC-5双相曼彻斯特编码等。通过仔细测量每个位周期内高电平和低电平的宽度我排除了PWM脉冲宽度调制方式因为“0”和“1”的脉冲宽度模式没有明显区别。最终我判断这是一种不归零NRZ编码的串行数据流。在NRZ编码中一个位周期内的电平保持不变高电平代表“1”低电平代表“0”或者相反这需要后续确定。于是我将示波器捕获的波形手动翻译成了二进制位流。以“开机”指令为例我记录下的24位数据是00101101 00101100 00101011。这正是三个字节0x2D, 0x2C, 0x2B。用同样的方法我解码了所有指令指令二进制位流 (24位)对应的十六进制字节开机 (On)00101101 00101100 001010112D 2C 2B关机 (Off)00101010 00101001 001010002A 29 28风速0 (V0)00100111 00100110 0010010127 26 25风速1 (V1)10010011 10010010 1001000193 92 91风速2 (V2)10010000 10001111 1000111090 8F 8E风速3 (V3)00011110 00011101 000111001E 1D 1C风速4 (V4)10001101 10001100 100010118D 8C 8B3.3 解码逻辑的确认与软件实现要点得到字节数据后还需要确认逻辑电平定义。即接收头输出的高电平对应数据“1”还是“0”这对于编写单片机解码程序至关重要。一个实用的方法是结合上下文判断通常“关机”指令的所有数据位可能都是“0”或都是“1”。观察我的数据关机指令2A 29 28的字节值并非全0或全FF因此不能直接判断。我采取的策略是在解码软件中保持灵活性。我的解码程序会先按照一种假设比如高电平为1去解析如果解析出的指令与预期不符例如按下开机键却解析出风速码则尝试另一种假设高电平为0。在实际测试中我确认了对于我的信号接收头输出高电平时对应的是数据位“1”。实操心得协议逆向时一定要记录原始波形图。用示波器的单次触发和存储功能保存关键波形。手动翻译位流虽然枯燥但能让你对协议有最深刻的理解。可以编写一个简单的Python脚本根据你测量的高/低电平时间阈值来自动化翻译示波器导出的CSV数据能大大提高效率。4. 构建测试工具发射器与接收器在把解码逻辑烧进最终控制器之前我必须先验证我的理解是否正确。最好的方法就是自己做一个“假”电磁炉和一个“假”抽油烟机来模拟通信。4.1 红外测试发射器的设计与制作我不想每次测试解码程序都跑到厨房去对着真正的电磁炉按键。于是我制作了一个便携式的红外测试发射器。核心器件选择我选用了一颗小巧且廉价的PIC12F1822单片机。它仅有8个引脚但功能齐全有两个定时器非常适合产生精确的38kHz载波和调制信号。电路设计要点红外发射管驱动红外发射二极管IR LED需要较大的瞬时电流才能有足够的发射距离。我使用了一个NPN三极管如2N2222来驱动。单片机的I/O引脚通过一个限流电阻连接到三极管的基极三极管的集电极串联IR LED和另一个限流电阻到电源。发射管需指向外部。载波生成38kHz载波由单片机的一个定时器Timer2产生工作在PWM模式。我将载波信号输出到一个I/O引脚。数据调制解码得到的NRZ数据位流需要“搭乘”在38kHz载波上发送出去。在软件中当需要发送“有效信号”时我让控制IR LED的I/O引脚输出载波PWM信号当需要发送“无效信号”即低电平时则将该引脚拉低。这样就实现了幅移键控ASK调制。用户界面我焊接了三个轻触开关分别对应“开机”、“关机”和“风速循环”。一个额外的LEDD2用于指示发射状态方便调试。供电使用3节AAA电池约4.5V供电简单可靠。也可以使用手机充电器通过USB口提供5V电源。软件逻辑按键检测与指令映射。严格按照捕获的时序生成包含起始位和24位数据的帧。关键点将每一帧数据连续发送三次以模拟原厂设备的抗干扰策略。在发送数据期间点亮状态LED。这个发射器制作在一小块万用板Veroboard上没有设计专门的PCB。制作完成后我用示波器检查其输出波形并与之前捕获的电磁炉波形进行对比确保形状、时序一致。我还用另一个Arduino搭配IR接收头写了简单的接收测试程序确认能收到我发射的码值。4.2 红外测试接收器与验证平台有了可靠的发射源接下来就需要一个“翻译官”来验证解码逻辑。我制作了一个带显示功能的测试接收器。核心器件选择为了便于调试和显示我选择了资源更丰富的PIC16F1847单片机并搭配一个常见的1602字符液晶显示屏LCD。电路与功能红外接收使用相同的38kHz红外接收头其输出端直接连接到单片机的一个具有中断功能的I/O引脚如INT。这允许我们在信号边沿到来时立即响应实现精确的时序测量。解码核心——脉冲间隔计时这是解码NRZ串行数据的关键。我不依赖固定的波特率而是采用测量相邻边沿时间间隔的方法。在起始位长低电平的下降沿开启定时器。在随后的每一个信号边沿上升或下降触发中断记录当前定时器值然后重置定时器。通过分析这些时间间隔我可以判断出一个位周期的中点。在位周期中点采样信号电平高或低即可得到该位的值1或0。连续采样24次就得到了一个数据帧。连续成功接收三个相同的数据帧则认为指令有效。结果显示解码得到的三个字节十六进制值会实时显示在LCD屏幕上。当我按下测试发射器上不同的按钮时LCD上应该稳定地显示对应的十六进制码如2D 2C 2B。这个测试接收器成功验证了我的解码算法完全正确。它成为了后续编写最终控制器固件的坚实基础。我将测试接收器的程序进行精简移除LCD显示部分保留核心的解码状态机和指令判断逻辑就得到了最终控制器的程序雏形。5. 最终控制器的硬件设计与实现测试验证通过后便可以着手制作最终安装在抽油烟机上的控制器了。这个控制器需要稳定、安全、长期运行。5.1 主控与解码电路我再次选择了PIC12F1822这颗小巧的单片机作为核心。原因如下资源足够解码程序经过优化后对Flash和RAM需求很小PIC12F1822绰绰有余。成本与体积价格低廉封装小巧8-PIN DIP或SOIC适合嵌入到狭小空间。低功耗在待机状态下功耗极低。电路极其简洁红外接收头信号线连接到单片机的一个I/O口支持外部中断为佳。电源一个5V/1A的USB充电器提供稳定电源。输入端增加一个稳压芯片如AMS1117-5.0和滤波电容确保电源干净。输出驱动单片机需要驱动继电器来控制抽油烟机的高压线路。我的抽油烟机有“灯”和“低、中、高”三档风速共需4路控制。我直接使用了单片机I/O口来驱动光耦进而控制一个4路继电器模块。这种模块集成度高自带反电动势保护二极管使用方便。重要计算需要确认单片机I/O口的拉电流能力。PIC12F1822的I/O口典型拉电流约为25mA。我选用的继电器模块线圈电压为5V线圈电阻约为100欧姆则工作电流 I V/R 5V / 100Ω 50mA。这远超单片机的驱动能力。因此必须使用三极管或MOSFET进行电流放大。我每个输出口连接一个NPN三极管如S8050单片机I/O口通过一个1kΩ电阻驱动三极管基极继电器线圈连接在集电极回路中。5.2 输出逻辑映射与安全隔离我的抽油烟机有3档风速但电磁炉提供了5个风速指令V0-V4。我需要做一个合理的映射V0 (关)关闭所有风速继电器。V1 (低)开启“低速”继电器。V2, V3 (中)开启“中速”继电器。将V2和V3都映射到中速覆盖更广的烹饪强度V4 (高)开启“高速”继电器。Light On/Off独立控制照明灯的继电器。安全是重中之重我们控制的是220V交流电必须实现强弱电隔离。电气隔离使用光耦在继电器模块内部通常已有或继电器本身的线圈与触点进行隔离。确保单片机侧的5V地线与交流220V侧完全无电气连接。机械开关旁路绝不能剥夺用户手动控制的权利我在220V线路中引入了一个双刀三掷开关。位置1 (手动)开关将抽油烟机的原始控制线路直接接通控制器完全断电或被旁路用户使用抽油烟机自身的按钮操作。位置2 (关闭)同时切断手动和自动控制线路抽油烟机完全断电。位置3 (自动)将抽油烟机的电机和灯的控制线路接入继电器模块的常开触点此时由我们的Hob2hood控制器全权管理。这样的设计保证了即使控制器故障用户也可以通过机械开关切回手动模式不影响抽油烟机的基本使用安全且人性化。5.3 组装、调试与安装我将单片机解码电路焊接在另一小块万用板上与4路继电器模块、电源模块、机械开关一起安装在一个合适的塑料绝缘盒内。所有220V接线务必使用压线帽或焊接后热缩管绝缘并确保线径足够至少0.75mm²。上电调试流程不接220V只给控制器供5V电。用测试发射器发送指令用万用表测量各继电器输出端确认对应的继电器能正确吸合/释放。确认机械开关在不同档位下能正确切换线路。最后在断电情况下将控制器串联接入抽油烟机的供电线路。将红外接收头用热熔胶固定在抽油烟机底部并确保其能“看到”电磁炉方向可稍微向外倾斜。通电测试。操作电磁炉观察抽油烟机是否按预设逻辑响应。踩坑记录初期安装后发现偶尔会误触发。原因是厨房环境中的其他红外源如遥控器、阳光产生了干扰。我在解码软件中增加了信号验证机制必须连续两次或三次解码出完全相同的、有效的指令码才执行动作。同时将红外接收头的接收窗口用深色热缩管套住一部分略微缩小其接收视角有效减少了侧向干扰。6. 软件解码核心算法详解硬件是躯体软件是灵魂。这里深入讲解最终控制器中运行的核心解码算法这是项目稳定性的关键。6.1 状态机设计与中断服务程序为了不阻塞主程序且精准捕捉信号我采用了“外部中断定时器状态机”的架构。// 状态定义 typedef enum { STATE_IDLE, // 空闲状态等待起始位 STATE_START_BIT, // 检测到起始位下降沿开始计时 STATE_DATA_BITS // 正在接收数据位 } decode_state_t; volatile decode_state_t ir_state STATE_IDLE; volatile uint16_t pulse_widths[50]; // 存储边沿间隔 volatile uint8_t pulse_index 0; volatile uint32_t last_edge_time 0; // 用于计算间隔外部中断服务程序ISR配置在红外接收头信号线的下降沿和上升沿都触发中断。void interrupt isr(void) { if (INTF) { // 外部中断标志 uint32_t current_time read_timer(); uint16_t width current_time - last_edge_time; last_edge_time current_time; if (ir_state STATE_IDLE) { // 检测到长低电平起始位 if (width START_BIT_MIN_WIDTH) { ir_state STATE_START_BIT; pulse_index 0; // 重置并准备接收数据 } } else if (ir_state STATE_START_BIT || ir_state STATE_DATA_BITS) { // 记录数据位期间的脉冲宽度 pulse_widths[pulse_index] width; if (pulse_index 48) { // 24位数据 * 2个边沿/位估算 ir_state STATE_IDLE; // 接收完成等待处理 } else { ir_state STATE_DATA_BITS; } } INTF 0; // 清除中断标志 } }6.2 从脉冲宽度到数据位的转换pulse_widths数组存储了相邻边沿的时间间隔。对于NRZ编码一个位周期内可能没有边沿连续相同比特也可能有一个边沿比特变化。我们需要从这些间隔中恢复出时钟和数据。算法步骤估算位周期Bit Time将所有pulse_widths相加得到总时间。理论上24位数据加起始位至少有25个位周期。用总时间除以25得到一个粗略的位周期时间T_bit。位中点采样创建一个长度为24的数组bits。设置一个时间指针t从起始位结束后的第一个边沿开始。遍历pulse_widths每次增加一个间隔width。在时间点t T_bit/2处即每个位周期的中心我们需要知道当时的电平高低。由于我们记录的是边沿时间我们可以通过维护一个当前电平状态初始为起始位后的第一个电平并在每次遇到pulse_widths中的时间点时翻转电平来模拟出信号波形。通过判断t T_bit/2这个时刻落在哪个电平区间即可确定该数据位的值高为1低为0根据之前确定的逻辑。帧验证将24个位每8个一组转换成3个字节。检查这三个字节是否符合我们已知的指令表例如是否在{2D2C2B, 2A2928, ...}这个集合内。如果符合则是一个有效的指令帧。6.3 抗干扰与指令执行逻辑为了应对红外干扰和误码我实现了两级过滤连续帧校验电磁炉会发送三次相同指令。我的程序会维护一个缓冲区存储最近解码成功的几帧。当连续2-3帧数据完全相同时才认为接收到一个有效指令。指令去抖与状态机抽油烟机的继电器不宜频繁动作。程序内部维护一个当前风速状态。只有当接收到的新指令与当前状态不同且经过验证是有效的才会改变输出。例如当前是“低速”再次收到“V1”指令则忽略。主程序循环非常简单void main(void) { init_all(); // 初始化IO、定时器、中断 while(1) { if (new_valid_command_received) { decode_and_set_outputs(); // 解析指令控制继电器 new_valid_command_received 0; } sleep(); // 进入低功耗模式等待中断唤醒 } }7. 常见问题排查与优化建议在实际制作和调试过程中你可能会遇到以下问题。这里是我的排查经验和解决方案。7.1 信号接收不稳定或完全无反应问题现象控制器偶尔响应或完全没反应。排查步骤供电检查首先用万用表测量单片机VDD引脚确保电压在4.5V-5.5V之间且稳定。劣质USB充电器在带载时电压可能跌落。红外接收头方向确保接收头的感光面正对或有一定接收角度朝向电磁炉的红外发射区域。可以用手机摄像头观察大多数手机摄像头能看到红外光当按下电磁炉按键时查看是否有紫白色光点闪烁。环境光干扰强烈的日光灯或自然光可能包含红外成分。尝试遮挡接收头或在接收窗口前加一片深红色滤光片甚至用黑色电工胶带贴一小部分这能显著抑制可见光干扰同时透过940nm的红外光。软件解码阈值检查程序中判断起始位最小宽度START_BIT_MIN_WIDTH和位周期T_bit的阈值。这些值需要根据你实际示波器测量的数据微调。如果阈值设得太严格可能无法触发解码。中断冲突确保红外解码使用的外部中断是最高优先级且中断服务程序执行时间尽可能短。避免在中断中进行复杂的计算或延时。7.2 解码错误触发错误的风速档位问题现象按下电磁炉“开机”键抽油烟机却切换到高速档。排查步骤逻辑电平确认这是最常见的原因。回顾第3.3节确认你的解码程序里“高电平”对应的是“1”还是“0”。用测试接收器配合测试发射器打印出原始电平序列与你的假设对比。位采样点漂移如果位周期T_bit计算不准或在位周期中点采样时发生了偏差就会导致错位解码。可以在测试接收器阶段将解码过程中的中间变量如计算出的T_bit、每个位的采样点时间通过调试接口输出与示波器波形严格比对。信号畸变长距离或非直视传输可能导致信号边沿变缓。在单片机输入端可以尝试加入一个小的RC低通滤波器例如1kΩ电阻串联对地接一个10nF电容滤除高频毛刺但要注意RC时间常数不能影响正常信号带宽。7.3 继电器频繁跳动或嗡嗡作响问题现象在自动模式下继电器不停吸合断开或吸合后发出嗡嗡声。排查步骤电源功率不足继电器线圈吸合瞬间电流很大可能导致5V电源电压瞬间被拉低造成单片机复位或程序跑飞。确保你的5V电源特别是线性稳压器能提供至少1A的持续电流并在稳压器输入输出端并联大容量电解电容如220μF和一个小陶瓷电容0.1μF进行退耦。软件去抖确保你的指令执行逻辑中有“去抖”功能。即只有确认指令稳定改变后才动作。参考6.3节的连续帧校验和状态机。继电器型号劣质继电器的触点容易抖动。选择知名品牌如Omron Songle的优质继电器模块。嗡嗡声可能是交流线圈的继电器在直流驱动下产生的确认你使用的是直流线圈继电器DC5V。反电动势吸收继电器线圈是感性负载断开时会产生很高的反向电动势。务必确保继电器模块上已经集成了续流二极管通常有或者自己在线圈两端反向并联一个二极管如1N4007。7.4 扩展与优化建议如果你想让这个项目更上一层楼可以考虑以下方向无线化将红外接收头从控制器分离通过一个无线模块如433MHz发射/接收对或蓝牙低功耗将解码后的指令发送给控制器。这样可以将接收头更灵活地布置在最佳接收位置。状态反馈增加一个红外发射管让控制器也能向电磁炉发送信号如果电磁炉支持接收。或者增加一个Wi-Fi模块如ESP-01S将抽油烟机状态接入家庭自动化平台如Home Assistant实现远程查看和更复杂的联动。学习功能如果你的单片机Flash足够大可以设计一个“学习模式”。长按某个按钮进入学习然后按下电磁炉的某个键控制器自动记录下该信号的波形特征并存储。这样理论上可以适配任何支持红外遥控的厨电。功耗优化目前控制器持续工作。可以修改程序在长时间如30分钟未收到任何红外指令后让单片机进入深度睡眠模式仅由外部中断唤醒进一步降低待机功耗。这个项目从发现问题到最终解决花费了不少时间和精力但带来的成就感和厨房体验的提升是巨大的。它不仅仅是一个简单的联动开关更是一次对消费电子产品背后通信协议的深入探索。最重要的是它让你对家中电器的掌控力提升了一个维度——从使用者变成了创造者和改造者。希望这份详细的记录能为你打开一扇窗让你看到硬件 hacking 的乐趣与可能性。如果在复现过程中遇到任何问题不妨回到信号分析的第一步用示波器看看真相往往就在波形之中。