One button for operation of NEMA 23

I am seeking some help on the coding side of things. The goal is to drive the motor with one switch. Push button once, motor moves one direction for x number of steps and stops. Push same button again and motor moves opposite direction for the same number of steps and stops.

I did a search this topic is close to what I need:

Equipment running:
Nema23 Stepper Motor (2.5A)
TB6600 driver (2.5A)
Arduino UNO R3
Power supply for driver and motor rated for 12V/30A output

I have the stepper motor up and running. It runs forward x number of steps and then reverses.

int PUL=7; //define Pulse pin
int DIR=6; //define Direction pin
int ENA=5; //define Enable Pin
void setup() {
  pinMode (PUL, OUTPUT);
  pinMode (DIR, OUTPUT);
  pinMode (ENA, OUTPUT);

}

void loop() {
  for (int i=0; i<6400; i++)    //Forward 5000 steps
  {
    digitalWrite(DIR,LOW);
    digitalWrite(ENA,HIGH);
    digitalWrite(PUL,HIGH);
    delayMicroseconds(50);
    digitalWrite(PUL,LOW);
    delayMicroseconds(50);
  }
  for (int i=0; i<6400; i++)   //Backward 5000 steps
  {
    digitalWrite(DIR,HIGH);
    digitalWrite(ENA,HIGH);
    digitalWrite(PUL,HIGH);
    delayMicroseconds(50);
    digitalWrite(PUL,LOW);
    delayMicroseconds(50);
  }
}

I added in one momentary switch. The motor now starts on button press, rotates one direction and then back the other direction and stops. This is accomplished using this code:

const int PUL=7; //define Pulse pin PUL-
int DIR=6; //define Direction pin DIR-
int ENA=5; //define Enable Pin ENA-
int ledPin = 13; //LED light
//ENA+, PUL+ and DIR+ are pulled HIGH to 5V
//stepper motor wiring A+=yellow, A-=blue, B+=orange, B-=green
int SW1=10; //define Switch 1
//int SW2=11; //define Switch 2
int SW1State=0;
//int SW2State = 0;

void setup() {
  Serial.begin(9600);
//  stepper.setSpeed(1);
  pinMode (PUL, OUTPUT);
  pinMode (DIR, OUTPUT);
  pinMode (ENA, OUTPUT);
  pinMode (ledPin, OUTPUT);
  pinMode(SW1, INPUT);
 // pinMode(SW2, INPUT);

}
int x=0;
void loop() {
      ++x;
      SW1State = digitalRead(SW1);
  //    SW2State = digitalRead(SW2);

  if (SW1State == HIGH) {
  for (int i=0; i<6400; i++)    //Forward 5000 steps
  {
    digitalWrite(DIR,LOW);
    digitalWrite(ENA,HIGH);
    digitalWrite(PUL,HIGH);
    delayMicroseconds(50);
    digitalWrite(PUL,LOW);
    delayMicroseconds(50);
  }
  } 
  if (x % 2 == 0);
    if (SW1State == HIGH) {
    for (int i=0; i<6400; i++)   //Backward 5000 steps
    {
      digitalWrite(DIR,HIGH);
      digitalWrite(ENA,HIGH);
      digitalWrite(PUL,HIGH);
      delayMicroseconds(50);
      digitalWrite(PUL,LOW);
      delayMicroseconds(50);
    }
  }
}

Goal:

  1. Press button once and motor moves “forward” and stops. (Odd number of button presses)
  2. Press button second time and motor moves “backward” and stops. (Even number of button presses)
  3. Ignore next action until a new button press occurs. For example, if I hold down button it is continually held HIGH and will cycle through #1 and #2 above.

Approach:
I was thinking of using a count function coupled with modulo to meet Goal #1 on odd button presses and Goal #2 on even button presses.

Questions:

  1. Is this the correct approach?
  2. Is there a better approach?
  3. Can you help me augment the code to make this work?

Thanks for your willingness to share your vast knowledge with the community.
Matthew

I would have a variable the keeps track of the state of the system. For example it starts WAITING-FOR-FWD and then a button press changes it to MOVING-FWD. When the forward movement is finished it changes to WAITING-FOR-REV and you can probably figure out the rest yourself.

You probably also need to check that the button is released between presses.

Have a look at this recent Thread with a similar requirement.

...R

This is a completely different way of thinking. I will see what I can work up. If I understand you correctly, I would be looking at the events of MOVINGFWD, etc. and not tying the IF statements to a switch activation.

I'll play and report back.
Matthew

Unfortunately, I am stuck. I am going to see if I can find a local support person to walk this through with me. Thanks all.
Matthew

Hi,

Did you have pull down 10k resistor on the digital input you had the button connected to?

This so the input would be pulled to gnd when you take your finger off the button, otherwise the code will think you have still got the button pressed.

Tom... :slight_smile:

Your proposed scheme would be easier to implement. You detect that a switch has become pressed. If so, you increment the counter, and move the stepper. The direction is defined by whether counter is odd or even.

The issue with your approach, that Robin2 was trying to help you avoid, is that the Arduino can do nothing else while the stepper is stepping. If there is nothing else to do, don't worry about the stepping part blocking.

If there is, then the state change approach Robin2 proposed is the only solution.

Its not really hard to implement. You have 4 known states - waiting to move forward, moving forward, waiting to move backwards, and moving backwards. You know when to transition from waiting to move forward to moving forward or from waiting to move backwards to moving backwards. Those transitions depend on the switch becoming pressed. Which transition happens depends on the current state.

You know when to transition from the moving Xxx to the waiting to move Yyy states - you have completed the required number of steps.

On any given pass through loop(), you are in exactly one state, so a switch statement, with 4 cases is the construct to use.

void loop()
{
   switch(state)
   {
       case 0: // waiting to move forwards
          if(checkForSwitchPress())
          {
             state = 1;
          }
          break;
      case 1: // moving forward
          moveOneStep(+1);
          if(movedEnough)
             state = 2;
          break;
       case 2: // waiting to move backwards
          if(checkForSwitchPress())
          {
             state = 3;
          }
          break;
      case 3: // moving forward
          moveOneStep(-1);
          if(movedEnough)
             state = 2;
          break;
   }
}

The moveOneStep() function moves the stepper one step, if it is time to step again. If the step is the last one needed, it sets movedEnough to true. Otherwise, it sets it to false. The checkForSwitchPress() function should be pretty easy to implement. Return true if the switch changed state, to pressed. Otherwise, return false.

Typically, you'd use an enum to define the state variable, with values like waitingToMoveForwards, movingForwards, waitingToMoveBackwards, and movingBackwards. But, with only 4 cases, some comments that describe the relationship between the values and the meaning are often good enough.

I would strongly suggest learning to use the AccelStepper library - ramped speed is essential with large stepper
motors to avoid miss-stepping and stalling, and AccelStepper makes this easy. It also handles the step pulse
generation for you so long as you call run() method every time through loop() and don't use delay() in your code.

The normal way to achieve this sort of sequenced activity is to implement a state-machine in the code, and
have loop() check for every kind of transition every time through, be it an input from a button or a time-out.

Drawing out the transition diagram for a state machine really helps you clarify what needs to happen and
where all the edge-cases are so you can decide how they should be handled.

Do you know the library Clickbutton.h?

Example of short clicks:
Single click - Toggle LED on/off
Double click - Blink (Toggles LED 2 times/second)
Triple click - Fast blink (Toggles LED 5 times/second)

Long clicks (hold button for one second or longer on last click):
Single-click - Slow blink (Toggles LED every second)
Double-click - Sloow blink (Toggles LED every other second)
Triple-click - Slooow blink (Toggles LED every three seconds)