First attempt - running stepper motor with two limit switches

Hello all,

I am very much over my head here. This is my first project that isn't guided by a tutorial and can't seem to get it right. I could be on track for something or I could be way off base.

Attempting to run:
Arduino Uno
A4988 motor driver (only one)
12v 2A nema 17
2x limit switches
1x initiation switch (one key 2pin membrane)

The project is moving an arm to open and close. When the initiation switch is pressed, the stepper needs to continuously run one direction (i.e. clockwise) until it runs into one limit switch and stop. When the initiation switch is pressed again, the stepper needs to continuously run the opposite direction (i,e, counterclockwise) until it runs into the other limit switch and stop.

Through my searches, I have come up with this sketch that I thought would work:

const int dirPin = 2;  //to driver
const int stepPin = 3; //to driver
const int enablePin = 4; //to driver

const int motionSwitch = 10; //to expand/retract button
const int expandSwitch = 11;  //to opened switch
const int retractSwitch = 12; //to closed switch

const int STEPS_PER_REV = 200;

int motionSwitchState = 0;
int expandSwitchState = 0;
int retractSwtichState = 0;

int motorEnabled = 0;
int motorSpeed = 0;
int motorDirection = 1;

void setup() {
  pinMode(dirPin, OUTPUT);
  pinMode(stepPin, OUTPUT);
  pinMode(enablePin, OUTPUT);
  pinMode(expandSwitch, INPUT);  //signal motor stop
  pinMode(retractSwitch, INPUT); //signal motor stop
  pinMode(motionSwitch, INPUT);  //signal motor start w/ opposite direction

  digitalWrite(dirPin, LOW);
  digitalWrite(stepPin, LOW);
  digitalWrite(enablePin, LOW);
}

void loop() {
  motionSwitchState = digitalRead(motionSwitch); //signals the action

  if (motionSwitchState == HIGH){ //reads limit switch states to decide which direction to turn
    digitalRead(expandSwitch);
    digitalRead(retractSwitch);
    if(expandSwitch == 1){        //turn clockwise if expand limit switch is pressed
      digitalWrite(dirPin, HIGH);
      digitalWrite(stepPin, HIGH);
    }

    if(retractSwitch == 1){       //turns counterclockwise if retract limit switch is pressed
      digitalWrite(dirPin, LOW);
      digitalWrite(stepPin, HIGH);
    }
  }
  else{                           //if initiation switch is not pressed, take no action
    digitalWrite(dirPin, LOW);
    digitalWrite(stepPin, LOW);
  }

}

Fritzing attempt (again, I am new to all of this):

project1.pdf (777 KB)

You should enable the internal pullups on the switch pins (pinMode(pin,INPUT_PULLUP)). The switch inputs will read LOW when the switch is closed (pressed).

If that does not help, describe what the code actually does and how that diffes from what you want.

One problem in your logic is: you don't save the value of the two limit switches so you can refer to the status next time through the loop. If you move the arm, the limit switch will no longer be pressed and the program will not know which way to continue moving. Then when you reach the second limit switch, you must forget the value of the previous limit switch.

Paul

This code

digitalRead(expandSwitch);
    digitalRead(retractSwitch);
    if(expandSwitch == 1){

is mixing up the pin number with the state of the pin. I suggest you re-write it like this

expandSwitchState = digitalRead(expandSwitchPin);
retractSwitchState = digitalRead(retractSwitchPin);
    if(expandSwitchState == 1){

Using meaningful variable names reduces the likelihood of that sort of mistake

...R

Thank you all for the help. I am doing more research on Pull up resistors so that I can apply that to my sketch.

Robin2, that does make a lot of sense.

Sounds like a job for a state machine.

I am getting - what I think are - decent readings from the serial port; 1's when each switch is pressed and 0's when the switches are released. I am still not getting any movement from the motor.

groundFungus:
You should enable the internal pullups on the switch pins (pinMode(pin,INPUT_PULLUP)). The switch inputs will read LOW when the switch is closed (pressed).

If that does not help, describe what the code actually does and how that diffes from what you want.

Maybe this is just a cop out but, being that I am so new to this, I don't know how the difference from my code errors and what I want it to do. From my uneducated mind, the sketch should be working.

Paul_KD7HB:
One problem in your logic is: you don't save the value of the two limit switches so you can refer to the status next time through the loop. If you move the arm, the limit switch will no longer be pressed and the program will not know which way to continue moving. Then when you reach the second limit switch, you must forget the value of the previous limit switch.

Paul

Would this be fixed by the "previousinitiateSwitchState = initiateSwitchState;" statement? I don't mind doing the searching but I just don't know where to start.

Doing a little searching on the 'State Machine' seems even more complex than standard sketches. What is the difference between a sketch and a state machine?

Did I apply the recommendations correctly and any ideas on what could be holding up the motor?

const int dirPin = 2;  //to driver
const int stepPin = 3; //to driver
const int enablePin = 4; //to driver

const int initiateSwitch = 10; //to expand/retract button
const int expandSwitch = 11;  //to opened switch
const int retractSwitch = 12; //to closed switch

const int STEPS_PER_REV = 200;

int initiateSwitchState = 0;
int previousinitiateSwitchState = 0;
int expandSwitchState = 0;
int previousexpandSwitchState = 0;
int retractSwitchState = 0;
int previousretractSwitchState = 0;

int motorEnabled = 0;
int motorSpeed = 0;
int motorDirection = 1;

void setup() {
  pinMode(dirPin, OUTPUT);
  pinMode(stepPin, OUTPUT);
  pinMode(enablePin, OUTPUT);
  pinMode(initiateSwitch, INPUT);  //signal motor start w/ opposite direction
  pinMode(expandSwitch, INPUT_PULLUP);  //signal motor stop
  pinMode(retractSwitch, INPUT_PULLUP); //signal motor stop

  digitalWrite(dirPin, LOW);
  digitalWrite(stepPin, LOW);
  digitalWrite(enablePin, LOW);

  Serial.begin(9600);
}

void loop() {
  initiateSwitchState = digitalRead(initiateSwitch); //signals the action
  delay(1);
  
  if (initiateSwitchState != previousinitiateSwitchState){     //reads limit switch states to decide which direction to turn
    if (initiateSwitchState == HIGH){       //should this be "HIGH" or "1"?
      expandSwitchState = digitalRead(expandSwitch);
      retractSwitchState = digitalRead(retractSwitch);
      if(expandSwitchState == HIGH){    //turn clockwise if expand limit switch is pressed
        digitalWrite(dirPin, HIGH);
        digitalWrite(stepPin, HIGH);
        digitalWrite(enablePin, HIGH);
      }

      if(retractSwitch == HIGH){         //turns counterclockwise if retract limit switch is pressed
        digitalWrite(dirPin, LOW);
        digitalWrite(stepPin, HIGH);
        digitalWrite(enablePin, HIGH);
      }
    
    }
    else{                             //if initiation switch is not pressed, take no action
      digitalWrite(dirPin, LOW);
      digitalWrite(stepPin, LOW);
      digitalWrite(enablePin, LOW);
    }
  Serial.print("initiateSwitchState: ");
  Serial.println(initiateSwitchState);
  Serial.print("expandSwitchState: ");
  Serial.println(expandSwitchState);
  Serial.print("retractSwitchState: ");
  Serial.println(retractSwitchState);
  }
  delay(50);
  previousinitiateSwitchState = initiateSwitchState;
}

In my searches I also found this sketch which may or may not be finished. Would this be a better base to start from or am I on the right track?

const int ForwardLimitSwitchPin = 2;
const int ReverseLimitSwitchPin = 3;
const int StepperStepPin = 4;
const int StepperDirectionPin = 5;
const int LimitSwitchActivated = LOW;  // Limit switch grounds pin
const int StepperMaxRPM = 100;

Stepper stepper(200, StepperStepPin, StepperDirectionPin);

void setup() {
   pinMode(ForwardLimitSwitchPin, INPUT_PULLUP);
   pinMode(ReverseLimitSwitchPin, INPUT_PULLUP);
   stepper.setSpeed(StepperMaxRPM);
}

void loop() {
   // Step forward until the limit switch is activated
   while (digitalRead(ForwardLimitSwitchPin) != LimitSwitchActivated) {
       stepper.step(1);
   }
   // Step reverse until the limit switch is activated
   while (digitalRead(ReverseLimitSwitchPin) != LimitSwitchActivated) {
       stepper.step(-1);
   }
}

Some descriptions of State Machine can make it seem complex. It is really nothing more than a variable that keeps track of the state of a system. In your case it might be implemented with a char variable called motorState that can have the values 'W' (waiting or idle), 'R' moving Right and 'L' (moving Left)

There could be a start switch that changes it from W to (say) R and when it hits the right limit switch it changes to L.

Then the code for the motor does nothing if it is W and moves in the appropriate direction if it is R or L

If you wish your code to look a little more sophisticated and more obvious at a quick read you can use an ENUM to hold a list of the states - I will leave it to you to research that. The project functionality will be identical.

...R

Hi,
Welcome to the forum.
OPs fritzy.

Tom... :slight_smile:

Hi,
Before you start with limit switches and such, have you written code just to make sure your circuit will run the stepper?

Don't try and write code in one big attempt, develop it in stages and get each stage working before attempting the next stage.
It may sound a longer way to program, but it will save you so much time in debugging.

Thanks.. Tom... :slight_smile:

TomGeorge:
Hi,
Before you start with limit switches and such, have you written code just to make sure your circuit will run the stepper?

Don't try and write code in one big attempt, develop it in stages and get each stage working before attempting the next stage.
It may sound a longer way to program, but it will save you so much time in debugging.

Thanks.. Tom... :slight_smile:

Hi Tom, following your advice I took out the limit switches and got motor running as I would like it. I can push the initiate button and have the motor run one direction for a specified duration and speed. Once it stops, I can push the initiate button again and the motor will run the opposite direction for a specified duration.

Honestly, for this project I could just modify the number of rotations to move the appropriate amount but I will keep playing with the code to accommodate the limit switches.

I am not giving up on researching State Machines but just wanted to find out if I could get this working before changing gears.

const int dirPin = 2;  //to driver
const int stepPin = 3; //to driver
const int enablePin = 4; //to driver

const int initiateSwitch = 10; //to expand/retract button

const int STEPS_PER_REV = 200;

int initiateSwitchState = 0;
int previousinitiateSwitchState = 0;
int dirPinState = HIGH;
int previousdirPinState = LOW;

void setup() {
  pinMode(dirPin, OUTPUT);
  pinMode(stepPin, OUTPUT);
  pinMode(enablePin, OUTPUT);
  pinMode(initiateSwitch, INPUT);  //signal motor start w/ opposite direction

  Serial.begin(9600);
}

void loop() {
  initiateSwitchState = digitalRead(initiateSwitch); //signals the action
  delay(1);

  
  if (initiateSwitchState != previousinitiateSwitchState){     //reads limit switch states to decide which direction to turn
    if (initiateSwitchState == HIGH){       //should this be "HIGH" or "1"?

      if (dirPinState != previousdirPinState){
         digitalWrite(dirPin, HIGH);
         previousdirPinState = digitalRead(dirPin);
         for(int x = 0; x < STEPS_PER_REV; x++){ //STEPS_PER_REV*2 = 2 revolutions
         digitalWrite(stepPin, HIGH);
         delayMicroseconds(1000); //1000 = fast turn
         digitalWrite(stepPin, LOW);
         delayMicroseconds(1000); //1000 = fast turn
       }
       
      }
      else {
        digitalWrite(dirPin, LOW);
         previousdirPinState = digitalRead(dirPin);
         for(int x = 0; x < STEPS_PER_REV; x++){
         digitalWrite(stepPin, HIGH);
         delayMicroseconds(2000);  //2000 = slow turn
         digitalWrite(stepPin, LOW);
         delayMicroseconds(2000);  //2000 = slow turn
       }
     }
  }
    
  
  Serial.print("initiateSwitchState: ");
  Serial.println(initiateSwitchState);
  Serial.print("Direction: ");
  Serial.println(previousdirPinState);
  }
  delay(50);
  previousinitiateSwitchState = initiateSwitchState;
 
}

Would anybody be willing to explain how to use the 'enable pin' on the A4988? I have the 'sleep' and 'reset' pins bridged together but I can physically feel the stepper jittering when it is not in motion. I'd love to be able to turn the A4988 off when the motor is not being used to save power.

Also, would anybody be willing to explain the aspects of this section of code?
What does 'x' represent in the 'for(...)' code? Why does 'x' need to be less than 'STEPS_PER_REV' (200)? What does 'x++' mean?
Why is there a 'digitalWrite(stepPin, HIGH)' AND a 'digitalWrite(stepPIN, LOW)? If I am correct, these values ca not be interrupted by anything, such as a limit switch?

for(int x = 0; x < STEPS_PER_REV; x++){
         digitalWrite(stepPin, HIGH);
         delayMicroseconds(2000);  //2000 = slow turn
         digitalWrite(stepPin, LOW);
         delayMicroseconds(2000);  //2000 = slow turn
}

Thank you for all of your help!

More:

From A4988 datasheet, page 10:

Enable Input . This input turns on or off all of the
FET outputs. When set to a logic high, the outputs are disabled.
When set to a logic low, the internal control enables the outputs
as required. The translator inputs STEP, DIR, and MSx, as well as
the internal sequencing logic, all remain active, independent of the
ENABLE input state.

https://www.allegromicro.com/~/media/Files/Datasheets/A4988-Datasheet.ashx?la=en&hash=B0052E915715F54E7338E1274BC43698E610D8A9