Article
Build a WiFi-Controlled Smart Curtain with ESP32
🪟
Off-the-shelf smart curtains usually require replacing the entire track system, at a steep price. With a stepper motor, a GT2 timing belt, and an ESP32, you can retrofit an automated puller onto your existing curtain track — opening curtains with the morning light, closing them on a schedule at night, or controlling them straight from your phone without getting out of bed.
How it works
- A stepper motor, fitted with a small pulley, pulls a timing belt connected to the curtain hook — as the motor turns, the belt moves and drags the curtain open or closed.
- The ESP32 drives the stepper motor through a driver (A4988 or TMC2209), tracking exactly where the curtain is by counting steps taken from a reference point of “fully closed” or “fully open” (established initially with a limit switch or manual calibration).
- The ESP32 runs a small web server that accepts open/close/set-percentage commands from an app or a phone browser, while also supporting a time-based schedule (e.g. open at 6:30am, close at 9:00pm) via NTP sync.
- Optionally add a light sensor (LDR) to auto-open the curtain once it’s bright enough outside instead of relying on a fixed time, adapting better across seasons.
Parts list
- An ESP32 (with WiFi, for the web server and NTP sync)
- A NEMA17 stepper motor (strong enough for a medium-sized curtain) and an A4988 or TMC2209 driver
- A GT2 timing belt and matching pulleys, or a pre-made DIY curtain-puller kit built around a stepper motor
- An optional light-dependent resistor (LDR) for auto-opening by daylight
- A 12V power supply for the motor driver, plus a separate 5V/3.3V regulator for the ESP32
- A mounting bracket/frame to attach the motor to the end of the existing curtain track
Sample code
#include <WiFi.h>
#include <WebServer.h>
#include <AccelStepper.h>
const char* WIFI_SSID = "your-wifi";
const char* WIFI_PASS = "your-wifi-password";
#define STEP_PIN 26
#define DIR_PIN 27
#define LDR_PIN 34
AccelStepper stepper(AccelStepper::DRIVER, STEP_PIN, DIR_PIN);
WebServer server(80);
const long STEPS_FULLY_CLOSED = 0;
const long STEPS_FULLY_OPEN = 8000; // calibrate to your actual track length
long currentTarget = STEPS_FULLY_CLOSED;
void moveTo(int percentOpen) {
percentOpen = constrain(percentOpen, 0, 100);
currentTarget = map(percentOpen, 0, 100, STEPS_FULLY_CLOSED, STEPS_FULLY_OPEN);
stepper.moveTo(currentTarget);
}
void handleOpen() { moveTo(100); server.send(200, "text/plain", "Opening curtain"); }
void handleClose() { moveTo(0); server.send(200, "text/plain", "Closing curtain"); }
void handleSetPercent() {
if (server.hasArg("value")) {
moveTo(server.arg("value").toInt());
}
server.send(200, "text/plain", "OK");
}
void setup() {
Serial.begin(115200);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) delay(500);
Serial.println(WiFi.localIP());
stepper.setMaxSpeed(1000);
stepper.setAcceleration(500);
server.on("/open", handleOpen);
server.on("/close", handleClose);
server.on("/set", handleSetPercent); // e.g. /set?value=50
server.begin();
}
void loop() {
server.handleClient();
stepper.run();
// Auto-open when it's bright out, if using an LDR (uncomment to enable)
// int light = analogRead(LDR_PIN);
// if (light > 2500 && currentTarget != STEPS_FULLY_OPEN) moveTo(100);
}
Common pitfalls
- No limit switch or initial reference point lets the “fully closed”/“fully open” positions drift over repeated runs due to step skipping — add a limit switch at one end of the track to recalibrate the reference point whenever the curtain hits it.
- Choosing an underpowered stepper motor relative to the curtain’s weight causes it to skip steps mid-run, throwing off the computed position — pick a NEMA17 with enough torque, or gear it down for extra pulling force if needed.
- Not setting the driver’s current limit correctly on the A4988/TMC2209 leaves the motor overheating or underpowered — always tune the current-limit trimpot to the motor’s spec before running it under real load.
- A web server with no authentication lets anyone on the LAN control the curtain — add basic auth or restrict access if the WiFi network isn’t fully trusted.
Extensions
Pair it with the Bluetooth presence detector project to auto-close the curtains when everyone’s left (saving on cooling), add voice control through Home Assistant, or add an outdoor temperature sensor to auto-close the curtains against harsh midday summer sun.
Tags:#esp32#home-automation#stepper-motor
Comments