My rollerblind runs Linux

The British climate. You’re thinking rain, right?

Thanks to the Gulf Stream, it’s less-obvious just how ridiculously far north we are.  London, at 51°30′N, is the same latitude as the southern part of Hudson Bay (James Bay). That’s 2° above Vancouver, 7° above Montreal and a full 12° north of New York City. Edinburgh shares the 55th parallel with Moscow.

The Gulf Stream: keeps us rainy and warm(ish)

Our high latitude means we have a lot of seasonal variation in our daylight hours. Around summer solstice the sun rises at 04:40. In January the lazy git doesn’t get out of bed until after 08:00.

Our ancestors lived in harmony with the seasons. We think we’re smarter than them because we have electric lights. Our body-clocks beg to differ – nothing beats sunlight at the right times.

Fear not, this hasn’t turned into a wellness blog; nonetheless, we computer folk are the worst offenders against diurnal rhythms. We medicate with caffeine. Some “treat” Seasonal Affective Disorder with wine (northern Europe) or vodka (very northern Europe). Gwyneth Paltrow can probably sell you something obscene and expensive made of jade.

I’ll use a Raspberry Pi Zero.

The goal

I want the rollerblind in my bedroom to slowly rise half an hour before my alarm goes off, letting in natural sunlight to gently rouse me. I want it to lower again at local sunset.

Optional: connect to my rooftop media server and play Also sprach Zarathustra as the blind rises in the morning (until the neighbours complain).

Stepper motors

Stepper motors are great. Not only do they move stuff, they move it to very precise positions. There’s a good explanation of how they work here so I won’t repeat it.

The 28BYJ-48, as featured in that video, seems to be the model of choice for hobbyists doing stuff with Arduinos and Raspberry Pis. It normally comes paired with a ULN2003 Darlington Array that amplifies the control signals (such as the 3.3V Raspberry Pi GPIO) to something meaty enough to turn the motor.

5V stepper motors are not designed for moving heavy loads like rollerblinds, but I reckon I can get away with it by gearing it down and turning the motor very slowly. I’ll use dual-phase stepping for maximum torque, and power the Darlington Array directly from a USB lead instead of drawing too much current from the GPIO port 5V pins.


I have another new toy: a 3D printer. Specifically, a Prusa i3 Mk3S + MMU2S built into a Lack cabinet with a few of my own modifications (soundproofing, storage drawers, cabinet temperature monitoring, IP camera, etc). I call it Hephaestus, for obvious reasons. I’ll blog about it when I get the time.

After experimenting with several unsatisfactory CAD packages, I discovered that Autodesk Fusion 360 is free for personal use. Don’t bother with anything else – it’s excellent. I designed this case, gears and drive train in half a day. The motor is geared down 2:1.

I made a separate case for the electronics and some feet to lift that box over the sash mechanism in my windowframe.

STL files for all the components are here. I’ve designed them to print without supports, although the rollerblind shaft is best printed vertically with base supports.

The motor module is slim enough to replace the beaded string winder. The drive shaft sits on a pair of skateboard bearings.

A gearbox Ricardo would be proud of

I’ve printed the case in white biodegradable PLA to blend with the white windowframe, and the drivechain in black PETG for its better mechanical properties. I chopped the end off a micro-USB Y-splitter and soldered some jumper connectors to it to power both the Darlington array and the Raspberry Pi from the same PSU without drawing too much current through the GPIO port. The cases use M3 screws and the PCBs are held down by M2.5 screws.

The bits look like this:

I know where I can shove that…


This is an extensible networked capability, so an Arduino won’t cut it. I’ll be running a network stack, a webserver, some simple scripts, performing some ephemeris calculations and ultimately making a blind go up and down. A Raspberry Pi Zero W is perfect. You could install a real-time clock (RTC) module to keep the time, but since it’s networked I’ve just used an NTP client.

All the cool kids are using Python3 nowadays. Yeah, I hear you greybeards. I’m still a fan of curly braces and semicolons too. I still haven’t learnt to love systemd. Python3 just makes everything so easy, though!

Here’s a chunk from a nasty hacky Python3 script to move a stepper motor (it’s just a chunk so it’s missing declarations). In production you’re obviously better off using a stepper motor library, but this illustrates how the motor is trivially controlled via the GPIO pins with single-phase stepping.

# Clean up GPIO

# Use BCM GPIO references instead of physical pin numbers

# Define Step Pins
stepPins = [10,9,11,25]

# Set all Step pins as OUTPUTS
for pin in stepPins:
  GPIO.output(pin, False)

# Initialise Step Counter
stepCounter = 0

# Start main loop
  distance = 0
  for _ in range(magnitude):
   for pin in range(0, 4):
      xpin = stepPins[pin]
      if seq[stepCounter][pin]!=0:
        GPIO.output(xpin, True)
        GPIO.output(xpin, False)
    stepCounter += 1

    # If we reach the end of the sequence then start again
    if (stepCounter==stepCount):
      stepCounter = 0

  # Wait before moving on


# Clean up and set pins to low again
  for pin in stepPins:
    GPIO.output(pin, False)

The four chosen GPIO ports (BCM 10,9,11,25) correspond to the four pins on the amplifier board that control the magnets within the motor. Using dual-phase stepping (below) each step activates pairs of these magnet sets (for greater torque), and stepping through these configurations causes the motor to turn one step at a time. You could save some code by looping through the array backwards to reverse the direction, but I’ve kept the option of changing the stepping to meet the different speed and torque requirements for up and down.

# Set up Dual Phase (Full) Stepping for maximum torque
stepCount = 4
seq = [0,1,2,3]

# Blind direction: up
if (direction == "up"):
   seq[0] = [0,0,1,1]
   seq[1] = [1,0,0,1]
   seq[2] = [1,1,0,0]
   seq[3] = [0,1,1,0]

# Blind direction: down (default)
   seq[0] = [0,1,1,0]
   seq[1] = [1,1,0,0]
   seq[2] = [1,0,0,1]
   seq[3] = [0,0,1,1]

I haven’t calibrated magnitude and speed, but a magnitude of 35000 works well to move the blind from the top of my window to the bottom. Too great a speed risks stalling the motor, I use 200 down and 100 up. The slow speed in the morning helps wake me up gently.

For now I’ve just set up a simple cronjob to raise the blind at 06:45 and lower it at 21:00:

#m h  dom mon dow       command

45 6    * * *           /home/pi/ -m 35000 -d up -s 100

00 21   * * *           /home/pi/ -m 35000 -d down -s 200

That’ll do for one day. Now I’m going to bed. Oh look, I don’t have to close the blind!

There’s not much to see out the window anyway. It’s either raining or pixellated.

Next steps…

A trivial system is stateless. When I tell the blind to go down by 35,000 steps at 21:00 it assumes that the blind is starting in the “up” position. As per the famous dictum about assumptions, there is a real risk that I’ll end up with a pile of rollerblind on the floor. There are two things I can do to improve this:

  1. Store state in the system. If the blind saves its current position to non-volatile storage (such as the SD card) then it will “remember” where it is starting from when instructed to move to a new position. This assumes that the stored position is correct, which might also be wrong. To re-calibrate it, I need to…
  2. Introduce feedback. A sensor that is triggered when the blind reaches a certain position can reset the state to that known baseline. This might also be wrong if, for example, a (literal) bug gets into the sensor. No system is foolproof. It’s a rollerblind, not a nuclear reactor. Chalk this risk up as a “tolerate”.

I wanted the blind to go down at local sunset. To do that I need to get the local ephemeris data to work out the daily time of local sunset. There’s a program called sunwait that makes it easy. This is how you can use it in your crontab:

# Monday to Friday mornings:
# Blind up at 0645 local
45 6    * * 1,2,3,4,5   /home/pi/ -p 50000 -s 100

# Monday to Friday evenings:
# Blind down at 1800 (London) or sunset, whichever is later
00 17   * * 1,2,3,4,5   /home/pi/sunwait/sunwait wait set 51.5N 0.2W; /home/pi/ -p 00000 -s 100

Shockingly, I might even want to make the blind go up and down independent of my morning and evening routine. I connect a pair of push-buttons to the Raspberry Pi’s GPIO port to give me local control but, as is often the case these days, the lazy option is to install a web front-end. If I’m feeling keen I’ll link it to my openHAB system for more-holistic automation of my increasingly-automated residence.

I might even go back and write the Python script properly…

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *