Code for rolling shade using Uno and Stepper

I've reviewed lots of code for roller shade control using an Uno board and a stepper motor, but I haven't found what i need. I'm trying to control a large outdoor shade with an IR remote. I want to have three functions. One button to return the shade to its highest point, one button to unravel the shade to its lowest point, and one button to stop the shade at any point in between. The trouble I'm having is figuring out how to get the shade to know where it is when it stops in relation to the top or bottom. Can anyone help me with this code? I'm very new to this.

My setup is an Uno, Nema 23 stepper (with TB6600 driver), and the basic IR remote that came with an Elegoo starter kit. Thanks!

You also need to have some type of switch to tell your program when the shade has reached each end of travel. That is usually referred to as "limit switches".

The stepper needs to start from a known position. That is the function of the limit switch that @ Paul_KD7HB mentions. Run the shade to the top or bottom limit switch. Zero the step count. Then you can count steps to the desired position.

So is the limit switch an actual switch that sends an input to the Uno, or is it a line of code?

It is an actual switch. Can be a mechanical switch, optical switch or a hall effect switch (use with a magnet).

Is there no way to set the code to read how many steps the motor took from an initial point. Then when I press "down", the motor would be programmed to stop at X-steps which would be the bottom of the blind. When pressing up, it would count that many steps in the negative direction to reach the top. If it was stopped in between, it would write the number of steps it took until being stopped. Then when up or down is pressed it would calculate how many steps it would need to achieve the particular direction.

For example, if the shade is completely rolled up at 0. It takes 10 steps to reach bottom. Then press down would initiate 10 steps and initiate a count of the steps. If stopped before reaching 10 steps, say at 5 steps, it would write "5" as stoppoint. Then if up is pressed, it would read how many steps are written and subtract 10, giving it -5. Then initiate -5 steps to go back up to 0 point. If you stopped before getting to 0, it would write how many steps in reverse it took (lets say -3) as a whole number and subtract that from the stoppoint 5. Then it would write a new stoppoint of 2. Then loop. Would that not work?

Yes, but it assumes that you always start with the blind rolled up. If that isn't true, without a limit switch to home the system, you may do too many steps, possibly breaking something.

That is exactly how it would work AFTER your code discovers the starting point using the limit switch. The program must first determine the beginning point by stepping one step and testing to see if the switch has been pressed or triggered. Then all the normal step counting can begin.

Gotcha. So the concern is that if the Arduino isn't constantly running, the loop calculations will be broken and not know where to begin?

Not just a concern, but a reality!

1 Like

It is common practice for any machine that uses steppers to perform a homing cycle at start up to establish the reference point (called home). My CNC engraver won't do anything until it is homed. Niether will my 3D printer. Even the dash gauges in my car go through a homing cycle when i go to start it. You see the gauge needles all swing to the extreme ends before settling at the current readings.

True Paul! Thanks everyone. I'll try setting up a hall switch with a magnet on the end of the shade. Then, I'll take my first crack at the code.

Stepper motors develop maximum torque when moving slowly, and often are powerful enough to damage the hardware at the ends of travel, making limit switches absolutely essential. The reason is that the torque is much lower when running at speed, and this has to be big enough to move the load, usually resulting is much more torque than is safe when the physical endstop is hit.

For this reason actuators for something like this usually just use DC motors and current-limiting to control the maximum torque. DC motor torque is independent of speed.

Here I see no reason to need a stepper motor at all. And if you have current-sensing and a DC motor you might be able to detect endstops electrically without limit switches. Some motor drivers have current sensing pins you can use to do this.

ahh Mark good points. But I already have the stepper and no regular DC motor.

But you have not tested the stepper to see if it up to the job. And have you designed the mounting of the stepper motor and power supply?

I've done the calculations and have the mounting drawn up in Fusion360. I'm using a 24v, 2A power supply that should be plenty. I'm not going to actually mount it until I have the code working.

To help avoid trouble with resonances, enable microstepping. 4X or more will help.

1 Like

Ive done this in the past with dead reckoning….
Crude, but it worked in a pinch.

If the blinds take a ‘known’ 10 seconds from fully open to fully closed,

During initialisation, run them for 10.5 secs in one direction, then you know where the blind is.

To go half way, run 5 secs in the required direction, etc.
It does get out of sync over time, but every time you move to fully open or close, you can recalibrate your known position.

If the current position is saved in EEPROM, it does a pretty good job of remembering where it is across power failures. Simply move to one extent if you want to recalibrate your known position.

I used this on two motorised blinds in the same room about five years ago, and they work perfectly.

1 Like

Can you share your code with me?

I'm trying to setup a hall magnet sensor code line to set the stepcount to zero, but I have no idea how to do that. Here is my first shot at the code. Any advice to fix the hall sensor line that isn't working?

#include "Stepper.h"
#include "IRremote.h"

/*----- Variables, Pins -----*/
#define STEPS  32   // Number of steps per revolution of Internal shaft
int  Steps2Take;  // 2048 = 1 Revolution
int receiver = 6; // Signal Pin of IR receiver to Arduino Digital Pin 6
int val ; // define numeric variables
int stepCount = 0; // number of steps the motor has taken
int magnet_detect;

/*-----( Declare objects )-----*/
// Setup of proper sequencing for Motor Driver Pins
// In1, In2, In3, In4 in the sequence 1-3-2-4

Stepper small_stepper(STEPS, 8, 10, 9, 11);
IRrecv irrecv(receiver);    // create instance of 'irrecv'
decode_results results;     // create instance of 'decode_results'

void setup()
  irrecv.enableIRIn(); // Start the receiver
  attachInterrupt(0, magnet_detect, RISING);

void loop()
  if (irrecv.decode(&results)) // have we received an IR signal?

    switch (results.value)


      case 0xFF629D: // UP button pressed
        small_stepper.setSpeed(500); //Max seems to be 700
        Steps2Take  =  -(stepCount);  // Subtract number of steps left to reach top

      case 0xFFA857: // DOWN button pressed
        Steps2Take  =  2048 - (stepCount); // Number of steps to reach bottom

      case 0xFFA858: // STOP button pressed
        Steps2Take  = 0; // Rotate CCW


    irrecv.resume(); // receive the next value

  void magnet_detect() //This function is called whenever a magnet/interrupt is detected by the arduino
    set stepCount = "0";
  /* --end main loop -- */