💡 划重点

  • 项目定位:基于 ESP32 的多功能红外遥控器,解决客厅遥控器数量多、易丢失、操作繁琐的问题。
  • 核心能力:红外信号学习与回放、按键矩阵直接控制、LCD 菜单指引、Wi‑Fi 联网扩展。
  • 成本控制:全部元器件 BOM 成本控制在 45 元以内,利用模块化设计降低焊接难度。
  • 接线纪律:IR 接收使用 GPIO15,IR 发射使用 GPIO4,LCD I2C 挂载在 GPIO21/22,按键矩阵占用独立 GPIO,避免冲突。
  • 交付物:可编译的 Arduino 骨架代码、接线图、物料表与调试说明书。

需求洞察

目标用户

客厅中拥有电视机、机顶盒、空调、音响等 3 个以上红外家电的家庭用户,尤其适合老年用户、租房青年以及不愿反复找遥控器的人群。

真实痛点

  1. 遥控器泛滥:每个家电至少一个遥控器,茶几上堆积多个遥控器,外观相似、功能交叉,家人经常拿错。
  2. 操作割裂:开关电视要分别用电视、机顶盒、音响遥控器,切换信号源效率低。
  3. 学习成本高:老人记不住复杂按键组合,普通万能遥控器设置步骤繁琐,难以二次配置。
  4. 物理丢失:小遥控器极易滑入沙发缝或床底,关键时刻找不到。

为什么值得做

硬件成本仅需 40 多元,却能集中管理 5‑8 个红外设备。利用 ESP32 的 Wi‑Fi 能力,后期可接入智能家居系统,实现手机远程遥控、语音联动,远比购买多个官方替代遥控器经济。项目完全开源,可以按家庭需求定制按键布局和宏命令,用户粘性极高。

方案设计

感知层

控制层

执行层

日志/联网层

物料清单

序号 物料名称 数量 用途 估算单价(元) 小计(元)
1 ESP32‑WROOM‑32E 开发板 1 主控及 Wi‑Fi 14.00 14.00
2 VS1838B 红外接收模块 1 学习红外信号 1.50 1.50
3 5 mm 红外发射管 1 发射控制信号 0.50 0.50
4 2N2222 NPN 三极管 1 发射管驱动 0.30 0.30
5 1 kΩ、100 Ω 电阻各一 2 基极/限流 0.10 0.20
6 I2C LCD1602 液晶屏 1 显示交互界面 9.50 9.50
7 4×4 薄膜按键矩阵 1 物理控制输入 4.50 4.50
8 面包板 + 跳线 1 套 原型搭建 5.00 5.00
9 Micro USB 电源线 1 供电 2.00 2.00
10 旧遥控器 1 测试学习源 0.00 0.00
合计 37.50
实际成本 37.50 元,在 45 元预算内留出余量用于外壳或扩展。若直接焊接洞洞板,面包板成本可进一步压缩。

接线与结构

接线表

组件 模块引脚 ESP32 GPIO 说明
IR 接收 OUT 15 学习红外码,电平触发
IR 接收 VCC 3.3V 接收模块供电
IR 接收 GND GND 共地
IR 发射 阳极(串电阻后) 4 经三极管驱动
三极管基极 串 1kΩ 电阻 → 4 GPIO4 输出 38 kHz 载波
LCD I2C SDA 21 I2C 数据线
LCD I2C SCL 22 I2C 时钟线
LCD I2C VCC 5V 液晶屏供电
LCD I2C GND GND 共地
按键矩阵 行 1‑4 25, 26, 27, 14 行扫描输出
按键矩阵 列 1‑4 12, 13, 32, 33 列扫描输入

微调理由:IR 接收使用 GPIO15 以避开 ADC2 和常用 JTAG 引脚,确保学习时不干扰 Wi‑Fi;发射使用 GPIO4 可配置内部分频产生精准 38 kHz PWM 载波。按键矩阵占用独立 GPIO,避免与 I2C 和红外功能冲突。结构上将所有模块固定在一块 8×6 cm 洞洞板上,按键矩阵贴在正面,LCD 屏竖起,红外收发窗口朝向前方,形成握持舒适的手持遥控器形态。

核心代码

以下给出 Arduino 骨架,基于 PlatformIO 或 Arduino IDE 均可编译。


#include <Arduino.h>
#include <IRremote.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <Preferences.h>

// 红外引脚
#define IR_RECEIVE_PIN 15
#define IR_SEND_PIN    4

// LCD

// 按键矩阵
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};
byte rowPins[ROWS] = {25, 26, 27, 14};
byte colPins[COLS] = {12, 13, 32, 33};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

// 红外对象
IRrecv irrecv(IR_RECEIVE_PIN);
IRsend irsend(IR_SEND_PIN);

// 存储
Preferences prefs;

// WiFi 及 MQTT
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASS";
WiFiClient espClient;

// 当前设备索引
int currentDevice = 0;
bool learningMode = false;

void setup() {
  Serial.begin(115200);
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("IR Controller");

  irrecv.enableIRIn();
  prefs.begin("ircodes", false);

  // 连接 WiFi
  WiFi.begin(ssid, password);
  int retry = 0;
  while (WiFi.status() != WL_CONNECTED && retry < 20) {
    delay(500);
    retry++;
  }
  if (WiFi.status() == WL_CONNECTED) {
    client.connect("ESP32_IR");
  }
}

void loop() {
  // 按键处理
  char key = keypad.getKey();
  if (key) {
    handleKey(key);
  }

  // 红外学习
  if (learningMode && irrecv.decode()) {
    irrecv.resume();
    learningMode = false;
    lcd.clear();
    lcd.print("Code Saved");
    delay(1000);
  }

  // MQTT 保活
  if (client.connected()) {
    client.loop();
  }
}

void handleKey(char key) {
  switch (key) {
    case 'A': // 学习键
      learningMode = true;
      lcd.clear();
      lcd.print("Learning...");
      break;
    case 'B': // 发送测试
      sendIR(currentDevice, '1');
      break;
    case 'C': // 设备切换
      currentDevice = (currentDevice + 1) % 3;
      lcd.clear();
      lcd.print("Device:");
      lcd.print(currentDevice);
      break;
    case 'D': // 上报
      if (client.connected()) {
        client.publish("ir/status", "online");
      }
      break;
    default:  // 直接发射对应码
      sendIR(currentDevice, key);
      break;
  }
}

void storeIRCode(int dev, char btn, IRData data) {
  String key = String(dev) + "_" + String(btn);
  prefs.putBytes(key.c_str(), &data;, sizeof(IRData));
  lcd.setCursor(0, 1);
  lcd.print("Stored ");
  lcd.print(key);
}

void sendIR(int dev, char btn) {
  String key = String(dev) + "_" + String(btn);
  IRData data;
  if (prefs.getBytes(key.c_str(), &data;, sizeof(IRData)) == sizeof(IRData)) {
    irsend.write(&data;);
    lcd.clear();
    lcd.print("Sent ");
    lcd.print(btn);
  }
}

调试步骤

  1. 基础测试

上传 Blink 例程,确认 ESP32 工作正常,串口监视器输出无乱码。

  1. LCD 及 I2C 验证

烧录 I2C 扫描程序,确认地址 0x27 被检测到,观察 LCD 背光亮起并打印 hello world。

  1. 红外接收测试

使用电视或空调遥控器对准 VS1838B 按下,串口打印出 NEC 或 RC5 原始码,确保中断响应无遗漏。

  1. 发射电路调试

在 sendIR 函数中直接发送预置 NEC 码,用手机摄像头观察发射管是否发出紫红色光晕,配合电视验证开关机功能。

  1. 按键矩阵标定

逐键按压,在串口打印对应字符,确认行列映射正确,接触不良按键进行替换。

  1. 学习‑发射联调

长按 A 键进入学习模式,对着接收头按原遥控器任意键,LCD 显示 “Code Saved”。随后短按对应数字键,家电应正确响应。

  1. Wi‑Fi 及 MQTT

修改代码中的 SSID 和密码,重启后通过 MQTT 客户端工具监听 ir/status 主题,确认上线报文正常推送。

设计图

红外家电统一遥控器结构flow
1
感知输入(按键矩阵、 IR 接收模块)
2
控制处理(ESP32 主控、学习/发射逻辑)
3
执行反馈(IR 发射驱动、LCD 状态显示)
4
数据上报(Wi‑Fi/MQTT、OTA 升级)