基于ESP32-C3的WiFi通用红外遥控器DIY:Web控制与红外码学习

发布时间:2026/10/9 4:27:27
基于ESP32-C3的WiFi通用红外遥控器DIY:Web控制与红外码学习
1. 项目缘起与整体设计思路1.1 为什么要自己做一个通用红外遥控器家里电视、空调、机顶盒、风扇、投影仪每个设备配一个遥控器茶几上堆得跟小山似的。更烦的是空调遥控器经常找不到夏天半夜热醒了翻箱倒柜。市面上确实有万能遥控器但要么是红外码库不全要么是按键手感稀烂要么是App做得跟十年前似的。我前后买过三四个所谓的“智能遥控”最后都吃灰了。后来我把目光投向了ESP32系列。原因很简单这玩意儿自带WiFi价格便宜社区生态成熟而且有现成的红外发射方案。我手头正好有几片ESP32-C3的开发板这东西是RISC-V架构单核160MHz内置WiFi和蓝牙LEGPIO数量虽然不算多但驱动一个红外发射管绰绰有余。最关键的是它支持ESP Web Tools可以直接在浏览器里刷固件不用装一堆工具链对新手极其友好。这个项目的核心目标很明确做一个基于ESP32-C3的WiFi通用红外遥控器通过网页界面控制支持红外码的学习和发射能覆盖家里绝大多数红外设备。我给它起了个名字叫RemodazWifi纯粹是个人习惯方便在路由器里识别设备。1.2 方案选型为什么是ESP32-C3加Web界面选ESP32-C3而不是ESP8266或者ESP32-S3有几个实际考量。ESP8266虽然更便宜但GPIO和内存都紧张跑Web服务器加红外编码有点吃力。ESP32-S3性能更强但价格上去了而且对于红外遥控这个场景C3的单核RISC-V完全够用。C3的WiFi是2.4GHz单频穿墙一般但遥控器通常和路由器在同一个房间问题不大。软件层面我选择用Arduino框架来开发而不是ESP-IDF。原因很直接Arduino的IRremoteESP8266库现在叫IRremote对红外协议的支持非常完善NEC、Sony、RC5、RC6、Samsung、LG、Daikin、Mitsubishi等主流协议都有现成解码和编码。自己从头写红外时序光是调试载波频率和占空比就能耗掉一周。Web界面这块我没有用现成的Home Assistant或者MQTT接入而是让ESP32-C3自己跑一个轻量HTTP服务器直接提供控制页面。这样做的好处是零依赖不需要额外的服务器或云服务插上电连上WiFi就能用。页面用纯HTML加JavaScript写通过WebSocket或者简单的HTTP请求和ESP32通信。ESP Web Tools则用来做固件刷写用户拿到板子后打开一个网页就能把固件烧进去不需要安装Arduino IDE或者PlatformIO。1.3 系统架构与数据流整个系统的数据流是这样的用户在手机或电脑浏览器打开ESP32-C3的IP地址页面加载后显示已学习的遥控器按钮。点击某个按钮浏览器发送一个HTTP POST请求到ESP32请求体里包含按钮对应的红外码。ESP32收到后调用IRremote库的发送函数通过GPIO驱动红外发射管发出对应的红外信号。设备收到信号后执行相应操作。学习模式则是反过来的用户点击页面上的“学习”按钮ESP32进入红外接收模式等待红外信号。用户拿原遥控器对着红外接收头按一下ESP32解码出协议和码值通过HTTP响应返回给浏览器浏览器再发送一个保存请求把码值存到ESP32的闪存里。整个流程不需要重启也不需要连接外部服务。这个架构的优点是简单、直接、可控。缺点也明显ESP32-C3的闪存有限能存的遥控器数量受限于SPIFFS或LittleFS的容量。不过对于普通家庭存十几个遥控器、几百个按键完全没问题。2. 核心硬件选型与电路连接细节2.1 ESP32-C3开发板的选择与引脚规划市面上ESP32-C3的开发板很多我手头用的是合宙的C3迷你板也有乐鑫官方的DevKitM-1。选板子的时候要注意几点首先确认板子引出了足够的GPIO特别是要有一个能输出PWM的引脚来驱动红外发射管。ESP32-C3的GPIO0到GPIO10都可以用作PWM输出但GPIO18和GPIO19是USB D/D-如果要用USB串口调试这两个脚最好别占用。我一般用GPIO3来驱动红外发射GPIO4接红外接收头。其次要注意板子的供电。红外发射管瞬间电流可能达到100mA以上如果直接用GPIO驱动电流不够发射距离会很短。所以必须用三极管或者MOSFET来扩流。我用的是一颗S8050 NPN三极管基极通过1kΩ电阻接GPIO3集电极接红外发射管的负极发射极接地红外发射管的正极通过一个100Ω限流电阻接3.3V。这样GPIO只需要提供几毫安的基极电流就能控制上百毫安的发射管电流。红外接收头我用的是VS1838B这是最常见的38kHz载波接收头输出端直接接GPIO4内部有上拉不需要额外电阻。注意VS1838B的引脚定义面对接收面从左到右依次是OUT、GND、VCC。接反了不会烧但收不到信号。2.2 红外发射管的驱动电路与参数计算红外发射管选型很关键。普通5mm红外LED的波长是940nm正向压降约1.2V到1.4V额定电流20mA峰值电流可以到100mA甚至更高。为了增加发射距离我们通常用脉冲方式驱动占空比很低所以平均电流不大但峰值电流可以拉高。限流电阻的计算假设用3.3V供电红外LED正向压降1.3V三极管饱和压降0.2V那么电阻上的电压是3.3 - 1.3 - 0.2 1.8V。如果目标峰值电流是100mA电阻值就是1.8V / 0.1A 18Ω。但实际我用的是100Ω峰值电流约18mA发射距离在3米左右够用了。如果想更远可以减小电阻到33Ω或22Ω但要注意三极管的功耗和LED的发热。载波频率方面IRremote库默认用38kHz占空比1/3。这个频率是由定时器产生的PWM决定的不需要外部晶振。ESP32-C3的LEDC外设可以很方便地产生38kHz PWMIRremote库已经封装好了直接调用irsend.sendNEC()之类的函数就行。2.3 电源与外壳的实操建议供电方面ESP32-C3开发板通常有Micro USB或Type-C接口直接插5V充电头就行。板载的LDO会把5V降到3.3V。但要注意如果红外发射管峰值电流较大LDO可能会发热。我实测用100Ω限流电阻时LDO温温的不烫手。如果用22Ω电阻建议在3.3V和GND之间并一个100μF的电解电容给瞬间电流提供缓冲。外壳我一开始用3D打印的盒子后来发现没必要直接用热熔胶把红外发射管和接收头固定在开发板两端塞进一个旧遥控器壳里用热熔胶封住。丑是丑了点但放在电视柜上没人看。如果你追求美观可以设计一个简单的亚克力外壳把发射管露出来就行。注意红外发射管和接收头不要挨得太近否则发射时的强信号会直接串到接收头导致自激。我一开始把两个管子并排放在一起结果一发射就死机后来把接收头用黑色热缩管套住只留正面问题就解决了。3. 固件开发与红外码学习发射实现3.1 开发环境搭建与ESP Web Tools刷写如果你不想装Arduino IDE可以直接用ESP Web Tools。这是一个基于Web Serial API的网页工具Chrome和Edge都支持。你只需要把ESP32-C3用USB线连到电脑打开ESP Web Tools的页面选择固件文件点击刷写就行。固件文件是编译好的.bin我一般用PlatformIO编译然后在.pio/build/esp32-c3-devkitm-1/firmware.bin找到。如果你要自己改代码那就得装PlatformIO或者Arduino IDE。PlatformIO的配置很简单在platformio.ini里写[env:esp32-c3-devkitm-1] platform espressif32 board esp32-c3-devkitm-1 framework arduino monitor_speed 115200 lib_deps crankyoldgit/IRremoteESP8266^2.8.6 bblanchon/ArduinoJson^6.21.3IRremoteESP8266库虽然名字里有ESP8266但完全兼容ESP32和ESP32-C3。ArduinoJson用来处理Web请求里的JSON数据。3.2 红外接收与解码学习模式的核心逻辑学习模式的代码逻辑是这样的当用户点击网页上的“学习”按钮ESP32收到请求后把红外接收引脚设置为输入模式启动IRrecv对象。然后在一个循环里不断调用irrecv.decode(results)如果解码成功就把results.value和results.decode_type存下来通过HTTP响应返回给浏览器。这里有个坑IRremote库的decode()函数会阻塞如果一直没收到信号它会一直等。所以我在循环里加了超时比如10秒没收到就返回错误。另外有些遥控器的码比较长比如空调遥控器一次按键会发送一整串数据IRremote库可能只解出第一部分。对于空调这种复杂设备我建议直接用irrecv.decode()配合results.bits和results.state来保存原始时序而不是只存一个码值。#include IRremoteESP8266.h #include IRrecv.h #include IRsend.h const uint16_t kRecvPin 4; const uint16_t kIrLedPin 3; IRrecv irrecv(kRecvPin); IRsend irsend(kIrLedPin); decode_results results; void setup() { irrecv.enableIRIn(); irsend.begin(); } void loop() { if (irrecv.decode(results)) { Serial.println(results.value, HEX); Serial.println(results.decode_type); irrecv.resume(); } }上面是最简化的接收代码。实际项目中我会把解码结果通过WebSocket推送到浏览器而不是让浏览器轮询。3.3 红外发射与码值存储发射就简单了拿到码值和协议类型后调用对应的发送函数if (protocol NEC) { irsend.sendNEC(code, 32); } else if (protocol Sony) { irsend.sendSony(code, 12); } else if (protocol RC5) { irsend.sendRC5(code, 12); }码值存储我用的是LittleFSESP32-C3的Arduino核心自带LittleFS支持。存成一个JSON文件结构大概是{ devices: [ { name: 客厅空调, buttons: [ {name: 开机, protocol: NEC, code: 0x12345678, bits: 32}, {name: 关机, protocol: NEC, code: 0x87654321, bits: 32} ] } ] }每次学习新按钮就读取JSON追加一个对象再写回去。注意LittleFS的写入次数有限不要频繁写最好在内存里维护一份定期保存。实操心得ESP32-C3的LittleFS在断电时可能丢失最后几次写入所以我在每次保存后调用LittleFS.commit()确保数据落盘。另外JSON文件不要太大超过4KB解析会变慢建议每个遥控器单独一个文件。4. Web控制界面与交互设计4.1 前端页面的轻量化实现ESP32-C3的内存有限跑不了React或Vue所以前端我用纯HTML加原生JavaScript。页面结构很简单一个设备列表点击设备展开按钮列表每个按钮绑定一个点击事件发送HTTP POST到/send请求体是{protocol:NEC,code:0x12345678,bits:32}。为了好看一点我用了一个极简的CSS框架叫Water.css只有几KB自动给HTML元素加样式不需要写class。页面加载速度很快手机浏览器打开几乎秒开。!DOCTYPE html html head meta charsetutf-8 meta nameviewport contentwidthdevice-width, initial-scale1 link relstylesheet hrefhttps://cdn.jsdelivr.net/npm/water.css2/out/water.css titleRemodazWifi 遥控器/title /head body h1RemodazWifi/h1 div iddevices/div script fetch(/devices).then(r r.json()).then(data { // 渲染设备列表 }); /script /body /html注意这个页面是存在ESP32的LittleFS里的不是从外部CDN加载。Water.css我提前下载好存到LittleFS的/www/style.css页面里用相对路径引用。这样即使没有外网页面也能正常显示。4.2 HTTP接口设计与WebSocket实时通信HTTP接口我设计了几个接口方法功能/devicesGET获取所有设备和按钮/learnPOST进入学习模式返回解码结果/sendPOST发送红外码/savePOST保存学习到的码/deletePOST删除某个按钮学习模式如果用HTTP轮询体验很差因为用户不知道什么时候开始接收。所以我用了WebSocket。ESP32-C3跑WebSocket服务器用WebSocketsServer库很轻量。用户点击“学习”后浏览器建立WebSocket连接ESP32收到连接后开始接收红外信号解码成功后通过WebSocket推送消息浏览器收到后弹出输入框让用户命名按钮。WebSocketsServer webSocket WebSocketsServer(81); void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { if (type WStype_TEXT) { String msg String((char*)payload); if (msg learn) { startLearning(num); } } }4.3 移动端适配与离线使用移动端适配主要靠viewport meta标签和响应式布局。按钮我设了最小高度48px方便手指点击。页面背景用深色晚上看不会刺眼。另外我把页面做成了PWA渐进式Web应用加了一个manifest.json和service worker这样用户可以把它添加到手机主屏幕打开时就像原生App一样没有浏览器地址栏。离线使用是刚需。因为遥控器不需要外网所以所有资源都本地化。service worker缓存了HTML、CSS、JS即使ESP32重启页面也能从缓存加载。不过要注意service worker的缓存更新比较麻烦我一般改版本号强制刷新。注意ESP32-C3的Web服务器并发能力有限不要同时打开太多页面。我实测同时3个设备访问就会变慢所以建议一个家庭只用一个控制页面。5. 常见问题排查与避坑经验实录5.1 红外发射距离短或时好时坏这是最常见的问题。原因通常有三个一是限流电阻太大发射管电流不足二是红外发射管质量差波长不对三是供电不稳发射瞬间电压跌落。排查方法先用手机摄像头对着红外发射管按发射按钮看摄像头画面里有没有紫白色闪光。如果有闪光但距离短说明电路没问题是功率不够减小限流电阻。如果没有闪光检查三极管引脚是否接错或者GPIO是否配置为输出。我踩过的坑买了一批便宜的940nm发射管结果发现波长偏差很大有的甚至偏到850nm接收头根本收不到。后来换了品牌货问题解决。所以不要贪便宜红外管选正规厂家的。5.2 学习模式解码失败或码值错误IRremote库虽然强大但有些遥控器的协议比较冷门比如某些空调的变长码。这时候decode()会返回UNKNOWN。解决办法是用irrecv.decode()的原始数据模式把整个时序存下来发射时用irsend.sendRaw()原样发出去。if (results.decode_type UNKNOWN) { irsend.sendRaw(results.rawbuf, results.rawlen, 38); }注意rawbuf里的数据是微秒级的rawlen是数组长度。存的时候要存成整数数组不要存成字符串否则解析很慢。另一个坑是红外接收头的供电。VS1838B对电源噪声很敏感如果和WiFi模块共用3.3VWiFi发射时的噪声会干扰接收。我一般在接收头的VCC和GND之间并一个0.1μF的陶瓷电容紧挨着接收头引脚效果很明显。5.3 WiFi连接不稳定或无法访问Web页面ESP32-C3的WiFi是2.4GHz如果路由器开了双频合一有时候会连到5GHz上导致连不上。解决办法是在代码里强制指定WiFi频段或者把路由器的2.4GHz和5GHz分开命名。WiFi.begin(ssid, password); WiFi.setSleep(false); // 关闭省电模式提高响应速度另外ESP32-C3的IP地址默认是DHCP分配的每次重启可能变。我建议在路由器里给ESP32的MAC地址绑定一个固定IP或者在代码里设置静态IP。这样你收藏的页面地址就不会失效。如果Web页面打不开先ping一下IP如果ping不通说明WiFi没连上。检查串口输出的IP地址确认是否获取成功。如果ping通了但页面打不开可能是LittleFS没挂载成功检查LittleFS.begin()的返回值。5.4 常见问题速查表现象可能原因解决方法发射距离短限流电阻太大减小到33Ω或22Ω接收不到信号接收头接反确认OUT、GND、VCC顺序解码失败协议不支持用sendRaw原始模式WiFi连不上频段不对强制2.4GHz或分开SSID页面打不开LittleFS未挂载检查begin()返回值发射时死机电源跌落并100μF电容码值保存丢失未commit每次保存后commit()最后分享一个小技巧如果你家里有多个红外设备建议按房间分组比如“客厅”“卧室”“书房”每个房间一个设备列表。这样页面不会太长找按钮也快。另外空调遥控器的码值通常比较长建议单独存一个文件不要和电视遥控器混在一起否则JSON解析会变慢。这个项目我断断续续做了两个月从最初的面包板到现在塞在电视柜里稳定运行中间踩了不少坑但最终效果很满意。现在家里所有红外设备都用这一个遥控器控制手机、平板、电脑都能开网页操作再也不用满屋子找遥控器了。如果你也想做一个建议先从NEC协议的设备入手比如电视或机顶盒成功后再挑战空调这种复杂协议。