4、红外接收解码实战:使用IRremote库接收红外信号、NEC协议解码、原始脉冲波形分析、调试技巧
好,咱们进入第四章节。前面几章我们把硬件搭好了,也把开发环境跑通了。这一章,才是真正让ESP32「听懂」红外信号的关键一步。
说白了,红外遥控的核心就两件事:发出去和收回来。这一章我们专门讲「收」——怎么用IRremote库把红外信号抓下来,怎么把NEC协议解码成我们能看懂的数据,以及遇到问题怎么调试。
我个人习惯,做任何通信协议的第一步,都是先看波形。你想想看,如果连原始信号长什么样都不知道,后面解码再漂亮也是空中楼阁。所以这一章我会带着你从波形入手,一步步走到解码成功。
4.1 IRremote库的安装与基础使用
IRremote库是目前ESP32上最成熟的红外库之一。我最早用的时候还是v2.x版本,现在已经到v4.x了,API变化不小。不过核心思想没变。
安装方式:在Arduino IDE的库管理器里搜索「IRremote」,找到由shirriff、z3t0、ArminJo维护的那个版本。注意别装错了,有些同名库功能不全。
安装完成后,先写一个最简单的接收程序:
#include <IRremote.hpp>
const int RECV_PIN = 15; // 我习惯用GPIO15
void setup() {
Serial.begin(115200);
IrReceiver.begin(RECV_PIN, ENABLE_LED_FEEDBACK);
Serial.println("红外接收器已启动,等待信号...");
}
void loop() {
if (IrReceiver.decode()) {
Serial.println(IrReceiver.decodedIRData.decodedRawData, HEX);
IrReceiver.resume();
}
}
这段代码很简单,但有几个坑我要提醒你。
另外,ENABLE_LED_FEEDBACK这个参数,我建议你打开。它会在收到信号时让板载LED闪烁一下,调试时特别有用。你想想看,有时候串口没连上,但LED闪了,至少说明硬件通路是通的。
4.2 NEC协议解码:从原始数据到按键码
串口打印出来的那一串十六进制数,比如0xFF30CF,就是NEC协议的按键码。但你真的理解它代表什么吗?
NEC协议的数据帧结构是这样的:
| 字段 | 长度 | 说明 |
|---|---|---|
| 引导码 | 9ms高 + 4.5ms低 | 表示一帧数据的开始 |
| 地址码 | 8位 | 设备地址,通常为0x00 |
| 地址反码 | 8位 | 地址码取反,用于校验 |
| 命令码 | 8位 | 按键对应的功能码 |
| 命令反码 | 8位 | 命令码取反,用于校验 |
| 停止位 | 562.5μs高 | 帧结束标志 |
你打印出来的0xFF30CF,其实是把地址码和命令码拼在一起了。拆开来看:
- 地址码:0xFF(取反后是0x00,校验通过)
- 命令码:0x30(取反后是0xCF,校验通过)
嗯,这里要注意:IRremote库默认会把地址码和命令码合并成一个32位整数输出。如果你只想拿命令码,可以这样:
uint8_t command = IrReceiver.decodedIRData.command;
uint8_t address = IrReceiver.decodedIRData.address;
我在项目中遇到过一个问题:有些遥控器发送的是NEC扩展协议,地址码是16位的。这时候用上面的方法拿到的地址就不对。解决办法是检查decodedIRData.protocol字段,如果是NEC就是标准协议,如果是NEC2就是扩展协议。
4.3 原始脉冲波形分析:用逻辑分析仪看真相
说实话,光靠串口打印数据,你永远不知道信号在物理层到底经历了什么。我强烈建议你准备一个逻辑分析仪,哪怕是20块钱的USB版都行。
接好逻辑分析仪后,把探头接到红外接收头的OUT引脚。触发条件设为下降沿触发,采样率至少2MHz。然后按一下遥控器,你会看到这样的波形:
引导码: ████████████████████░░░░░░░░░░░░░░░░░░░░
|--- 9ms ---| |---- 4.5ms ----|
数据位 '0': ██░░░░
|560μs| |560μs|
数据位 '1': ████████░░░░
|560μs| |1.69ms|
你看,NEC协议用脉冲宽度来区分0和1。560μs的高电平加上560μs的低电平表示0,560μs的高电平加上1.69ms的低电平表示1。这个时序非常关键,我调试时经常用这个来验证遥控器是否真的在工作。
IrReceiver.printIRResultShort(&Serial);,它会打印出每个脉冲的时长。虽然不如波形直观,但也能分析问题。
4.4 常见问题与调试技巧
做红外接收,我踩过的坑比走过的路还多。这里挑几个典型的说说。
问题一:收不到任何信号
我曾经花了一整天排查这个问题,最后发现是红外接收头的引脚接反了。接收头一般是三脚:VCC、GND、OUT。但不同厂家的引脚顺序可能不同,一定要看数据手册。
问题二:收到乱码
如果你打印出来的数据一直在变,或者跟遥控器上的按键对不上,多半是供电问题。红外接收头对电源噪声很敏感,我建议在VCC和GND之间加一个10μF的电解电容。
问题三:重复码处理
NEC协议有个特性:长按按键时,会每隔110ms发送一个重复码(9ms高+2.25ms低+560μs高)。IRremote库默认会过滤掉重复码,但如果你需要检测长按事件,可以这样:
if (IrReceiver.decodedIRData.flags & IRDATA_FLAG_IS_REPEAT) {
Serial.println("重复码,按键正在被长按");
}
问题四:中断冲突
IRremote库默认使用GPIO15的RTC中断。如果你同时用了其他占用RTC中断的外设(比如某些触摸传感器),就会冲突。解决办法是在IrReceiver.begin()之前调用IrReceiver.setReceivePin(14);换一个引脚。
4.5 实战:做一个红外信号分析仪
最后,我带你写一个实用的小工具。它可以打印出每个按键的详细信息,包括协议类型、地址、命令码、原始脉冲数。调试时特别好用。
#include <IRremote.hpp>
const int RECV_PIN = 15;
void setup() {
Serial.begin(115200);
IrReceiver.begin(RECV_PIN, ENABLE_LED_FEEDBACK);
Serial.println("红外信号分析仪已启动");
Serial.println("按任意遥控器按键...");
}
void loop() {
if (IrReceiver.decode()) {
Serial.println("----------------------------");
Serial.print("协议: ");
Serial.println(IrReceiver.decodedIRData.protocol);
Serial.print("地址: 0x");
Serial.println(IrReceiver.decodedIRData.address, HEX);
Serial.print("命令: 0x");
Serial.println(IrReceiver.decodedIRData.command, HEX);
Serial.print("原始数据: 0x");
Serial.println(IrReceiver.decodedIRData.decodedRawData, HEX);
Serial.print("脉冲数: ");
Serial.println(IrReceiver.decodedIRData.numberOfBits);
// 打印原始脉冲时长
IrReceiver.printIRResultRawFormatted(&Serial, true);
IrReceiver.resume();
}
}
这个程序我每次调试新遥控器都会先用它跑一遍。把每个按键的码值记录下来,后面做万能遥控器时直接查表就行。
- IRremote库的初始化、解码、恢复三步曲不能少
- NEC协议用脉冲宽度编码,0和1的时序不同
- 逻辑分析仪是调试红外信号的最佳工具
- 供电滤波和引脚选择是常见坑点
好了,这一章的内容就到这儿。下一章我们会把学到的解码知识用起来,开始做真正的万能遥控器——把收到的信号存下来,再原样发出去。你想想看,到时候你就能用一个ESP32控制家里所有的红外设备了,是不是挺酷的?