Automating patio sun shade with stepper motor and limit switches

I am very new to Arduino (and fritzing so please excuse my poor layout). I am trying to automate my patio sun shade, eventually it will incorporate a photocell but I wanted a proof of concept first with buttons. The first and last buttons represent limit switches and the middle button represents a toggle switch (which will eventually become the photocell).

An example of what I am trying to do is; If the upper limit switch is active and I press the toggle button, the stepper runs until it hits the lower limit switch, if the lower limit switch is active and I press the toggle button then the stepper should run the other direction until it hits the upper limit switch.

I have serial prints to show what is happening, if I hold either of the "limit switches" then the serial monitor displays (rather spams) the corresponding print line, however the stepper motor is not moving how I would expect it to. I have to be holding a limit switch and holding the toggle for it to move in a direction, even then it only moves approximately 1 step per second. If I release the limit switch and continue to hold the toggle then the stepper steps one back and one forward for the duration that I depress the switch. The code I have is based off of someone attempting the same thing but with a DC motor, I tried to accommodate it to a stepper motor and am having trouble.

 // constants won't change. They're used here to set pin numbers:
const int uplimitPin = 9;     // pin number for upper limit switch
const int togglebuttonPin = 10;     // pin number for the toggle switch
const int downlimitPin = 11;     // pin number for the lower limit switch
const int dirPin =  2;      // direction pin for stepper
const int stepPin =  3;      // step pin for stepper

// variables will change:
boolean uplimitState = 0;         // variable for reading the status of the upper limit switch
boolean togglebuttonState = 0;         // variable for reading the toggle switch status
boolean downlimitState = 0;   // variable for reading the status of the lower limit switch


int shadePosition = 2;  // Tells progam what position the shade is in


/**
 *  setup inputs for switches and outs for motor pins
 *  serial begin to read the switches to test for errors
 */
void setup() {
    // initialize the stepper pins as outputs:
    pinMode(dirPin, OUTPUT);
    pinMode(stepPin, OUTPUT);
    // initialize the pushbutton pins as an inputs:
    pinMode(uplimitPin, INPUT);
    pinMode(togglebuttonPin, INPUT);
    pinMode(downlimitPin, INPUT);
    Serial.begin(9600);
}

/**
 * this function turns motor foward 
 */
void shadeDown() {
     // turn motor foward:
    digitalWrite(dirPin, HIGH);
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(1000);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(1000);
}

/**
 * this function turns motor backwards
 */
void shadeUp() {
     // turn motor in other direction:
    digitalWrite(dirPin, LOW);
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(1000);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(1000);  
}

/**
 * Stop the motor form moving
 */
void stopShade() {
  digitalWrite(stepPin, LOW);

}


/**
 * 
 */
void loop() {
   // read the state of the pushbutton value:
   uplimitState = digitalRead(uplimitPin);
   togglebuttonState = digitalRead(togglebuttonPin);
   downlimitState = digitalRead(downlimitPin);



   // check if the limit is active. If it is, the limitState is HIGH:
   if (uplimitState == HIGH) {
      shadePosition = 2; 
      Serial.println(F("The shade is up")); 
      stopShade();
   }else if (downlimitState == HIGH) {
      shadePosition = 1;
      Serial.println(F("The shade is down"));        
      stopShade();
  } 

  if (togglebuttonState == HIGH) {
    if( shadePosition  == 1 ){
      shadeUp(); 
      delay(100);      
      shadePosition = 2;
      delay(100);  
    }else if (shadePosition == 2 ) {
      delay(100); 
      shadeDown();
      delay(100); 
      shadePosition = 1;       
    }

  }
}
arduino-uno

The shadeUp and shadeDown functions only do a single step at a time. The code in loop changes direction after each of those functions have been called (doing a single step).

1 Like

Hi @balthozar .
see if this sketch meets your need.

RV mineirin

// constants won't change. They're used here to set pin numbers:
const int uplimitPin = 9;     // pin number for upper limit switch
const int togglebuttonPin = 10;     // pin number for the toggle switch
const int downlimitPin = 11;     // pin number for the lower limit switch
const int dirPin =  2;      // direction pin for stepper
const int stepPin =  3;      // step pin for stepper

// variables will change:
boolean uplimitState = 0;         // variable for reading the status of the upper limit switch
boolean togglebuttonState = 0;         // variable for reading the toggle switch status
boolean downlimitState = 0;   // variable for reading the status of the lower limit switch
boolean flagUp  = false;
boolean flagDn  = false;

int shadePosition = 2;  // Tells progam what position the shade is in


/**
    setup inputs for switches and outs for motor pins
    serial begin to read the switches to test for errors
*/
void setup() {
  // initialize the stepper pins as outputs:
  pinMode(dirPin, OUTPUT);
  pinMode(stepPin, OUTPUT);
  // initialize the pushbutton pins as an inputs:
  pinMode(uplimitPin, INPUT);
  pinMode(togglebuttonPin, INPUT);
  pinMode(downlimitPin, INPUT);
  Serial.begin(9600);
}

/**
   this function turns motor foward
*/
void shadeDown() {
  // turn motor foward:
  digitalWrite(dirPin, HIGH);
  digitalWrite(stepPin, HIGH);
  delayMicroseconds(1000);
  digitalWrite(stepPin, LOW);
  delayMicroseconds(1000);
}

/**
   this function turns motor backwards
*/
void shadeUp() {
  // turn motor in other direction:
  digitalWrite(dirPin, LOW);
  digitalWrite(stepPin, HIGH);
  delayMicroseconds(1000);
  digitalWrite(stepPin, LOW);
  delayMicroseconds(1000);
}

/**
   Stop the motor form moving
*/
void stopShade() {
  digitalWrite(stepPin, LOW);
  flagUp = false;
  flagDn = false;
}

/**

*/
void loop() {
  // read the state of the pushbutton value:
  if (digitalRead(togglebuttonPin) == HIGH)
  {
    flagUp = true;
    flagDn = true;
  }

  // check if the limit is active. If it is, the limitState is HIGH:
  if (digitalRead(uplimitPin) == HIGH)
  {
    shadePosition = 2;
    Serial.println(F("The shade is up"));
  }
  else if (digitalRead(downlimitPin) == HIGH)
  {
    shadePosition = 1;
    Serial.println(F("The shade is down"));
  }
  if ( flagUp == true && shadePosition  == 1 )   // Is down
  {
    flagDn = false;
    shadeUp();
    Serial.println(F("The shade up"));
  }
  if (flagDn == true && shadePosition  ==  2 ) // Is Up
  {
    flagUp = false;
    shadeDown();
    Serial.println(F("The shade down"));
  }
}

Thank you!
This makes the buttons work as intended, and I think I understand what is going on in the code with the flags. But now my problem is that I can't change the speed at which the stepper turns, I tried adjusting the delays but it still spins at about 18RPM. I tried diving into the stepper and AccelStepper libraries to no avail, somehow ended up making it spin at one step per second. I think this is way out of my league for a first project...

Hi @balthozar .
The motor speed is controlled by the "delayMicroseconds(1000);"
within the functions shadeDown() and shadeUp().
The largest value that can be used in delayMicroseconds() is 16383.

If you need the motor to be slower than 16383 useg, switch to using delay(), but the values must be written 1000 times smaller, as delay(1) equals delayMicroseconds(1000).

The larger the delay value, the slower the motors speed.

But each engine has its maximum speed limit.

When it reaches this speed it does not turn more and is emitting an audible medium
or high frequency noise.

RV mineirin

Hi @ruilviana

I have tried to adjust the delays, that is how I adjusted the speeds in the simple stepper control tutorial, however that is not working here, no matter what value I change "delayMicroseconds(1000); to, the speed of the motor while running stays the same.

Hi.
Which step driver are you using to control the motor?
RV mineirin

Hello,
The driver is an A4988

Hi @balthozar .
This driver has 3 pins for Microstep Select (MSx).
MS1, MS2 and MS3.
How are they connected?

If all are off each step will be executed complete.

Use the following table to program the movement.
The higher the number of microsteps, the lower the motor torque

MS1 MS2 MS3 MicroStep
L L L Full Step
HL L Half Step
L H L Quarter Step
H H L Eighth Step
H H H Sixteenth Step

  H = HIGH (+5V),  L = LOW (GND or not connected)

RV mineirin

Hello @ruilviana

I left them unconnected for full stepping. I'm probably wrong, but the only difference I can see between the Arduino example code on steppers (where I am able to adjust speed) and the code above(where I can't get the speed to change) is that the example code writes the dirPin outside of the step loop. The code above is writing dirPin inside of the shadeDown() and shadeUp() functions.

Thanks,
Balthozar

Hi .
I'll set up a test here like your project.
Which step motor are you using (model, link).

RV mineirin

Hello,
Thanks for all your help. The motor I am using is this NEMA17

Hi .
NEMA17 is a specification of engine mechanics.
I have a 17hs4401 model here.
Could you tell the model of yours, or the current it drains and the torque?

RV mineirin

Hi,

Sorry, I hyperlinked it. Mine is also a 17HS4401.

The following code works for being able to adjust the speed by adjusting the delayMicroseconds
This was the code I used to make sure I had the stepper and driver hooked up correctly before I tried to use the code with the limit switches.

// Define pin connections & motor's steps per revolution
const int dirPin = 2;
const int stepPin = 3;
const int stepsPerRevolution = 200;

void setup()
{
	// Declare pins as Outputs
	pinMode(stepPin, OUTPUT);
	pinMode(dirPin, OUTPUT);
}
void loop()
{
	// Set motor direction clockwise
	digitalWrite(dirPin, HIGH);

	// Spin motor slowly
	for(int x = 0; x < stepsPerRevolution; x++)
	{
		digitalWrite(stepPin, HIGH);
		delayMicroseconds(2000);
		digitalWrite(stepPin, LOW);
		delayMicroseconds(2000);
	}
	delay(1000); // Wait a second
	
	// Set motor direction counterclockwise
	digitalWrite(dirPin, LOW);

	// Spin motor quickly
	for(int x = 0; x < stepsPerRevolution; x++)
	{
		digitalWrite(stepPin, HIGH);
		delayMicroseconds(1000);
		digitalWrite(stepPin, LOW);
		delayMicroseconds(1000);
	}
	delay(1000); // Wait a second
}

Hi .
I found the problem.
Printouts are affecting motor step time.
Test this new sketch without any messages printed.
Increase the speed by changing the value of variable myTime at the start.
I left it initially with 500 mSec, because with 400 my motor
it vibrated but did not rotate.
If necessary increase this value.
RV mineirin

// constants won't change. They're used here to set pin numbers:
const int uplimitPin = 9;     // pin number for upper limit switch
const int togglebuttonPin = 10;     // pin number for the toggle switch
const int downlimitPin = 11;     // pin number for the lower limit switch
const int dirPin =  2;      // direction pin for stepper
const int stepPin =  3;      // step pin for stepper

// variables will change:
boolean uplimitState = 0;         // variable for reading the status of the upper limit switch
boolean togglebuttonState = 0;         // variable for reading the toggle switch status
boolean downlimitState = 0;   // variable for reading the status of the lower limit switch
boolean flagUp  = false;
boolean flagDn  = false;
unsigned long myTime = 500;

int shadePosition = 2;  // Tells progam what position the shade is in


/**
    setup inputs for switches and outs for motor pins
    serial begin to read the switches to test for errors
*/
void setup() {
  // initialize the stepper pins as outputs:
  pinMode(dirPin, OUTPUT);
  pinMode(stepPin, OUTPUT);
  // initialize the pushbutton pins as an inputs:
  pinMode(uplimitPin, INPUT);
  pinMode(togglebuttonPin, INPUT);
  pinMode(downlimitPin, INPUT);
  Serial.begin(9600);
}

/**
   this function turns motor foward
*/
void shadeDown() {
  // turn motor foward:
  digitalWrite(dirPin, HIGH);
  digitalWrite(stepPin, HIGH);
  delayMicroseconds(myTime);
  digitalWrite(stepPin, LOW);
  delayMicroseconds(myTime);
}

/**
   this function turns motor backwards
*/
void shadeUp() {
  // turn motor in other direction:
  digitalWrite(dirPin, LOW);
  digitalWrite(stepPin, HIGH);
  delayMicroseconds(myTime);
  digitalWrite(stepPin, LOW);
  delayMicroseconds(myTime);
}

/**
   Stop the motor form moving
*/
void stopShade() {
  digitalWrite(stepPin, LOW);
  flagUp = false;
  flagDn = false;
}

/**

*/
void loop() {
  // read the state of the pushbutton value:
  if (digitalRead(togglebuttonPin) == HIGH)
  {
    flagUp = true;
    flagDn = true;
  }

  // check if the limit is active. If it is, the limitState is HIGH:
  if (digitalRead(uplimitPin) == HIGH)
  {
    shadePosition = 2;
    //Serial.println(F("The shade is up"));
  }
  else if (digitalRead(downlimitPin) == HIGH)
  {
    shadePosition = 1;
    //Serial.println(F("The shade is down"));
  }
  if ( flagUp == true && shadePosition  == 1 )   // Is down
  {
    flagDn = false;
    shadeUp();
    //Serial.println(F("The shade up"));
  }
  if (flagDn == true && shadePosition  ==  2 ) // Is Up
  {
    flagUp = false;
    shadeDown();
    //Serial.println(F("The shade down"));
  }
}

Wow, thank you so much. That works like a charm. Can you explain why the printouts were causing such trouble so I can understand?

Hi .
The arduino takes about 14 to 16 milliseconds to print this line "Serial.println(F("The shade up"));".
And at each step of the motor it printed a line of this one.
Note that the time for each step is around 500 to 1000 microseconds, which equates to 0.5 to 1 milliseconds.
So these 14 milliseconds made the motor very slow, as this time had to pass between each step.
I don't know if I was clear in my explanation.
RV mineirin

Yes, that made perfect sense.

Thank you so much for all of your help, it led to a solution and a better understanding of Arduino for me.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.