Automatic Wi-Fi Shades

#006 Automatic Wi-Fi Shades

last updated: 2/1/2025

Here is a timelapse of the shades moving. Raising takes 5m 30s to raise, about a minute to lower.

TLDR: Mini project to raise and lower my blinds on a timer. Uses a stepper motor and a ESP32 which fetches the time via wifi and also the open and close times from a Firebase DB.

Rationale

I like waking up to natural light, and I was bored because the semester hadn't started yet so I decided to see how long it would take me to do this little project. Yes, it is easy to raise them manually.

Electronics

I used an ESP32 that comes in the same form factor as an arduino (link) and a really old motor driver that uses an L298P (don't use this board for your projects, I just had one in an Arduino shield form factor) The whole thing is powered from a 19V laptop charger that can do 4 amps (overkill). The motor driver sits on top of the ESP32 and so there is no wiring excluding the screw terminals to motor and power supply.

Driving the stepper motor

The most interesting part of this project was driving the stepper motor. At first I just drove each phase in order but the motor was really loud. This was because the rotor would accelerate and decelerate for each step.

Unable to load shades/gears.JPG
Unable to load shades/cad.png
Unable to load shades/all.JPG
Unable to load shades/esp32.JPG

FOC

I understand the general working principle of FOC motor driving - and with two perpendicular phases it is so simple! Just a sin and cos needed to be applied. However the motor when driven with sin waves was not moving linearly. After a few days of pondering it hit me it could be that the relationship between duty cycle and current might not be proportional.

Unable to load shades/voltsVsDuty.png
It is clear that the relationship between duty cycle and current is not truly proportional. There is very little current until ~50% duty.

Sure enough, after writing a bit of code to find the required duty for a certain current, and then controlling for current, the motor drove almost silently! The gear noise is now what prevents faster operation.

Here's the relevant code below. Pretty simple! I used a 25kHz PWM which was not audible.


while (true) {
    // Calculate normalized positions on sine wave [0..1], offset coil B by 90°
    const double angle = fmod(degs, PI * 2); // NOLINT(*-narrowing-conversions)
    const double a = cos(angle);
    const double b = sin(angle) * dir;
    if (degs > 27000) {
        break;
    }
    // Set coil A
    ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, limit(a));
    ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);
    digitalWrite(dir_a, a > 0);
    // Set coil B
    ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1, limit(b));
    ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1);
    digitalWrite(dir_b, b > 0);
    degs += PI / 100;
    delayMicroseconds(dir == 1 ? 200 : 100);
}
Go To Top Guess what? I made this website! Check it and many of my other projects on my github.