Creating a Servo-Controlled Automated Blinds System with Raspberry Pi

Micro Servo Motor with Raspberry Pi / Visits:4

Why I Ditched My $200 Commercial Blinds Controller

Last summer, I sat in my living room squinting at my laptop screen while the afternoon sun blasted through my west-facing windows. My expensive “smart” blinds—purchased from a big-box retailer—had failed to close on schedule for the third time that week. The cloud dependency was flaky, the app was bloated, and the motor sounded like a dying wasp.

So I ripped them out. Literally.

What I replaced them with cost me about $38 in parts, took one weekend to build, and has been running flawlessly for six months. The secret sauce? A micro servo motor—specifically, an SG90-class unit—paired with a Raspberry Pi Zero 2 W. This article is the complete blueprint for building your own servo-controlled automated blinds system, with every gotcha, wiring diagram, and code snippet I wish I’d had on day one.


The Core Hardware: Why Micro Servo Motors Are Perfect for Blinds

Let’s talk about the star of this show. A micro servo motor (the SG90, MG90S, or similar) is a tiny DC motor with a built-in gearbox, feedback potentiometer, and control circuitry. It’s designed for precise angular positioning—typically 0° to 180°—and that’s exactly what venetian blinds or horizontal slat blinds need.

Key Specs That Matter for Blinds

  • Torque: The SG90 delivers about 1.8 kg·cm at 5V. That’s enough to rotate a typical 60-inch blind wand with a gentle pull, but not enough to force a stuck mechanism. For heavier blinds, step up to an MG90S (2.2 kg·cm) or a metal-gear MG996R.
  • Rotation Range: Standard servos cap at ~180°. If your blinds require more than half a turn, you’ll need a continuous-rotation servo or a gear reduction. Most wand-operated blinds only need 90°–120° of twist, so we’re golden.
  • Power Draw: At idle, a servo draws ~10mA. Under load, it can spike to 250mA or more. The Raspberry Pi’s 5V rail can handle one servo fine, but two or more? You’ll want a separate BEC (battery eliminator circuit) or a small 5V 2A adapter.

Hot Take: The micro servo motor is the unsung hero of DIY home automation. It’s cheap, precise, and has a ridiculously simple control protocol (PWM). No stepper drivers, no encoders, no closed-loop PID nightmares. Just send a pulse width, and it holds position.


System Architecture: The 30,000-Foot View

Before we touch a single jumper wire, here’s how the whole system fits together:

[Light Sensor (BH1750)] --> [Raspberry Pi Zero 2 W] --> [PCA9685 PWM Driver] --> [Micro Servo Motor] --> [Blind Wand] ^ | [RTC Module (DS3231)] ^ | [Wi-Fi / MQTT / Local Web UI]

The Pi runs a Python script that does three things:

  1. Reads ambient light from a digital lux sensor.
  2. Checks the time from a battery-backed RTC (so it works even after a power outage).
  3. Decides whether to open, close, or partially tilt the blinds based on your rules.

The decision is sent as a PWM signal to the servo, which physically rotates the blind wand via a 3D-printed coupler.


Wiring the Micro Servo Motor to the Pi (Without Letting the Magic Smoke Out)

Here’s the wiring table. Note that I’m using a PCA9685 breakout board because it handles up to 16 servos and only needs two I2C pins from the Pi. If you’re building a single-blind prototype, you can skip the PCA9685 and use a GPIO pin directly—but you’ll lose precise timing control.

Direct Connection (1 Servo, No Extra Board)

| Servo Wire | Raspberry Pi GPIO | Notes | |------------|-------------------|-------| | Red (5V) | Pin 2 (5V) | Use a separate 5V supply if you add a second servo | | Brown (GND)| Pin 6 (GND) | Common ground is mandatory | | Orange (Signal) | GPIO 18 (Pin 12) | Hardware PWM-capable pin |

The “Proper” Way (PCA9685 + Multiple Servos)

| PCA9685 Pin | Raspberry Pi GPIO | Notes | |-------------|-------------------|-------| | VCC | Pin 1 (3.3V) | Logic power for the board | | GND | Pin 6 (GND) | Common ground | | SDA | GPIO 2 (Pin 3) | I2C data | | SCL | GPIO 3 (Pin 5) | I2C clock | | V+ | 5V external supply | Powers the servos, NOT the Pi |

Then connect each servo’s signal wire to channels 0–15 on the PCA9685.

The Grounding Rule You Cannot Break

Never power the servo from the Pi’s 3.3V rail. The servo’s stall current will brown-out the Pi’s CPU, causing random reboots and corrupted SD cards. I learned this the hard way after killing two microSD cards in one afternoon.


The 3D-Printed Coupler: Connecting a Micro Servo to a Blind Wand

This is the part everyone forgets. You can’t just tape a servo horn to the blind wand—it will slip, strip, and make you curse.

Design Requirements

  • Inner diameter: Must match your blind wand (typically 6mm or 8mm).
  • Outer shape: A hexagon or spline that fits the servo horn’s spline teeth.
  • Set screw: A small M2 or M3 screw to lock the coupler onto the wand.

I designed a simple two-piece clamp in Fusion 360:

  1. Bottom piece: Slides over the servo horn, has a 6.5mm hole in the center.
  2. Top piece: A cap that screws down, compressing a rubber O-ring around the wand.

Print it in PETG or ABS—PLA will deform in a hot window over time. If you don’t have a printer, you can use a thick-walled heat-shrink tube and a hose clamp, but it’s janky.

Mounting the Servo

You need a rigid bracket. I used a simple L-bracket screwed into the window frame’s top trim. The servo sits horizontally, with the horn pointing straight down. The coupler then grips the vertical wand. This works for both tilt-only blinds and lift-and-tilt systems (though for lift systems, you’ll need a continuous-rotation servo and a different coupler).


The Control Software: From PWM to “Smart” Logic

Step 1: Test the Servo with Python

Save this as servo_test.py and run it. It sweeps the servo from 0° to 180° and back.

python import RPi.GPIO as GPIO import time

SERVOPIN = 18 GPIO.setmode(GPIO.BCM) GPIO.setup(SERVOPIN, GPIO.OUT)

pwm = GPIO.PWM(SERVO_PIN, 50) # 50 Hz = 20ms period pwm.start(0)

def set_angle(angle): # Map angle (0-180) to duty cycle (2.5-12.5) duty = 2.5 + (angle / 180.0) * 10 pwm.ChangeDutyCycle(duty) time.sleep(0.5) pwm.ChangeDutyCycle(0) # Stop sending signal to avoid jitter

try: while True: for angle in range(0, 181, 10): setangle(angle) for angle in range(180, -1, -10): setangle(angle) except KeyboardInterrupt: pwm.stop() GPIO.cleanup()

Critical Note: The pwm.ChangeDutyCycle(0) line is essential. If you leave the duty cycle at a non-zero value, the servo will constantly fight to hold position, drawing current and buzzing. By setting it to 0 after the servo reaches its target, you let the servo’s internal potentiometer hold position passively.

Step 2: Calibrate Your Blind’s Range

Every blind is different. My IKEA Tupplur blinds need exactly 78° to go from fully open to fully closed. Yours might need 110°.

Here’s a calibration script that lets you type an angle and watch the servo respond:

python import RPi.GPIO as GPIO import time

SERVOPIN = 18 GPIO.setmode(GPIO.BCM) GPIO.setup(SERVOPIN, GPIO.OUT) pwm = GPIO.PWM(SERVO_PIN, 50) pwm.start(0)

def set_angle(angle): duty = 2.5 + (angle / 180.0) * 10 pwm.ChangeDutyCycle(duty) time.sleep(0.3) pwm.ChangeDutyCycle(0)

try: while True: angle = float(input("Enter angle (0-180): ")) if 0 <= angle <= 180: set_angle(angle) else: print("Out of range") except KeyboardInterrupt: pwm.stop() GPIO.cleanup()

Run this, manually rotate the blind wand with your hand to find the open and closed positions, then note the servo angles. Store them in a config file.

Step 3: The Full Automation Logic

Here’s the meat of the system. This script does the following:

  • Reads light every 30 seconds.
  • If light > 500 lux and time is between 10 AM and 6 PM, close blinds to 70% (partial tilt).
  • If light > 1000 lux, close fully.
  • If light < 200 lux, open fully.
  • Override via MQTT or a simple web form.

python import time import board import busio import adafruit_bh1750 from datetime import datetime import RPi.GPIO as GPIO

--- Servo Setup ---

SERVOPIN = 18 GPIO.setmode(GPIO.BCM) GPIO.setup(SERVOPIN, GPIO.OUT) pwm = GPIO.PWM(SERVO_PIN, 50) pwm.start(0)

Calibration values (from Step 2)

ANGLEOPEN = 10 ANGLECLOSED = 88 ANGLE_PARTIAL = 45

def set_angle(angle): duty = 2.5 + (angle / 180.0) * 10 pwm.ChangeDutyCycle(duty) time.sleep(0.3) pwm.ChangeDutyCycle(0)

--- Light Sensor Setup ---

i2c = busio.I2C(board.SCL, board.SDA) sensor = adafruit_bh1750.BH1750(i2c)

--- Main Loop ---

while True: try: lux = sensor.lux now = datetime.now() hour = now.hour

    if 10 <= hour <= 18:         if lux > 1000:             set_angle(ANGLE_CLOSED)             print(f"{now} - Bright ({lux} lux) -> Closed")         elif lux > 500:             set_angle(ANGLE_PARTIAL)             print(f"{now} - Medium ({lux} lux) -> Partial")         else:             set_angle(ANGLE_OPEN)             print(f"{now} - Dim ({lux} lux) -> Open")     else:         set_angle(ANGLE_OPEN)         print(f"{now} - Night -> Open")      time.sleep(30) except KeyboardInterrupt:     GPIO.cleanup()     break 

Step 4: Adding an RTC for Time-Aware Logic

The Pi Zero 2 W has no battery-backed clock. If it loses power and comes back without Wi-Fi, it thinks it’s January 1, 1970. That breaks the time-based logic above.

Add a DS3231 RTC module:

  1. Wire VCC to 3.3V, GND to GND, SDA to GPIO 2, SCL to GPIO 3.
  2. Enable I2C in raspi-config.
  3. Install python3-smbus and python3-adafruit-ds3231.
  4. Set the RTC time once: sudo hwclock -w.

Then in your script, read from the RTC instead of datetime.now():

python import adafruitds3231 rtc = adafruitds3231.DS3231(i2c)

Replace datetime.now() with:

currenttime = rtc.datetime hour = currenttime.tm_hour


Advanced: Smooth Motion with Acceleration Curves

A micro servo motor jerking from 0° to 90° in one jump will snap your blind wand’s plastic gears eventually. Real blinds move smoothly. So should yours.

Instead of a single set_angle() call, break the motion into 5° increments with a 20ms delay. Better yet, use a cosine interpolation curve:

python import math

def smoothmove(targetangle, steps=20): currentduty = 2.5 # Starting at 0° targetduty = 2.5 + (target_angle / 180.0) * 10

for i in range(1, steps + 1):     # Ease-in-out curve     t = i / steps     eased = (1 - math.cos(t * math.pi)) / 2     duty = current_duty + (target_duty - current_duty) * eased     pwm.ChangeDutyCycle(duty)     time.sleep(0.02)  pwm.ChangeDutyCycle(0) 

This reduces stress on the servo and the blind mechanism. It also looks way cooler when someone watches the blinds move.


Power Management: Don’t Starve Your Micro Servo

Here’s the dirty secret about micro servos: they draw far more current than the Pi’s regulator expects. If you power the servo from the Pi’s 5V pin, you’ll see voltage dips that corrupt your SD card or crash the Python process.

The Bulletproof Power Setup

  1. Separate 5V 2A supply for the servo (a phone charger works).
  2. Common ground between the Pi and the servo supply.
  3. A 470µF electrolytic capacitor across the servo’s power pins to absorb stall spikes.

plaintext [Wall Adapter 5V 2A] --> [Servo V+] [Servo GND] --> [Pi GND] [Cap +] --> [Servo V+] [Cap -] --> [Servo GND]

The Pi stays on its own supply. The only shared connection is ground, which is mandatory for signal integrity.


Wi-Fi Control: The MQTT Integration

Now that the servo works locally, let’s make it remotely controllable. I use MQTT with a simple dashboard on my phone.

Install Mosquitto Broker (Optional, on Another Pi or a Cloud VPS)

bash sudo apt install mosquitto mosquitto-clients

Python MQTT Client

python import paho.mqtt.client as mqtt

BROKER = "192.168.1.100" TOPIC = "home/blinds/living_room"

def onmessage(client, userdata, msg): command = msg.payload.decode() if command == "OPEN": smoothmove(ANGLEOPEN) elif command == "CLOSE": smoothmove(ANGLECLOSED) elif command.startswith("SET"): angle = int(command.split("")[1]) smooth_move(angle)

client = mqtt.Client() client.onmessage = onmessage client.connect(BROKER) client.subscribe(TOPIC) client.loop_forever()

Now you can send mosquitto_pub -h 192.168.1.100 -t home/blinds/living_room -m "SET_45" from your laptop, and the blinds will tilt to 45°.


The 3D-Printed Enclosure: Making It Look Less Like a Science Fair Project

You’ve got a servo, a Pi, and a mess of wires. Let’s fix that.

I designed a two-part enclosure:

  1. Servo cradle: Holds the servo snugly, with a slot for the horn to poke through.
  2. Pi box: Slips onto the back, with cutouts for USB, HDMI, and the GPIO ribbon cable.

Print both in black PETG. Add a small vent slot over the Pi’s CPU. Mount the whole thing to the window frame with 3M Command strips—no drilling required.


Troubleshooting: The Top 5 Micro Servo Failures (And Fixes)

1. Servo Jitters or Buzzing Non-Stop

Cause: You left the duty cycle at a non-zero value. The servo is constantly hunting for position.

Fix: Always send ChangeDutyCycle(0) after the servo reaches its target.

2. Servo Moves But Blind Wand Doesn’t

Cause: The coupler is slipping. Either the inner diameter is too large, or the set screw isn’t tight.

Fix: Add a layer of electrical tape around the wand before inserting it into the coupler. Or reprint with a 0.2mm smaller inner diameter.

3. Servo Gets Hot

Cause: You’re stalling the servo against the blind’s mechanical stop. The blind is fully closed, but the servo keeps pushing.

Fix: Calibrate your ANGLE_CLOSED value to be 5° less than the mechanical stop. Use the smooth_move() function and add a current-sense resistor to detect stalls (advanced).

4. Pi Reboots When Servo Moves

Cause: Power brown-out. The servo is pulling too much current from the Pi’s 5V rail.

Fix: Use a separate power supply for the servo. Check your ground connections.

5. Servo Drifts Over Time

Cause: The internal potentiometer is worn out, or the PWM signal is noisy.

Fix: Replace the servo (they’re $2). Add a 100nF capacitor between the signal line and ground to filter noise.


Scaling Up: 8 Blinds, 8 Servos, One Pi

You don’t need eight Pis. With a PCA9685 breakout board, you can control up to 16 servos from a single Pi’s I2C bus. Just daisy-chain the PCA9685 boards if you need more.

The only limit is power. Each servo needs ~250mA at stall. Eight servos = 2A peak. Use a 5V 5A supply and a distribution board with individual fuse holders.

For the code, create a dictionary of blinds:

python blinds = { "living_room": {"channel": 0, "open": 10, "close": 88}, "bedroom": {"channel": 1, "open": 15, "close": 92}, "kitchen": {"channel": 2, "open": 8, "close": 85}, }

Then loop through each blind when a command arrives. The servo control is identical—only the channel number changes.


Final Thoughts on the Micro Servo Motor Approach

The micro servo motor is the perfect actuator for this project because it’s:

  • Cheap: $2–$5 each, so even if you fry one, it’s no big deal.
  • Precise: 0.1° resolution is overkill for blinds, but it means you can fine-tune light levels.
  • Self-contained: No external drivers, no encoders, no limit switches. Just PWM in, position out.

The Raspberry Pi adds the “smart” layer—sensors, networking, and logic—without breaking the bank. Together, they turn a dumb stick-and-string blind into a responsive, automated light management system that actually respects your circadian rhythm.

If you build this, you’ll never go back to cloud-dependent commercial “smart” blinds again. And when a friend asks, “How did you get your blinds to do that?” you get to smile and say, “Oh, just a little micro servo motor and a $15 computer.”

Happy building. And don’t forget to calibrate that angle range.

Copyright Statement:

Author: Micro Servo Motor

Link: https://microservomotor.com/micro-servo-motor-with-raspberry-pi/automated-blinds-servo-raspberry-pi.htm

Source: Micro Servo Motor

The copyright of this article belongs to the author. Reproduction is not allowed without permission.

About Us

Lucas Bennett avatar
Lucas Bennett
Welcome to my blog!

Tags