Function looping: execute function with delay, repeat (AccelStepper)

I have working code that moves a stepper motor a given distance after a button is pressed (using the AccelStepper library). However, I need to make it so that my sketch/function will move the stepper motor a given distance, then pause for x milliseconds, then call/execute the function again. This way the motor will move, pause, and repeat infinitely until it is stopped.

I'm a little confused about how to go about this. I suspect it's a simple implementation of a for loop. The full original code that only moves the motor forward once is below, but here is a snippet with my idea for making the code do what I want.

void loop() {
  buttonsOne(); // when pressed moves stepper reverse
  buttonsTwo(); // when pressed moves stepper forward
}
void buttonsTwo() 
{
  if (digitalRead(LEFT_ONE_PIN) == 0)
    for (int i = 0; i <= 1000; i++)
    {
      stepper1.move(200);
      // I'll add code here to that will trigger my camera/Optocoupler
      delay(2000);
    }
  stepper1.run();
}

A few notes/questions:

I know that void buttonsTwo() is continuously looping. It's hard for me to understand, then, how the Arduino keeps track of actions that occur over time, like my motor moving a certain distance. But, I assume it keeps track of what's going on.

The for loop seems like a good solution because it will loop/repeat. But maybe this is no different than the void buttonsTwo() looping already? Though, I think void buttonsTwo() will stop looping once the button is no longer pressed.

I suspect I could also simply just have a function inside of void buttonsTwo() that simply calls itself?

Lastly, void buttonsTwo() is only executed when (digitalRead(LEFT_ONE_PIN) == 0). Do I need the function that repeatedly moves the motor to be outside of the void buttonsTwo() function so that the motor will keep moving even after the button is no longer pressed?

Full Original Code

#include <AccelStepper.h>

AccelStepper stepper1(AccelStepper::DRIVER, 9, 8);

bool flag = false;

#define  LEFT_PIN  4
#define  STOP_PIN  3
#define  RIGHT_PIN 2
#define  LEFT_ONE_PIN 5
#define  RIGHT_ONE_PIN 6

void setup() {

  pinMode(ledPin, OUTPUT);      // sets the digital pin as output for Optocoupler
  // digitalWrite(ledPin, LOW);

#define  SPEED_PIN 0
#define  MAX_SPEED 500
#define  MIN_SPEED 0.01

  stepper1.setMaxSpeed(10000.0);

  pinMode(LEFT_PIN, INPUT_PULLUP);
  pinMode(STOP_PIN, INPUT_PULLUP);
  pinMode(RIGHT_PIN, INPUT_PULLUP);
  pinMode(RIGHT_ONE_PIN, INPUT_PULLUP);
  pinMode(LEFT_ONE_PIN, INPUT_PULLUP);
}

void loop() {
  buttonsTwo();
}

void buttonsTwo() { // X number of steps forward and reverse
  if (digitalRead(LEFT_ONE_PIN) == 0)
  {
    stepper1.setMaxSpeed(200.0);
    stepper1.setAcceleration(500.0);
    stepper1.move(200);
  }
  stepper1.run();
}

Thanks!

forestkelley:
I suspect it's a simple implementation of a for loop.

No. loop() loops. Let it do its job.

forestkelley:
I know that void buttonsTwo() is continuously looping. It's hard for me to understand, then, how the Arduino keeps track of actions that occur over time, like my motor moving a certain distance. But, I assume it keeps track of what's going on.

The bare Arduino doesn't keep track of anything. The program (sketch) which you write does that.

This kind of "remembering where you're up to" is often called a "state machine". The state is the memory of what part of the process you are currently in. By looking at where you are, you know where to go next.

I'm a little confused by the initial description which says move-pause-move-pause "forever" but then you have two or more buttons. The buttons are Start and Stop? Something else? If the only thing you had to do was move-pause forever, then just use .runToPosition() and delay().

The best introduction to state machines I know of is here: http://www.thebox.myzen.co.uk/Tutorial/State_Machine.html

@MorganS

Thanks for the state machine info. That is helpful!

To clarify the functionality, I have a stepper motor that is rotating a circular platform with an object that will be photographed as the object rotates 360 degrees. I need the stepper motor to rotate a given amount of steps, then stop long enough for a photograph to be taken, and then I need this process to repeat until I decide to stop it. That way the camera can take photographs of a rotating object, but each time a photo is taken, the motor stops while the exposure is happening.

That's right, I'll have 5 total buttons: Stop, Forward/Reverse one increment/distance, and Forward/Reverse infinitely in increments with one press.

I've been researching .runToPosition(). I may misunderstand how it works, but won't it go to a specific fixed position on a 360 circle (which is not what I want) instead of simply moving forward x steps?

Here is an updated approach. What do you think?

#include <AccelStepper.h>

AccelStepper stepper1(AccelStepper::DRIVER, 9, 8);

int ledPin = 10;                // Optocoupler (light sensitive) pin

bool flag = false;

#define  LEFT_PIN  4
#define  STOP_PIN  3
#define  RIGHT_PIN 2
#define  LEFT_ONE_PIN 5
#define  RIGHT_ONE_PIN 6

void setup() {

  pinMode(ledPin, OUTPUT);      // sets the digital pin as output for Optocoupler
  // digitalWrite(ledPin, LOW);

#define  SPEED_PIN 0
#define  MAX_SPEED 500
#define  MIN_SPEED 0.01

  pinMode(LEFT_PIN, INPUT_PULLUP);
  pinMode(STOP_PIN, INPUT_PULLUP);
  pinMode(RIGHT_PIN, INPUT_PULLUP);
  pinMode(RIGHT_ONE_PIN, INPUT_PULLUP);
  pinMode(LEFT_ONE_PIN, INPUT_PULLUP);

  // these were in the loop. but research suggests it should go in setup. that way it doesn't update every loop.
  stepper1.setMaxSpeed(200.0);
  stepper1.setAcceleration(500.0);
}

void loop() {
  buttonFF(); // keeps calling buttonFF waiting for button press
}

void buttonFF() // when button is pressed, it starts stepperForwardForever();
{
  if (digitalRead(LEFT_ONE_PIN) == 0)
  {
    void stepperForwardForever() 
    {
      stepper1.move(200);
      stepper1.run(); // this may need to live outside this function?
      // I'll add code to press camera shutter
      delay(2000); // wait while photo is being taken
      stepperForwardForever(); // recursive so that the motor will move-stop-move-stop infinitelly
    }
  }
}

Does that even compile? It looks like you have a function definition inside another function.

Let me give you an example: you are in an American city, standing at the intersection with 4th street. I need you to move by 2 blocks "up" and wait there. Which street do you go to?

AccelStepper doesn't know anything about circles. If you want to reset the step counter back to zero when you get to 360 you can, but it is not usually necessary.

...and you are calling a function from inside the same function. Don't do that. Let loop() loop.

forestkelley:
Here is an updated approach. What do you think?

Without in any way meaning to be unkind I think you need a completely different approach.

Start at the back :slight_smile: The stepper.run() function should be called in loop() - perhaps put it as the last thing in loop() - and ensure that loop() repeats very frequently and calls stepper.run() all the time the program is running. There should be no delay()s anywhere in your program.

Then the trick is to give the Accelstepper library the approporiate data to move from time to time using stepper,move().

You can check when a move is complete using stepper.distanceToGo() which will return 0 when the motor gets to its destination.

If you need to manage timing use millis() to manage timing without blocking as illustated in Several Things at a Time. And see Using millis() for timing. A beginners guide if you need more explanation.

Now embrace those concepts into a state machine. Maybe the states are simple such as 'U' for waiting-for-user-input; 'M' for move-in-progress and 'T' for waiting-or-time-to-elapse in a char variable. Or you could use an ENUM with more explicit names. Assume (for simplicity) that the state is 'U' and the user signals a move. Then give the appropriate stepper.move() instruction and change the state to 'M'. When the stepper reaches its destination the value of millis() can be saved and the state changes to 'T'. When the time expires either a new destination and a change to state 'M' takes place or the state changes to 'U'

You could, if needed, have a succession of different states - but I can't envisage what they might be based on your description so far.

...R