用一块ESP32,把普通窗帘变“智能”:从零开始打造可远程控制的窗帘系统
你有没有过这样的经历?冬天赖床不想起,阳光却早早照进房间;出门前总担心窗帘没关,回头还得折返一趟。其实,这些小烦恼背后藏着一个很典型的智能家居需求——能不能让窗帘自己动起来?
好消息是,现在不需要花几千块买成品智能窗帘。只要一块几十元的ESP32 开发板,加上一些常见的电子模块,就能亲手做一个支持手机远程控制、未来还能自动运行的智能窗帘控制器。
这篇文章不讲空泛概念,也不堆砌术语,我会像朋友聊天一样,带你一步步走完整个项目流程:从选什么电机、怎么接线,到如何通过手机网页一键开关窗帘,甚至为它加上自动保护机制。哪怕你是第一次碰单片机,也能照着做出来。
为什么选 ESP32?因为它真的省事
市面上能做物联网的开发板不少,Arduino、STM32、树莓派Pico……但如果你目标是“连Wi-Fi”,那ESP32 是目前最省心的选择。
它由乐鑫科技出品,最大的亮点就是——Wi-Fi 和蓝牙功能直接集成在芯片里。不像 Arduino Uno 得额外加个 Wi-Fi 模块(比如 ESP-01),又要供电又要调试通信,麻烦不说还容易出问题。
ESP32 自带双核处理器,主频最高 240MHz,跑 FreeRTOS 都绰绰有余。更重要的是,它的 GPIO 引脚多达 34 个,还内置了 PWM、ADC、I2C、SPI 等常用外设,几乎不用外扩就能完成大多数 IoT 小项目。
最关键的一点:它完全兼容Arduino IDE。这意味着你可以用熟悉的 C++ 写代码,调用现成的库快速实现联网功能,不用啃底层寄存器。
举个例子,下面这段代码就能让它连上家里的 Wi-Fi:
#include <WiFi.h> const char* ssid = "你的Wi-Fi名称"; const char* password = "你的密码"; void setup() { Serial.begin(115200); WiFi.begin(ssid, password); Serial.print("正在连接 "); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\n连接成功!"); Serial.print("本机IP地址:"); Serial.println(WiFi.localIP()); }就这么几行,ESP32 就加入了家庭局域网。有了 IP 地址,接下来就可以接收指令了——这才是远程控制的基础。
而且这颗芯片功耗控制也不错,深度睡眠模式下电流只有 5μA,适合长期通电部署。虽然我们这个项目不会让它睡觉,但至少说明它是为物联网场景认真设计过的。
电机怎么动起来?L298N 驱动模块全解析
光会联网不行,还得让窗帘“动”起来。通常有两种方式:用直流减速电机带动滑轨,或者用步进电机精准定位。考虑到成本和安装难度,我们这里选择前者——12V 直流减速电机 + L298N 驱动板。
为什么不能直接用 ESP32 控制电机?很简单:IO 口输出能力太弱了。ESP32 的每个引脚最多只能输出约 12mA 电流,而电机启动瞬间可能需要几百毫安甚至更高。强行驱动轻则失控,重则烧毁芯片。
所以必须借助中间“放大器”——也就是L298N 模块。别看它长得像个积木块,核心是一个 H 桥电路,可以通过四个开关管的不同组合来改变电流方向,从而控制电机正转、反转或刹车。
它的逻辑也很直观:
-IN1=HIGH,IN2=LOW→ 正转(开帘)
-IN1=LOW,IN2=HIGH→ 反转(关帘)
-IN1=LOW,IN2=LOW→ 刹车
-ENA接 PWM 信号 → 调速
更贴心的是,L298N 支持 3.3V~5V 逻辑电平输入,正好和 ESP32 匹配,不需要电平转换。
接线也很简单:
- ESP32 的 GPIO18 → L298N 的 IN1
- GPIO19 → IN2
- GPIO21 → ENA(用于 PWM 输出)
- L298N 的 OUT1 和 OUT2 接电机两极
- 外接 12V 电源给 L298N 供电
-千万记得把 ESP32 和 L298N 的 GND 连在一起,否则信号不通
软件方面,我们可以利用 ESP32 内置的 LEDC 模块生成 PWM 波。这个模块本来是给 LED 调光用的,但拿来控电机也完全没问题。
#define IN1 18 #define IN2 19 #define ENA 21 void setupMotor() { pinMode(IN1, OUTPUT); pinMode(IN2, OUTPUT); pinMode(ENA, OUTPUT); ledcSetup(0, 5000, 8); // 通道0,5kHz频率,8位分辨率 ledcAttachPin(ENA, 0); // 把ENA绑定到PWM通道0 } void openCurtain() { digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); ledcWrite(0, 180); // 占空比 ~70%,中速开启 } void closeCurtain() { digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH); ledcWrite(0, 180); } void stopMotor() { digitalWrite(IN1, LOW); digitalWrite(IN2, LOW); ledcWrite(0, 0); }你会发现,ledcWrite(0, 180)实际上设置了 180/255 ≈ 70% 的电压输出,相当于降低了电机转速。这样启停更柔和,对机械结构更友好。
手机怎么控制?做个网页就行,不用 App!
很多人以为远程控制就得开发 App,其实大可不必。既然 ESP32 已经接入局域网,完全可以让它自己当一个小 Web 服务器,手机浏览器打开就能操作。
这种方式有几个明显优势:
- 不用装 App,iOS 和 Android 都能用;
- 页面可以自定义按钮、状态提示;
- 后续扩展方便,比如加入定时任务或传感器数据展示。
我们用 Arduino 环境下的WebServer库来实现。它足够轻量,运行在 ESP32 上毫无压力。
先注册几个 URL 路由:
-/→ 返回控制页面
-/open→ 执行开帘
-/close→ 执行关帘
-/stop→ 停止电机
完整代码如下:
#include <WiFi.h> #include <WebServer.h> WebServer server(80); String htmlPage = R"( <!DOCTYPE html> <html> <head> <title>智能窗帘</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> body { font-family: Arial; text-align: center; margin-top: 50px; } button { font-size: 18px; padding: 15px; margin: 10px; width: 200px; border-radius: 10px; background: #007bff; color: white; border: none; } </style> </head> <body> <h1>窗帘控制面板</h1> <button onclick="send('/open')"> открывание </button> <button onclick="send('/close')"> закрывание </button> <button onclick="send('/stop')"> Стоп </button> <script> function send(cmd) { fetch(cmd).then(() => alert('Команда отправлена')); } </script> </body> </html> )"; void handleRoot() { server.send(200, "text/html", htmlPage); } void handleOpen() { openCurtain(); server.send(200, "text/plain", "Открываю штору..."); } void handleClose() { closeCurtain(); server.send(200, "text/plain", "Закрываю штору..."); } void handleStop() { stopMotor(); server.send(200, "text/plain", "Мотор остановлен."); } void setupServer() { server.on("/", HTTP_GET, handleRoot); server.on("/open", HTTP_GET, handleOpen); server.on("/close", HTTP_GET, handleClose); server.on("/stop", HTTP_GET, handleStop); server.begin(); Serial.println("Веб-сервер запущен"); }注意看那个<script>标签里的fetch(),它是现代浏览器发起异步请求的方式,点击按钮后不会刷新页面,用户体验更好。
上传代码后,打开串口监视器,你会看到类似这样的输出:
Connected! IP Address: 192.168.31.105 HTTP server started然后拿出手机,确保连接同一个 Wi-Fi,打开浏览器输入这个 IP 地址,就能看到控制界面了!点击“打开窗帘”,窗帘就开始移动。
实际搭建时要注意哪些坑?
想法很美好,落地总有意外。我在实际组装过程中踩过几个典型坑,分享给你避雷:
⚠️ 坑一:电机堵转发热严重,差点烧掉
一开始我没加限位开关,测试时忘了及时停止,电机一直顶着轨道末端转。几分钟后 L298N 发烫得吓人,摸上去快60℃了。
解决办法:
- 加装两个微动开关,分别装在轨道两端。当窗帘开合到位时触发开关,程序检测到信号就自动停机。
- 或者用电流采样模块(如 ACS712)监测电机电流,异常升高时判断为堵转,立即断电。
示例代码片段(使用限位开关):
#define LIMIT_OPEN 4 // 到达最大开度时闭合 #define LIMIT_CLOSE 5 void setup() { pinMode(LIMIT_OPEN, INPUT_PULLUP); pinMode(LIMIT_CLOSE, INPUT_PULLUP); } // 修改开帘函数 void openCurtain() { if (digitalRead(LIMIT_OPEN) == LOW) { Serial.println("已到最大开度"); return; } // 正常启动电机... }⚠️ 坑二:电机启停导致 ESP32 复位
这是因为共用电源引起的电压波动。电机启动瞬间电流突增,造成电源电压跌落,ESP32 供电不足自动重启。
解决办法:
- 给 ESP32 单独用 USB 供电(比如插充电头),不要从 L298N 取电;
- 在电源端并联一个 1000μF 的电解电容,起到稳压缓冲作用;
- 使用带稳压输出的 DC-DC 模块(如 AMS1117-3.3V)单独供电。
⚠️ 坑三:Wi-Fi 连不上,IP 获取失败
常见于路由器开启了 MAC 过滤或 DHCP 分配异常。
排查步骤:
- 串口打印WiFi.status()查看具体错误码;
- 尝试将 ESP32 设置为 AP 模式自建热点,确认模块本身 Wi-Fi 功能正常;
- 检查 SSID 和密码是否含中文或特殊字符;
- 添加超时机制避免无限等待:
int timeout = 0; while (WiFi.status() != WL_CONNECTED && timeout < 20) { delay(500); timeout++; } if (timeout >= 20) { Serial.println("Wi-Fi连接超时,进入AP模式"); WiFi.softAP("CurtainCtrl", "12345678"); }还能怎么升级?这些扩展思路值得尝试
基础版实现了手机控制,但这只是起点。真正的智能,应该是“不用动手”。
以下是一些低成本升级方向:
🌞 光照感应自动开合
加一个 BH1750 数字光照传感器(I2C 接口),白天光线强时自动开帘,傍晚关闭。代码不到 20 行就能搞定。
⏰ 定时任务
配合 NTP 时间同步,设定每天早上 7 点自动开帘唤醒,晚上 10 点关闭遮光。
☁️ 接入 Home Assistant
改用 MQTT 协议通信,把窗帘变成 HA 中的一个实体,与其他设备联动(如“播放音乐时缓缓拉开窗帘”)。
🔊 语音控制
接入 Alexa 或小爱同学,说一句“打开窗帘”即可执行。
📊 状态反馈
加编码器或霍尔传感器记录位置,下次知道该开多大,而不是每次都全开全关。
结语:动手的意义,在于理解每一个细节
这个项目总成本不到百元:ESP32 约 30 元,L298N 模块 10 元,12V 电源适配器 20 元,电机根据行程长短几十到百元不等。比起动辄上千的成品智能窗帘,性价比极高。
更重要的是,你不再只是一个“用户”,而是真正理解了“智能”背后的逻辑:
从物理层的电路连接,到控制信号的生成,再到网络协议的交互,每一环都掌握在自己手中。
当你躺在床上用手机轻轻一点,看着窗帘缓缓滑开,透进第一缕晨光时,那种成就感,远比买一个“即插即用”的产品来得真实。
如果你也在寻找一个入门物联网的实践项目,不妨就从这个窗帘控制器开始。
硬件简单、逻辑清晰、成果可见,最重要的是——做完就能用。
如果你在实现过程中遇到任何问题,欢迎留言交流。也可以告诉我你想让它支持哪些新功能,我们一起想办法实现。