Build Wifi-connected smart blinds using ESP32 and off-the-shelf components for <$50.
π Including...
- HTTP/REST API
- MQTT API & client
- Native Homekit integration
- Halts on home detection
- Web UI for controlling/configuring
These are some suggestions for hardware to use. The first link in each row is the product I used for the prototype. All links are non-affiliate and can be found elsewhere if you look around. Prices are estimates including shipping at the time I purchased and may have changed since then; I suggest searching around if so.
Part ~~~~~~~~ | Purchase Links ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | Notes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
---|---|---|
ESP32 |
β’ 32S Devkit "V1" (AliExpress, $4) β’ 32S Devkit M1 (Mouser, $8) β’ NodeMCU-32 (Amazon, $11) |
Circuit below wass using the first link. Pin assignments may vary between boards, often the same pins but more/less extra pins, or power pins are in different places. Be sure to check the pinout. |
Stepper Driver |
β’ A4988 (AliExpress, $2) β’ A4988 (Polulu, $6) β’ 5x A4988 (Amazon, $8) |
Other drivers would work too, currently only A4988 is tested (using FastAccelStepper). Be sure to set the current limit before attaching your motor. |
Stepper Motor |
β’ 1.5A 45Ncm (AliExpress, $18) 1.5A 42Ncm (StepperOnline, $5 at time of writing) β’ 1.7A 36Ncm (Polulu, $18) β’ 1.5A 42Ncm (Amazon, $10) |
This may be over/underpowered for your needs, depending on the weight of the blinds. See choosing a motor. Note: The newest 3d designs use a worm gear with a 30:1 reductions so a much smaller motor will work, but the movement speed is greatly reduced with that design. |
3.3V Step-down Regulator |
β’ 5-40V to 3.3V (AliExpress, $2) β’ D24V5Fx (Polulu, $5) β’ 10x 5-12V to 3.3v (Amazon, $9) |
Circuit below is using the one from AliExpress. Others may have different number of pins or pin orders, so check the pinout before wiring. |
Power supply |
β’ 24V 5A (AliExpress, $13) β’ 12V 5A (Polulu, $19) β’ 12V 5A (Amazon, $10) |
You can (and probably want to) use a voltage over the motor's rating. That's fine, you just need to stay below it's rated current. The current needed depends on motor rating and how many blinds you'll be moving simultaneously with the same power supply. |
DC barrel jack |
β’ 5.5mm x 2.1mm (AliExpress, $3) |
|
Microswitch |
β’ 5x (AliExpress, $2) β’ 1x (Polulu, $1) β’ 6x (Amazon, $6) |
Optional, used as a limit switch for the "fully open" position. Any switch could work here. |
Blinds |
β’ HOPPVALS (IKEA, $20-45) β’ TRIPPEVALS (IKEA, $40-60) |
My builds use cellular shades from IKEA, modified to wrap the cord around a 3D printed axis, instead of the retail spring mechanism. Any blinds that can be moved by wrapping a cord around an axis could easily be adapted. Most cordless blinds could be used with some modifications. Roller blinds would be the easiest in most cases. |
Schematic files are located in the pcb/
directory. Currently there is a devkit that can be fabricated that fits header pins and will use the following components. Check the parts list carefully, only the first item in each section will fit, since they are what I ordered, but other parts may not. The gerber output is a 2 layer board created with JLCPCB's CAM generator.
- 30 pin ESP32 devkit
- 3 pin buck converter (middle pin GND)
- 3 pin DC barrel jack
You'll also want some pin headers to solder on, both male and female. The parts are throughhole and the gerber has drill holes so you can skip them if you'd like, but I'd recommend at least making the A4988 removable in case you pop one.
- Remember to adjust the current limit of the A4988 BEFORE connecting it to the circuit -- keep the magic smoke inside.
- The pins can be set via the web UI, feel free to use different ones.
- Be sure not to apply 12/24V to your ESP or
Vc
(or VDD) of the driver IC. It only goes toVm
to power the motor, everything else can use 3.3v. - A4988's
RST
andSLP
can be connected to each other if you don't care about the ability to sleep the motor.
Included in the /3d
directory are 2 revisions of printable designs for WBlinds, crafted specifically for Ikea HOPPVALS. The same 2 mechanisms could be reworked for any other blinds. r1
has the added benefit of a worm gear mechanism, so power doesn't need to be constantly applied to keep the blinds in place, even with heavy materials.
These are screenshots of the design to give you an idea of how parts fit together. It may be useful to put a damper (Amazon | AliExpress) between the bracket and the stepper to cut down on vibration & noise.
Default
The
3d/r1/cam/
contains an experimental (and not great) barrel cam mechanism to guide the string along an alternating path around the center spool, functionally similar to what a fishing reel does with a string guide. This can be sticky with 3D-printed parts, and with a good placement ofstring-guide.stl
s, it isn't necessary to keep the spooling even.
With optional cam exposed
- Connect ESP32 to your computer via USB
git clone https://github.com/maxakuru/WBlinds.git
cd WBlinds && yarn
yarn build:cpp
yarn flash
- On first boot an access point will be started, connect to it:
- SSID:
WBlinds-XXXXXX
- Default password:
Wbl1nds-1337
- SSID:
- After connecting, a webpage should open to configure settings. If not, go to http://4.3.2.1/settings?tab=gen
- Configure SSID, password, pins, etc. and tap "SAVE"
- You will be disconnected from the AP. Find the IP address of the device on your network and enter it in your browser.
Steps 3-5 assume you have PlatformIO, Node.js, and Yarn installed. You can also flash without Node/Yarn by using the PlatformIO VSCode Plugin. You can also use the scripts to do other stuff.
These APIs are in flux!
PUT <host:port>/api/state
// All fields optional
{
"tPos": 50, //[0-100] (target position %, starts move)
"pos": 50, // [0-100] (current position %)
"accel": 99999, // [0-UINT32_MAX] (steps/s/s)
"speed": 1000, // [0-something reasonable] (Hz)
}
GET <host:port>/api/state
// 200
{
"tPos": 50,
"pos": 50,
"accel": 99999,
"speed": 1000
}
POST <host:port>
{
"op": "up" | "down" | "stop" | "sleep"
}
PUT <host:port>/api/settings
// All fields optional
// NOTE: data is sent in body as JSON. If that isn't acceptable for your use, set sensitive fields at compile time.
{
gen: {
deviceName: "WBlinds",
mdnsName: "WBlinds",
emitSync: true,
ssid: "My Network",
pass: "supersecurepassword"
},
hw: {
pStep: 19,
pDir: 18,
pEn: 23,
pSleep: 21,
pReset: 3,
pMs1: 1,
pMs2: 5,
pMs3: 17,
pHome: 4,
cLen: 1650,
cDia: 0.1,
axDia: 15,
stepsPerRev: 200,
res: 16,
},
mqtt: {
enabled: true,
host: "192.168.1.99",
port: 1883,
topic: "WBlinds",
user: "max",
pass: "mysupersecurepassword"
}
}
GET <host:port>/api/settings
{
gen: {
deviceName: "WBlinds",
mdnsName: "WBlinds",
ssid: "Some SSID",
mac: "abcdef123456",
emitSync: true
},
hw: {
pStep: 19,
...
res: 16
},
mqtt: {
enabled: true,
host: "192.168.1.99",
port: 1883,
topic: "WBlinds",
user: "max"
}
}
POST <host:port>/api/restore
// 202, ESP resets once completed
// This wipes Homekit config & state files
WBlinds subscribes to a wildcard based on the topic provided.
For example, a topic name blinds/living_room
will subscribe to blinds/living_room/#
.
The topic defines a number of actions without any payload, for example:
topic="blinds/living_room/up"
topic="blinds/living_room/down"
topic="blinds/living_room/stop"
topic="blinds/living_room/sleep"
Some topics can specify additional data:
topic="blinds/living_room/move"
body="{\"pos\":50,\"speed\":1000,\"accel\":1000}"
Where pos
is %, speed
is Hz, and accel
is steps/s^2.
Native Homekit integration allows the ESP32 to be detected as a device without a bridge/emulator. This mode requires percentage-based position values, so it's important to calibrate the settings first.
The default Homekit pin is 111-22-333
, and it will be correctly identified as a "window covering".
There is a Calibrate
button at $HOST/settings?tab=gen
that will guide you through a 2 step calibration to find the correct bottom and top positions for the blinds on first setup. These settings will be persisted across power cycles and flashes of new firmwares, as long as you use the same partition.
Pin | Use | Description |
---|---|---|
17 |
pDir | Controls direction (optional) |
5 |
pStep | Controls steps via PWM with A4988 (required) |
18 |
pSlp | Allows putting motor to sleep to save most power possible. (optional) |
23 |
pEn | Allows enabling/disabling motor with A4988. By default the motor is disabled and enabled between uses which lowers power consumption. (optional) |
19 |
pRst | Reset pin (optional) |
1 |
pMs1 | Microstep resolution pin 1, using microsteps can decrease sound (optional) |
3 |
pMs2 | Microstep resolution pin 2 (optional) |
21 |
pMs3 | Microstep resolution pin 3 (optional) |
4 |
pHomeSw | Home trigger switch, recommended as a way to hard stop when the blinds reach the fully contracted position to avoid damaging hardware when steps are skipped or malfunctions. (optional) |
There are 2 projects that make up this repo: the C++ ESP controller and a TypeScript-based web UI. The web UI is transpiled to Javascript, inlined into HTML, then gzipped/chunked into header files with a script. All generated header files as well as transpiled JS is included in the repo, so no additional build steps are needed.
These can be run with npm
or yarn
, I'm currently using yarn. None of these are required to build and flash the ESP bin, as mentioned above all the files are checked into git, so you can compile the C part however you like.
However, these scripts are used for pre-commit hooks, so if you want to contribute you'll need Node.js.
Build web UI in development/watch mode (outputs to public/
):
yarn dev
Build web UI minified (outputs to public/
, also runs build:uih
afterwards):
yarn build:ui
Only generate header files from public/
:
yarn build:uih
The following scripts depend on PlatformIO, and can be replaced with the toolbar quick actions in VSCode if you prefer.
Compile C++ (assumes *nix machine right now):
yarn build:cpp
Build & flash using pio:
yarn flash
Flash over-the-air (requires an initial flash while plugged in and WiFi connection established through settings):
yarn flash:ota $IP_OR_HOSTNAME
You can combine all build scripts with:
yarn build
I recommend using VSCode to build/flash. Eventually I plan to cut releases of precompiled binary that can be flashed directly, but for the time being you'll need to pull the code and compile it yourself. That is made very simple by the wonderful PlatformIO - I highly recommend using it with their VSCode plugin.
There are some other suggested plugins in the .vscode
directory; VSCode should suggest installing any missing plugin on first boot, but none are required.
Additional integrations can be built in by extending the WBlindsObserver
class and attaching to it, specifying the event flags you are interested in.
#include "defines.h"
#include "state.h"
#include "event.h"
#include <SpecialLib.h>
class SpecialLibIntegration : protected WBlindsObserver {
public:
explicit SpecialLibIntegration() {
EventFlags flags;
flags.pos_ = true;
flags.speed_ = true;
flags.accel_ = true;
flags.targetPos_ = true;
// ... any other flags you're interested in
State::getInstance()->Attach(this, flags);
};
~SpecialLibIntegration() override {
specialLib.teardown();
State::getInstance()->Detach(this);
};
void SpecialLibIntegration::handleEvent(const WBlindsEvent& event) {
// ... do something with the event
auto state = State::getInstance();
if(event.flags_.pos) {
int pos = state.getPosition();
specialLib.doStuffWithPosition(pos);
}
};
}
The /3d
directory contains some STL files that may be useful for building WBlinds. Those are designed for use with IKEA HOPPVALS, but would be close to the TRIPPVALS, too. There are 2 revisions:
r0
uses a direct drive mechanism with a center axle along the entire width of the blind bracket.r1
uses a worm gear and single spool in the center of the blinds bracket. This won't work with in window mounting.
Note: Like everything else, those designs are a WIP :)
TL;DR: putting it together.
Out of the several variables a stepper motor has I've narrowed it down to the most important for our purposes. It's worth mentioning that WBlinds could be built using a regular DC motor and an encoder. Stepper motors are cheap and widely available, largely thanks to 3D printing. They also simplify the design quite a bit, so they're what I will focus on.
A skipped step happens most often when:
- The motor isn't provided with enough volts/amps to move the load.
- The load is heavier than the stepper's holding torque.
- Power is switched off, the motor is "released", and the load is greater than the motor's detent torque.
- Resonance/vibrations.
What skipped steps means to us is that the current state the motor "thinks" it's at is actually off by the number of steps skipped. This is especially bad if you don't have a limit switch to avoid damage, but also just annoying for the end user.
#1 & #2 can be avoided by choosing a proper motor, power supply, and current limit for your stepper driver.
#3 can be avoided by using a high gear ratio, a brake, or a homing switch and rehoming on reset.
#4 is tricky to identify and to fix (you'll probably be fine Β―\_(γ)_/Β― ).
Not all stepper motors will have gears, but many do. Some also have a gearbox that increases the gear ratio. There are different kinds of gearboxes: planetary, worm, spur. Then there are different shapes of gears.. different gear profiles.. different tooth pitches etc.
For this guide we'll just think about gear ratios and planetary stepper motors.
Higher gear ratio benefits:
- Higher torque for less amperage
- Lower noise
- High holding torque (less drift when it's off)
- Fewer skipped steps (both from torque and resonance)
Drawbacks:
- Higher cost ($30+, or ~3-5x)
- Lower speed
- Often large or awkward shapes (for our purpose)
Considering our speed requirements are pretty low (or at least mine are), you could reasonably go with either. If you have large/heavy blinds it may make sense to look for a planetary stepper. I personally have been using regular NEMA17 steppers, but that's because I already had them.
Note: There's also a difference between permanent magnet (PM) planetary steppers and "hybrids".
If you find a good, inexpensive planetary stepper to include in the parts list, contact me!
Whether you choose a regular stepper or gearbox stepper, the same final choices come up: how fast it moves vs. how much weight it can move.
As speed increases torque (generally) decreases, you can see this in the torque curves of a regular stepper motor and planetary stepper.
You want to find a motor that has a high enough speed that you aren't waiting until sunset for your blinds to open, and a high enough torque that you aren't skipping steps or stalling at 90% open.
This won't be exact, but is a good enough estimate. I'll use the defaults from the included STLs and IKEA TRIPPEVALS, along with a constant speed. In practice, torque will be higher at the moment it starts to move, as acceleration is applied to go from stopped to max speed. I'll ignore it since the acceleration will be very low (as will top speed).
This is assuming a setup where a cord (A) or fabric (B) wraps around an axis that is directly driven by the motor.. something like these:
# A
|```````| __________________
| Motor |==|__||||______||||__| <- cord spooled (ie. TRIPPEVALS)
|_______| | || || |
| || || |
|___||________||___|
# B
|```````| __________________
| Motor |==|__________________| <- roll of fabric (ie. roller blinds)
|_______| | |
| Blocking window |
|__________________|
- Get some variables:
# constants
g = 9.8m/s^2
# The thicker the axis, the fewer rotations needed and faster it can move, at the cost of higher torque required.
Axis diameter (Da) = 15mm = 1.5cm
# Thickness of cord or fabric, since Da increases as the cord is wrapped around.
Cord diameter (Dc) = 0.5mm = 0.05cm
# Roughly length from top to bottom of window.
Cord Length (Lc) = 1650m
# You can use a luggage scale to get a rough estimate, or just guesstimate conservatively.
Maximum load (m) = 5kg
# Range of acceptable durations to go from fully opened to fully closed or vice versa. The faster it moves the louder it will be.
Slowest (t-) = 3min
Fastest (t+) = 1min
- Calculate revolutions to fully wrapped:
See handy calculator here
Dc - Da + sqrt[(Da - Dc)^2 + (4 * Dc * Lc) / pi]
Nrev = ------------------------------------------------
2 * Dc
Nrev = (1 - 15 + sqrt((15 - 1)^2 + (4 * 0.5 * 1650) / 3.14)) / (2 * 0.5)
Nrev ~= 21
- Calculate acceptable RPM range:
RPM = Nrev / t
RPM- = 21 / 3 = 7
RPM+ = 21 / 1 = 21
- Calculate peak torque:
# Some worst-case fudge here, in this example it barely impacts the result.
T = m * g * ((Nrev * Dc + Da) / 2)
T = 5 * 9.8 * ((30 * 0.0005 + 0.0015) / 2)
T = 0.404 N.m ~= 40N.cm
So, a motor that can pull 40N.cm at around 7 to 21 RPM is good for this example. Now you can browse motors and look at torque curves to find a suitable choice, like this $10 stepper (torque curve) or this $24 planetary (torque curve).